I’m currently working on a little native C application using the C-runtime libraries (CRT) to detect Windows power events. I ran into a problem when testing my application on Windows XP Professional 32-bit RTM (e.g. “Gold Release”, no service packs). When I attempt to run the application, I get the following error message:
Huh?!
After a little digging, it turns out the Microsoft rather quietly discontinued targeting the Windows 2000 and Windows XP RTM/SP1 platforms with Visual Studio 2010. The last release that will target these platforms is Visual Studio 2008.
There were various suggestions on how to workaround this issue including recompiling the CRT, implementing the missing functions in your own w2kcompat.lib, FASM/MASM assembly magic, and reverting to using Visual Studio 2008. However all of these offered significant drawbacks for me. I do not want to be in the business of supporting a custom compiled version of the CRT. I don’t understand the assembly editing based solutions sufficiently to feel comfortable supporting them on x86 and x64 platforms in the field.
The workaround that works for me and that I ultimately used is to target the Visual Studio 2008 (VC 9.0) Platform Toolset from within my Visual Studio 2010 projects as suggested here and here. There were three key upsides for me with this solution:
This seems like the most reasonable option to support in deployments. No custom builds and no assembly hacking.
I can continue to use VS2k10 and all it’s goodness. (Well, almost. Any feature added in the VC10 runtime won’t be available. )
This modification can be done on a per project (and actually, per Build Configuration) basis, so I don’t have to make system-wide changes to my VS install.
The major downside to this option is that it requires me to have Visual Studio 2008 installed, but this was something I was willing to live with.
One way to accomplish this workaround that was suggested is to place the path to the Visual Studio 2008 VC libs (e.g. “C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\lib” for 32-bit builds) first in the Additional Library Directories under the Linker options for your project. This messy because you have to do this for every configuration of every project in your solution that needs it and it varies for x86 versus x64 builds. Making matters worse is that the location of these files may vary from system to system, so what works for one developer’s machine may not work for another. Not ideal.
The cleaner and easier way to do this is to set the Platform Toolset in the project’s general properties pane. You still need to do this for each project as well as all configurations and all platforms. However, unlike the previous method, this one uses the same value across configurations and platforms and it’s portable across machines.
The basics of how to accomplish this are much the same as in the mercurial example. The major difference is that I don’t use WSH to generate the version file. Unlike TortoiseHg, which includes the CLI tools for Mercurial, TortoiseSVN does not. Therefore you have two choices:
Download a CLI SVN implementation like SlikSVN and implement a script similar to the one that we did for Hg.
Use the SubWCRev.exe utility that comes with TortoiseSVN to generate your version file from a template
Since I already use TortoiseSVN and SubWCRev has already done the heavy-lifting for me, I’ve decided to go that route. As usual, I’ll be doing my example using Visual Studio 2010 Professional although these techniques should work on other versions of VS as well.
Next we create a file named svn_version.h.tmpl in our project. I generally keep it under the Resources filter. This file serves as the basis of the SVN version header that we ultimately want to include in our project. The tokens (e.g. $WCREV$) will be automatically replaced by the SubWCRev utility later.
#ifndef _SVN_VERSION_H_
#define _SVN_VERSION_H_
#define SVN_LOCAL_MODIFICATIONS $WCMODS?1:0$ // 1 if there are modifications to the local working copy, 0 otherwise
#define SVN_REVISION $WCREV$ // Highest committed revision number in the working copy
#define SVN_TIME_NOW $WCNOW$ // Current system date & time
#endif
Finally we need to add a pre-build step which will execute the SubWCRev tool, thus generating the svn_version.h file prior to the binary being built.
Right-click on your project
Select Properties
Click Build Events
Click Pre-Build Event
In the Configuration drop-down, select All Configurations
In the Command Line field, enter: SubWCRev.exe $(SolutionDir) $(ProjectDir)\svn_version.h.tmpl $(ProjectDir)\svn_version.h
In the Description field, add a comment such as: Generate the svn_version.h file with the necessary repo identify info for versioning
NOTE:If you have multiple projects in the same solution that all need to use the same information from Subversion you have a few choices. One is to put the template and pre-build event in one project which all the other projects depend on and add a link to the svn_version.h file that gets generated. Another option is to create a specific version project that all it does is generate the svn_version.h file and define common version information and then have every project in the solution depends on it so it’s executed first in the build order.
The version.h file we created in the basic versioning post needs to be updated to include the generated svn_version.h file and to make use of its defines.
Finally, we need to go ahead and add an svn:ignore on svn_version.h because we don’t want its difference checking in every commit and we don’t want it constantly marking the working copy as modified if it’s tracked.
Now when each time you build your projects, the latest Subversion information of the working copy is automatically included in the file version information.
So there you have it, you can now automatically include all the necessary information from your Subversion working copy in your build to map them one-to-one with the source code that was used.
Now that we’ve covered why we should include version information in our binaries and how to do that for native C/C++ and managed .NET projects, it’s time to up our game. One of the major shortfalls of the previous solutions is that the version information on the binary didn’t map one-to-one to revisions from source control. As a result, while you may be able to determine the version of a particular file, you can’t easily get to the corresponding source code, if at all. One solution to this is to include the source control revision information in the version so you know exactly what was used to build it. Here I’ll be building on the previous posts and discussing how to do this with Visual Studio 2010 Professional and Mercurial using TortoiseHg for a native C/C++ application. Other versions of Visual Studio should work similarly and other Mercurial packages will work as well, as long as they provide command-line tools that are in the path of your IDE. The steps can be easily modified for use with a .NET project based on the previous post.
Update 2011/02/17: Doing this for a .NET assembly is actually a bit trickier than I originally thought because you can’t use static variables or class data in the assembly attributes – you have to use constant literals or expressions. I’ll do a separate post on how to do this with a .NET project.
The basic concept of how this works is explained in the here. The source code for this post is available here.
The first step is to add a file to your project named hg_version.jse. Personally, I add this file under the Resource filter of my project. Copy the code below in to the script file. This code does two things:
Creates hg_version.h
Extracts the desired Mercurial version info from the working copy and places it in the header
The extracted version info includes the full node identity, the short node identity, the revision number, and if there are any local modifications to the working copy.
var fso = new ActiveXObject("Scripting.FileSystemObject");
var shell = new ActiveXObject("WScript.Shell");
var outFile = fso.CreateTextFile("hg_version.h", true);
var hgRevNum = shell.Exec("hg identify --num");
var rev = hgRevNum.StdOut.ReadAll();
var hg_revision = String(rev).replace(/\n/g,"").replace(/\+/g,"");
var hg_local_modifications = 0
if( String(rev).replace(/\n/g, "").indexOf("+") != -1 )
{
hg_local_modifications = 1;
}
outFile.WriteLine( "#define HG_REVISION " + hg_revision );
outFile.WriteLine( "#define HG_LOCAL_MODIFICATIONS " + hg_local_modifications );
var hgChangeset = shell.Exec("hg parents --template \"{node}\"");
var changeset = hgChangeset.StdOut.ReadAll();
var hg_changeset = String(changeset).replace(/\n/g,"");
outFile.WriteLine( "#define HG_CHANGESET \"" + hg_changeset +"\"" );
var hgChangesetShort = shell.Exec("hg parents --template \"{node|short}\"");
var changeset_short = hgChangesetShort.StdOut.ReadAll();
var hg_changeset_short = "#define HG_CHANGESET_SHORT \"" + String(changeset_short).replace(/\n/g,"") + "\"";
outFile.WriteLine( hg_changeset_short );
outFile.Close();
The version.h file we created in a previous post needs to be updated to use the information from the generated header. The mercurial revision number is a human friendly integer and can be used directly in the file version. However, due to the nature of a DVCS it is not guaranteed to be globally unique (and it often won’t be on projects with multiple developers using common workflow patterns). The node identity however, does uniquely identify the changeset globally. We include this information in the file description field which maps the binaries one-to-one with the source code they were built with. It’s important to note that the identity field is not a 16-bit integer and thus cannot be used in the file version field directly. Finally, we want to know if the binary was built with local modifications to the working copy, which would complicate reproducing the build. As a result we append an ‘M’ to the end of the file version string if local modifications are present.
Finally we need to add a pre-build step which will execute the hg_version.jse script, thus generating the hg_version.h file prior to the binary being built.
Right-click on your project
Select Properties
Click Build Events
Click Pre-Build Event
In the Configuration drop-down, select All Configurations
In the Command Line field, enter: cscript.exe hg_version.jse
In the Description field, add a comment such as: Generate the hg_version.h file with the necessary repo identify info for versioning
NOTE:If you have multiple projects in the same solution that all need to use the same information from Mercurial you have a few choices. One is to put the hg_version.jse script in one project which all the other projects depend on and add a link to the hg_version.h file. Another option is to create a specific version project that all it does is generate the hg_version.h file and define common version information and then have every project in the solution depends on it so it’s executed first in the build order.
Now when each time you build your projects, the latest Mercurial information of the working copy is automatically included in the file version information.
So there you have it, you can now automatically include all the necessary information from your Mercurial working copy in your build to map them one-to-one with the source code that was used. In an upcoming post I’ll discuss how to do this using Subversion as your VCS.
Last time in Mapping Binaries in the Field to Source Code in the Repository we talked about the value of including version information in your binaries. Today I’m going to explain how to accomplish this in Visual Studio for a native C/C++ binary. I’m using 2010 Professional, but it should work on other versions as well.
This will give you two files: resource.h and <project_name>.rc. I generally rename the .rc file to be version.rc
Step 2: Updating Version.rc
Out of the box the version.rc file will have you define the values right there. I recommend that you instead define the values in a version.h file and use those defines in the version.rc file.
Edit the resource file in a text editor:
In the Solution Explorer, Right-click on version.rc
Select Open With
Select C++ Source Code Editor
Scroll down to the Version section
Here is a template Version section that I frequently use (and will build on below):
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_FILE_VERSION
PRODUCTVERSION VER_PRODUCT_VERSION
FILEFLAGSMASK 0x3fL
FILEFLAGS VER_FILEFLAGS
FILEOS VER_FILEOS
FILETYPE VER_FILETYPE
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", VER_FILE_DESCRIPTION_STR "\0"
VALUE "FileVersion", VER_FILE_VERSION_STR "\0"
VALUE "InternalName", VER_INTERNAL_NAME_STR "\0"
VALUE "LegalCopyright", VER_COPYRIGHT_STR "\0"
VALUE "OriginalFilename", VER_ORIGINAL_FILENAME_STR "\0"
VALUE "ProductName", VER_PRODUCTNAME_STR
VALUE "ProductVersion", VER_PRODUCT_VERSION_STR "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
Step 3: Adding a Version Header
Next we create a file named version.h to provide a more convenient location to set the various version information. This is especially useful if you are sharing version information across multiple projects in a single solution. Here’s the information I generally start with mine:
The final step is to add the necessary include line to the version.rc file for the version.h file:
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#include "version.h"
#define APSTUDIO_READONLY_SYMBOLS
Results
Now when you build your application, all the version info above will be defined in the binary itself. Much of this information is available directly from the Details pane of the Properties window in Explorer.
Final Thoughts
Note that this information is static from build to build. You must change the version numbering yourself or use a script to auto-increment values. I’ll be discussing how to make the BUILD number automatically correspond to the revision info from the working copy of the source code using Mercurial or Subversion in an upcoming post.