Wednesday, July 23, 2008

Save your Visual Studio window layouts

Before I had my nice new laptop I had a normal desktop for work. It had two monitors so when I worked, I spread the windows of Visual Studio to cover both screens and give me maximum working space. If I worked from home I would connect to my work desktop using Remote Desktop and work like that. The problem was that this only gave me one monitor so I had to readjust the window layout for single-monitor usage. If I then would go to the office the next day, Visual Studio would still be in single-monitor window layout from the day before and I would have to drag all the windows around to get my dual-monitor layout back.

Now I have a nice laptop, so whether I work from home or in the office, I use the same machine. But I still have two monitors at work and only one at home. So even though I drag my PC with me everywhere, I still have the window layout problem.

Two or so years ago I discovered the VSWindowManager. This is a plug-in for Visual Studio that allowed you to save up to three profiles of different window layouts. You could then add three buttons to your toolbar to access those profiles. It did exactly what I wanted, but I still had two minor issues with it. First I couldn't rename the buttons, so I had to remember which button mapped to which layout. This wasn't that big of a problem. Secondly, and this was much more annoying, if you clicked the button of the profile you had currently loaded, it would save the window layout into to profile. So if I would come at work after having worked from home and I accidentally clicked the wrong button while trying to restore my dual-monitor layout, I would overwrite that layout with the then current single-monitor layout and I would have layout all the windows by had again.

If you look at the VSWindowManager project, it was last updated in September 2006. Now I do most of my work in Visual Studio 2008 and the plug-in doesn't work on this newer version. Sure, someone downloaded the source and got it to work for VS2008, but it didn't feel good to me if the original author had abandoned the project. So it was back to doing stuff by hand.

Earlier tonight I got fed up with it and decided to find a solution (again). I hoped that maybe someone else had written a similar plug-in. Instead, I found something way better. After googling a little, I came upon this MSDN article titled: Visual Studio 2005 IDE Tips and Tricks. It has a section specially dedicated to saving window layout and it provided a very simple solution to my problem. It uses the possibility to export user settings, together with a small macro to achieve exactly what I need.

Basically it works like this. You start by laying out the windows the way you like it. Then you export your IDE settings using the "Import and Export settings wizard", but in this wizard you can specify exactly what settings you want to export. So you choose only to export your window layout. You save the settings file in a known and accessible location (like your profile or documents folder) and name it so you know what settings these are. Then you create a new macro using the built-in macro editor. This macro will simply import the settings file. And since you only exported your window layout, this is the only setting that will be changed. So I can make a macro for my single-monitor setup and another macro for my dual-monitor setup. I can then add these macros to my toolbar and give them any icon and name I want.

Multi-/single-monitor problem solved!

Saturday, July 12, 2008

Code reduction through generic methods

I've build and now maintain a webservice application at work. The application has around 40 webservice functions and earlier this week I had to dive into the application again because of some issues. It occurred to me how redundant all the code in the webservice functions was. I'm talking about the top-level code, the stuff in the *.asmx.cs files.

In this application the code there deals with input validation, authentication/authorization and sending back the results of the operation (success or error data). So most of the code in those ~40 functions is basically the same. The only thing that differs each time is the specific business logic that the function offers to the world, but that logic is nicely abstracted from the interface part of the application in the business logic part of the application so it's only a few lines of code in each top-level function to setup and call that specific handler. The main code of each function is stuff that each and every function needs to do. All copied and pasted. That can be done better.

I started off by creating a base class from which all webservices should inherit. This base class would provide the functions for input validation (each input object now implements an IValidating interface that is called so the object can basically validate itself) and authentication/authorization. This still left behind a large chunk of code (large chunk being relative here. It was no more then 18  lines of code, but for most functions that was still more then 50% of code in that function). The problem is that each separate webservice function returns a specific return type based on the function itself. A function called "CreateAccount" returns a "CreateAccountResult" object, a function called "SetOptions" returns a "SetOptionsResult" object, etc. So it's not possible to write a helper method, or a method in the base class like I could with validation code or the authentication/authorization code. But then I remembers generic methods.

I don't understand why I've never done these optimizations before. I've used generic methods years before in C++ and I know it was possible in C#. I just never thought about it. So with this new idea I mind I started coding.

