Assembly Version, File Version and Product Version: The Butterfly Effect

Assembly Version, File Version and Product Version are “related” if you don’t specify them explicitly… and you could be surprised by one of the side effect: loosing you application data or user settings when incrementing the Assembly File Version.

Click to Read More

How are those versions specified:

By default, any new Visual Studio Project includes an AssemblyInfo file defining default values (major, minor, build and revision) for the Assembly Version and Assembly File Version:

[csharp][assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")][/csharp]
  • A difference in the build number represents a recompilation of the same source. This is usually appropriate because of processor, platform, or compiler changes.
  • Assemblies with the same name, major, and minor version numbers but different revision numbers are intended to be fully interchangeable. This is usually appropriate for security fix, etc …
  • “[assembly: AssemblyVersion(“65534.65534.65534.65534“)]” is maximum.

MSBuild can auto-increment the build and revision numbers at each build if we provide a (*) in place of those figures. Ex.

[csharp][assembly: AssemblyVersion("1.0.*")][/csharp]

or

[csharp][assembly: AssemblyVersion("1.0.0.*")][/csharp]

In such cases, the build number is generated based on the current day and the revision number is generated based on the number of seconds since midnight. Replacing the revision number only with a (*) would be irrelevant. Indeed, consecutive builds would most probably have lower revisions.

There is no default value set for the Product Version when creating a new Visual Studio Project. But a value can be set manually in the AssemblyInfo file using:

[csharp][assembly: AssemblyInformationalVersion("1.0.0.0")][/csharp]

Usually, the Product Version is a value like major.minor.build, but it can actually be any string, like “1.0 RC”, etc… Don’t use (*) in this string as it wouldn’t be replaced by an auto-incremented figure. Instead, it would crash your application in some cases (See further for the explanation). Notice also that before VS 2010, using anything else that major.minor.build.rev was resulting in  false warning during compilation.

What do those versions mean:

Assembly Version : This is the version number used by framework during build and at runtime to locate, link and load the assemblies. When you add reference to any assembly in your project, it is this version number which gets embedded. At runtime, CLR looks for assembly with this version number to load. But remember this version is used along with name, public key token and culture information only if the assemblies are strong-named signed. If assemblies are not strong-named signed, only file names are used for loading.
 
Assembly File Version : This is the version number given to file as in file system. It is displayed by Windows Explorer. Its never used by .NET framework or runtime for referencing. But it can be used by OS tools.

Product Version (a.k.a Assembly Informational Version): Defines an additional version information for an assembly manifest. This is the version you would use when talking to customers or for display on your website..

What if a version is not specified:

  • If the Assembly version is not explicitly specified, it takes the value of 0.0.0.0.
  • If the Assembly File version is not explicitly specified, it takes the value of the Assembly version.
  • If the Product version is not explicitly specified, it takes the value of the Assembly File version.
How could issues occurred due to this relation:

By your fault 🙂

At work, we recently decided to manage the major and minor numbers of the Assembly Versions at build time, within our TFS Build Process Template. Assembly Versions’ build and release numbers are kept equal to 0 while major and minor are enforced with values provided through Build Definitions’ parameters. At the same time,  we auto-increment the build and release numbers of the Assembly File Versions while keeping their major and minor numbers aligned with those of the Assembly Versions. We don’t touch the Product Version. This was designed as recommended in KB 556041.

As mentioned, major and minor numbers are specified through custom parameters of our Build Definitions. The build number is computed to reflect directly the current day using a format like “YMMdd” with Y = year modulo x to be <= 6 (None of our product has a longer life). The release number is set with the auto-incremented value provided out-of-the box by Team Build.

Previously, most of the binaries were compiled locally, on development workstations, and there was no version management at all (There was simply no need for any such versioning): Assembly Version and Assembly File Version were always kept unchanged.

The change introduced with our Build Process Template had an unexpected consequence: As there never was any Product Version specified explicitly in the AssemblyInfo, binaries’ Product Versions were always equal to the Assembly File Version… kept therefore unchanged through all builds. However, since we started to auto-increment the Assembly File Version, the Product Version started to increment too…

And ? And some users started to complain that their custom settings were lost after the installation of each new version…

We quickly noticed that, as usually recommended, developers were making use the two following properties to store  respectively application’s data and users’ data:

[csharp]Application.CommonAppDataPath[/csharp]
[csharp]Application.LocalUserAppDataPath[/csharp]

And after some investigations on MSDN, it appeared that the path returned by those properties depend among other on the Product Version 🙁

Depending on the OS, CommonAppDataPath is usually like:

  • %SystemDrive%\Documents and Settings\All Users\Application Data\<CompanyName>\<ProductName>\<ProductVersion> or
  • %SystemDrive%\ProgramData\<CompanyName>\<ProductName>\<ProductVersion>

And LocalUserAppDataPath is like:

  • “%SystemDrive%\Documents and Settings\<UserId>\Local Settings\Application Data\<CompanyName>\<ProductName>\<ProductVersion>” or
  • “%SystemDrive%\Users\<UserId>\AppData\Local\<CompanyName>\<ProductName>\<ProductVersion>“.

So, indeed, each new release of our product having now a new Product Version, application’s data and users’ data of the previous versions are not used anymore…

Any (*) used in the Product Version (e.g.: 1.0.*) wouldn’t be replaced by auto-incremented build/release numbers (as mentioned previously), the “AppDataPath”  above would contain an illegal character; reason why the application would crash when accessing this path.

This also impacted applications accessing settings in the registry with methods like:

[csharp]Application.UserAppDataRegistry.SetValue()[/csharp]
[csharp]Application.CommonAppDataRegistry.SetValue()[/csharp]

Those methods access respectively data under 

  • HKCU\Software\<CompanyName>\<ProductName>\<ProductVersion>\
  • HKLM\Software\<CompanyName>\<ProductName>\<ProductVersion>\
Read also this paper on User Settings Management.
See also one of my sources here about this topic.

Loading


Categories:

,

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *