Click here to Skip to main content
15,870,130 members
Articles / Desktop Programming / MFC
Article

Updating version information at run-time

Rate me:
Please Sign up or sign in to vote.
4.63/5 (14 votes)
14 Jul 20056 min read 232.1K   3.1K   45   89
Updating version information at run-time rather than at design-time.

Sample Image - UpdateVersion.jpg

Introduction

The question "How can I update the version information in my file?" usually yields a handful of solutions. Everyone has their favorite. Some are generic, while others are specific to a particular environment. This type of problem is one of the reasons that the "There's more than one way to skin a cat" idiom exists.

Back when I was still using the 16-bit compiler (v1.52c), the solution I put in place required the version information structure in the .rc file to be modified to look something like:

#include "version.h"
VS_VERSION_INFO VERSIONINFO
 FILEVERSION _FILEVERSION_
 PRODUCTVERSION _PRODUCTVERSION_
 ...

The version.h file contained a whole slew of #define directives, a pair for each module. I then put together a little utility that ran during the automated build process that would open the version.h file, find the right #define directives, and increment them accordingly. This solution worked well, with little to no user interaction. For some reason, when our development team moved to the 32-bit compiler, version numbers, or at least the auto-incrementing of them, moved to the back burner. It wasn't that they were any less important, but was because we only had a small handful of 32-bit modules and it was more efficient to change the version numbers manually than to re-tool the utility to work with the 32-bit compiler's way of processing the .rc file. Maybe the .rc2 file could have been used. I give VB a thumbs-up for having this functionality in its IDE.

When the question is coupled with "at runtime rather than compile time", the solution becomes a tad more complicated. Such a question came across my viewport the other day.

You've no doubt used the suite of version-related functions to retrieve file version, description, copyright, platform, etc. from a file. Most have even seen the handy class, CModuleVersion, put together by Paul DiLascia back in 1998 to read from both new and old files. Knowing that the VERSIONINFO resource was stored in a file in an easy-to-retrieve location (we can use GetFileVersionInfo() for this or go Matt Pietrek's way (an up-to-date article is here) and navigate the PE file structure manually!), it only makes sense that it should be easy to update. The remainder of this article is my attempt at just that.

Implementation

The first thing we need to do is get a pointer to the start of the version information block. This is done with:

LPCTSTR lpszFilename = _T("c:\\winnt\\system32\\atl.dll");
DWORD   dwHandle,
        dwSize;

// determine the size of the resource information
dwSize = GetFileVersionInfoSize(lpszFile, &dwHandle);
if (0 < dwSize)
{
    LPBYTE lpBuffer = new BYTE[dwSize];

    GetFileVersionInfo(lpszFile, 0, dwSize, lpBuffer);
    ...
    delete [] lpBuffer;
}

At this point, lpBuffer now points to the start of the version information block, a VS_VERSIONINFO structure actually. In my example, that address is 0x007b25a8. From dwSize, I know that the block is 1700 bytes in size. From my tests, it seems that the compiler allocates more room for the version information block than is actually consumed by the data. Maybe, this is for performance reasons, or to ensure that boundaries are aligned following the version information block. It may also be that GetFileVersionInfoSize() is just overly cautious!

This image shows some of the version information block. What's outlined in red is the data that will be updated.

Sample screenshot

Next, we get to do a little pointer arithmetic. You'll note that the VS_VERSIONINFO structure contains a VS_FIXEDFILEINFO member structure. No big deal except that it follows a variable-length member and some padding bytes. The number of characters in the szKey member of VS_VERSIONINFO rounded to the nearest 4-byte boundary is where the VS_FIXEDFILEINFO member actually starts. So, if the szKey member contained 15 characters (usually "VS_VERSION_INFO"), plus 1 for the NULL character, the number of bytes offset from the beginning of the version information block would be calculated as:

// 'point to' the start of the version information block
VS_VERSIONINFO *pVerInfo = (VS_VERSIONINFO *) lpBuffer;

// the fixed section starts right after the 'VS_VERSION_INFO' string
LPBYTE pOffsetBytes = (BYTE *) &pVerInfo->szKey[_tcslen(pVerInfo->szKey) + 1];

VS_FIXEDFILEINFO *pFixedInfo = 
    (VS_FIXEDFILEINFO *) roundpos(pVerInfo, pOffsetBytes, 4);

After rounding to the nearest 4-byte boundary, we get 28. So, pFixedInfo starts 28 bytes after the beginning of the version information block, or address 0x007b25d0. roundpos() is a macro that handles the rounding. In this case, it uses a 4-byte boundary. Consult the source code for the actual implementation.