We throw our own exceptions in the application. And we make a distinction between two kinds of errors: functional errors and technical errors. The main difference between the two being that function errors are logged as warnings and technical errors are logged as errors. Function errors come from business logic decisions (invalid input, creating an account that already exists, etc) and technical errors come from unexpected problems (SQL server unavailable, External webservice unavailable, etc). So all our own exceptions inherit from one of these two base exceptions. Also all exceptions have an error message and an error code.

The results we return in our webservice functions also inherit from a common base (the BaseResult class) that provides them with at least an error message and an error code (which get filled from the exception). With all that in mind I copied and changed the code that looked like:

1 string errorMessage = "CreateAccount error: " + e.Message; 2 if (e is TechnicalException) 3 { 4 TechnicalException technicalException = (TechnicalException)e; 5 ErrorLog.Error(errorMessage); 6 result = new CreateAccountResult(technicalException.ErrorCode, technicalException.Message); 7 } 8 else if (e is FunctionalException) 9 { 10 FunctionalException functionalException = (FunctionalException)e; 11 ErrorLog.Warn(errorMessage); 12 result = new CreateAccountResult(functionalException.ErrorCode, functionalException.Message); 13 } 14 else 15 { 16 ErrorLog.Error(errorMessage, e); 17 result = new CreateAccountResult(-1, "An unhandled exception occured: " + e.Message); 18 }

Into a function in the base class that looks like:

1 protected T HandleException<T>(Exception e, string functionName) where T : BaseResult, new() 2 { 3 T result = null; 4 string errorMessagePrefix = functionName + " "; 5 6 if(e is TechnicalException) 7 { 8 TechnicalException technicalException = (TechnicalException)e; 9 ErrorLog.Error(errorMessagePrefix + technicalException.Message); 10 result = new T { Code = technicalException.ErrorCode, Message = technicalException.Message }; 11 } 12 else if(e is FunctionalException) 13 { 14 FunctionalException functionalException = (FunctionalException)e; 15 ErrorLog.Warn(errorMessagePrefix + functionalException.LogErrorMessage); 16 result = new T { Code = functionalException.ErrorCode, Message = functionalException.Message }; 17 } 18 else 19 { 20 ErrorLog.Error(errorMessagePrefix + ": " + e.Message, e); 21 result = new T { Code = -1, Message = "An unhandled exception occured: " + e.Message }; 22 } 23 24 return result; 25 }

And replaced all code that is similar to the first code block with this one line:

1 result = HandleException<CreateAccountResult>(e, "CreateAccount");

A significant reduction as you can see. I definitely should have thought about this before.

Tuesday, July 8, 2008

Windows Server 2008 on a laptop

Last week I got a new laptop for work. It's a nice fast Dell Precision M6300. On my old workstation I had Windows Server 2003 R2 running. I'm a developer, so I want to develop on the platform my software will run on. So on my new laptop I wanted to run Windows Server 2008 the Vista Server version of Windows.

Using this very nice blog entry from Vijayshinva Karnure I was able to make Windows Server 2008 look and work almost like Vista. Almost, because I'm missing two things that I have noticed so far. First off is the Windows Sidebar and secondly is Bluetooth support.

Now if you google for these problems, you'll find solutions to both of them. To enable Bluetooth support you'll need to install your drivers as usual and then perform some INF magic to persuade Windows to use them. To enable the sidebar you'll need to copy some files from a Vista installation and copy some registry settings. From what I read this will then work just fine, but I'm not comfortable with it.

You see, these things aren't part of the supported Windows platform, so if there are any security issues, you are on your own. Microsoft won't provide sidebar patches for Windows Server 2008, because they never released a Windows Server 2008 with Windows Sidebar. So you'll have to keep an eye on all security updates and patch the files by hand, or (more likely) let the software run with all its security issues. And that's not something I'm willing to do.

I also discovered, that if you install the Hyper-V because you might want to run some virtual machines for testing, sleep and hibernate will be disabled. Sleep and hibernate are incompatible with Hyper-V so you won't be able to use them. That sucks, so I uninstalled the Hyper-V, but at least I have the option of installing it if I do need it some time in the future. I will just need to learn to live without hibernate then.

And then I just ran into another issue. For remote workers my organization uses Check Point VPN-1 software, but as it turns out they don't have 64-bit versions of their client software. Even though 64-bit versions of Windows have been available for (rough estimate) 3 years now, they never saw fit to make their software work. According to their forums they will release a 64-bit version of some new software in Q4 2008 and this will fall under the existing licenses for the time being. In other words, you'll have to pay extra for 64-bit versions of their client software eventually.

Other then that, I'm pretty happy with my flashy new laptop :-)