Introduction

There are problems we need to solve when we start writing a new Win Forms application. First of all, we want our applications to have a neat look, XP-style, or better, XP-theme aware. You'd like to have a nice splash screen taking the user's attention away while the application loads, especially, when it really takes some time for the application to load. Also, many applications should only run a single instance, and should not permit multiple instances simultaneously. There is also an exception handling problem, which is quite tricky, because not every exception is handled, especially while debugging. In release, however, all the exceptions should be caught, at least providing the application with an ability to crash 'gracefully'.

This article is an attempt to provide a template for solving these most typical tasks of an application creation.

Enabling XP themes

A control is considered to be XP-theme aware when its appearance changes in accordance with the XP-theme that is currently active.

Most of the .NET controls are just wrappers over standard Windows controls. In Windows XP, there are two versions of Windows control set - 6.0, which is XP-theme aware and 5.0, which is not. By default, .NET uses version 5.0. In .NET 1.0, the only way to make it use the 6.0 version was to write an application manifest, which is a separate file stored in the application directory. Somehow, this solution was not elegant enough, and in .NET 1.1 a new method - Application.EnableVisualStyles()was introduced. It should be run in the Main() procedure before Application.Run(). Its use, however, is not always successful. E.g. while putting a toolbar on the main form, the toolbar appears without any icons on the tool buttons. To correct this situation, run Application.DoEvents() immediately after Application.EnableVisualStyles(). This combination has proved to be quite reliable:

For some controls to be XP-theme aware you also need to set the FlatStyle property to FlatStyle.System. These controls include ButtonBase inheritors (Button, CheckBox and RadioButton), GroupBox and Label, although for the Label control there is hardly any visual difference.

Ensuring one-instance running

If due to some considerations you don't want your application to have multiple instances running, you can use this simple one instance loader:

The trick is to use one of the kernel named objects, e.g. mutex, to mark the presence of application in the memory. The successive loads of the application should check for this mutex, and run only if it is not found.

You can use GUID to ensure that the mutex name is unique. Also, if your application could be run in terminal services, you should choose your strategy for one-instance application running. If you want your application to be unique in session scope - use the Local\ prefix for the mutex name. If you want it to be unique across sessions - use the Global\ prefix.

To handle unhandled exceptions

Some people recommend catching every exception that you can imagine, thinking that the number of try-catch blocks directly correspond to the robustness of your application. They hunt exceptions like dangerous beasts, considering every exception that escapes unhandled as the worst thing that can ever happen to your application.

Some people like using exceptions as a probe. Like "let's poke it and see if it growls". E.g. let's convert a string to a number and catch the exception if it couldn't.

Both forget one thing that exception is, by its name and nature, exceptional. It is not normal for an application to run into exceptions. Therefore, the presence of too many exception handlers clearly shows the bad design, not robustness. Also, using try-catch to find out things is definitely a bad coding practice. Quite contrary to this, you should do every possible check before making a risky call, to ensure that it would not result in exceptions.

Generally, an exception handling strategy should follow three simple rules:

preventing is better than handling,

handle only those exceptions that you know how to handle and leave the rest alone,

throwing exceptions is not the best way of error reporting, do not overuse it.

And what to do with exceptions that can come out unhandled. On one hand, exceptions should not go away unhandled leading your application to a violent crush, and on the other, there is no sense in handling the exception that you haven't expected, because no one can say if it is safe to continue application execution after it has happened. All we can do is to catch it in a generic way, log it and finalize our application gracefully.

There are three known ways of doing this:

Putting Application.Run() in a try-catch block,

Using Application.ThreadException event,

Using AppDomain.UnhandledException event.

The first is the easiest one, although it is rarely mentioned and least recommended. Its main drawback is that it does not give you the possibility to continue with execution. Otherwise, it works OK, at least I have positive experience with it.

It is important to differentiate the unhandled exception handling for Debug and Release versions. While debugging, it's good to take advantage of the debugger break-in-the-code feature, in the release version you should log all the information about the error to a text file, so that the user could send it to you for analysis. To achieve this, the code should look like this:

The catcher shows an exception handling dialog with 'save to text file' ability:

The Application.ThreadException and AppDomain.UnhandledException events provide you with the ability to handle exceptions in abort-retry-ignore fashion. Unfortunately, I have no experience using them, and can only recommend you some useful articles on the topic:

Showing the splash screen

The main purpose of the splash screen is to inform the user that the program has really started, present useful information about the name, version and copyright, and draw attention while the program loads. The main splash screen requirements are:

start as soon as possible before the main form loads,

remain at the topmost form of the project,

when the project fully loads - hide on first user click anywhere on the project.

To start as soon as possible we need to show the splash form in the very beginning of the main form constructor, before any other component loads:

The Deactivate event is necessary to handle the case of the user clicking anywhere else on the application after it fully loads. The Started counter is used to ensure correct behavior during maximized or normal main form load, because different number of deactivation events occurs in each case. The Splashstatic class is used to simplify splash screen handling and taking most of the necessary code away from the main form.

The result is consistent and pleasant behavior of the splash form. All the complexity of its behavior is taken to the Splash class and the formSplash code. All you need to do in the main form is to call:

Splash.Show(this);

in the first line of the constructor.

Summary

What we have finally acquired is a convenient template of a general purpose Win Forms project, implementing all the basic requirements of an up-to-date application.

This article uses the ideas and concepts described in my previous publications:

Acknowledgements

Jörgen Sigvardsson - for very useful ideas on mutex naming.

History

3rd December, 2005 - Article submitted.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

System.Diagnostics.Debugger.IsAttached works a treat. Another idea is to concatenate the application name (calling ToString on the App GUID will suffice), the exception type's full name, the exception message string, and stack trace into a long string and get the hash code of this to get a unique error code (actually, this'll probably only work on a debug compilation - better still run something like MD5 on the caught exception serialized into a memory stream). This will enable you to feed back fixes for known issues, and build statistics for prioritising bugfixes.

MessageBox says 'debugger' whenever we run the application from Studio, does not matter which configuration it is in - Release or Debug. It says 'no debugger' if we run the release version of application outside of Studio, but it somehow fails to catch the unhandled exception with ExceptionDialog.