We're now ready to increment the version-related members of the VS_FIXEDFILEINFO structure. Just for demonstration purposes, I incremented each word by one using:

pFixedInfo->dwFileVersionMS    = pFixedInfo->dwFileVersionMS + 0x00010001;
pFixedInfo->dwFileVersionLS    = pFixedInfo->dwFileVersionLS + 0x00010001;
pFixedInfo->dwProductVersionMS = pFixedInfo->dwProductVersionMS + 0x00010001;
pFixedInfo->dwProductVersionLS = pFixedInfo->dwProductVersionLS + 0x00010001;

This was so I could tell that everything was working when I viewed the "before" and "after" files (see the pictures at the top of this article). At this point, we have an in-memory snapshot of an updated version information block. It's time to commit it to disk. Using the resource-related functions, we have:

HANDLE hResource = BeginUpdateResource(lpszFile, FALSE);
if (NULL != hResource)
{
    UINT uTemp;

    // get the language information
    if (VerQueryValue(lpBuffer,
                      _T("\\VarFileInfo\\Translation"),
                      (LPVOID *) &lpTranslate,
                      &uTemp) != FALSE)
    {
        // could probably just use LANG_NEUTRAL/SUBLANG_NEUTRAL
        if (UpdateResource(hResource,
                           RT_VERSION,
                           MAKEINTRESOURCE(VS_VERSION_INFO),
                           lpTranslate->wLanguage,
                           lpBuffer,
                           dwSize) != FALSE)
        {
            EndUpdateResource(hResource, FALSE);
        }
    }
}

Note the comment about using LANG_NEUTRAL/SUBLANG_NEUTRAL as the fourth parameter to UpdateResource(). The section of the version information block that we updated was independent of language and code page, so we could probably get away without having to call VerQueryValue() to get the translation array.

At this point, the file's version information has been updated. However, there remains but one problem. Can you spot it? The corresponding version numbers in the file's .rc file remains unchanged. The next time the file is built, the previous numbers will be used. I guess that's one reason for updating the .rc file rather than the compiled file.

Updating the StringFileInfo section

The above code snippets focused on the VS_FIXEDFILEINFO section of the file. This is somewhat easier to do because the information contained within it is, well, fixed. The information contained in the StringFileInfo section is not necessarily more difficult to change, but it does have the constraint that the new information be the same size or smaller than the old information. In other words, if I wanted to change the string "Copyright 2004" with an updated string of "Copyright 2004-05" the results would not be pretty since the string is three characters longer. Those extra characters would inevitably write on something important.

The first thing we need to do is get a pointer to one of the sub-blocks in the StringFileInfo section. We do this using VerQueryValue() like:

CString strSubBlock;
LPTSTR  pValueBuffer;

strSubBlock.Format(_T("\\StringFileInfo\\%04x%04x\\CompanyName"),
                   lpTranslate->wLanguage, 
                   lpTranslate->wCodePage);

VerQueryValue(lpBuffer, (LPTSTR) ((LPCTSTR) strSubBlock), 
           (LPVOID *) &pValueBuffer, &uTemp);

At this point, the variable pValueBuffer is pointing to the CompanyName key. Whatever change I make to this value must be the same length or smaller and end with a '\0' character. So you can either right-pad the new string with '\0' characters or zero the whole string out and then update it. For the latter, I used:

ZeroMemory(pValueBuffer, _tcslen(pValueBuffer) * sizeof(TCHAR));
_tcscpy(pValueBuffer, _T("My Company, Inc."));

You can watch this transition if you have the Memory window open in the IDE. At this point, BeginUpdateResource(), UpdateResource(), and EndUpdateResource() can be called as shown above.

Notes

Two of the language-specific strings that relates to this are FileVersion and ProductVersion. They're contained in the Children member of the StringTable structure and are dependent on language and code page. The updating of those is left as an exercise for the reader, or the author if time and interest permits.

References

Thanks goes out to Ted Peck for supplying the two macros I used in this sample.

Enjoy!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior) Pinnacle Business Systems
United States United States

The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

HTTP 404 - File not found
Internet Information Services

