I was planning on writing a couple of entries on C macros, including assert macros and why you should wrap your macros in do/while loops. While doing a bit of research I came across a couple of excellent blog posts by Charles Nicholson on these very topics. Rather than try and out do him, I’ll just refer you to him!
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.
I use warning level 4 (/W4) for all of my projects as well as “warnings as errors” (/WX) on Release builds. During development I often run into C4100 warnings for unreferenced parameters or variables that are either:
Currently unused but will be used once development is complete
Due to a specific required signature (e.g. for use in a Windows callback in an API) but that I don’t need
In the past I’ve just used a cast to suppress the warnings like this:
I came across a collection of handy macros today in WinNT.h (you should include <Windows.h>):
//
// Macros used to eliminate compiler warning generated when formal
// parameters or local variables are not declared.
//
// Use DBG_UNREFERENCED_PARAMETER() when a parameter is not yet
// referenced but will be once the module is completely developed.
//
// Use DBG_UNREFERENCED_LOCAL_VARIABLE() when a local variable is not yet
// referenced but will be once the module is completely developed.
//
// Use UNREFERENCED_PARAMETER() if a parameter will never be referenced.
//
// DBG_UNREFERENCED_PARAMETER and DBG_UNREFERENCED_LOCAL_VARIABLE will
// eventually be made into a null macro to help determine whether there
// is unfinished work.
//
#if ! defined(lint)
#define UNREFERENCED_PARAMETER(P) (P)
#define DBG_UNREFERENCED_PARAMETER(P) (P)
#define DBG_UNREFERENCED_LOCAL_VARIABLE(V) (V)
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.
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.