P3.NET

Integrating GAC Assemblies with Visual Studio

Originally published: 19 February 2006

Updated: 20 May 2010

The question comes up all the time about how to get documentation and debugging support for GAC assemblies into Visual Studio (VS).  This article will discuss the steps it takes to make this happen.  The steps are straightforward once you understand what goes on behind the scenes.

Visual Studio and the GAC

The first thing that must be understood is the fact that VS does not reference assemblies from the GAC.  If you open the Add References dialog in VS you will notice that none of the assemblies are actually coming from the GAC.  Instead the .NET framework assemblies are pointing to C:WindowsMicrosoft.NETFrameworkvx.y.z while SQL and Office point to different directories.  VS ALWAYS refers to assemblies from regular directory paths.  At runtime the CLR will load GAC assemblies from the GAC but this is independent of VS.

The next thing to understand is that the GAC only supports storing assemblies.  Documentation and debug files, along with configuration or other files, can not go in the GAC.  Therefore if VS did use the GAC it still would not have access to the documentation or debug files of assemblies.  So how do we get VS to recognize these files?

Assembly Folders

All assemblies along with their debug (.pdb) and documentation (.xml) files should be installed into a normal directory.  This is in addition to placing the assemblies in the GAC.  The target directory for these files is known as the assembly folder.  It is this folder that you will point VS to.  When VS loads an assembly it will automatically pick up any .pdb and .xml files in the same directory provided they match the assembly.  If VS loads the .pdb file then debug information is available for the assembly.  If VS loads the .xml file then Intellisense will display documentation about the members of the assembly.  This is automatic.

So, when installing your GAC assemblies, copy the original assemblies, the associated .pdb files and the .xml documentation files to a normal directory.  Then install the assemblies into the GAC.  Now all you need to do is have your users point VS to the appropriate folder and they’ll get debugging and Intellisense support.

Registering Your Assembly Folder

I know what you are thinking.  “But SQL, Office and .NET itself automatically shows up in Add References dialog.  My assemblies do not even though I put them into the GAC.  What is going on?”  Please refer to what I said in the first part of this article: “VS ALWAYS refers to assemblies from regular directory paths.”  The Add References dialog IS NOT populated from the assemblies in the GAC.  VS must be told where the assemblies come from.  Enter the Registry.

When VS wants to get the assemblies for the Add References dialog it will query the Registry for the keys under HKLMSoftwareVisualStudio8.0AssemblyFolders.  Each key under the main key represents an assembly folder.  The actual name of each key is irrelevant but the default value of the key must be the full path to an assembly folder.  VS will add all the assemblies from the assembly folder to the Add References dialog.

Update: Beginning with Visual Studio 2008 the way VS finds assemblies has changed.  The aforementioned registry key still works but a new path is also used (although this has not been formally confirmed).  For VS2008 the following key can also be used: HKLMSoftwareVisualStudio9.0ExpAssemblyFolders .  For VS2010 the path is HKLMSoftwareVisualStudio10.0ExpAssemblyFolders.

Therefore if you want your assemblies to automatically show up in the dialog all you need do during installation is to add a new subkey under the base key, given earlier, and specify the full path to your assembly folder as the default value.  VS will automatically list your assemblies when the dialog is displayed.  Assuming that you followed the steps given earlier in the article the user will also automatically have debugging and documentation support. 

Here is an example of what you might have in the Registry:

HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFoldersAvalonAssemblies => (Default): C:WindowsMicrosoft.NETWindowsv6.0.4030
HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFoldersKraken => (Default): C:Program FilesKrakenv2.1.0

Multi-targeting in Visual Studio

Beginning with VS2008 you can now target different versions of the framework.  This introduces an issue for assembly references.  Some references can only be used with certain versions of the framework.  The existing registry key doesn’t differentiate between the (now) large number of frameworks available so a new set of keys are used.  Under HKLMSoftwareMicrosoft.NETFramework is a new set of directories that VS will also search.  Through some simple (aka not official) testing it turns out that VS will search a bunch of different paths.  Under the key it searches AssemblyFolders.  Additionally it will search for <version>AssemblyFoldersEx under the version of the framework you are targeting.  This is how VS can filter out assemblies that are not valid for a particular framework. 

A word of caution here: if you are running x64 of Windows then some of these keys reside under the x64 tree (HKLMSoftware) while others reside under the x86 tree (HKLMSoftwareWow6432Node).  VS will search both sets.  Also, as an aside, note that VS will check in HKCU as well.

Here is an example of what would be searched if you are targeting the v4 framework in VS2010

 HKLMSoftwareMicrosoftVisualStudio8.0AssemblyFolders
HKLMSoftwareMicrosoftVisualStudio9.0ExpAssemblyFolders
HKLMSoftwareMicrosoftVisualStudio10.0ExpAssemblyFolders
HKLMSoftwareMicrosoft.NETFrameworkAssemblyFolders
HKLMSoftwareMicrosoft.NETFrameworkv4.0.30319AssemblyFoldersEx
HKLMSoftwareMicrosoft.NETFrameworkv3.0AssemblyFoldersEx
HKLMSoftwareMicrosoft.NETFrameworkv2.0.50727AssemblyFoldersEx 

Putting It All Together

So, in summary, to expose your GAC assemblies, debug files and documentation files to VS do the following.

  1. Create a regular directory for your files.
  2. Copy the assemblies, debug files and documentation files to the directory.
  3. Install the assemblies into the GAC.
  4. Create a new subkey in the registry in the appropriate version-specific key (HKLMSoftwareMicrosoft.NETFramework<version>AssemblyFoldersEx).
  5. Set the default value of the key to the directory created in step 1.

Caveats

When I say that VS does not use the GAC please be aware that it might but not in a manner in which we can take advantage of it here.  You will also notice that the core framework is not listed in the Registry.  Whether the framework is hard-coded into VS or whether it uses a different techique I can not say for sure.  Nevertheless it is not extensible for our purposes.

Configurations and Platforms in Visual Studio

(Originally published: 7 June 2008)

Anyone who has worked with Microsoft Visual Studio (VS) knows about configurations but not many people, other than handheld developers, know about platforms.  This article will discuss configurations and platforms as they pertain to Visual Studio.  There seems to be more interest in extending Visual Studio these days so I will provide Visual Studio macros, where possible, to give you a better grip on the discussion.

Before we get into configurations and platforms we need to first clarify the relationship between a solution and its projects.

Solutions

A solution is a collection of projects.  A solution is used to group a set of related projects into one cohesive unit.  Most of VS is solution centric.  Therefore when we talk about building one’s code we are generally talking about building the solution and all the projects within it.  There are exception of course.  Most of the debug functionality is specific to the startup project, for example.  VS only supports one open solution at a time. 

The important part to remember about a solution is that it has few configurable properties beyond the projects it contains.  The few configurable properties a solution does have generally match the same properties in a project.  These properties are initially linked such that the property value for each project matches that of the solution.  This can be changed when we discuss configurations later. 

A solution folder is a virtual folder contained within a solution.  Unlike most project folders it does not actually exist on disk and, hence, will not show up in any source control system (CM).  A solution folder is generally used to hold solution-level files such as make files, documentation files, etc.  Items in a solution folder are not part of any build nor are they deployed with the solution.

Projects

A project is a collection of files.  Most files within a project are source file that get compiled down to a single output file.  The output file is generally an assembly.  These types of projects can be referred to as code projects.  Other project types that do not (necessarily) generate assemblies include reporting projects, unit tests and database projects.

Each project has a project type such as WinForms app, console app, web service, etc.  The project type determines the properties available on each project.  Code projects generally have properties to control compilation and the output files.  Code projects generally have an associated source langauge as well.  All these properties are exposed through the project’s properties.  Some of these properties, such as type and language, are set when the project is created and cannot be changed.  (Note: you can change between WinForm, class library and console apps but this is an exception rather than the norm because it merely changes a linker setting.)

For many projects you can create project folders.  Some project types and languages (such as C#) create physical folders to back the project folders.  If you look at these folders on disk then they should mimic the layout you see in Solution Explorer.  Given that a solution is a collection of projects and a project is a collection of files we can write a macro that will dump all projects, folders and files in the active solution.

Public Sub DisplaySolutionFiles()
    ‘Get the output window 
    Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Files”)

    ‘Enumerate projects of the solution 
    For Each proj As Project In DTE.Solution.Projects
        window.OutputString(String.Format(“Project: {0} Kind: {1}” & vbCrLf, _
                                    proj.Name, proj.Kind))

        ‘Enumerate the files 
        ListProjectItems(proj.ProjectItems, window, 1)
    Next
End Sub

