Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VC9.0

Synchronized Enterprise File Versions

3.83/5 (4 votes)
20 Nov 2009CPOL11 min read 26.1K   194  
Automate file version updates for large suites of C++, C#, and VB apps.

Introduction - Why Yet Another Version Counter?

Proper installation of enterprise apps requires accurate file versions. This can become quite complex for a large numbers of apps, and synchronization of build numbers can help reduce this complexity.

Build number identification of apps (for feature tracking, bug tracking, customer patches, QA testing, etc.) can be made manageable by synchronization of build numbers across an entire suite.

However, manually synchronizing all the file versions and product versions of a very large suite of applications can be a time-consuming and error-prone task. It is preferable to do this in an automated fashion, with as few failure points as possible. And when manual changes are unavoidable, it is preferable to change the version numbers in only one place.

Several excellent how-to solutions exist on the Internet for single-app automated version advancement, and I am not attempting to duplicate those. This how-to is specifically geared for large suites of C++, C#, and VB apps that are built by an automated process (e.g., a nightly build performed by a tool, such as CruiseControl).

Note: This is a beginner-level article.

If my explanation below seems a little pedantic, please understand that I am attempting to make this fairly thorough, in order to address multiple skill levels. I am hoping to answer as many questions as possible before they come up, by including, in excruciating detail, the steps that I actually performed to make this running / working project.

The development tool I have used for the solution outlined here is Visual Studio 2008. I have done something similar with Visual Studio .NET 2003, with only a few subtle differences. A solution is implemented in three languages in this article: C++, C#, and VB.

For convenience sake (and in the spirit of attempting to make life easier for folks of all skill levels), a second solution is available in the VS 2003 zip file at the top of this article. See the ReadmeVS2003.pdf in the zip file for an abbreviated explanation of the minor differences between the two solutions. I hope to edit this article periodically and add other VS versions as I get time and access to them. Earlier versions such as VS 6.0 would, of course, only support C++, and possibly VB.

Creating the Base Project

In this demo:

  • I create a blank project
  • I add C# file version support
  • I add C++ version support
  • I add VB version support hack, and then
  • I add simple batch version updates (implemented in DOS, for the sake of simplicity) to the compile process

The first step is to create a blank project.

Figure 1 – Create a Blank Project

Image 1

Creating C# Version Support and a Sample App

Version support is implemented as a C# Library project.

Figure 2 – Create a C# Library Project

Image 2

My wizard pre-created a stubbed out Class1.cs. We will reuse this to implement our automated C# version. Add the const String from Figure 3 to Class1.cs.

Figure 3 – Insert Version Source Code Stub
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyCSharpVersion
{
    public class Class1
    {
        public const String MyCurrentVersionNumber = "1.2.3.4";
    }
}

For the sake of completeness, we use this class to update the file and product versions of the C# version library itself. Edit the code in the library assembly info. In VC Studio 2008, you can find that under Properties.

Caveat: In this project, there will be two C# assembly info files, one for the C# version library and one for the sample C# app that uses it. This is the former.

Figure 4 – Open the Library Assembly Info File

Image 3

Enter the code in Figure 5 to access the version number at compile time.

Note: You will be adding only one line from Figure 5 and editing one other line. The rest are included so that you can find the places to insert/edit the code.

Figure 5 – Insert Code to Access the Version
C#
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using MyCSharpVersion;
.
.
.
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion(MyCSharpVersion.Class1.MyCurrentVersionNumber)]

Compile the library project, and open Windows file explorer in the folder that contains the compiled DLL. You may need to change the View to “details.” Right-click on the column headers in Details view in order to display the two new columns. Select “more…”. Check the “file version” and “product version” checkboxes.

Figure 6 – Insert Version Source Code

Image 4

Your display should now look something like Figure 7.

Warning: The file version displayed here will not necessarily match the file version you get by right-clicking the DLL and viewing the Version tab.

As we should see in the C++ example, the file version displayed by file explorer is an integer embedded in the binary, while the file version displayed by the Properties dialog is a string resource (also embedded in the binary). The product version displayed in Windows file explorer, on the other hand, is one and the same as the product version displayed in the Properties dialog.

Figure 7 – Insert Version Source Code

Image 5

Next, we create a sample C# version app that uses the version library project.

Figure 8 – Create a Sample C# App

Image 6

This app will also have an assembly info file. Open it and add the same code that you did to the first assembly info file (see Figure 5). The difference this time is that you will also need to add a reference in your new C# app’s project to the C# version library.