Comments and Discussions

 
GeneralAdd FileVersion to exe at runtime. Pin
JohnnyH!13-Dec-06 5:17
JohnnyH!13-Dec-06 5:17 
QuestionRe: Add FileVersion to exe at runtime. Pin
David Crow15-Dec-06 4:41
David Crow15-Dec-06 4:41 
QuestionWhat about the exe file has two version part(one is english, one is french) Pin
Zhengjun Yang14-Aug-06 22:45
Zhengjun Yang14-Aug-06 22:45 
AnswerRe: What about the exe file has two version part(one is english, one is french) Pin
David Crow15-Aug-06 3:08
David Crow15-Aug-06 3:08 
AnswerRe: What about the exe file has two version part(one is english, one is french) Pin
Zhengjun Yang15-Aug-06 22:52
Zhengjun Yang15-Aug-06 22:52 
GeneralRe: What about the exe file has two version part(one is english, one is french) Pin
David Crow16-Aug-06 5:51
David Crow16-Aug-06 5:51 
GeneralThanx a lot for this, what about managed assembly DLL Pin
Mushtaque Nizamani21-Jul-06 1:56
Mushtaque Nizamani21-Jul-06 1:56 
QuestionCan anyone help me with changing CompanyName dynamically? Pin
ehaerim12-Jun-06 12:46
ehaerim12-Jun-06 12:46 
There is a VC++6.0 project called "A.dsw" which will be compiled into "A.exe". Its VS_VERSION_INFO block has "MyCompany" for the CompanyName field. And I need to use a COM component called X.dll inside this project.

Unfortunately, this X.dll reads the CompanyName field of loading program (A.exe) and checks if the value is "YourCompany" (Please don't ask me why. Smile | :) ) If not, it does NOT work.

So, I would like to temporarily change the CompanyName value from "MyCompany" to "YourCompany" right before loading X.dll and use the X component. After done with X component, I would like to change the CompanyName back to "MyCompany". I would call such function as "string SetCompanyName(string strCompanyName)".

For example,

...
string strPrevCompany = SetCompanyName("YourCompany"); // strPrevCompany will have "MyCompany"
... // use X.dll COM
strPrevCompany = SetCompanyName(strPrevCompany);
...

If anyone can provide me with a sample working project, that will be greatly appreciated.

Thanks.

LHR
AnswerRe: Can anyone help me with changing CompanyName dynamically? Pin
David Crow13-Jun-06 3:00
David Crow13-Jun-06 3:00 
AnswerRe: Can anyone help me with changing CompanyName dynamically? Pin
ehaerim13-Jun-06 8:27
ehaerim13-Jun-06 8:27 
QuestionRe: Can anyone help me with changing CompanyName dynamically? Pin
David Crow13-Jun-06 8:37
David Crow13-Jun-06 8:37 
Generalcompliaton errors Pin
ehaerim12-Jun-06 12:23
ehaerim12-Jun-06 12:23 
QuestionRe: compliaton errors Pin
David Crow13-Jun-06 2:56
David Crow13-Jun-06 2:56 
AnswerRe: compliaton errors => stdafx.h Pin
ehaerim13-Jun-06 8:20
ehaerim13-Jun-06 8:20 
QuestionRe: compliaton errors => stdafx.h Pin
David Crow13-Jun-06 8:33
David Crow13-Jun-06 8:33 
AnswerRe: compliaton errors => stdafx.h Pin
ehaerim13-Jun-06 9:53
ehaerim13-Jun-06 9:53 
GeneralRe: compliaton errors => stdafx.h Pin
ehaerim13-Jun-06 9:57
ehaerim13-Jun-06 9:57 
GeneralRe: compliaton errors => stdafx.h Pin
David Crow13-Jun-06 9:58
David Crow13-Jun-06 9:58 
GeneralRe: compliaton errors => stdafx.h Pin
ehaerim13-Jun-06 11:17
ehaerim13-Jun-06 11:17 
GeneralUsing Visual Studios 6.0 Pin
Christopher Stratmann27-Apr-06 1:03
Christopher Stratmann27-Apr-06 1:03 
GeneralCorruption Pin
sguay17-Mar-06 3:05
sguay17-Mar-06 3:05 
QuestionIs this possible? Pin
Drca11-Jan-06 3:44
Drca11-Jan-06 3:44 
AnswerRe: Is this possible? Pin
Denis Zabavchik8-Mar-06 21:36
Denis Zabavchik8-Mar-06 21:36 
GeneralProblem with VerQueryValue Pin
Drca10-Jan-06 6:30
Drca10-Jan-06 6:30 
QuestionRe: Problem with VerQueryValue Pin
David Crow10-Jan-06 6:37
David Crow10-Jan-06 6:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.