Private Sub ListProjectItems(ByVal items As ProjectItems, _
                       ByVal window As OutputWindowPane, ByVal level As Integer)
    If Not items Is Nothing Then
        For Each item As ProjectItem In items
            If item.Collection Is items Then
                ‘List the item 
                window.OutputString(IndentString(level) & item.Name & vbCrLf)

                ‘List any children 
                If Not (item.ProjectItems Is NothingThen
                    ListProjectItems(item.ProjectItems, window, level + 1)
                End If
            End If
        Next
    End If
End Sub

A few points about the code.  The active project (as determined by Solution Explorer) is accessible via DTE.ActiveSolutionProjects.  To get a list of all the projects in a solution use DTE.Solution.Projects.  You should first consider verifying a solution exists.  If the solution has a solution folder then it will appear as a project even though it is not truly one.

Now that we can get the projects and files inside a solution the next step is to get the properties of a project.  Here is a macro to dump the properties of a project.

Public Sub DisplayProjectProperties()
    Dim window As OutputWindowPane = GetOutputWindowPane(“Project Properties”)

    ‘Enumerate projects 
    For Each proj As Project In DTE.Solution.Projects
        window.OutputString(“Project: “ & proj.Name & vbCrLf)

        ListProperties(proj.Properties, window, 1)
    Next
End Sub

Private Sub ListProperties(ByVal props As Properties, ByVal window As OutputWindowPane, _
                           ByVal level As Integer)
    If Not (props Is NothingThen
        For Each prop As [PropertyIn props
            window.OutputString(IndentString(level) & prop.Name & “: “)
            Try
                window.OutputString(prop.Value)
            Catch ex As Exception
                window.OutputString(“<“ & ex.Message & “>”)
            End Try
            window.OutputString(vbCrLf)
        Next
    End If
End Sub

You should see properties for the target framework version, output file name (but not path) and root namespace information.  Some projects will not have any properties so we have to check for nothing first.  Notice also the try-catch to handle the case where retrieving a property value will throw an exception.  It can happen as some properties don’t apply to all projects.  Also be aware that if a property is not set within a project then it might not appear in the list.  The various build events (which are backed by properties) will only appear as properties if they are set within the project.

Configurations

You might have noticed that the above macro doesn’t seem to display many important properties such as the output path and compilation settings.  That is because build settings (and friends) might change depending upon the type of build you want (debug or release, for example).  This is where configurations come in.  A configuration is a collection of project properties.  For most projects you will have two configurations by default: Debug and Release.  The Debug configuration is used during development.  It sets up the project properties to allow for easier debugging.  The Release configuration is used when you are ready to deploy a project.  This configuration removes most of the debugging information and enables many code optimizations to speed up your code.  You can create your own custom configurations as well.  The following code will display the name of each configuration for a project.

Public Sub DisplayProjectConfigurations()

   ‘Get the output window
   Dim window As OutputWindowPane = GetOutputWindowPane(“Project Configurations”)

   ‘Enumerate projects
   For Each proj As Project In DTE.Solution.Projects
      ListProjectConfigurations(proj, window)
   Next
End Sub

Private Sub ListProjectConfigurations(ByVal proj As Project, _
                                      ByVal window As OutputWindowPane)
   window.OutputString(“Project: “ & proj.Name & vbCrLf)

   ‘ Get the configuration manager
   Dim cm As ConfigurationManager = proj.ConfigurationManager

   If Not (cm Is NothingThen
         ‘Get the active configuration
         Dim active As String = cm.ActiveConfiguration.ConfigurationName

         ‘Enumerate the configuration names
         For Each config As String In cm.ConfigurationRowNames
            window.OutputString(IndentString(1) & configName)

         If active = configName Then
            window.OutputString(” [Active]”)
         End If
         window.OutputString(vbCrLf)

         ‘List configuration properties
      Next
   End If
End Sub

The configurations of a project are accessed via the ConfigurationManager property.  The CMs job is to keep track of the various configurations defined for a project.  The active configuration is accessible via the CM’s ActiveConfiguration property.  Now that we know what configurations are available we can list the properties associated with a specific configuration.

Private Sub ListProjectConfigurations(ByVal proj As Project, ByVal window As OutputWindowPane)
   window.OutputString(“Project: “ & proj.Name & vbCrLf)

   ‘ Get the configuration manager
   Dim cm As ConfigurationManager = proj.ConfigurationManager

   If Not (cm Is NothingThen
      Dim active As String = cm.ActiveConfiguration.ConfigurationName

      For Each configName As String In cm.ConfigurationRowNames
         window.OutputString(IndentString(1) & configName)

         If active = configName Then
            window.OutputString(” [Active]”)
         End If
         window.OutputString(vbCrLf)

         ‘List configuration properties
         Dim configs As Configurations = cm.ConfigurationRow(configName)
         For Each config As Configuration In configs
            ListProperties(config.Properties, window, 2)
         Next
      Next
   End If
End Sub

The only tricky part is that a configuration row is actually a collection of Configuration objects.  Generally it’ll only have a single configuration but just to be safe we enumerate them all.  You can compare the results of the macros with Visual Studio by using Solution Explorer and the project’s property pages.  At the top of the property pages you should see a combo listing each of the supported configurations. For pages that contain configuration-level properties the combo is enabled.  The results you get from the pages should match the macro results.

Configuration Manager

Solutions have configurations just like projects.  Unlike a project configuration however a solution configuration has only one set of properties: the configuration of the projects.  The settings of a solution configuration is controlled by the Configuration Manager.  You can access the CM by right-clicking the solution and selecting Configuration Manager.  It might look something like this.

By default the solution configuration shares the same name as all the project configurations within the solution.  This is by design and generally how you will want to work.  If you have ever changed active configurations using the toolbar in Visual Studio you should realize that what you are actually changing is the solution configuration, not the project configuration.  The active solution configuration determines the active configuration for each project it contains.  You also have control over whether the project gets built or not.  In general all projects will be built but if you mix, say, unit tests with your source code you might create a solution configuration that builds all the projects while you have another configuration that builds only the source code.  You configure all that through this dialog.  Here is a macro to enumerate the properties of all the solution configurations.

Public Sub DisplaySolutionConfigurations()
   Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Configurations”)

   ‘Enumerate the solution configurations 
   Dim build As SolutionBuild = DTE.Solution.SolutionBuild
   Dim configs As SolutionConfigurations = build.SolutionConfigurations
   For Each config As SolutionConfiguration In configs
      window.OutputString(“Configuration: “ & config.Name & vbCrLf)

      For Each context As SolutionContext In config.SolutionContexts
         window.OutputString(IndentString(1) &
                String.Format(“{0} – {1} – Build: {2}, Deploy: {3}” & vbCrLf, _
                   context.ProjectName, context.ConfigurationName, _
                   context.ShouldBuild, context.ShouldDeploy))
      Next
   Next
Endd Sub

 When you run this macro you might notice (what appears to be) duplicate entries for the same project.  You will see why shortly.

Platforms

Up to this point we have totally ignored the platform that you see next to each configuration.  It is time to take a look at it now.  A configuration controls what settings to use for building a project (or solution).  A platform determines what target machine a project will be built for.  Take a look again at Configuration Manager in Visual Studio. For .NET code you’ll see the platform is Any CPU.  This means that your code is machine agnostic.  If you run your code on a 32-bit OS then your code will be x86.  If you run it on a 64-bit OS then your code will run as x64.  This is one of the advantages of .NET.  It doesn’t target the machine until you run it.  This is generally what you want so you’ll rarely make any changes to this.

There are exceptions though.  If you are working with native code or mixed mode then you have to make sure that the code is all either x86 or x64.  If you don’t then your code will not run.  You cannot mix x86 and x64 code in the same process.  If you are using .NET to interop to unmanaged code then you have to ensure the platform for both sets of code are the same otherwise the code might run on a 32-bit OS but fail on a 64-bit OS.

Update: Starting with Visual Studio 2010 all executables will target the x86 platform rather than Any CPU.  The reasoning behind this (reasonable) decision is discussed here.

This is where platforms come in.  By default only a couple of platforms are associated with a solution or project.  Like configurations a solution can have one set of platforms while a project has another but it is generally a good idea to keep them in sync.  You will again use Configuration Manager to modify the platforms in a solution or project.  There are no configurable properties for a platform other than determing what platform will be targeted for each solution configuration.

Let us take a look at a more real-world situation.  Say you are migrating an application from C++ to .NET.  Your .NET code is, by default, platformed to Any CPU since you do not care what the architecture is.  However your C++ code is x86 only.  In order to support both 32-bit and 64-bit OSes you have no choice but to either build your .NET code as 32-bit only or create two different versions of your C++ code, one for x86 and one for x64.  Generally speaking it is easier to target x86 in .NET.  You would therefore leave the default platforms alone (to save time when you eliminate the C++ code later) and create a new platform called x86.  Under this context, for each solution configuration, you will build your projects for the same configuration. For each project you will also modify the platform to target x86.  In the VS toolbar next to the solution configuration you will also need to select the solution platform (x86).  Your built code will now run as x86 whether it is running under a 32-bit OS or a 64-bit OS..

An important point to remember about platforms is that each solution configuration/platform combination represents a different solution context.  Yes this is the exact same context the macro we wrote earlier displays.  As an example it is possible to have a Debug/x86 solution context in which project A Debug/x86 is built and project B Debug/x86 is not built.  At the same time you can also have the Debug/x64 solution context in which both project A and B Debug/x64 is built.  Each context is distinct.  The project configuration/platforms work the same way.  In general you don’t realize this because the platform is automatically determined but in reality it is the combination of configuration/platform that determines the project (and solution) configuration settings.

Here is an updated macro to include the platform information.

Public Sub DisplaySolutionConfigurations()

   ‘Get the output window
   Dim window As OutputWindowPane = GetOutputWindowPane(“Solution Configurations”)

   ‘Enumerate the solution configurations
   Dim build As SolutionBuild = DTE.Solution.SolutionBuild
   Dim configs As SolutionConfigurations = build.SolutionConfigurations
   For Each config As SolutionConfiguration In configs
      window.OutputString(config.Name & vbCrLf)
      For Each context As SolutionContext In config.SolutionContexts
         window.OutputString(IndentString(1) & context.PlatformName & vbCrLf)
         window.OutputString(IndentString(2) & String.Format(“{0} – {1}|{2} – Build: {3}, Deploy: {4}” & vbCrLf, _
                            context.ProjectName, context.ConfigurationName, _
                            context.PlatformName, context.ShouldBuild, context.ShouldDeploy))
      Next
   Next
End Sub

If you got duplicate entries again then your code is correct.  The problem is that there doesn’t seem to be any way, at this time, to get the platform associated with each solution configuration.  The solution context information refers to the project for which it is associated.  Unfortunately I am not aware of a way around this limitation at this time.

Using the FileSystemWatcher

(Originally published: 10 June 2007)

The FileSystemWatcher (FSW) component available in .NET is being used more and more every day.   Unfortunately FSW does not work quite the way you would expect.  This article will discuss how to use the FSW in a .NET application.  An additional problem with using FSW is performing complex tasks without impacting the performance of the system overall.   This article will discuss a basic approach to working with FSW while maintaining good performance.

What Is the FileSystemWatcher?

Before discussing how to use the FSW it is important to understand what it is and what it is used for. The FSW is a component used to monitor the file system for file or directory changes. The FSW can be used to receive notifications when files or directories are created, moved, deleted or updated. Although it is generally only useful in Windows applications it can be used in any type of application including web applications.

Under the hood the FSW relies on the underlying file system in Windows. As a result the events raised by FSW are low level. However they are accurate and fast, being raised by the file system as soon as the operation occurs. No buffering of events occurs either. This will prove to be problematic as we will see later. FSW identifies the following events for both files and directories: 

  • Create
  • Delete
  • Change
  • Rename

To avoid raising too many events the FSW also supports filtering of files and directories based on a file path or a file mask. This is very useful as the file system is used a lot. It would be wasteful to receive every event raised by the file system if all we cared about was the modification of a specific file.  To demonstrate how to use FSW refer to the simple file monitoring application. FileMonitor will monitor any folder it is pointed at. As events are raised it will log the event data to a simple UI control. To keep things simple the application simply exposes a PropertyGrid for interacting with the FSW properties. Run the sample application as you read through the article so that you can better understand what is going on.

Configuring the FileSystemWatcher

The FSW is a standard .NET component. It can be dragged from the Toolbox in Visual Studio and dropped onto a form or created programmatically in code. It is generally easier to create and use the FSW programmatically rather than dragging and dropping it onto a parent form. For the sample application however dragging and dropping the component from the Toolbox works just fine. The FSW resides in the System.IO namespace since it is a general component rather than a WinForms component.

Before the FSW can be used you must configure the events you are interested in and apply any filtering desired. Once the FSW is configured you must enable it using the EnableRaisingEvents property. This property starts (or stops) the FSW. We do not want to set the property until we have fully configured the component otherwise we might miss some events.

The Path property indicates the folder to be monitored. If the IncludeSubdirectories property is set then any subfolder is also monitored. Use this property with care. If you set Path to a folder with many subdirectories you might get more events then you want.

Set Path to some folder on your machine (for discussion purposes assume c:temp). Leave IncludeSubdirectories as false and set EnableRaisingEvents to true. The FSW is now watching the given folder for any changes made to the file name, directory name or last write time. Go to Explorer and copy a file into the folder. Notice that several events were generated. We will discuss why shortly. Rename the file and then delete it. In each case more events are raised. As you can see from the list the full path to the file is given each time along with the event that occurred.

Now set IncludeSubdirectoriesto true and create a new folder in the folder from earlier. Copy a file from the original folder to the new folder. In each case you should see some events being raised. It appears that you can change the properties of the FSW without

FileSystemWatcher fsw = new FileSystemWatcher(); 
fsw.Path = @”c:temp”
fsw.IncludeSubdirectories = true
fsw.SynchronizingObject = mainForm; 
fsw.EnableRaisingEvents = true

 

Listening For Events

Since the FSW is a standard .NET component it raises events in the same way as everybody else. The FSW only defines five events of interest.

We will ignore the Error event. Each event does exactly what you think it does. All the events except for Renamed have an FileSystemEventArgs argument. This argument specifies the following useful properties.

  • ChangeType – An enumeration identifying the change (in lieu of the event).
  • FullPath – The full path and name of the file or folder causing the event.
  • Name – The name (without path) of the file or folder causing the event.

The Renamed event has an argument of type RenamedEventArgs which derives from FileSystemEventArgs. It adds or modifies the following properties.

  • FullPath – The full path to the new file or folder.
  • Name – The name of the new file or folder.
  • OldFullPath – The original path of the file or folder.
  • OldName – The original name of the file or folder.

In the sample application an event handler is defined for each event. A formatted message is generated in the list box containing the event arguments.

I know what you are thinking. “What a minute! You have a threading issue in your code”. Since the FSW is not a WinForms component and is called by the operating system there is a good chance that the event will be raised on a non-UI thread. You are absolutely correct…sort of. The event will indeed be invoked from a non-UI thread. However since the component is generally designed for WinForms applications it is built to work without too much effort. Internally the component will transition to the UI thread before it raises the event. Therefore you can safely access the UI from within any event handler hooked up to the FSW.

FileSystemWatcher fsw = new FileSystemWatcher(); 

fsw.Changed += OnFileChanged; 
fsw.Created += OnFileCreated; 
fsw.Deleted += OnFileDeleted; 
fsw.Renamed += OnFileRenamed; 
fsw.EnableRaisingEvents = true

 

Filtering Events

The FSW supports two different types of filtering: file/folder name and change type. The Filter property is used to filter the events based upon a file/folder name. It supports standard wildcard characters so if you want to monitor a specific file specify the file name in the Filter property. If you want to monitor a type of file (such as text files) then enter the appropriate mask (such as *.txt). Note that only a single mask can be used. Do not attempt to use a semicolon to separate multiple file masks because it will not work.

The other filtering option, change type, is available strictly for Changed events. The Changed event is raised when anything about the file changes including its name, size or even its attributes. Generally all you care about is if a file is modified. Receiving a change notification when a file changes from readonly to read-write is wasteful. Enter the NotifyFilter property. This property is a list of one or more enumerated values indicating the types of information for which a change notification should be generated. The default is whenever the name or last write date changes. You can watch for other changes as well such as attribute changes or even when the file was last modified.

The only downside to the NotifyFilter is that when the Changed event is raised there is no way to tell why it was raised. Ideally the ChangeType property on the event argument would indicate the exact change that occurred but it does not. It will always say the same thing. Therefore if you care about the exact change that was made you will need to keep track of that information yourself.

For the Renamed event if either the old or new file matches Filter then the event is raised.

Demystifying FSW Events

One of the most confusing issues with using FSW is figuring out why certain events are not raised. It is a common misconception that if you modify a file that you will receive a change event. This is not necessarily true. We will now discuss a few common file monitoring scenarios and discuss how to handle each one.

Monitoring for Creation

There are basically two ways to create a file: create a new file or rename an existing file. To successfully detect the creation of a file you should monitor the following events.

  • Created
  • Renamed

Created is raised in all cases if you have no filter and only if FullPath matches the filter otherwise. For Renamed you have to do a little more work if you are using a filter. If FullPath matches the filter then a file has been renamed to a file type that you care about. If OldFullPath matches the filter then a file has potentially been renamed from a file type that you care about. Notice I said potentially. You can rename a file without changing its file type. Therefore use FullPath first and only use OldFullPath if needed to determine what the event is signifying.

private void OnFileCreated ( object sender, FileSystemEventArgs e ) 

    Display(“Created file {0}”, e.FullPath); 
}

private void OnFileRenamed  ( object sender, RenamedEventArgs e ) 

    Display(“Renamed file {0} to {1}”, e.OldFullPath, e.FullPath); 
}

 

 Monitoring for Deletion

As with creation there are two ways to delete a file: delete the file or rename an existing file. To successfully detect the deletion of a file you should monitor the following events.

  • Deleted
  • Renamed

Deleted is raised in all cases if you have no filter and only if FullPath matches the filter otherwise. Renamed works the same way as it does for file creation.

A special case exists for deletion of a directory with subdirectories and/or files. A deletion event will be raised for each file in the directory (and each subdirectory if IncludeSubdirectories is set). Additionally if subdirectories are being monitored then a change event will be raised for each directory in which a file or directory was deleted.

private void OnFileDeleted ( object sender, FileSystemEventArgs e ) 

    Display(“Deleted file {0}”, e.FullPath); 
}

 

 Monitoring for Changes

Think about how a program might save data to an existing file called mydata.dat.  The naïve approach would be to open the original file and overwrite whatever was there. Although this is fast and easy to do if something goes wrong during the save process not only are the changes lost but so is the original file. Almost everyone has experienced this at least once. Now think about the events that would be involved.

  • Change for mydata.dat
  • Change for mydata.dat

The “superior” approach would be to create a temporary file, save the file to the temporary file, delete the original file and then rename the temporary file to the original file. Although there is more work involved and an opportunity for failure at no point would the original and changed data be lost. You could expect to see the following events.

  • Create of someTemp.tmp
  • Change of someTemp.tmp
  • Change of someTemp.tmp
  • Delete of mydata.dat
  • Rename from someTemp.tmp to mydata.dat

If you only monitor events for mydata.dat you would completely miss the saving of the file. Therefore you will often need to use heuristics to decide what events to process and what events to ignore. The following events should be monitored.

  • Changed
  • Deleted
  • Renamed

Changed is raised whenever the file changes. Remember that the Win32 API is used behind the scenes and it is low level. Therefore you can receive several Changed events for a single logical change in an application. You must handle this case.

Deleted is raised when the file is deleted. This is normally considered to be a change of the file although how it is handled is application specific.

Renamed is the most difficult event to handle. Go back to the file change discussion of earlier as you read this description. If FullPath is the file of interest (mydata.dat) then you can assume that the superior approach was used. Treat this case as though the entire file has changed. If OldFullPath is the file (mydata.dat) then treat it as though a deletion has occurred.

private void OnFileChanged ( object sender, FileSystemEventArgs e ) 

    Display(“Changed file {0}”, e.FullPath); 
}

 

 Locked Files

You will not get to far into using the FSW before you will want to do something with the file that you are receiving the event about. You might want to copy the file to another directory or perhaps read the new file contents. Remember, again, that the events are ultimately raised in the low level details of the operating system. There is a real good chance that the application that is manipulating the file still has a lock on it.  Some applications allow shared read access when they update the file while other applications deny all access to the file. Therefore it is generally a good idea to assume that, while processing the event, the file in question is locked. This puts quite a limitation on what you can do with the file.

To work around a locked file there is little option but to wait until the lock is released. Rarely is the lifetime of a lock predictable so generally the file has to be polled. Depending on the application polling every second or every couple of seconds should be sufficient. Polling generally involves just trying to get access to the file. Since file operations generally throw exceptions when things go wrong exception handling will probably be needed. It will not perform well but we will handle that later.

A final issue with dealing with locked files is the problem of leaked locks. An application may refuse to release a lock for whatever reason. It is generally not a good idea to continually poll for a file. After some point it is best to just give up. Therefore it is a good idea to define a retry count for each event. After the retry count is exceeded report an error and ignore the event. Depending on the application this could require that all additional events for the same file be ignored as well.

