P3.NET

FAQ: Temporary Projects

Q: I create a lot of test projects and I am getting tired of cleaning up the temporary projects directory and clearing out the recent projects menu.  Is there some way to get VS to do this for me?

A: Yes there is.  Under ToolsOptions -> Projects and Solutions there is an option called Save new projects when created.  Unchecking this option will allow you to create (most) new projects without saving them.  It will still end up in your temporary projects folder but it will be automatically deleted when you close the project unless you chose to save it.  Furthermore it will not be added to the MRU project list.

 There are some restrictions to this option.  Firstly not all project types support it.  C++ does not while C# and VB do.  Secondly, you cannot create multiple projects in a single solution without saving it first.  You also cannot do some project changes without first saving the project.  Avoid using the Save All button as this will prompt you to save the project.  However you can add files and compile and debug without saving.

FAQ: Toolbox is slow to populate

Q: Whenever I accidentally mouse over the Toolbox it locks up VS for a while.  What is going on?

A: This should only happen the first time you mouse over the Toolbox after starting VS.  VS is populating the window with the controls that are defined within your solution in addition to the pre-defined controls.  This can be a slow process.  To speed it up go to ToolsOptions -> Windows Form DesignerGeneral and set AutoToolboxPopulate to false.  This will tell VS not to scan your solution looking for controls to add to the toolbox.

 

FAQ: Adding a lot of files to a solution

Q: I need to add a lot of existing files and folders to a project but using Add Existing Item is slow.  Is there a faster way?

A: When you select a project in Solution Explorer you might have noticed the toolbar at the top of the window changing.  One of the option (in most projects) is Show All Files.  This option shows all files and folders under the project folder whether they are in the project or not.  Files/folders not in the project are shown grayed out.  If you right-click the files/folders (multiple selection is supported) you can right-click and select Include in Project to automatically add them to the project.  If you add a folder it will automatically include any children in the folder.  This is a great way to add a lot of items when they already reside in the project directory.

If the files do not already reside in the directory then you can drag the files from Windows Explorer to the project directory.  Unfortunately this does not work in all projects.  Also be careful about what actually happens when you drop the file.  For projects like C# that assume all files are in the project directory then a copy is performed.  However some project types may result in a link being created to the original file location.

FAQ: Access Denied when signing an assembly

Q: When I try to sign an assembly I get an access denied error.  What is going on?

A: By default you can not sign an assembly unless you have administrative privileges.  This is due to security set on the key(s) used for signing.  To enable your account for signing assembly you will need to modify the NTFS security on the folder %ALLUSERS%Application DataMicrosoftCryptoRSAMachineKeys to give your user account (or better yet a group in which your account is a member of) full control.  You will need read & execute, list and modify rights.  You can then sign assemblies without administrative privileges.

Web Sites vs Web Applications

(Originally published: 3 January 2009)

One of the biggest confusions in the ASP.NET world is that of web project type.  Visual Studio (VS) supports two web project types: web sites and web applications.  Unfortunately, they are different enough that it is important to understand which type you are using and why.  In this article we will discuss the differences between the two types by looking at their history.  By the end of the article you should be comfortable enough to determine which type is best for you.

Web Application

When Visual Studio .NET (VS.NET) was released it supported a single web project type, the web application.  A web application looks and acts like a regular Windows or console application.  A web application contains a set of project property pages which the developer can set including build and debugging options.  This allows, for example, the developer to generate documentation for any code contained in the project.

When a web application is compiled all the source files (including the codebehind files) are compiled into a single assembly and stored in the Bin directory.  The ASPX files are deployed with the binaries.  At runtime the ASP.NET runtime compiles the ASPX files into an assembly when they are first needed.  If an  ASPX file ever changed then a new assembly is generated.  The initial version of .NET did not support dynamic code generation like it does now so the performance overhead was a little high when recompilation occurred.  As of v2.0 ASP.NET compiles the ASPX files differently to reduce performance hit and to optimize memory.

The fact that web applications work similarly to other project types is also one of its greatest disadvantages.  A web application has to be deployed as one entity.  If different parts of the application are managed by different teams then some repository is needed to allow changes to be consolidated and deployment to occur.  This infrastructure also slows down the rate of the site changes.

Web applications are designed for projects where the entire site is a single entity.  Any changes to the project requires redeployment.  Multiple teams must synchronize their code before a build can occur.  The application code itself is protected from prying eyes when it is compiled into an assembly.

Web Sites