Figure 9 – Add a Reference to the C# Library

Image 7

Now, compile the project and view the file and product version of the new C# sample app executable, using a similar technique to Figure 6 and Figure 7.

Figure 10 – Verify the File Version

Image 8

Creating C++ Version Support and a Sample App

To implement version control for C++, we need to create a set of header and source files. We can add this to the blank project that we created in the first part of this demo. First, we need a header that contains the macros we need to merge the version info into our resource file (shown in Figure 19).

Figure 11 – Add Header for Version Macros

Image 9

Insert the set of macros in Figure 12 into the header.

Figure 12 – Add Code for Version Macros
C++
#ifndef STRINGER
#define STRINGER( x ) #x
#endif
#ifndef RESTRINGER
#define RESTRINGER( x ) STRINGER( x )
#endif

#define MY_BUILD_FILE_NUMBER( rel, svcpk, minver, build ) \
 RESTRINGER(rel) "." RESTRINGER(minver) "." RESTRINGER(svcpk) "." \
 RESTRINGER(build) "\0"
#define MY_BUILD_PROD_NUMBER( rel, svcpk, minver, build ) \
 RESTRINGER(rel) "." RESTRINGER(minver) "." RESTRINGER(svcpk) "." \
 RESTRINGER(build) "\0"

#include "MyVersionNumber.h"

Add a header for the version numbers themselves.

Figure 13 – Add Header for Version Numbers

Image 10

Insert the code for the version numbers from Figure 14. If you alter these names, you must also change them in the resource files that use them (shown in Figure 19).

Figure 14 – Add Code for Version Numbers
C++
#define MY_RELEASE_NUMBER       5
#define MY_SERVICE_PACK_NUMBER  4
#define MY_MINOR_VERSION_NUMBER 3
#define MY_BUILD_NUMBER         210

To make this compile without errors under VC 2008, we also need a dummy source file.

Figure 15 – Add Dummy Source File to Make it all Compile

Image 11

Add some code to make it compile. You may be creative if you wish. Figure 16 has something that will work.

Figure 16 – Add Dummy Source Code to Make it all Compile
C#
void main()
{
    return;
}

Now, we need to add the C++ app that will use this header info. This time, for the sake of variety, we will make a Win32 app.

Figure 17 – Add a Win32 Product to Use the Version Info

Image 12

Edit the resource file that the C++ wizard created. This will typically have a .rc or a .rc2 extension. For some projects, no resource file is created by the VC wizards. In those cases, you will have to add your own resource file. The step of adding your own resource file is not shown here.

Edit the resource file you chose, using text mode. Here, I chose to edit WindowsApplication2.rc.

Figure 18 – Edit the App’s Resource File

Image 13

If the resource file you selected does not already have a VS_VERSION_INFO block, you need to add one; otherwise, just edit the existing VS_VERSION_INFO block. The important sections of code in Figure 18 have references to file version and product version. There are four lines in all (containing "FILEVERSION", "PRODUCTVERSION", "FileVersion", and "ProductVersion").

Figure 19 – Add a Version Info Block to the Resource
C++
#include "..\VersionTest\MyVersionHeader.h"

VS_VERSION_INFO VERSIONINFO
 FILEVERSION MY_RELEASE_NUMBER,MY_SERVICE_PACK_NUMBER,
     MY_MINOR_VERSION_NUMBER,MY_BUILD_NUMBER
 PRODUCTVERSION MY_RELEASE_NUMBER,MY_SERVICE_PACK_NUMBER,
     MY_MINOR_VERSION_NUMBER,MY_BUILD_NUMBER
 FILEFLAGSMASK VS_FF_DEBUG
 FILEFLAGS 0x0L
 FILEOS VOS_NT_WINDOWS32
 FILETYPE VFT_APP
 FILESUBTYPE VFT2_UNKNOWN
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904B0"    // concatenation of US-English, Unicode
        BEGIN
            VALUE "Comments", "Prototype Version Auto Info\0"
            VALUE "CompanyName","My Company\0"
            VALUE "FileDescription", "Version Prototype\0"
            VALUE "FileVersion", MY_BUILD_FILE_NUMBER(MY_RELEASE_NUMBER, 
                  MY_MINOR_VERSION_NUMBER,MY_SERVICE_PACK_NUMBER,MY_BUILD_NUMBER)
            VALUE "InternalName", "Version Prototype\0"
            VALUE "LegalCopyright","Copyright © My Company 2009\0"
            VALUE "LegalTrademarks","My Trademark\0"
            VALUE "OriginalFilename", "versionautoinfo.exe\0"
            VALUE "PrivateBuild", "\0"
            VALUE "ProductName", "Version Prototype\0"
            VALUE "ProductVersion", 
                  MY_BUILD_PROD_NUMBER(MY_RELEASE_NUMBER, 
                  MY_MINOR_VERSION_NUMBER,MY_SERVICE_PACK_NUMBER,MY_BUILD_NUMBER)
            VALUE "SpecialBuild", "\0"
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200  // US-English, Unicode
    END