A complication added by locked files is how to handle other events. Ideally a single locked file should not stall all file processing. If file A is locked events for file B should still be processed. This will quickly become complicated due to file reuse. File reuse occurs when a file name is reused for several different files. Examples of file reuse include renaming a file and deleting a file and then recreating it (i.e. changing a file). Here is a sample of events that can cause file reuse.

  • File B is created
  • File B is changed
  • File A is deleted
  • File B is renamed to file A
  • File B is created
  • File B is changed

If file A is locked then file B can not be renamed nor can any subsequent events against file B be handled until file A is complete. Tracking this can become quite complex. In most applications it is not worth the effort therefore processing events in order (with retry counts) is a simple, reasonable requirement.

Buffering of Events

The sample application simply logs messages to the UI. In general however you will be doing far more work. Work that will take a reasonable amount of time (such as retry counting). The FSW communicates to the underlying file system through a shared buffer. This buffer is limited in size. If more events come from the file system than the FSW can handle the buffer will overflow. In this case two things will happen: you will miss events and the Error event will be raised.

If you start missing events then there is little you can do about it. Therefore the best option is to ensure that any FSW event handler processes the event as quickly as possible. This pretty much mandates that you push the actual event processing to a secondary thread. Moving work to a secondary thread is pretty standard fare in most applications. Therefore we will forego the usual discussions of thread safety and synchronization and instead focus on solving the general problem of handling FSW events on a secondary thread.

The general algorithm would work as follows.

private void OnSomeFswEvent ( object sender, FileSystemEventArgs e )
{
    //Create instance of event data structure containing the event type and file(s) of interest 
    //Lock the shared queue for writing 
    //Push the event data onto the queue    
    //Unlock the shared queue 
}

private void SomeWorkerThread ( )
{
    //While not terminating sleep for a fixed period of time  
    //    While there are events in the queue 
    //        Lock the shared queue 
    //            Pop the event data 
    //         Unlock the shared queue 
    //          Process the event 
    //    If a request to terminate was received 
    //        Terminate the worker thread 
}

 This is a pretty straightforward algorithm that should be easy to implement. Returning to the issue of file locks from earlier this algorithm introduces some issues however. Using the simple approach to locked files if a file is locked then the worker thread needs to stop processing any more events until the original event is processed or it times out. A local variable can be used to store the event. The variable needs to be checked before processing any events from the queue. Here is an updated algorithm with the changes in bold.

private void SomeWorkerThread ( )
{
    //While not terminating sleep for a fixed period of time  
    //    If current is set 
    //        Process the event 
    //    If failed 
    //        Increment current retry count 
    //    If retry count exceeds maximum 
    //        Report error 
    //        Clear current 
    //    Else 
    //        Clear current  
    // If current is empty  
    //    While there are events in the queue 
    //        Lock the shared queue 
    //            Pop the event data into current 
    //        Unlock the shared queue 
    //        Process the event 
    //        If failed 
    //            Increment current retry count 
    //        Else 
    //            Clear current 
    //    If a request to terminate was received 
    //        If current is set 
    //            Report error 
    //        Terminate the worker thread 
}

 

Enhancements

Access to the queue is serialized. This is overly restrictive given that the only time there will be a conflict is when FSW is trying to insert the first item into the queue while the worker is trying to retrieve it. A better synchronization approach may be useful. A bounded queue (MSDN Magazine, CLR Inside Out, May 2007) would solve the problem nicely and improve performance.

The second scalability issue involves the worker thread itself. Depending upon how many events are received the worker thread can easily get behind. Multiple worker threads would allow better scalability such that many events can be handled simultaneously. Ignoring locked files this is pretty straightforward to handle since multiple worker threads would access the same shared queue. However locked files make using multiple worker threads very complex. Some sort of handshaking must occur such that locked files are shared across multiple worker threads to ensure events are handled in order.  A better approach might be to use a single worker thread but have the worker thread be state aware for each of the files being processed.  Some events can be handled in parallel (even in the case of errors) provided the events do not work on the same set of files.  This is actually a lot harder than it sounds because of the possibility of files being renamed but nevertheless would solve the problem nicely.

Caveats

A few final caveats are in order about FSW. You can not use FSW on read only media like CDs or DVDs. There really is not much benefit anyway. You also can not use FSW on removable media.

If Path does not exist then FSW will attempt to create it when EnableRaisingEvents is set. The FSW requires read access to Path otherwise an exception will occur.

Earlier it was stated that you could call UI methods in the event handlers without worrying about what thread you are on. That is not entirely true. If you drag and drop the component onto a form then it will work. However if you programmatically create the component then it is not. The SynchronizingObject property of FSW controls this. When you drag and drop the component onto a form the form is assigned to the SynchronizingObject property. This causes the event handlers to be raised on the UI thread. When you programmatically create the component you should manually set the SynchronizingObject property to the main form or another UI control so synchronization will occur automatically.

You might think that filtering will help alleviate the buffering problem but it will not. Filtering occurs in FSW. Therefore the event must still travel from the file system to FSW through the shared buffer. There is little you can do besides processing events as fast as you can to avoid buffering issues.

The Case of the Mysteriously Changing Font

A while back I wrote up a simple IP address control for WinForms. This control relied on the underlying IP Address common control from Windows.

The goal was to provide the ability, in WinForms, to enter and validate an IP address without having to write a bunch of code. 

But a mysterious problem occurred when the control was used more than once in the same process.  The font would mysteriously change to bold.  This problem will be discussed later. 

Here is the process I used to create the control and ultimately track down and fix the issue.

Window Classes

When a window is created it must be assigned a window class.  The class determines, amongst other things, some basic properties that all windows of the class inherit.  This includes the default window procedure used to process messages.  Each window class must be registered using RegisterClassEx before it can be used.  The common controls in Windows each use their own window class.  Before the common controls can be used InitCommonControlsEx must be called to registered the common window classes.  Once a window class has been registered an instance of the window class is created by passing the class to the CreateWindowEx function in Windows.  When a common control’s window class is used in the call an instance of one of the common controls is created.

In WinForms the common controls are initialized automatically so no additional effort is needed.  For most of the common controls an existing WinForm control exists to wrap it.  Under the hood each control will pass the appropriate window class to the create call.  The property CreateParamss is responsible for configuring the window before it is created.  To create an instance of a common control the property is overridden to pass the appropriate window class. 

Creating the Control

To create the IP control first we create a new class called IPAddressTextBox and derived it from TextBox.  Because this is a common control CreateParams has to be overridden to use the appropriate window class.  Now when the control is created it will appear and work like the standard IP Address control in Windows.  Here is the basic code.

public class IPAddressTextBox : TextBox
{
   protected override CreateParams CreateParams
   {
      get
      {
         CreateParams parms = basee.CreateParams;

         parms.ClassName = “SysIPAddress32”;;
                                
         return parms;
      }
   }
}

 

Propertiess

