Expert Texture Home Contact me About Subscribe Digipede Connect on LinkedIn rwandering on Twitter rwandering on FriendFeed

rwandering.net

The blogged wandering of Robert W. Anderson

Using UpdateVersion with Resource Scripts

Introduction
At release build time, my company updates our version numbers using the UpdateVersion utility by Mike Gunderloy, Scott Hanselman, and Matt Griffith. This works fine for managed assemblies, but it doesn’t work for versioning unmanaged Win32 DLLs using the resource script. In this article, I show one way to populate the resource script with version numbers from another source.

A new version file
First I created a text file (resourceVersions.txt) to include assembly attributes as would be found in managed code. This file gets added to the project. I am using this format to be compatible with the UpdateVersion utility. It also has the side-benefit that version-related search (and replace) are the same across managed and unmanaged projects. This new file looks like this:

// This file is the source for the resourceVersions.h target
// The full AssemblyVersion is set here 
[assembly : AssemblyVersion("1.2.0.0")]
// The full AssemblyFileVersion is set in the release process
[assembly : AssemblyFileVersion("1.2.0.0")] 

Note that I use UpdateVersion to change the build and revision numbers in the AssemblyFileVersion during release-build time — that is why the two numbers above are the same.

Fixing the default resource script
The resource script that is generated by the IDE has a version section that looks something like this:

///////////////////////////////////////////////////////
//
// Version
//

VS_VERSION_INFO VERSIONINFO
 FILEVERSION 1,0,645,0
 PRODUCTVERSION 1,0,645,0
 FILEFLAGSMASK 0x17L
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "FileDescription", "Description here"
            VALUE "FileVersion", "1, 0, 645, 0"
            VALUE "InternalName", "Name"
            VALUE "LegalCopyright", "Copyright (C) 2005"
            VALUE "OriginalFilename", "Name.dll"
            VALUE "ProductName", " Product Name"
            VALUE "ProductVersion", "1, 0, 645, 0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

Note that the version numbers (Product and File) appear in multiple places. My next step was to move the version numbers (both as numbers and as strings) into a header file (i.e., resourceVersions.h):

// this is an auto-generated file and should not be in source control!
// Change these versions in resourceVersions.txt!
#define ASSEMBLY_VERSIONSTRING "1.2.0.0"
#define ASSEMBLY_MAJOR 1
#define ASSEMBLY_MINOR 2
#define ASSEMBLY_BUILD 0
#define ASSEMBLY_REVISION 0
#define FILE_VERSIONSTRING "1.2.0.0"
#define FILE_MAJOR 1
#define FILE_MINOR 2
#define FILE_BUILD 0
#define FILE_REVISION 0

And I changed the hard-coded values in the resource script to these constants (with an include on top):

#include "resourceVersions.h"

VS_VERSION_INFO VERSIONINFO
 FILEVERSION FILE_MAJOR, FILE_MINOR, FILE_BUILD, FILE_REVISION
 PRODUCTVERSION ASSEMBLY_MAJOR, ASSEMBLY_MINOR, ASSEMBLY_BUILD, ASSEMBLY_REVISION
 FILEFLAGSMASK 0x17L
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x2L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "FileDescription", "Description here"
            VALUE "FileVersion", FILE_VERSIONSTRING
            VALUE "InternalName", "Name"
            VALUE "LegalCopyright", "Copyright (C) 2005"
            VALUE "OriginalFilename", "Name.dll"
            VALUE "ProductName", " Product Name"
            VALUE "ProductVersion", ASSEMBLY_VERSIONSTRING
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

Auto-generating resourceVersions.h

The next step is to use a nant task to generate the header file. The version numbers are retrieved from resourceVersions.txt by capturing text using regular expressions. Following is a complete nant project file that will accomplish this task:

<?xml version="1.0" ?>
<project name="Solution Build Example" default="rebuild" xmlns="http://nant.sf.net/release/0.85-rc3/nant.xsd">
  <!-- target to make resourceVersions.h from resourceVersions.txt -->
  <target name="makeResourceVersions">
    <loadfile property="resourceVersions" file="resourceVersions.txt" />
    <!-- get the assemblyVersion and FileVersion -->
    <regex pattern="AssemblyVersion\(\042(?'assemblyVersion'(?'avMajor'[0-9]+)\.(?'avMinor'[0-9]+)\.(?'avBuild'[0-9]+)\.(?'avRev'[0-9]+))" input="${resourceVersions}" />
    <regex pattern="AssemblyFileVersion\(\042(?'fileVersion'(?'fvMajor'[0-9]+)\.(?'fvMinor'[0-9]+)\.(?'fvBuild'[0-9]+)\.(?'fvRev'[0-9]+))" input="${resourceVersions}" />
    <!-- echo -->    
    <echo message="Found AssemblyVersion ${assemblyVersion}, ${avMajor}.${avMinor}.${avBuild}.${avRev}"/>
    <echo message="Found AssemblyFileVersion ${fileVersion}, ${fvMajor}.${fvMinor}.${fvBuild}.${fvRev}"/>
    
    <!-- write the new file -->
    <echo file="resourceVersions.h" message="// this is an auto-generated file and should not be in source control!" />
    <echo file="resourceVersions.h" message="// Change these versions in resourceVersions.txt!" append="true"/>

    <!-- assembly (product) versions -->
    <echo file="resourceVersions.h" message='#define ASSEMBLY_VERSIONSTRING "${assemblyVersion}"' append="true"/>
    <echo file="resourceVersions.h" message="#define ASSEMBLY_MAJOR ${avMajor}" append="true"/>
    <echo file="resourceVersions.h" message="#define ASSEMBLY_MINOR ${avMinor}" append="true"/>
    <echo file="resourceVersions.h" message="#define ASSEMBLY_BUILD ${avBuild}" append="true"/>
    <echo file="resourceVersions.h" message="#define ASSEMBLY_REVISION ${avRev}" append="true"/>

    <!-- file versions -->
    <echo file="resourceVersions.h" message='#define FILE_VERSIONSTRING "${fileVersion}"' append="true"/>
    <echo file="resourceVersions.h" message="#define FILE_MAJOR ${fvMajor}" append="true"/>
    <echo file="resourceVersions.h" message="#define FILE_MINOR ${fvMinor}" append="true"/>
    <echo file="resourceVersions.h" message="#define FILE_BUILD ${fvBuild}" append="true"/>
    <echo file="resourceVersions.h" message="#define FILE_REVISION ${fvRev}" append="true"/>
  </target>
</project>

Integrating into Visual Studio
The final step is to integrate the generation of the include file into your Visual Studio project. This ought to work in VS2003, though I have only confirmed it in VS2005. Simply do the following:

1. Add the resourceVersions.txt file to your project.
2. Give it the following custom build line: nant makeResourceVersions

Conclusion
Once you follow these steps, you should be able to treat your managed and unmanaged versioning identically. Please let me know if you have any comments (or even corrections).

4 Comments »