END

Before you compile, add the VersionTest project folder to your #include path.

Figure 20 – Add VersionTest Folder to Include Path

Image 14

Now, compile, and check the file and product versions, as you did in Figure 6 and 7.

Creating Visual Basic Version Support and a Sample App

My VB support for automatic versioning is (I suspect) a hack, which actually, in a spooky sort of way, looks a whole lot like the C# solution. It involves editing the assembly info file. I'm sure there are dire and dreadful warnings about violating the crypt of the VB assembly info text file, but I tend to be very pragmatic and little bit of a cowboy when it comes to software development... Maybe there are some VB gurus out there (I'm certainly not one of them) that can shed more light and give some sound advice on this.

So! All that being said, use this technique at your own risk!

As we did for C#, create a VB Library project.

Figure 21 – Add a VB Library Project

Image 15

In the Class1 class that my wizard automagically created for me, we put in the const string, very much like we did for C#.

Figure 22 – Add a Version Number String
VB
Public Class Class1
    Public Const MyCurrentVersionNumber = "11.0.0.20"
End Class

This time, for the sake of variety, let's add a VB Windows service.

Figure 23 – Add a VB Service Project

Image 16

Now comes the hack part. Close the solution! Then, edit the VB library assembly info thus, from Start + Run.

Figure 24 – Edit the VB Library Assembly Info
notepad C:\VersionTest\MyVisualBasicVersion\My Project\AssemblyInfo.vb

Edit the line below in your library assembly info file that contains AssemblyFileVersion.

Figure 25 – Use the Version String Instead of a Literal
VB
' You can specify all the values or you can default the Build and Revision Numbers 
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> 
<Assembly: AssemblyVersion("1.0.0.0")> 
<Assembly: AssemblyFileVersion(Class1.MyCurrentVersionNumber)>

Now edit the VB service assembly in a similar way, but note the extra "Imports" line with this one.

Figure 26 – Edit the VB Service Assembly Info
notepad C:\VersionTest\WindowsService3\My Project\AssemblyInfo.vb
Figure 27 – Insert One Line And Edit One Line This Time
VB
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports MyVisualBasicVersion
.
.
.
' You can specify all the values or you can default the Build and Revision Numbers 
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> 
<Assembly: AssemblyVersion("1.0.0.0")> 
<Assembly: AssemblyFileVersion(Class1.MyCurrentVersionNumber)>

Now restart, and add the library to your Windows Service.

Figure 28 – Adding the VB Library to the VB Service

Image 17

Compile, and check the VB file and product versions, as you did in Figure 6 and 7.

Automating the Version Updates for Each Build

The final step to make this work in a batch environment is to add the version increment mechanism to the build process. This can be done in a number of ways, such as Apache Ant, Cygwin bash scripts, AWK, Perl, Python, or a host of other scripting and automated utilities. My demo uses DOS batch commands, for the sake of simplicity.

Create a “seed” version control file with your initial versions and build number in it. For instance, use notepad.exe to create a file named C:\VersionTest\BUILDINFO.TXT, and insert the text in Figure 29.

Figure 29 – Seed Version Info
MAJORVERSION=2 
MINORVERSION=3 
SERVICEPACK=4 
BUILDNUMBER=19

Again, use notepad.exe to create a batch file named MYBUILD.BAT, and insert the text in Figure 30.

Note: In this demo, it is assumed that this batch file and the BUILDINFO.TXT version info file have been created in C:\VersionTest.

Figure 30 – Builder Batch File
@ECHO ON
REM (DV) THIS HAS BEEN TESTED ON WINDOWS XP USING VS.NET-2003 AND VS-2008
CALL "C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\vsvars32.bat"
SET LOGNAME=MYBUILD.LOG
SET BUILDINFOFILE=BUILDINFO.TXT
SET BUILDDRIVE=C:
SET BUILDROOT=%BUILDDRIVE%\VersionTest
REM Yes, I did mean to use "VersionTest" again in CPPHEADER :)
SET CPPHEADER=%BUILDROOT%\VersionTest\MyVersionNumber.h
SET CSHARPCLASS=%BUILDROOT%\MyCSharpVersion\Class1.cs
SET VBCLASS=%BUILDROOT%\MyVisualBasicVersion\Class1.vb
SET MAJORVERSION=0
SET MINORVERSION=0
SET SERVICEPACK=0
SET BUILDNUMBER=0
%BUILDDRIVE%
CD %BUILDROOT%