The IP Address control does not have any custom styles so there is no need to expose any additional properties.  However the base TextBox class has a couple of properties that do not make sense for the IP control such as maximum text length and if the control supports multiline.  These properties are (fortunately) virtual so they are overridden to do nothing, hidden from the editor and hidden from the designer.  Here is the code used to hide the unneeded properties.

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override int MaxLength
{
   get { returnn 15; }
   set { }
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Multiline
{
   get { returnrn false; }
   set { }
}

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool ShortcutsEnabled
{
   get { return true; }
   set { }
}

The Browsable attribute controls whether the property shows up in the Properties grid or not.  The DesignerSerializationVisibility attribute controls what and if the property is serialized into the designer file.  The EditorBrowsable attribute controls whether the property is always, sometimes or never shown in the editor (Intellisense).

To make the control easier to use the control’s value will be exposed as an IPAddress value.  This simply requires that the text value be parsed.  Here is the property to expose the IP address.

[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IPAddress Value
{
   get 
   {
      IPAddress addr;
      IPAddress.TryParse(basee.Text, out addr);

      return addr ?? IPAddress.None;
   }
                
   set
   {                                
      string addr = ((value != nulll) ? value.ToString() : “”);
      if (basee.Text != addr)
      {
         base.Text = addr;

         OnValueChanged();
      };
   }
}

This basic implementation ignores invalid data.  The setter calls a protected virtual method to raise an event when the value changes. This provides parallel functionality to the TextChanged event that all controls inherit.

Methods

The IP address control has only one custom message that can be exposed.  The IP address supports setting the focus to the individual portions of the address.  A SetFocus method is exposed to allow developers to set the focus to each of the individual fields of the address.

public void Focus ( byte field )
{
   if (IsHandleCreated)
      SafeNativeMethods.SendMessage(Handle, IPM_SETFOCUS, field, 00);
}

The method simply sends the appropriate message to the window using P/Invoke.

A few additional methods are defined to handle some basic input processing and to update the value if the text changes.

Problem In Paradise

The basic control is done.  Compiling and running the control in a sample Winform app shows that it is working correctly.  But there is a problem.  Once the control is destroyed (such as by closing the owning form) then subsequent uses of the control cause the font to be in bold.  It does not matter whether the same instance was used or an entirely new one was created.  Debugging the problem and tweaking with the control did not resolve the problem.  The font was somehow getting corrupted.  A little INet searching revealed that the IP Address control has a “bug” (this came from a message posted by an MS engineer).  The underlying common control will delete the font it is using when the control is destroyed.  While this sounds like a good thing it does introduce a serious problem.  In Winforms controls will use the same standard font by default.  If the default font is passed to the IP control and the control then deletes it then the standard font is corrupted and subsequent use will cause bizarre behavior. 

Fixing the Problem

Fortunately fixing this problem is not that difficult.  What needs to happen is that the underlying IP control needs to get its own font when it is created.  When the control eventually gets destroyed it will then delete the copy rather than the original. 

A control gets its font from the Font property.  To ensure that the control always gets its own copy of the font it needs to be cloned.  This is easily handled by overriding the Font property and simply cloning the font in the setter.

public override Font Font
{
   get { return base.Font; }
   set
   {
      base.Font = (Font)value.Clone();
   }
}

One final change is needed.  When the control is first created it will get the default font so the constructor of the control needs to ensure that a copy of the default font is used instead.

public IPAddressTextBox ( )
{
   this.Font = base.Font;
}

With these changes in place the control now gets its own copy of the font.  When the underlying IP control is destroyed it will delete the copy and the default font remains unaffected. 

Caveats

The attached file contains the control and a simple test program.  The control, as provided, is used in production code but might still contain some issues.  Before using this code in any production code be sure to fully test it.

Demystifying Auto Properties

There seems to be a lot of confusion going around about auto properties in C# and, starting with Visual Studio 2010, VB. Quite a few people are erroneously comparing them to fields and then trying to compare the two approaches. This is just plain wrong. This article attempts to clarify exactly what auto properties are and when they are useful. Before diving into auto properties it is important to differentiate between properties and fields and when to use each.

Fields

A field is a type member that holds a value. Every field has a type.  A field can be read or written. There is not any way to limit code to read or write access. The best that can be done is limit the field using the standard accessibility (public, protected/family, internal). A field simply stores a value so any (legal) value can be stored (if it gets past the compiler).

Fields, because they do little, are very fast to access and use. Fields execute no code when they are accessed so there is no worries about custom code being executed. All in all fields are low level. Because fields are so low level they can do almost nothing other than store a value. If a value is type-compatible with a field then it can be stored there irrelevant of any business rules or assumptions that might be desired. The following code demonstrates a few simple fields.

class Employee
{
   public int Id;

   public bool IsFullTime;
   public EmployeeType Status;

   public string FirstName;
   public string LastName;
}

 

Properties

A property is a type member that looks like a field but acts like a method. Every property has a type just like a field.  A property stores no data on its own. In fact a property is actually just a set of two methods: a getter and a setter. These accessors are used to get or set the value of a property. The getter has no parameters and returns the same type as the property.  The setter accepts a single parameter (usually) of the property’s type and returns nothing.

Most properties have both accessors but some have only a getter and, rarely, a setter. A property with only a getter cannot be assigned a value whereas a property without a getter can not be retrieved.

Properties are generally one of two types: a field wrapper or a computed field. A field wrapper property has a field that backs the property value, known as the backing field. Field wrappers are commonly used to add validation to field assignments. A computed field property generally only has a getter and returns a calculated value. The calculated value is often based upon some underlying field values but not always.  The following demonstrates a class with some properties.

class Employee
{
   public int Id 
   {
      get { return m_id; }
      set { m_id = value; }
   }

   public bool IsFullTime
   {
      get { return m_type == EmployeeType.Full; }
   }

   public EmployeeType Status
   {
      get { return m_status; }
      set { m_status = value; }
   }

   public string FirstName rstName
   {
      get { return m_firstName ?? “”; }
      set { m_firstName = value; }
   }

   public string LastName astName
   {
      get { return m_lastName ?? “”; }
      set { m_lastName = value; }
   }

   private int m_id; p;m_id;
   private EmployeeType m_status;

   private string m_firstName;
   private string m_lastName;
}    

In the above code most of the properties are wrapper properties.  IsFullTime is a computed property.  Notice that most wrapper properties simply get or set a backing field value.  This is pretty typical code.

Properties vs Fields

One of the big areas of debate is when to use properties and when to use fields.  Properties provide two very powerful features: field-like access and accessors.  A property is accessed just like a field.  In the following code can you identify which value is the field and which is the property?

Thread.CurrentThread.Join(Timeout.Infinite);

CurrentThread is a property of Thread while Infinite is a field of Timeout.  Yet they both look and behave like fields.  The CurrentThread getter is used to get the current thread from the Thread class.  The Infinite field is used to determine how long to wait.  This makes it real easy to use properties.  Note however that the underlying generated code is not the same for properties and fields.  For fields the underlying value is read or written directly.  But for properties a call to the getter or setter is inserted into the code.  Hence properties can be, theoretically, slower.

Because the property accessors are used to access the property you can execute arbitrary code whenever a propery is read or written.  This allows for things like validation, redirection or logging.  In the code given earlier the FirstName and LastName properties ensured that the string properties always returned a non-null value (a guideline that should be followed).  If a field would have been used instead then the caller would have had to handle the case of a null value.

The general guideline is to use public properties to expose private fields.  Even if the property simply returns the field the benefits outway the disadvantages.  For one if, at a later date, validation or some other code needs to be executed when accessing the field (or if the field is removed altogether) then a property accessor can be modified without breaking compatibility with existing code.  If a field is used then it is a breaking change to convert code from using a field to using a property.  In other words all the existing code would have to be recompiled before the “new” property would be used.

Public fields are not all bad.  Actually they are except for the very special case of read only fields.  It is generally OK to expose a public field if the field is read only (or constant) and is unlikely to change.  In all other cases expose a property instead.  Protected fields should generally be avoided as well.  Create a private field and expose a protected property.  Remember that a protected member is still public to derived types so it should follow the same rules as public members.

Where the debate often comes in is the performance.  Many people will point out that fields are faster than properties and this is technically true.  But the JIT can optimize property calls to all but eliminate the actual accessor call so the actual runtime performance will be negliable in almost all cases.  In general the 80/20 rule of performance tuning will eliminate any need to use public fields to avoid property performance issues.  Given the advantages of properties and their seamless integration into code there really is no good reason not to use them.

Auto Properties

The only real disadvantage of properties is all the code you have to write.  For just a simple wrapper you have to write the property declaration, the two accessors and the backing field.  Even worse is that the getter generally just returns the field while the setter just sets it.  This is ideally suited for code generators since it is boiler plate code.  In fact there are code snippets to do just that.

But if it is so easy to generate then why can’t the compiler just do it for us?  Well, as of VS 2008, it can.  Enter auto properties.  An auto property is nothing more than a wrapper property and its backing field.  The difference is that it can be defined in a single line of code.  Here is the Employee class using auto properties.

class Employee
{
    public int Id { getset; }

    public bool IsFullTime
    {
        get { return Status == EmployeeType.Full; }
    }

    public EmployeeType Status { getset; }

    public string FirstName
    {
        get { return m_firstName ?? “”; }
        set { m_firstName = value; }
    }

    public string LastName
    {
        get { return m_lastName ?? “”; }
        set { m_lastName = value; }
    }
            
    private string m_firstName;
    private string m_lastName;
}    

The Id and Status properties have been converted to auto properties.  Notice the backing fields were removed and the bodies of the accessors have been as well.  In this case the properties allow get and set operations but either keyword can be removed to make the property read-only or set-only.  The big advantage here is less code to read and write.  The name properties cannot be converted to auto properties because they do more than get or set the backing field.  Furthermore the IsFullTime property remains unchanged because it is a computed property and had no backing field anyway.

What’s going on behind the scenes here?  It is actually pretty straightforward.  When the compiler sees an auto property it generates a private backing field for the property.  Then the compiler generates the accessor(s) to get or set the backing field.  Basically all that boilerplate code is generated for you.  So whenever you need a property without any custom accessor code, use an auto property to save yourself some typing.

One of the questions often asked in the forums is why someone should use a field over an auto property.  The answer is that you should use a property over a field in almost all cases.  Whether that property is a normal property or auto property is not relevant.  An auto property just means that the compiler generates the property definition for you.  There is absolutely no other differences.  None.  Nadda.

Compatibility

Great new feature, but what if you are stuck on .NET v2?  Well here is the good news – the compiler generates all the code.  The VS 2008 compiler for C# (and VS 2010 for VB) generate all the code during compilation.  There is no runtime support needed.  Therefore the version of .NET you are using (other than v1.x) does not matter.  In fact there is no real way of distinguishing between auto properties and normal properties at runtime.  The best you can do is dump the code and look at the field name to see if it matches something the compiler might generate.  The point is that you can use this feature on any version of .NET provided you are using at least the VS 2008/2010 version of the C#/VB compiler. 

So, in summary, use an auto property whenever you have a wrapper property with no custom accessor code.  Use public properties (normal or auto) in lieu of public fields in every case except for readonly fields which may be exposed publicly.  Do not worry about the performance differences unless your performance tuning determines that the properties are truly slowing down your application.

Redirecting Dependent Assembly Versions In .NET

With the release of Windows Vista there has been a large number of additions and changes made to the existing Windows common controls.  Unfortunately many of these changes require that you use version 6.0 of the common controls library.  This is not the version WinForms apps will use by default. 

This article will discuss one solution for changing the version of a dependent assembly an application will use.  For example purposes this article will discuss how to redirect a WinForms application to use a specific version of the unmanaged common controls library.  The concept can be applied to any unmanaged library or assembly that an application may depend upon.

How .NET Finds a Dependent Assembly

Loading an assembly is a two step process for the runtime.  The first step is to identify the specific version to load.  The second step is to find the appropriate assembly.  A full discussion is beyond the topic of this article so I recommend you read the book Essential .NET: Volume 1 for a more detailed discussion.  The MSDN topic How the Runtime Locates Assemblies also covers this in detail. 

When an assembly needs to be loaded the loader first tries to determine which version to load.  If this is an assembly contained in the metadata (through a reference in the IDE) then the entire assembly information is available including the version number.  If you call Assembly.Load explicitly then the loader only has the information you provide.  Assuming a version is specified the loader will now look for the the appropriate assembly.  Note that only strongly named assemblies have version matching done.  For non-strongly named assemblies the first assembly found will be used.

Once the loader has identified the version to load it then goes through the process of finding the assembly.  For strongly named assemblies the loader will look in the GAC first.  If the appropriate version is not found then the loader continues using the standard search path.  The search path, slightly configurable, includes the application directory and a few child directories based upon the assembly name.  If the loader finds the assembly then it will try to load it otherwise it will fail the call.

The above discussion is a simplification of the process.  Refer to the resources mentioned earlier for full details.

A Typical Scenario

Let us set up a typical application architecture so we can have a better understanding of the issues involved in assembly versioning.  We will have the following solutions set up.

 

The SharedCompanyBusiness assembly is a binary reference to a company-wide assembly used in all products.  It is not under our direct control and is versioned and released independently.  It is not strongly named nor is it stored in the GAC.  Products must compile against the version they are most compatible with.  The shared assembly is stored with the application during installation.  It is currently at v3.5.

ThirdPartyControls is a strongly named assembly stored in the GAC.  It contains some UI controls the application uses.  It is currently at v10.0.

The two addin projects are built as part of the main solution but they are actually maintained separately.  Whenever a new version of the application is released then customers get new versions of the addins but the dev teams responsible for the addins can released interim versions as well.

All references other than the third party and shared assemblies are project references.  All the projects are currently v1.0.

Conflicting Assembly Versions

Non-strongly Named Assemblies

The above scenario is pretty common and will work as designed.  But now we will introduce a change into the application that will have a ripple effect.  SharedCompanyBusiness is updated to v4.0 and new classes are added.  The two addins are updated to use the newer version because they need some of the functionality it exposes.  The addins need to be released but with the newer shared assembly.  We have a problem.

The problem is that the application itself uses v3.5 but the addins are expecting v4.0.  Since the shared assembly is not strongly named version numbers do not matter.  If we ship the updated version of the shared assembly with the addins then the application will be using v4.0 even though it was never tested against that version. Provided v4.0 is backwards compatible with v3.5 the application will run fine.  If the addins do not update the shared assembly then they will crash at runtime because they will attempt to use a type or member that does not exist in the assembly.  The worse possible situation is when v4.0 makes a breaking change to the code, such as removing a type.  We are then in a no win situation as we cannot use either version without causing a crash.

In summary, for non-strongly named assemblies no version checking is done.  The first assembly that is found is used irrelevant of whether the code was compiled against it or not.  This can cause runtime crashes if the assemblies are not compatible.

Strongly Named Assemblies

Now suppose that ThirdPartyControls is updated from v10.0 to v11.0.  This is a strongly named assembly and resides in the GAC.  The GAC allows for side-by-side versioning of assemblies.  Therefore irrelevant of what versions are installed the application will still want to use v10.0.  This is great if the version is installed but suppose it gets uninstalled.  In that case the resolver will know to look for v10.0 but it will not find it.  The resolver will fail the call.  It does not matter whether a newer version is available or not. 

Versioning Policy

In some cases it is reasonable to use a newer version of an assembly if it is available.  Security fixes that result in a patched assembly come to mind.  The loader does not know the assembly versions are compatible so it will always fail the call.  You have to tell the loader that it is OK to use the newer version.  To do that you create a versioning policy inside the application’s config file.  Here is the entry we would place in CoolApp’s config file to tell it that it should use v11.0 of the ThirdPartyControls library.

<configuration>
   <runtime>
      <assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1“>
         <dependentAssembly>
               <assemblyIdentity 
                     name=”ThirdPartyControls” 
                     publicKeyToken=”abcdef1234567890ab“>
                  <bindingRedirect 
                       oldVersion=”10.0.0.0” 
                       newVersion=”11.0.0.0” />                            
               </assemblyIdentity>            
         </dependentAssembly>              
      </assemblyBinding>
   </runtime>
</configuration>

The assemblyIdentity element identifies the specific assembly we care about. For strongly named assemblies this will be the assembly name, the public key and any other information we would like to use to distinguish the assembly from other variants.  One attribute that was not included here is the type attribute. The type attribute indicates the processor type of the assembly such as x86, msil or amd64.  It is useful for differentiating between processor architectures.

The bindingRedirect element tells the loader that instead of using v10.0 of the assembly we should instead use v11.0.  This allows us to use newer versions of an assembly even though we compiled with an older version.  Of course if the versions are not compatible then a runtime error will occur.

Wildcards are not allowed in the oldVersion attribute but you can specify a range of versions like so: 10.0.0.0-10.99.99.99.  This allows you to redirect all versions of an assembly to a newer version.

Publisher Policy

The biggest issue with versioning policies is that it must be applied to each application.  If the shared assembly is used by many applications, which is primarily why you would store it in the GAC, then it can be a hassle to update each application.  For some changes, such as security fixes, you can be confident in compatibility and you want to force everyone to use the new, more secure, version.  As the publisher of the assembly you can create a publisher policy. 

A publisher policy is added to the updated assembly, as a manifest, and stored in the GAC.  The publisher policy works just like a versioning policy except it applies to all applications irrelevant of whether they realize it or not.  It is ideal for security fixes.  In fact the .NET framework uses this approach when releasing service packs.  When a service pack is released a publisher policy is included with it.  This causes all applications to use the new service packed version irrelevant of what version they were compiled against.

Mixed Assembly Versioning

The above discussion handles the original topic of this article but a few additional topics are worth mentioning.  In the original scenario there was not a situation where an assembly was referenced more than once within a single project.  We will modify the scenario to introduce this issue.

In this modified scenario the addin logic has been moved from CoolBusiness to CoolAddin.  The addin projects now reference the CoolAddin project.  CoolAddin requires some core logic so it references CoolBusinessCoolBusiness is removed from the addin projects as the core logic is in CoolAddinCoolApp requires access to the addins and the business logic so it references both CoolBusiness and CoolAddin

Technically CoolApp has two references to CoolBusiness: one explicit and one implicit through CoolAddin.  This is where mixed assembly versions can cause problems.  If CoolAddin uses a different version of CoolBusiness than CoolApp (possible if the projects were developed by different teams) then the compiler will not know which version to use.  The compiler will generate a warning in this case but the warning might be a little confusing.  The warning will say that CoolApp is using version X of assembly CoolBusiness but it depends on an assembly that uses version Y.  Ideally this should have been an error because it will cause untold suffering at runtime but there are very rare occasions where the message can be safely ignored.

If you ever get this warning then you need to fix it.  The problem will manifest itself at runtime in one of several different ways.  One of the more common ways is for a TypeLoadException to occur when trying to load a type from the assembly.  The exception will say that the type does not exist but using Reflector you will be able to verify that it does.  Another common exception is an InvalidCastException when you try to assign a variable of type A to an instance of type A where type A is defined in the conflicting assembly.  What is going on?

First a couple of important points about the runtime.  Firstly the loader will only load an assembly once.  Once it is loaded subsequent requests for the same assembly will result in the original assembly being returned.  A consequence of this is that the first assembly that the loader finds a match for will be the one it uses. 

The second point is that for strongly named assemblies with fully versioned names the loader can load multiple versions of the same assembly.  The runtime uniquely identifies a type by its fully scoped name, including assembly.  A type called Utility in namespace MyCompany.MyProduct of assembly A is distinct from the MyCompany.MyProduct.Utility type in assembly B.  The runtime knows the differences as they were fully generated during compilation.  You cannot automagically redirect a type to a different assembly without the runtime throwing an exception.

Do you see the problem yet?  If CoolApp loads v1.0 of CoolBusiness but CoolAddin tries to load v2.0 it will not work.  Since the assemblies are not strongly named whichever one gets loaded first wins.  In the case of v1.0 being loaded CoolAddin will likely try to load a type that does not exist and, hence, get an exception.  Of course if CoolApp was compiled with v2.0 and CoolAddin used v1.0 then things would likely work, for now at least.

If we now turn our attention to strongly named assemblies we can see the other common problem with mixed assembly versions.  Suppose for a minute that CoolBusiness was strongly named.  During compilation CoolApp used v1.0 but CoolAddin used v2.0.  Because it is strongly named two copies (v1.0 and v2.0) of CoolBusiness could be loaded at the same time.  But that would mean we have two copies of every type that is shared by the two versions.  Which one gets used at runtime depends upon where it is referenced.  Anywhere inside CoolApp would use v1.0 while anywhere inside CoolAddin would use v2.0.  Provided they remained separate things would work but this is unlikely.  Instead it is more likely that eventually CoolAddin would pass a v2.0 object to CoolApp, which expects v1.0, and we would get a type exception.  What makes this hard to trace down is that even in the debugger we would see that the object is of the correct named type but the full typename would not match.

To avoid the issues of mixed assembly versions ensure that all your projects use the same dependent assembly versions.  If necessary use versioning policies to enforce this.

Win32 Libraries

We have come full circle but we have not addressed the original example of the article: how do we force a WinForms app to use newer versions of Common Controls, a Win32 library?  We have already learned how to do it.  We just need to apply it.  We will create a versioning policy that tells our application to use the desired version, v6.0 in this case, rather than the standard version.  The problem is that we are not loading an assembly but a Win32 library.  Therefore we will place the versioning information in an application manifest and store it in the assembly directly.  The manifest will be located and read by the loader before our application gets far in the loading process.  The syntax is identical to the configuration file.  Here are the steps for Visual Studio 2008 (VS2005 has a few additional steps).

  1. Add a manifest file to your executable project if you do not already have one.  The application manifest is an item in the Add New Items dialog for the project.
  2. Modify the assemblyIdentity and description elements to match your application.
  3. Add the following XML fragment to the manifest.
    <assembly …>
       <dependency>
          <dependentAssembly>
             <assemblyIdentity
                type=”win32
                name=”Microsoft.Windows.Common-Controls
                version=”6.0.0.0
                processorArchitecture=”X86
                publicKeyToken=”6595b64144ccf1df
                language=”*” />                    
          </dependentAssembly>
       <dependency
    </assembly>

Compile and run the application and the appropriate version should be loaded.  You can do this with any Win32 library that supports side-by-side versioning.  The hard part is determining the values to use.  The easiest way is to load the code up in the debugger and get the library from the debugger.  The type will be win32 for Win32 libraries. 

Note that as of VS2008 SP1 Beta it appears that v6.0 of the Common Controls library is automatically used for v2.0 applications.  Still this technique can be used for other native libraries as well.

Creating Configuration Sections

The .NET configuration subsystem is used throughout the framework.  Entire subsystems rest on top of it including security policies, application settings and runtime configuration.  ASP.NET uses the configuration subsystem heavily.  Applications can take advantage of the subsystem as well by creating their own configuration sections.  Unfortunately it is not straightforward.  This article will discuss how to create new configuration sections in .NET.

A point of clarification is needed.  Application settings, while relying on the configuration subsystem, are not related to configuration sections.  This article will not discuss the creation or usage of application settings.  Application settings, for purposes of this article, would be though settings that you can define in a project’s property pages.

History

Since the configuration subsystem is used throughout the framework it has been available since the initial release of .NET.  In v1.x you could extend the configuration subsystem by implementing the IConfigurationSectionHandler.  This interface boiled down to parsing XML elements.  While usable it was a little much to implement.  It also didn’t allow much more than reading in XML attributes and translating them into usable values.

In v2.0 the v1.x interface was wrapped in a more complete, complex set of classes.  The v2.0 subsystem allows for declarative or programmatic declaration of strongly type configuration properties, nested configuration settings and validation.  The newer subsystem also allows reading and writing configurations.  Additionally the need to manually parse XML elements has been all but removed.  While this makes it much easier to define configuration sections it also makes it much harder to deviate from the default behavior. Adding in sparse documentation and confusing examples results in a lot of postings in the forums about how to get things to work.

This article will focus exclusively on the v2.0 classes.  While we will not discuss the v1.x interface any it is important to remember that it still exists and resides under the hood.

Are We there Yet?

Rather than building up an example as we go along we are going to first take a look at the configuration that we ultimately want to be able to read.  We will then spend the rest of the time getting it all set up.  This is a typical design approach for configurations.  You know what you want.  All you need to do is get the subsystem to accept it.

For this article we are going to build a configuration section for a hypothetical test engine.  The test engine runs one or more tests configured in the configuration file.  The engine simply enumerates the configured tests and runs them in order.  Here is a typical section that we will want to support.  This will be refered to as the target XML throughout this article.

<tests version=”1.0” logging=”True“>
    <test name=”VerifyWebServer” 
          type
=”TestFramework.Tests.ServerAvailable
          failureAction=”Abort“>
        <parameter name=”url” value=”http:\www.myserver.com“>
    </test>
    <test name=”CheckService1” 
          type
=”TestFramework.Tests.WebServiceInvoke” 
          async
=”true” timeOut=”120“>
        <parameter name=”url” 
                   value
=”http:\www.myserver.comservice1.asmx” />
        <parameter name=”parameter_1” value=”hello” />
        <parameter name=”returns” value=”HELLO” />
    </test>
</tests>

Each test is represented by an XML element and all tests are contained in a parent tests element.  Each test must have a unique name and a type attribute.  The type attribute is used by the engine to create the appropriate test class to run.  A test can have some optional attributes as well.  The failureAction attribute specifies what should happen if the test fails (continue, abort, alert).  The default is to continue.  The async attribute is a boolean value indicating whether the test should be run synchronously or asynchronously.  The default is false.  The timeOut attribute specifies how long to wait for the test to complete (in seconds) and is only meaningful for async tests.  The default is 10 seconds.  

Configuration Files

The configuration subsystem must be able to map each XML element to a .NET type in order to be able to read it.  The subsystem refers to a top level XML element as a configuration section.  Generally speaking each configuration section must map to a section handler.  This is where the v1.x configuration interface mentioned earlier comes in.  When creating a new configuration you must define the configuration section that the subsystem will load.  Let’s take a look at a standard application configuration for a moment.  This will be refered to as the example XML throughout this article.

<?xml version=”1.0” encoding=”utf-8” ?>
<configuration>    
    <configSections>
        <section name=”tests” 
            type
=”TestFramework.Configuration.TestsSection,ConfigurationTest” />
    </configSections>

    <tests>
        <!– Tests go here –>          
    </tests>        
</configuration>

All XML elements will reside inside the configuration root element.  In the above file the application has defined a new section for the tests and created an empty tests element where the tests will go.  Every section inside the configuration element must have a  section handler defined for it.

To define a custom section in the configuration file we have to use the configSections element.  This element might already exist in the file.  It is a best practice to always put it as the first section in the file.  In the above example the element tells the subsystem that whenever it finds a tests element it should create an instance of the TestFramework.Configuration.TestsSection class and then pass the XML on for processing.  The type is the full typename (including namespace) followed by the assembly containing the type.  It can be a full or partial assembly name.  If the subsystem cannot find a section handler for an XML element or the type is invalid then the configuration subsystem will throw an exception.

“So I have to define a section handler for every XML element?  That’s nuts, forget it.”  Well, not exactly.  Firstly the configuration subsystem only cares about the top level XML elements (anything direct under configuration).  Child elements do not need a configuration section (but they do need a backing class as we’ll discuss later).  Secondly the subsystem supports section groups.  Section groups can be used to group sections together without requiring a handler.  The framework itself generally separates sections by the namespace they are associated with.  For example ASP.NET configuration sections generally reside in the system.web section group.  You define a section group in the configSections element like so.

<?xml version=”1.0” encoding=”utf-8” ?>
<configuration>
    <configSections>
        <sectionGroup name=”testFramework“>
            <section name=”tests” 
                 type
=”TestFramework.Configuration.TestsSection,TestFramework.Configuration” />                 
        </sectionGroup>
          
    </configSections
    
    <testFramework>
        <tests>
            …                 
        </tests>          
    </testFramework>
</configuration>

Section groups are useful for grouping together related sections. Groups can be nested inside other groups.  We will not discuss section groups further as they have no impact on section handlers.  A section handler does not care about anything above it in the XML tree.

A question you may be wondering about is why you do not see any section definitions for the framework sections.  That is because the subsystem actually looks for section handlers in the application, machine and domain configuration files.  The machine and domain configurations reside in the framework directory and can be configured by an administrator to control .NET applications.  If you were to look into these files you will eventually find section handler definitions for each of the pre-defined sections.  Furthermore you can look into the system assemblies and find their corresponding handler classes.  There is nothing special about the pre-defined sections.

A final note about configuration files if you have never worked with them.  When you add an application configuration file to your project it will be called app.config.  The runtime expects the configuration file to be named after the program executable (so prog1.exe would be prog1.exe.config).  The IDE will automatically copy and rename the app.config project item to the appropriate name and store it with the binary during a build.  Any changes you make to the configuration file (in the output directory) will be lost when you rebuild. 

Mapping XML to .NET

Before diving into the .NET code we need to understand how the subsystem will map the XML data to .NET.  The subsystem uses sections, elements and properties to map from XML elements and attributes. 

A configuration element is a class that derives from ConfigurationElement.  This class is used to represent an XML element.  Every XML element will ultimately map to a configuration element.  XML elements are normally used to house data that is too complex for a simple attribute value.  Elements are also used for holding collections of child elements.  Configuration elements will be used, therefore, to represent complex objects and/or parent objects.

A configuration property is ultimately represented by a ConfigurationProperty.  Normally, however, we apply an attribute to a class property to define them so the actual declaration will be hidden.  Configuration properties represent XML attributes.  Every XML attribute associated with an XML element will map to a configuration property on the corresponding configuration element.  As we will discuss later properties can be optional, have default values and even do validation.

A configuration section is a special type of configuration element.  A section derives from ConfigurationSection, which itself derives from ConfigurationElement.  For our purposes the only distinction is whether the element is a top level element or not.  If the element is a top level element that is defined in the configSections of the configuration file then it will be a configuration section (and derive from the appropriate class).  For all other purposes it works like an element. 

The configuration subsystem requires that every XML element and attribute be defined by either a configuration element/section or configuration property.  If the subsystem cannot find a mapping then an exception will occur.  We will discuss later how we can have some control over this.

To start things off we will define the configuration section for our example.  Following the precedence set up by the framework we will isolate our configuration classes to a Configuration subnamespace.  We will name configuration sections as -Section and elements as -Element.  The beginning name will match the XML element name but using Pascal casing.

Here is how we would define our configuration section. 

using System;
using System.Configuration;

namespace TestFramework.Configuration
{
   public class TestsSection : ConfigurationSection
   {
      …
   }
}

We will fill this class in as we progress.  At this point we have discussed enough to get the subsystem to load our example XML and return an instance of the TestsSection class.

Configuration Manager

In v2+ you will use the ConfigurationManager static class to interact with the configuration subsystem.  This class provides some useful functionality but the only one we care about right now is GetSection().  This method requires the name of a section and, upon return, will give us back an instance of the associated configuration section class with the configuration data.  Here is how our test engine would get the list of tests to run. 

using System;
using System.Configuration;> 
using TestFramework.Configuration;

namespace TestFramework
{
   class TestEngine
   {
      public void LoadTests ( )
      {
         TestsSection section = 
             ConfigurationManager.GetSection(“tests”as TestsSection;
         …
      }
   }
}

The subsystem will only parse a section once.  Subsequent calls will return the same data.  If the file is changed externally then the changes will not be seen without restarting the application.  You can force the subsystem to reload the data from disk by using ConfigurationManager.RefreshSection.

ConfigurationManager is in the System.Configuration namespace of the same named assembly.  This assembly is not automatically added as a reference so you will need to add it manually.  For those of you familiar with the v1.x subsystem, especially AppSettings, note that most of the classes are obsolete.  You should use ConfigurationManager for all new development.

As an aside almost all errors in the subsystem will cause an exception of type ConfigurationException or a derived class.  It can be difficult to tell from the exception what went wrong.  Badly formed XML or a missing section handler, for example, will generate a generic error saying the subsystem failed to initialize.  Use exception handling around all access to the subsystem but do not expect to be able to generate useful error messages from the resulting exception.

Configuration Properties

XML elements normally have one or more attributes associated with them.  In the target XML the tests element has a version and logging attribute.  The version is used for managing multiple versions of the engine and must be specified.  The logging attribute specifies that the engine should log all test runs.  It defaults to false.  Modify the example XML to include these attributes.  Trying to load the tests at this point will cause an exception because the attributes are not supported by the configuration section.

For each attribute on a section/element a configuration property must be defined.  Configuration properties can be defined either declaratively through attributes or programmatically.  Declarative properties use an attribute on public properties to identify configuration properties.  Programmatically declaring configuration properties requires that a configuration property field be created for each attribute.  Declarative properties are easier to write and understand but run slightly slower than programmatic properties.  Otherwise either approach can be used or they can be used together. 

Declaratively

To define a property declaratively do the following.

  1. Declare a public property in the section/element class.
  2. Define a getter and, optionally, a setter for the property.
  3. Add a ConfigurationProperty attribute to the property.

The public property name will generally match the XML attribute but use Pascal casing.  The type will be a standard value type such as bool, int or string.  Use the type most appropriate for the property.

There are no fields to back the properties.  The subsystem is responsible for managing the property values.  The base class defines an indexed operator for the section/element that accepts a string parameter.  The parameter is assumed to be the name of an XML attribute.  The base class will look up the attribute and get or set the value as needed.  The properties are assumed to be objects so casting will be necessary.

The ConfigurationProperty only requires the name of the XML attribute.  There are several optional parameters that can be specified as well.

Parameter Default Meaning
DefaultValue None If the attribute is not specified then the corresponding property will have the specified value.
IsDefaultCollection False Used for collections.
IsKey False Used for collections.
IsRequired False True to specify that the attribute is required.
Options None Additional options.

The DefaultValue parameter should be used to give a property a default value.  Since the base class manages property values rather than using fields it is not necessary to worry about this parameter in code.

The IsRequired parameter specifies that the attribute must be specified.  If it is not then an exception will occur.  This parameter should be used for properties that can have no reasonable default value.  The IsRequired parameter for the attribute does not work when applied to a child element.  The subsystem will automatically create a new instance of any child elements when it reflects across the configuration properties.  Later when the subsystem tries to verify that all required properties have received a value it cannot tell a difference between default initialized elements and those that were contained in the configuration file.  To use the IsRequired parameter with a child element you must programmatically declare the property instead.

Here is the modified TestsSection class with the configuration properties declaratively defined.  After modifying the code run it and verify the attribute values are correct.  Try removing each attribute from the example XML and see what happens.

public class TestsSection : ConfigurationSection 

    [ConfigurationProperty(“logging”, DefaultValue=false)] 
    public bool Logging 
    { 
        get { return (bool)this[“logging”]; } 
        set { this[“logging”] = value; } 
    } 

    [ConfigurationProperty(“version”, IsRequired=true)] 
    public string Version 
    { 
        get { return this[“version”as string; } 
        set { this[“version”] = value; } 
    } 

 

Programmatically

To define a property programmatically do the following.

  1. Create a field for each attribute of type ConfigurationProperty.
  2. Add each field to the Properties collection of the base class.
  3. Declare a public property for each attribute.
  4. Define a getter and, optional, a setter for each property.

The configuration field that is created for each attribute is used in lieu of an attribute.  The constructor accepts basically the same set of parameters.  Once the fields have been created they must be associated with the section/element.  The configuration properties associated with a section/element are stored in the Properties collection of the base class.  When using declarative programming the properties are added automatically.  In the programmatic approach this must be done manually.  The properties cannot be changed once created so it is best to add the configuration  properties to the collection in the constructor.

A public property for each attribute is created in a similar manner as with declarative programming. The only difference is the lack of an attribute on the property.  Since the configuration property is a field in the class you can use the field rather than the property name if desired.

Here is a modified TestsSection class using the programmatic approach. 

public class TestsSection : ConfigurationSection 

    public TestsSection () 
    { 
        Properties.Add(m_propLogging); 
        Properties.Add(m_propVersion); 
    } 

    public bool Logging 
    { 
        get { return (bool)this[m_propLogging]; } 
        set { this[m_propLogging] = value; } 
    } 

    public string Version 
    { 
        get { return (string)this[m_propVersion]; } 
        set { this[m_propVersion] = value; } 
    } 

    private ConfigurationProperty m_propLogging = 
        new
 ConfigurationProperty(“logging”typeof(bool), false);
    private ConfigurationProperty m_propVersion = 
        new
 ConfigurationProperty(“version”typeof(string), 
               null, ConfigurationPropertyOptions .IsRequired); 
}

The programmatic approach can be optimized by using static fields and static constructors.  But that requires more advanced changes so we won’t cover that today.

Validation

The subsystem will ensure that an XML attribute can be converted to the type of the property.  If it cannot then an exception will occur.  There are times though that you want to do even more validation.  For example you might want to ensure a string is in a certain format or that a number is within a certain range.  For that you can apply the validator attribute to the property as well.  There are several different validators available and you can define your own.  The following table defines some of them.

Type Description
CallbackValidator Calls a method for more complex validation.
IntegerValidator Validates a numeric value including range and precision.
LongValidator Same As IntegerValidator but applies to longs.  (Notice that MS failed to follow the naming guidelines for this type :})
PositiveTimeSpanValidator Validates a time duration.
RegexStringValidator Validates a string against a regular expression.
StringValidator Validates a string for length and content.
SubclassTypeValidator Validates the type of a value.
TimeSpanValidator Validates a time duration.

The type name given is the name of the underlying validator class that is used.  You can, if you like, create an instance of the type and do the validation manually.  More likely though you’ll apply the attribute of the same name instead.  Here is a modified version of the version attribute to ensure that it is of the form x.y.

[ConfigurationProperty(“version”, IsRequired = true
[RegexStringValidator(@”(d+(.d+)?)?”)] 
public string Version 

   get { return this[“version”as string; } 
   set { this[“version”] = value; } 
}

If you are good with regular expressions you might have noticed that the expression allows for an empty string.  This is a peculiarity of the subsystem.  It will call the (at least the Regex) validator twice.  The first time it passes an empty string.  The validator must treat an empty string as valid otherwise an exception will occur.

Child Elements

Now that we can define sections and properties all we have to do is add support for child elements and we’re done.  A child element, as already mentioned, is nothing more than a configuration element.  In fact all that is needed to support child elements is a new configuration element class with the child configuration properties.  Remember that a configuration section is just a top-level configuration element.  Everything we have discussed up to now applies to configuration elements as well.

Here is the declaration for the configuration element to back the test XML element.  The properties are included.

public class TestElement : ConfigurationElement 

   [ConfigurationProperty(“async”, DefaultValue=false)] 
   public bool Async 
   { 
      get { return (bool)this[“async”]; } 
      set { this[“async”] = value; } 
   } 

   [ConfigurationProperty(“failureAction”, DefaultValue=“Continue”)] 
   public FailureAction FailureAction 
   { 
      get { return (FailureAction)this[“failureAction”]; } 
      set { this[“failureAction”] = value; } 
   } 

   [ConfigurationProperty(“name”, IsKey=true, IsRequired=true)] 
   public string Name 
   { 
      get { return this[“name”as string; } 
      set { this[“name”] = value; } 
   } 

   [ConfigurationProperty(“timeOut”, DefaultValue=120)] 
   [IntegerValidator(MinValue=0, MaxValue=300)]
   public int TimeOut 
   { 
      get { return (int)this[“timeOut”]; } 
      set { this[“timeOut”] = value; } 
   } 

   [ConfigurationProperty(“type”, IsRequired = true)] 
   public string Type 
   { 
      get { return this[“type”as string; } 
      set { this[“type”] = value; } 
   } 
}

public enum FailureAction { Continue = 0, Abort, }

A couple of things to note.  FailureAction is actually an enumeration.  This is perfectly valid.  The only restriction is that the attribute value must be a, properly cased, member of the enumeration.  Numbers are not allowed. 

The second thing to note is the IsKey parameter applied to Name.  Only one property can have this parameter set.  It is used to uniquely identify the element within a collection of elements.  We will discuss it shortly.

The configuration section needs to be modified to expose the element as a child.  Here is the modified class definition.

public class TestsSection : ConfigurationSection 

   …

   [ConfigurationProperty(“test”)] 
   public TestElement Test 
   { 
       get
 { return this[“test”as TestElement; } 
   } 
}

Notice that we did not add a setter here since the user will not be adding the test explicitly.  Modify the example XML to include (only) one of the elements from the target XML.  Comment out any parameter elements for now.  Compile and run the code to confirm everything is working properly.

As an exercise try creating the parameter child element yourself.  Modify the example XML to include (only) one of the elements from the target XML and verify it is working properly.  Do not forget to update the TestElement class to support the parameter.

Collections

The final piece of the configuration puzzle is collections.  To support a collection of elements a configuration collection class must be created.  This collection is technically just a class deriving from ConfigurationElementCollection.  What makes collections so frustrating is that there are quite a few things that have to be done to use them in a friendly manner.  Add to that confusing documentation and incorrect examples and it is easy to see why people are confused.

To keep things simple for now we are going to temporarily modify our example XML to fall in line with the default collection behavior.  We will then slowly morph it into what we want.  We will add support for multiple tests in the section first.  Here are the steps for adding a collection with default behavior to a section/element.

  1. Create a new collection class deriving from ConfigurationElementCollection.
  2. Add a ConfigurationCollection attribute to the class.
  3. Override the CreateNewElement method to create an instance of the appropriate type.
  4. Override the GetElementKey method to return the key property of an element.
  5. In the parent element modify the public property to return an instance of the collection type.

Here is boilerplate code for an element collection.  In fact you can create a generic base class if you want.  You’ll see why that is a good idea later.

[ConfigurationCollection(typeof(TestElement))] 
public class TestElementCollection : ConfigurationElementCollection 

   protected override ConfigurationElement CreateNewElement () 
   { return new TestElement(); } 

   protected override object GetElementKey ( 
           ConfigurationElement element ) 
   { return ((TestElement)element).Name; } 
}

CreateNewElement is called when the subsystem wants to add a new element to the collection as it is parsing.  The GetElementKey method is used to map an element to a unique key.  This is where the IsKey parameter comes in.  These are the only methods that have to be implemented but it is generally advisable to add additional methods for adding, finding and removing elements if the collection can be written to in code.

Now that the collection is defined it needs to be hooked up to the parent element.  Here is the updated TestsSection class property definition.  Notice that the property name and the XML element name were changed to clarify that it is a collection.

[ConfigurationProperty(“tests”)] 
public TestElementCollection Tests 

    get 
    { 
        return this[[“tests”as TestElementCollection; 
    } 
}

As an aside the ConfigurationCollection element can be applied to the public property in the parent class rather than on the collection type itself.  This might be useful when a single collection type can be used in several different situations.  In general though apply the attribute to the collection type.

The final changes we need to make are to the example XML itself.  The collection of tests need to be contained in a child element rather than directly in the section because that is how the collection is defined.  Additionally the default collection behavior is to treat the collection as a dictionary where each element maps to a unique key.  Elements in the collection can be added or removed or the entire collection cleared using the XML elements: add, remove and clear; respectively.  Here is the (temporary) updated example XML fragment.

<tests version=”1.0” logging=”True“>
    <tests>
        <add name=”VerifyWebServer” 
             type
=”TestFramework.Tests.ServerAvailable” 
             failureAction
=”Abort“>
            <parameter name=”url” value=”http:\www.myserver.com“>
        </add>
        <add name=”CheckService1” 
             type
=”TestFramework.Tests.WebServiceInvoke” 
             async
=”true” timeOut=”120“>
            <parameter name=”url” 
                 value
=”http:\www.myserver.comservice1.asmx” />
            <!–<parameter name=”parameter_1” value=”hello” />
              <parameter name=”returns” value=”HELLO” />–>                       
        </add>
    </tests>
</tests>

 

Altering Nameses

The first thing that you will likely want to change is the name used to add new items to the collection.  It is standard to use the element name when adding new items.  To change the element name for adding, removing and clearing items use the optional parameters on the ConfigurationCollection

[ConfigurationCollection(typeof(TestElement), AddItemName=“test”)] 
public class TestElementCollection : ConfigurationElementCollection
{
   …
}

 

<tests version=”1.0” logging=”True“>
    <tests>
        <test name=”VerifyWebServer” 
              type
=”TestFramework.Tests.ServerAvailable” failureAction=”Abort“>
            <parameter name=”url” value=”http:\www.myserver.com” />                  
        </test>
        …
           
    </tests>
</tests>

 

Default Collectionon

Having a parent element for a collection is necessary when you are dealing with multiple collections inside a single parent element.  Normally however this is not the case.  You can eliminate the need for an element around the collection children by using the default collection option on the configuration property.

Modify the configuration property that represents the default collection to include the IsDefaultCollection parameter.  Set the name of the property to an empty string.  If this is not done then the subsystem will fail the request.  During parsing any element that is found that does not match an existing configuration property will automatically be treated as a child of the default collection.  There can be only one default collection per element.

Here is the TestsSection modified to have Tests be the default collection.  The example XML follows.

public class TestsSection : ConfigurationSection 

   …

   [ConfigurationProperty(“”, IsDefaultCollection=true)] 
   public TestElementCollection Tests 
   { 
      get { return this[“”as TestElementCollection; } 
   } 
}

 

<tests version=”1.0” logging=”True“>
    <test name=”VerifyWebServer” 
          type
=”TestFramework.Tests.ServerAvailable” 
          failureAction
=”Abort“>
        <!–<parameter name=”url” value=”http:\www.myserver.com” />–>           
    </test>
    <test name=”CheckService1” 
          type
=”TestFramework.Tests.WebServiceInvoke” 
          async
=”true” timeOut=”120“>
        <!–<parameter name=”url” value=”http:\www.myserver.comservice1.asmx” /> 
        <parameter name=”parameter_1” value=”hello” /> 
        <parameter name=”returns” value=”HELLO” />–>            
    </test>
</tests>  

 

Collection Options

The default collection type allows for elements to be added, removed or the entire list cleared.  This is often not what is desired.  The alternative collection type allows for new elements to be added only.  To tell the subsystem that the collection should not allow changes to the existing elements and to allow only new elements it is necessary to overload a couple properties in the collection class.   

The CollectionType property specifies the type of the collection being used.  The default is AddRemoveClearMap which specifies a modifiable collection.  The alternative is BasicMap which allows only additions.  In the case of a basic map the add, remove and clear item names are not used.  Instead it is necessary to override the ElementName property to specify the name of child elements.

[ConfigurationCollection(typeof(TestElement))] 
public class TestElementCollection : ConfigurationElementCollection 

   protected override string ElementName 
   { 
      get { return “test”; } 
   } 

   public override ConfigurationElementCollectionType CollectionType 
   { 
      get { return ConfigurationElementCollectionType.BasicMap; } 
   }
   …
}

A warning about basic maps.  The collection type is a parameter to the ConfigurationCollection attribute.  However it does not appear to work properly when using a basic map.  Stick with overriding the property instead.

Now that you have seen how to add support for collections try updating the TestElement class to support multiple parameters using a default basic map collection.  At this point everything has been covered to create the code to read the target XML from the beginning of the article.

Updating Configurations

One of the features added in v2.x of the configuration subsystem was the ability to modify and save the configuration data.  While application configurations should remain read-only (for security purposes), user configuration files can be modified.  The example code is going to be modified to allow new tests to be added and saved.

To support modification of a configuration section/element the properties must support setters.  In our example code we made all the properties settable so we do not need to make any changes.  Some properties can have setters and others not.  It is all dependent upon what the configuration section needs to support.  The exception to the rule is collections.  By default a collection does not expose any methods to modify the collection elements.  It is necessary to manually add the appropriate methods if configuration collections can be modified.  Additionally the property IsReadOnly method must be overloaded to allow modifying the collection.

The following modifications need to be made to the test collection to support adding new tests, removing existing tests and clearing the collection.

[ConfigurationCollection(typeof(TestElement))] 
public class TestElementCollection : ConfigurationElementCollection 
{
   …
   
   public override bool IsReadOnly () 
   { return false; } 

   public void Add ( TestElement element ) 
   { BaseAdd(element); } 

   public void Clear () 
   { BaseClear(); } 

   public void Remove ( TestElement element ) 
   { BaseRemove(element.Name); } 

   public void Remove ( string name ) 
   { BaseRemove(name); } 
}

For test purposes the engine will be modified to generate a new test section (with a dummy test) if none can be found in the configuration file.

public void LoadTests () 

   //1 – Try and get the section
   m_Section = ConfigurationManager.GetSection(“tests”)
                   as TestsSection; 
   if ((m_Section == null) || 
       !m_Section.ElementInformation.IsPresent) 
   { 
      //2 – Open the configuration file 
      System.Configuration.Configuration cfg = 
            ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); 

      //3 – Create the section if necessary
      if (cfg.Sections[“tests”] == null
         cfg.Sections.Add(“tests”new TestsSection()); 
      m_Section = cfg.GetSection(“tests”as TestsSection; 

      //4 – Add a dummy test 
      TestElement test = new TestElement(); 
      test.Name = “Dummy Test”
      test.Type = “DummyTest”
      m_Section.Tests.Add(test); 

      //5 – Save the changes 
      m_Section.SectionInformation.ForceSave = true
      cfg.Save(); 
   }; 
}

Let’s walk through the code.  The engine first tries to get the section (1).  If it fails to get the section then it will create a new one.  The configuration subsystem (contrary to documentation) seems to always return an instance of the section handler even if the actual section does not exist in the file.  The example code checks to determine if the section actually exists or not.

The subsystem uses the Configuration class (not the namespace) to represent a configuration file.  ConfigurationManager maintains an instance internally for the application configuration but this field is not exposed.  Instead it is necessary to explicitly open the configuration file and modify it.  Earlier it was mentioned that the data is only parsed once and that remains true.  However multiple instances of the section class are returned.  Changes made in one instance of a section are not visible in another. 

The engine next (2) opens the configuration file explicitly.  The engine then (3) creates a new section in the off chance that it did not exist yet.  Now the engine (4) creates a dummy test and adds it to the section.  Finally (5) the updated section is saved back to disk.

Temporarily comment out the tests element in the XML file and run the code.  Look at the XML file and confirm the new test was created.  What! It wasn’t?  Actually it was.  The problem is that the debugger is getting in the way.  By default the vshost process is used to run the program.  As a result the actual configuration file is <app>.vshost.exe.config.  Additionally this file is overwritten when debugging starts and ends.  Hence you are likely to miss the change.  Place a breakpoint at the end of the LoadTests method and run it again.  Now examine the configuration file to confirm the changes were made.

There are many more things that can be done to update the configuration file.  You can save the file elsewhere, save only some changes or even modify other files.  The preceding discussion should be sufficient to get you started though.

Dynamic Sections

The configuration subsystem is based upon deterministic parsing.  At any point if the subsystem cannot match an XML element/attribute to a configuration element/property it will throw an exception.  Configuration elements/sections expose two overridable methods (OnDeserializeUnrecognizedAttribute and OnDeserializeUnrecognizedElement) that are called if the parse finds an unknown element/attribute during parsing.  These methods can be used to support simple dynamic parsing.

For unknown attributes the method gets the name and value that was parsed.  If the method returns true then the subsystem assumes the attribute was handled otherwise an exception is thrown.  The following method (added to TestElement) silently ignores a legacy attribute applied to a test.  Notice that the element is compared using case sensitivity.  Since XML is case sensitive comparisons should be as well.

protected override bool OnDeserializeUnrecognizedAttribute ( 
         string
 name, string value ) 

   //Ignore legacy baseType attribute 
   if (String.Compare(name, “baseType”
           StringComparison.Ordinal) == 0
      return true

   return base.OnDeserializeUnrecognizedAttribute(name, value); 
}

For unknown elements the method must parse the XML manually and return true to avoid an exception.  The important thing to remember about this method is that all child elements must be parsed otherwise the subsystem will not recognize the element and call the method again.  The following method (added to TestElement) silently ignores a legacy child element that contained some initialization logic.  In this particular case the child elements are not important (or parsed) so they are skipped.

protected override bool OnDeserializeUnrecognizedElement ( 
           string
 elementName, System.Xml.XmlReader reader ) 

   //Ignore legacy initialize element and all its children 
   if (String.Compare(elementName, “initialize”
           StringComparison.Ordinal) == 0
   { 
      reader.Skip(); 
      return true
   }; 
  
   return base.OnDeserializeUnrecognizedElement(elementName, reader); 
}

A word of caution is in order when using collections.  If a collection’s item name properties have been modified (for example from add to test) then the method is called for each item.  The underlying collection overrides this method to handle the item name overrides.  Therefore do not assume that just because this method is called a truly unknown element has been found.

Before getting any wild ideas of how to get around the subsystem’s restrictions on element contents be aware that you cannot use the above methods to parse certain XML elements including CDECLs and element text.  These XML entities will always cause the subsystem to throw an exception.

Standard Sections

The v1.x subsystem supported several standard section types that continue to be useful.  They allow for storing custom settings without creating a custom section handler. The only downside is that they cannot be configured. 

DictionarySectionHandler can be used to store a set of key-value pairs in a section.  The following example demonstrates such a section.

<configSections>
         <section name=”settings” 
                  type
=”System.Configuration.DictionarySectionHandler” />
   </configSections>

   <settings>
         <add key=”Setting1” value=”1” />
         <add key=”Setting2” value=”2” />
         <add key=”Setting3” value=”3” />
   </settings>

Here is how it would be used.  Notice that the return value is Hashtable rather than a section handler instance.

Hashtable settings = ConfigurationManager.GetSection(“settings”as Hashtable;

The NameValueSectionHandler works identically to DictionarySectionHandler except the returned value is NameValueCollection.

The SingleTagSectionHandler is used to store a single element with attribute-value pairs.  The returned value is a Hashtable where the attribute names are the keys.

The three legacy section handlers can be used in lieu of creating custom section handlers when simple dictionaries or attribute-value pairs are needed.  As a tradeoff they do not support any of the advanced functionality of the subsystem including modification, validation or default values.

Implementing IDisposable

This article will attempt to explain what the IDisposable is, when it should implement it and how to implement it properly.

Why Do We Need It

Anyone who has worked with .NET for any length of time knows that .NET uses automatic memory management.  The garbage collector (GC) runs in the background and will free up memory as needed.  This is a non-deterministic approach meaning there is little control over when it occurs.

In general this is a good thing.  Does it really matter whether or not when memory gets freed?  In general, no.  However some objects really must have deterministic behavior.  The canoncial example would be any class that uses unmanaged, shared resources like database connections, file handles or synchronization objects.  In these cases it is important to be able to free these objects when they are no longer needed.

Ideally the framework would detect that an object is no longer needed as soon as it occurs and automatically free up the memory.  However this would put an undo strain on the system.  Therefore for deterministic clean up it is still necessary for developers to manually free objects when they are no longer needed.  So how does a developer know when they should free an object or let .NET handle it.  Enter IDisposable.  This interface identifies a type that must be freed when it is no longer needed.  Of course there is always the chance that the user will forget to free the object so .NET still has to ensure that the object gets freed at some future point.

IDisposable

IDisposable has only a single member called Dispose.  This method is called when the object needs to be freed.  Internally .NET will always call this method when the object is freed.  However users should also call this method when the object is no longer needed.  Within this method any shared/unmanaged resources should be released.  Here is an example of file-type class’es implementation of the interface.

public class FileBase : IDisposable 

   private IntPtr m_pUnmanagedResource; 

   public void Dispose () 
   { 
      if (m_pUnmanagedResource != IntPtr.Zero) 
      { 
         //Free it 
         m_pUnmanagedResource = IntPtr.Zero; 
      }; 
   } 
}

This implementation uses implicit interface implementation support to expose a public Dispose method that clients can call to clean up the resource.  Dispose does not necessarily mean much to a caller so a separate method can be created that internally does the clean up and then explicitly implement the interface like so.

public class FileBase : IDisposable 

   private IntPtr m_pUnmanagedResource; 

   public void Close () 
   { 
      if (m_pUnmanagedResource != IntPtr.Zero) 
      { 
         //Free it 
         m_pUnmanagedResource = IntPtr.Zero; 
      }; 
   } 

   void IDisposable.Dispose () 
   { Close(); } 
}

Now the act of closing a file is exposed while under the hood the close method is used to dispose of the unmanaged resources.

Ensuring Clean Up

The above code handles the case where the user will remember to clean up the object when they are done but it still does not handle the case of .NET itself cleaning up the object.  In order to run code when an object is to be freed a finalizer is needed for the class.  A finalizer actually delays the process of freeing the object but it allows clean up code to execute.  Whenever IDisposable is implemented it is important to analyze whether a finalizer is also needed (see below for more information).  Whenever a finalizer is defined IDisposable should also be implemented.

For the example class the Close method should be called to clean up the unmanaged resource.

public class FileBase : IDisposable 

   ~FileBase () 
   { 
      Close(); 
   } 

   private IntPtr m_pUnmanagedResource; 

   public void Close () 
   { 
      if (m_pUnmanagedResource != IntPtr.Zero) 
      { 
         //Free it 
         m_pUnmanagedResource = IntPtr.Zero; 
      }; 
   } 

   void IDisposable.Dispose () 
   { Close(); } 
}

The above code ensures that the file is closed whether the user does it manually or not but there is a problem.  GC is non-deterministic for all objects.  Any reference fields within the class might or might not have already been freed by the time the finalizer is called.  When the finalizer is called the code cannot refer to any reference fields within the class.  A method is needed to tell the Close method not to refer to these fields.  The defacto method is to define a private (or protected) method called Dispose that accepts a boolean argument indicating whether the object is being disposed (i.e manually) or not (being invoked through GC).  Within this helper method is where the actual clean up work is done.

public class FileBase : IDisposable 

   ~FileBase () 
   { 
      Dispose(false); 
   } 

   private IntPtr m_pUnmanagedResource; 

   public void Close () 
   { 
      Dispose(true); 
   } 

   private void Dispose ( bool disposing ) 
   { 
      if (disposing) 
      { 
         //We can access reference fields in here only 
      }; 

      //Only value fields and unmanaged fields are  
      //accessible from this point on 
      if (m_pUnmanagedResource != IntPtr.Zero) 
      { 
         //Free it 
         m_pUnmanagedResource = IntPtr.Zero; 
      }; 
   } 

   void IDisposable.Dispose () 
   { Close(); } 
}

The above code works but is suboptimal in, what is hoped, the common case of a client explicitly freeing the object.  If the client calls Close then the object does not need to be finalized anymore.  What is needed is a way to tell .NET not to call the finalizer if the object has been disposed.  This requires a one line addition to the Close method (or whatever method is the explicit cleanup method).

public void Close () 

   Dispose(true); 
   GC.SuppressFinalize(this); 
}

The GC.SuppressFinalize method tells .NET not to call the finalizer for the object specified as a parameter.  Since the object has already been cleaned up there is no benefit in calling it anyway.  This is only needed for classes with finalizers. 

This completes the implementation of IDisposable.

Using 

C# and VB.NET both support the using statement.  This statement should be used whenever dealing with IDisposable objects.  The statement ensures that the object is explicitly disposed when it goes out of scope.  Since this is the best behavior it should be used it in almost all cases.  Here is an example of using the statement.

public string ReadFile ( string fileName ) 

   using(File file = new File(fileName)) 
   { 
      … 
   }; 
}

When the File object goes out of scope at the end of the using statement the IDisposable.Dispose method will be automatically called.  In the example code it will internally call Close which calls Dispose(true) to clean up the unmanaged resources.  Even if an exception occurs the object will be disposed.

In the few cases where using can not be used then use a try-finally block instead, like so.

public string ReadFile ( string fileName ) 

   File file = null

    try 
   { 
      … 
   } finally 
   { 
      if (file != null
         file.Close(); 
   }; 
}

This is not as clean as using but it works.

When To Implement

The IDisposable interface should only be implemented when it is needed.  Here are the common cases where it should be implemented. 

  1. When a type contains an unmanaged or shared resource it should implement the interface and a finalizer.
  2. When a type contains fields that implement IDisposable then the type should implement the interface but it SHOULD NOT implement a finalizer.
  3. When a type uses a lot of memory internally it should consider implementing IDisposable but it SHOULD NOT implement a finalizer.