When Visual Studio 2005 (VS2005) was released Microsoft removed the web application project type and replaced it with a web site.  Somebody at MS dropped the ball here because these project types are completely different.  A web site project is more ideally suited for intranet sites where separate teams are responsible for different areas of the site.  In an intranet environment the safety of the source code isn’t as important as a fast turnaround time.  In a web site project almost all the project settings are either removed or stored in the application configuration file.  Options like whether to generate documentation files or do code analysis simply don’t exist in web sites.  This fits in with the web site philosophy of compile as needed.

Only code residing in the App_Code directory is compiled down into an assembly at build time.  The remaining code becomes deployment items along with the ASPX files.  This has advantages and disadvantages.  Making changes to a web site requires that you merely replace the corresponding source or ASPX files.  The ASP.NET runtime will recompile the code as needed.  The turn around is therefore high.  Pushing most of the project settings to the configuration file also means that upgrading to newer components or tweaking build settings requires a simple change to the config file rather than compilation and redeployment.

Perhaps the biggest weakness of the web site project type is that the source code is visible and changeable to the outside world.  Outside world, here, meaning anyone with file access to the web directory.  IIS prevents web access to the source and configuration files.  Another issue is performance.  As files change recompilation has to occur.  ASP.NET only does this when a page is first referenced so viewing a page is noticeably slower the first time after a refresh.

After the VS2005 release there was an uproar in the community about the new web site project.  It wasn’t that nobody liked it but rather many people didn’t want to be forced to use the new model, especially since it didn’t fit in with many application models.  MS remedied the situation by shipping an addin that would add the web application project type back in.  In VS2005 SP1 web applications were formally added back into the VS project list.

Web Deployment Projects

One area that neither type of web project covers is the compilation of ASPX files to assemblies.  In the initial release each ASPX file resulted in a separate assembly.  This was great for ASPX files that changed a lot but bad for performance as each page was compiled, not to mention the number of assemblies generated.

After VS2005 was released MS released the Web Deployment Project (WDP) addin (here).  This addin exposed some new options for controlling how web sites were deployed.  Rather than compiling the ASPX files when they are needed the addin allows for precompilation of the site to speed things up.  This has no impact on the site behavior because if an ASPX file changes then a recompilation will occur anyway.  The benefit of this approach is that the initial load of the site will be fast since the ASPX files are already compiled.

Another area where the addin is useful is controlling how the site is compiled.  The addin allows: all the ASPX files in the site, all the ASPX files in a directory or each individual ASPX file to be compiled into separate assemblies.  The best choice depends upon the site in question.  The more ASPX files you combine into a single assembly the better the performance and memory usage will be but at the cost of more overhead when any of the files change.

The addin supports several additional features that makes it worthwhile if you are managing a web project.  I recommend that you take a look at it.  The multiple configuration file support is especially interesting although it was difficult to use in the initial release.

The addin technically doesn’t do a lot on its own.  Instead it relies on a couple of ASP.NET tools that ship with .NET: aspnet_compiler and aspnet_merge.  (aspnet_compiler is responsible for compiling the ASPX files.  It is used to for precompilation and to control whether ASPX files can be updated after deployment.  Refer to MSDN for full documentation.

aspnet_merge works in conjunction with aspnet_compiler to merge the various ASPX assemblies into a new assembly or set of assemblies.  It also is responsible for determining what gets combined and what remains separated.  Refer to MSDN for full documentation if you are interested.  It is probably just better to use WDP instead.

Note that VS supports publishing web projects (site or app) directly without the need for deployment addins.  However the publishing tool is strictly for publishing the existing project without changing most of the options.  The deployment project is for taking more control over the publishing process.

Feature Comparison

We will now compare the two web project types so that you can make a more informed decision.

Web Applications 

Advantages

  • Ideally suited for web projects that have little or no changes over time and in cases where the project code is proprietary.  Applications that use a web front end are good examples. 
  • Standard project settings are available so a developer can configure the application.
  • Since most of the code is pre-compiled the application performance is good.
  • Deployment involves copying the ASPX files and the Bin directory.

Disadvantages

  • Other than minor UI changes any application changes require recompilation and redeployment.
  • Upgrading to newer third-party components requires recompilation and redeployment.
  • Turn around time for changes can be long depending on deployment process.
  • Sites where different teams are responsible for the content are harder to synchronize.

Web Sites

Advantages

  • Faster turnaround for code and UI changes.
  • Multiple teams can be working on different areas of the same site without compilation or deployment issues.
  • Configuration and dependency changes can occur with a configuration file change.

Disadvantages

  • Page loading is slower after any changes are made.
  • Source code is visible to anyone with file system access.
  • Deployment requires shipping the ASPX files, Bin directory and source files.

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.