FOR /F %%I IN (%BUILDINFOFILE%) DO SET %%I

ECHO MAJOR VERSION = %MAJORVERSION%
ECHO MINOR VERSION = %MINORVERSION%
ECHO SERVICE PACK  = %SERVICEPACK%
ECHO BUILD NUMBER  = %BUILDNUMBER%

SET /A BUILDNUMBER=%BUILDNUMBER% +1

ECHO NEW BUILD NUMBER = %BUILDNUMBER%

ECHO MAJORVERSION=%MAJORVERSION% > %BUILDINFOFILE%
ECHO MINORVERSION=%MINORVERSION% >> %BUILDINFOFILE%
ECHO SERVICEPACK=%SERVICEPACK% >> %BUILDINFOFILE%
ECHO BUILDNUMBER=%BUILDNUMBER% >> %BUILDINFOFILE%

ECHO #define MY_RELEASE_NUMBER %MAJORVERSION% > %CPPHEADER%
ECHO #define MY_MINOR_VERSION_NUMBER %MINORVERSION% >> %CPPHEADER%
ECHO #define MY_SERVICE_PACK_NUMBER %SERVICEPACK% >> %CPPHEADER%
ECHO #define MY_BUILD_NUMBER %BUILDNUMBER% >> %CPPHEADER%

ECHO using System; > %CSHARPCLASS%
ECHO using System.Collections.Generic; >> %CSHARPCLASS%
ECHO using System.Linq; >> %CSHARPCLASS%
ECHO using System.Text; >> %CSHARPCLASS%
ECHO namespace MyCSharpVersion >> %CSHARPCLASS%
ECHO { >> %CSHARPCLASS%
ECHO public class Class1 >> %CSHARPCLASS%
ECHO { >> %CSHARPCLASS%
ECHO public const String MyCurrentVersionNumber = 
       "%MAJORVERSION%.%MINORVERSION%.%SERVICEPACK%.%BUILDNUMBER%"; >> %CSHARPCLASS%
ECHO } >> %CSHARPCLASS%
ECHO } >> %CSHARPCLASS%

ECHO Public Class Class1 > %VBCLASS%
ECHO Public Const MyCurrentVersionNumber = 
      "%MAJORVERSION%.%MINORVERSION%.%SERVICEPACK%.%BUILDNUMBER%" >> %VBCLASS%
ECHO End Class >> %VBCLASS%

TYPE %BUILDINFOFILE%
TYPE %CSHARPCLASS%
TYPE %CPPHEADER%
TYPE %VBCLASS%

CALL devenv VersionTest.sln /clean Release /out %LOGNAME%
CALL devenv VersionTest.sln /build Release /out %LOGNAME%

This batch file updates the version info control file with the latest build number, and then creates three version files, one for the C++ projects, one for the C# projects, and one for the VB projects. It then builds the projects using the devenv compiler.

Warning: If your source code control fiddles with the read-only bits on your source code files, you will need to add commands to either clear the read-only bit before you write your C# and C++ files (e.g., the ATTRIB command) or just force-delete them (i.e., DEL /F).

To run this:

C:\> CD VersionTest
C:\VersionTest\> MYBUILD.BAT

Check the file versions using the approach in Figures 6 and 7 above. Each time you run the build batch file, you should see the build version odometer tick up one number.

Final Comments

As with most of this project, there are many other ways that this last step could have been implemented. I recommend that you choose whatever is most compatible with your current build environment.

This solution is designed strictly to be run as a batch process. In my environment, it is unnecessary (in fact, undesirable) to update the build number every time the solution is built from the VS IDE. This is usually the case when there is simultaneous development on the same code/apps, especially where formal source code control is employed. However, other environments may require version numbers to roll every time a build is run. Keep these factors in mind when you are deciding whether to include build increments as a pre-build step (not shown here) or as a batch process.

History

  • 11/17/2009 - Added working sample for VS 2003 .NET.
  • 11/20/2009 - Added VB hack to both VS 2008 and VS 2003 .NET.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)