Caveats

Finally here are some caveats about the interface.

  1. An object can be disposed multiple times.  Therefore the dispose method must handle this case.
  2. When the GC calls a finalizer it will be on an arbitrary thread.  Do not access any thread-specific values.
  3. The GC runs all finalizers on the same thread and there is no exception handling so the dispose method should not throw exceptions nor deadlock.
  4. Some objects can support resurrection (meaning they are disposed and then recreated).  This is difficult to do properly and should be avoided.  Assume that a disposed object is permanently gone.
  5. The dispose method can be called on multiple threads so thread-safety should be taken into account.
  6. When called from the finalizer the dispose method can not reference any reference fields.

Distinguishing .NET Versions

.NET versions can be confusing when you are trying to determine what version of the framework a program uses.  Gone are the days of all binaries carrying the same version number.  This article will attempt to explain the current (as of v4) versioning of .NET assemblies.

Versioning Made Confusing

First it is necessary to clarify that there are actually two different version numbers in .NET: framework and runtime.  The runtime version determines what version of the CLR a program uses.  The framework version determines what version of the framework is being used.  In this article framework versions will be preceded by a v (as in v1.0 or v2.0) whereas the runtime version will be listed as simply a number.

The following table identifies the different runtime and framework versions.

Framework Version Assembly Version Runtime Version
1.0 1.0.0.0 1.0
1.1 1.1.0.0 (?)
2.0 2.0.0.0 2.0
3.0 3.0.0.0
3.5 3.5.0.0
4.0 4.0.?.? 4.0

As can be seen from the table there are currently three versions of the runtime while there are quite a few versions of the framework. The above table ignores the updates to the versions due to service packs.  Implicit in the table is the fact that if you install any version of the framework from v2-v3.5 on you will get the v2 runtime.  Therefore it is not necessary to install v2 and then v3.0 and then v3.5.  Just install v3.5 if you want v2.0 support.  v4 is a new runtime so if an application needs v2 support then a previous framework must be installed as well.

As evident from the table the current runtime version is 2 and it has been in use for several framework versions.  You can confirm this by looking at some of the core system assemblies such as Mscorlib, System or System.Windows.Forms.  Each of these assemblies shipped with .NET originally or were added in v2.0 and therefore contain a version number of 2.0.0.0, in Visual Studio.

When v3.0 came out it made a few changes to the core assemblies (a service pack, if you will) and added a few new assemblies (WPF and WCF).  Since the runtime did not change the runtime version remains 2.  However since the new WPF and WCF assemblies were added in v3.0 they received an assembly version of 3.0.0.0.

With the release of v3.5 some new assemblies were added.  Again, since the runtime did not change (still 2) the core assemblies remain 2.0.0.0 even though they were updated (a service pack that was also released for v2.0).  The WPF and WCF assemblies from v3.0 were also updated but remain 3.0.0.0.  The new assemblies added for v3.5 (LINQ, for example) get an assembly version of 3.5.0.0.

(NOTE: v4 will be released in 2010 so it will be come the new standard going forward.  Visual Studio 2010 will ship with only the v4 framework.  VS2010 will support previous versions but you must install them manually.  VS 2010 does support loading projects from previous versions.  If you load a legacy project and the appropriate framework is not installed then you will get a dialog prompting you to: download the missing version, ignore the project or retarget to the newer version.  Unfortunately retargeting is the only real option.  VS will not recognize any framework not available when it loaded.  Therefore if you try to load a legacy solution then you will get this dialog for each project unless the legacy framework is installed.  Be sure to install v3.5 before trying to convert a VS 2008 project under VS 2010.)

Determining the Version

Confused yet?  A general guideline you can use is to look at the version of the assembly.  It is an indication of either which framework the assembly was introduced in or the runtime version that it was built for.  Programmatically you can use Environment.Version to get the runtime version of an application.  For a specific assembly you can use Assembly.ImageRuntimeVersion to get the runtime version the assembly was built for.  In most cases it will be the same version as the application being run but, due to versioning policies, the assembly’s runtime version might be lower than the application’s runtime version.  It can never be higher.

At this time there is no real way to determine the framework version an application was built against.  The framework version is predominantly for determining which assemblies to reference anyway and what features to enable so it does not really have any runtime significants.  If you truly must know then you can use hueristics to find the highest assembly version for the system assemblies.  Once you have that you’ll have the (minimal) framework version.  As an aside note that the runtime enforces only the runtime version number of an assembly.  If you were to try to load a v2 application on a machine without v2 installed you’ll get an error saying the runtime version is invalid.  However if you try to load a v3.5 application on a machine with only v3.0 it might or might not work depending upon whether you actually use any v3.5 features and reference any v3.5-only assemblies. 

To determine the runtime (CLR) version use Environment.Version.

Side-by-side Versioning

Prior to v4 the application determined the CLR version to use.  If you built a v3.5 app then you used the v2 runtime.  This can cause all sorts of problems – take two examples.  In example one an assembly written for v1 is loaded.  The assembly was built and tested against v1.1 and might not be compatible with the newer v2 framework.  In example two we have a v1.1 app that attempts to load a v2 assembly.  In this case the assembly might not have all the features it expected and will generate an error.  Neither of these are good scenarios.

Starting with v4 a single application can actually be running multiple versions of the framework at the same time.  The goal was to allow each assembly to run against the version it was built against.  Can this cause issues?  Most definitely but time will tell if it was worth the effort involved.  A full discussion of side-by-side runtime versioning is beyond the scope of this article.  Refer to the following link for more information: http://msdn.microsoft.com/en-us/magazine/ee819091.aspx

Benefits of Virtual Machines

Virtual machines have been around for a long time but they really have not become common place in many development shops.  It is unfortunate since virtual machines provide so many benefits to developers and testers alike.  This article will discuss some of the benefits of virtual machines and review two of the most popular virtual machine software packages available for Windows.

A Virtual Machine?

A virtual machine, simply put, is a virtual computer running on a physical computer.  The virtual machine emulates a physical machine in software.  This includes not only the processor but the instruction set, the memory bus, any BIOS commands and critical machine hardware such as the system clock and and DMA hardware.  Depending upon the machine peripheral devices are generally virtualized including storage devices like floppy drives, hard drives and CD drives.  Video, keyboard and mouse support are also common.  A virtual machine must look and act just like the real thing so standard software, like operating systems and applications, can run without modification. 

Emulation software (the term we will use for applications that create and run virtual machines) generally define a basic machine to emulate rather than supporting a wide variety of devices.  This reduces the amount of work that must be done and keeps things simple.  For Windows-based emulations you can expect a Pentium 4+ processor with basic SCSI and/or IDE drive support, floppy disk and basic CD/DVD reading along with all the required hardware.  This is enough to run most applications.  So even if you are running a multiprocessor non-Intel processor the virtual machines will still see a Pentium 4.  The emulation software is responsible for mapping the virtual devices back to the real devices, when appropriate.  For example writes to the virtual hard drive must be written to the backing file for the drive.

Emulation software generally allows for some manipulation of the virtual devices.  At a minimum this would generally include how much memory to make accessible to the virtual machine, how many (and how large) the hard drives are, whether sound cards or ports are available, etc.  These virtual machine settings are generally stored in a custom file by the emulation software.  Additionally the virtual hard drives are also generally stored as files.  These files can get really large since they are emulating a real computer. 

In emulation software the machine running the virtual machines (in our case Windows) is known as the host.  The virtual machine itself is known as the guest.

Why Does It Matter To Me?

So what does this mean to developers and testers.  Let’s look at a few scenarios that developers and testers find themselves in.  For testers it is important that they test software against the various supported operating systems that an application runs against.  The traditional approach is to run multiple physical machines, each with a different operating system.  This is bad for several reasons.  Space, maintenance, power and feasibility come to mind.  Deployment of the software to these various machines can also be an issue.  Instead a tester can run multiple virtual machines on one physical machine.  Each virtual machine could have a different operating system.  The application can be deployed to the virtual machines and tested.

Another advantage of virtual machines is reproducibility.  Build and test environments generally need to be well controlled.  It would be undo work to have to wipe out a machine and rebuild it after each build or test run.  A virtual machine allows the environment to be set up once.  The environment is then captured.  Any changes made after the capture can then be thrown away after the build or test run.  Most emulation software packages offer this in some form or another.

Another scenario, your application is currently released as version 1.  Because of how the application is written you can only run a single version of your application on a machine.  When you start development on version 2 you have to remove version 1.  Part way through development an issue is found in the version 1 software that you need to replicate and fix.  You can uninstall version 2 and install version 1, find and fix the issue and then revert back but that is a lot of work.  A nicer approach is to have a virtual machine with version 1 installed.  When you need to go back to version 1 you just start up the virtual machine.  Even better is that you can easily compare the behavior of the two versions side by side rather than having to switch between two computers.

IT departments have already found the benefits of running virtual servers over having multiple physical servers.  Development and testing share many of the same benefits.  Virtualization has become a buzzword in the industry.  Windows is becoming more virtualized so even if you aren’t using virtual machines today you may be in the future.

Which Emulation Software Is Best?

You have decided to try virtual machines out.  Now which software to use?  Fortunately, or perhaps not, there are not too many options available.  Each have their strengths and weaknesses.  First we’ll give a brief overview of each and then we’ll compare them by looking at features important to good emulation software.

Microsoft Virtual PC

Version used: Virtual PC 2007
Website: http://www.microsoft.com/windows/products/winfamily/virtualpc/default.mspx

Microsoft purchased Connectix many years back for their virtual machine software.  They rebranded it Microsoft Virtual PC (VPC).  There have only beeen two versions: 2004 and 2007.  It comes in either PC or Server edition but we will only talk about PC.

VPC is the primary mechanism by which Microsoft deploys demo and beta products to customers.  They generate VPC images that can be downloaded and run.  If you do a lot of beta testing for Microsoft then VPC will be a requirement.

Windows Virtual PC

Version used: Windows Virtual PC
Website: http://www.microsoft.com/windows/virtual-pc/

This is an updated version of Virtual PC.  The reason it is listed separately is because it only supports Windows 7 and later operating systems.  WVPC is basically VPC with some new enhancements.  It is relevant enough that if you are running Windows 7 and you want to use VPC then you should be using WVPC instead.

WVPC supports loading of existing VPC images so you can easily upgrade from VPC.  Once you upgrade though you won’t be able to go back.

One very interesting feature of WVPC (which no other application has) is XP mode.  WVPC ships with (or at least you can download) a free image XP for use in WVPC.  When running this image you can seamlessly integrate any installed application into Windows 7.  What this means is that you can install an application under WVPC and run it directly from Win7.  When you click on the generated shortcut it will start WVPC and the XP image in the background and provide a seamless desktop to run it under.  This mode was developed almost entirely to allow you to run XP applications that normally would not run under Win7.  Primarily this is designed for corporate environments but it works well enough to be of general use. 

VMWare Workstation

Version used: VMWare Workstation 7
Website: http://www.vmware.com

VMWare has been around for years (since at least the 90s). As with VPC there are either workstation or server editions but we will restrict ourselves to the workstation edition.

A nice feature of VMWare is that it can run, albeit with a reconfiguration, VPC images as well.  Once you load it into VMWare though you will no longer be able to use it in VPC.

Qemu

Version used: Qemu v0.9.1
Website: http://fabrice.bellard.free.fr/qemu/

I have little experience with Qemu so I will not review it here.  It’s biggest strengths are that it is open source, free and can emulate non-Intel processors.  Its weaknesses include it is not as stable or easy to use as the other products and it does not perform as well, in my experience.  It is command-line driven although there are some addins that give it a user interface.  It is definitely something to watch for down the road.

Feature Comparisons 

A caveat is in order before we discuss the features.  I used VMWare for many years in the 90s.  I loved all the features it had.  When I switched jobs my new employer would not justify the cost of virtual machines.  At the same time I received a complimentary copy of VPC.  I used VPC for several years since I did not own a copy of VMWare anymore.  Beyond 64-bit support I could not justify the cost of VMWare.  Recently VMWare was nice enough to give me a complimentary copy of VMWare and I now run both versions, at least for now.

Cost

VPC: Free
VMWare Workstation : $199
WVPC: Free (requires Win7)

For some shops cost does not matter but for many it does.  $200 is not much money for software but for single developers, like myself, it can be hard to justify free for most situations.  VPC wins here.  However it is important to note that VMWare has a program called VMWare Player that allows you to run virtual machines.  It is free but does not allow for configuration changes.

Performance

Running a virtualized machine on top of a regular machine is going to be slower in most cases.  Therefore performance of both the guest and host are important.  Virtual machines make heavy use of the processor, memory and hard drive space.  A host system must have a really strong processor (preferably multiprocessor), a lot of memory and a lot of fast hard drive space to get good performance out of a guest.

VPC and VMWare both run about the same in my experience.  VMWare has a slight performance advantage when running guests when certain device options are set (discussed later) but otherwise they both run really well.  VMWare also seems to shut down guests faster than VPC.  However VPC handles undo faster.

WVPC has similar performance to VPC.  WVPC generally prefers to hibernate VMs rather than shutting them down.  This results in faster start ups at the cost of more hard drive space.

Device Support

Common: Pentium 4, SCSI and IDE drives, CD/DVD drive, network cards, SoundBlaster card, SVGA
VPC: —
VMWare: USB devices, multiple processors, 64-bit processors, 3D graphics
WVPC: USB devices, multiple processors

VMWare has superior device support to VPC.  Beyond support for USB devices attached to the host machine VMWare also supports emulating a 64-bit processor.  This is a fundamental feature that may sway many people to VMWare.  64-bit processors have been around a while.  Many people are running 64-bit versions of Windows as a host.  It is therefore logical that people will want to run a 64-bit guest machine.  Only VMWare can do that at this point. 

VMWare also supports 3D graphics with hardware acceleration.  My experience at this point though is that it is sufficent to run basic apps but not sufficient to run 3D games and the like.  It simply is too slow.

WVPC is catching up to VMWare in terms of hardware (especially with XP mode).  It does support multiple processors but only limited.  One area to be concerned about though is the host CPU.  The host CPU must support virtualization otherwise WVPC will not run.  All modern CPUs support this feature but some older processors do not.  Confirm virtualization support before deciding on WVPC.

(UPDATE: 3/28/2010 – Effective immediately WVPC no longer requires a host CPU with virtualization support.  An update to WVPC removes this requirement.)

Operating System Support

VPC: DOS (unsupported), All versions of 32-bit Windows except servers (Servers work but are unsupported), OS/2 Warp (certain versions), Others (unsupported)
VMWare: DOS, All versions of 32/64-bit Windows, most variants of Linux and FreeBSD
WVPC: 32-bit versions of Windows

VPC and VMWare support basically the same operating systems.  If it runs under the virtualized hardware then it will run.  Non-Windows operating systems are unsupported, but work, in VPC.  OS/2 Warp is documented as supported with VPC but I have never gotten it to work properly.  To be fair I have never gotten it to work under VMWare or Qemu either.

WVPC is really designed to get legacy XP machine running under Win7.  It does technically support any 32-bit Windows version but no other operating system is formally supported.  This is actually a step back from VPC’s support.

The big point here is that, since VMWare emulates a 64-bit processor, you can run 64-bit operating systems.  Therefore VMWare takes the win here just for that.  Running a 64-bit guest under 32-bit host can solve many issues when trying to migrate from 32 to 64-bits.  I run this way quite a bit and it works well.

Cloning Support

Both VMWare and VPC support cloning of existing virtual machines but in different ways.  Cloning is important when you want to set up multiple guests with the same, or similar, environment.  Installing an operating system is time consuming.  With cloning you can set up the environment once and then reuse the environment in different guests.  To understand the differences between VPC and VMWare’s approaches you must understand how cloning works.

The simplest approach to cloning is simply copying the virtual machine directory and doing a little renaming.  This works in both VPC and VMWare.  VMWare actually detects this and offers to make a few adjustments.  While doable this is not the best approach because it wastes a lot of space, especially if the cloned guest does not change much.  We will not refer to this approach anymore.

The better approach is to difference a base image.  With differencing only the changes made to a guest are stored.  With differencing the amount of space a clone takes is less because it only tracks the changes.  However it needs the base image to run properly.  Even more important though is that the base image cannot change without invalidating the clone. 

VPC supports cloning but it is not directly obvious how to do it.  Basically you would set up a new virtual machine with a regular virtual drive.  You would then create a new virtual drive.  Within the wizard is an option to create a differencing disk (off an existing virtual drive).  Replacing the original virtual drive created when the virtual machine was created with the new virtual drive results in a clone.  While not obvious, it works.

VMWare does basically the same thing but it exposes these options easily within the main UI.  The clone option is exposed for each virtual machine.  When you select this option you can chose to do a differencing clone or a full copy.  As with VPC this creates a brand new virtual machine.

WVPC follows in VPCs footprints for cloning support.  For WVPC, where you want XP mode support, the best solution is to copy the base XP image and then use it as the hard drive for the new virtual machine.  This is the closest you’ll get to cloning.

For easy of use VMWare wins here but otherwise the products are identical.

Undo Support

Along the same lines as cloning is undo support.  After you’ve made changes to a guest it is sometimes important to undo those changes.  Without direct undo support you would have to resort to cloning.  Both VPC and VMWare support undo. 

VPC/WVPC exposes this as an option in the virtual machine settings.  By default it is disabled so all changes made to the guest are permanent.  When undo support is enabled VPC tracks the changes made for the current session in a separate file (a difference disk again).  When the session finally ends VPC asks if you want to save the changes permanently, save them for later or throw them away.  If you decide to save the changes permanently then VPC will merge the differences in.  This can take up to 10 minutes.  If you opt to save the changes until later then the differencing disk is retained.  When the session starts again you will be able to resume from where you left off.  Finally if you decide to throw away the changes then the differencing disk is deleted. 

VMWare does things differently.  VMWare uses snapshots instead.  With snapshots VMWare basically uses differencing disks for each snapshot beyond the first one.  Unlike VPC it does not prompt after you close a session.  Instead you snapshot the guest when you want and it tracks changes from there.  Another advantage of snapshots is that you can have multiple versions.  With VPC you have the base image with or without changes.  With VMWare you have the base image along with any number of snapshots containing changes.  A big, big negative for VMWare is the number of files needed to support snapshots and all the space they take up.  There really is no good way to clean this mess up even when you don’t want snapshots.

For simple undo support VPC/WVPC wins here but if you want multiple variants then VMWare is the winner.  Personally I prefer the simplistic on/off support of VPC.  Normally I set up a VM with a bunch of changes.  If they stick then I want it permanent.  If they don’t then I want to undo it.  I don’t see the big benefit in multiple undo levels especially when cloning is involved.

Host-Guest Communication

Another critical area for emulation software is how well it integrates the guest and the host.  At a minimal the guest must be able to transfer files to and from the host.  VPC and VMWare both support this but in different ways. 

VPC allows you to identify one or more folders on the host machine to be exposed to the guest.  Each folder is exposed as a separate drive to the guest.  Whether the folder is exposed each time the virtual machine restarts or not is optional.  The guest can have read-only access, if desired.

VMWare also allows you to identify one or more folders on the host machine to be exposed to the guest.  However VMWare exposes all the folders as subfolders of a single network folder.  As with VPC the folders may or may not be writable and can be persisted or not.

WVPC has an even nicer integration over VPC.  It allows you to identify the local drives that you want to directly integrate with in the VM.  This eliminates the need to set up network shares or map drives and is really nice.

I personally prefer WVPC’s approach of using truly integrated drives.  In a close second is VPC’s mapped drives.  While you are limited to 26 shared folders they all look like drives.  With VMWare’s network folder approach the drives are accessed using UNC paths.  For some applications, like .NET, this introduces problems.  For .NET security of a network application is different than a local application.

Another host-guest communication features is copying and pasting.  It is very common to want to copy something (other than a file) from the guest to the host or vice versa.  Both VPC and VMWare support copying and pasting using the clipboard from/to the guest.  This is optional in the products but should generally be left enabled.   

Finally there is actually getting to and from the guest.  VPC creates a separate window for each guest.  To give control to the guest you need only move the mouse within the window and click (by default).    To return control to the host a keystroke is used.  If a certain keystroke is pressed while inside the guest the Ctrl+Alt+Del key sequence is generated.  If you accidentally press Ctrl+Alt+Del while the guest is active then VPC will intercept it and let you know, although the host will still receive it as well.

VMWare follows a similar approach in that moving the mouse within the window and clicking will give the guest control.  It also uses special keystrokes to send the special Windows keystrokes and detects if you press them while inside the guest.  What is different is that VMWare uses a single, tabbed window to house the virtual machines.  Each guest gets its own tab.  Within the tab you can see either the guest window or the configuration window. 

WVPC in normal mode works just like VPC.  In XP mode though you run apps as though they are running natively on the host machine when in fact they are running in VMs.  This is really cool, when you need that feature.

Additions

Each emulation software has tools that it can install into the guest.  These additions generally include optimized video and network drivers and support for the host-guest communications.  Neither product has an advantage here.

VMWare supports an interesting features that VPC/WVPC lacks.  VMWare can install a Visual Studio addin.  This addin allows you to debug code on the guest directly rather than relying on remote debugging.  For developers this is an excellent feature.