|
Introduction
There are several good articles here about reading module version information (by shaman74 and Armen Hakobyan), also there are some about modifying RC files during the build process (by Srinivas Vaithianathan and gugulea).
But sometimes, one needs to modify the version information in a compiled executable(s) or DLL:
- Change product name, company name, comments, etc. when deploying multiple products under different names (different skins, branding, resources) but same code;
- Update DLL version numbers without having to modify dozens of RC files;
- Modify a self-extractable archive's (simple installers) resources to reflect the product name and the version information of the contained application.
Description
This library provides an easy way to read, modify, and save version information resource(s) (RT_VERSION) in compiled executable modules (EXE, DLL, OCX, SCR, etc.).
CVersionInfo vi(strModulePath);
vi.SetFileVersion(4, 5, 0, 3,
TRUE );
vi["Comments"] = "We will stick to C++";
vi.Save();
Classes exported from VerLibInfo
CVersionInfo - The main class; reads and writes complete version information resources to/from modules. Contains CStringFileInfo and VS_FIXEDFILEINFO. The VarFileInfo structure is not a part of this object, because it is basically computed data. When saving modified version information, proper VarFileInfo is calculated from the number of string tables and their language-codepage keys.
CStringFileInfo - The wrapper for the StringFileInfo structure (contains string tables for different translations of the version information).
CStringTable- The wrapper for the string-table structure (contains the actual name-value pairs).
CVersionInfoString - The wrapper for the string structure.
CVersionInfo reference
CVersionInfo();
CVersionInfo(const CString& strModulePath,
LPCTSTR lpszResourceId = NULL ,
WORD wLangId = 0xFFFF );
virtual ~CVersionInfo();
BOOL FromFile(const CString& strModulePath,
LPCTSTR lpszResourceId = NULL ,
WORD wLangId = 0xFFFF );
BOOL ToFile(const CString& strModulePath = "",
LPCTSTR lpszResourceId = NULL ,
WORD wLangId = 0xFFFF );
BOOL Save();
void Reset();
BOOL IsValid() const;
BOOL GetInfoBlockOrder() const;
void SetInfoBlockOrder(BOOL bRegularStringsFirst);
const CStringFileInfo& GetStringFileInfo() const;
CStringFileInfo& GetStringFileInfo();
const CString operator [] (const CString &strName) const;
CString &operator [] (const CString &strName);
const VS_FIXEDFILEINFO& GetFixedFileInfo() const;
VS_FIXEDFILEINFO& GetFixedFileInfo();
void SetFileVersion(WORD dwFileVersionMSHi,
WORD dwFileVersionMSLo,
WORD dwFileVersionLSHi,
WORD dwFileVersionLSLo,
BOOL bUpdateStringTables = TRUE);
void SetFileVersion(DWORD dwFileVersionMS,
DWORD dwFileVersionLS,
BOOL bUpdateStringTables = TRUE);
void SetProductVersion(WORD dwProductVersionMSHi,
WORD dwProductVersionMSLo,
WORD dwProductVersionLSHi,
WORD dwProductVersionLSLo,
BOOL bUpdateStringTables = TRUE);
void SetProductVersion(DWORD dwProductVersionMS,
DWORD dwProductVersionLS,
BOOL bUpdateStringTables = TRUE);
Sample usage
The attached file contains a sample project that utilizes the library (VerInfoLibTest). The test tool understands several command line parameters:
Test Application for Version Info Library
USAGE:
VerInfoLibTest.exe -system
Prints out version numbers of all DLLs in Windows system folder
VerInfoLibTest.exe -f <FileName>
Prints out version info string tables of a specified module
VerInfoLibTest.exe -u <FileName> <StringName> "<Value>"
Modifies the StringName value in the first String Table
in module version information
EXAMPLES:
VerInfoLibTest.exe -u mydll.dll CompanyName "New company name"
VerInfoLibTest.exe -u mydll.dll Comments "Updated comment"
VerInfoLibTest.exe -f mydll.dll
The code for "-system":
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
TCHAR szBuf[MAX_PATH];
::GetSystemDirectory(szBuf, MAX_PATH);
CString strSystemFolder = szBuf;
hFind = FindFirstFile(strSystemFolder + "\\*.dll", &FindFileData);
if (hFind == INVALID_HANDLE_VALUE)
{
printf ("Invalid File Handle. "
"Get Last Error reports %d\n",
GetLastError ());
}
else
{
do
{
CString strModulePath = strSystemFolder +
"\\" + FindFileData.cFileName;
const CVersionInfo vi(strModulePath);
if (vi.IsValid())
{
_tprintf(_T("%s\t%s\n"),
FindFileData.cFileName,
vi["FileVersion"]);
}
}
while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
}
The code for "-f <FileName>":
const CVersionInfo vi(strFilePath);
if (vi.IsValid())
{
POSITION posTable =
vi.GetStringFileInfo().GetFirstStringTablePosition();
while (posTable)
{
const CStringTable &st =
*vi.GetStringFileInfo().GetNextStringTable(posTable);
_tprintf(_T("String table %s\n-----")
_T("-------------------------\n"),
st.GetKey());
POSITION posString = st.GetFirstStringPosition();
while (posString)
{
const CVersionInfoString &vistr =
*st.GetNextString(posString);
_tprintf(_T("%s=%s\n"), vistr.GetKey(),
vistr.GetValue());
}
_tprintf(_T("------------------------------\n"));
}
}
else
{
_tprintf(_T("Failed to get module version")
_T(" information for %s\n"), strFilePath);
}
This will produce an output similar to:
C:\...eProject\Article1\Output\Release>
VerInfoLibTest.exe -f VerInfoLib.dll
String table 040904b0
------------------------------
Comments=
CompanyName=Denis Zabavchik
FileDescription=VerInfoLib DLL
FileVersion=1, 0, 0, 1
InternalName=VerInfoLib
LegalCopyright=Copyright (C) 2006
LegalTrademarks=
OriginalFilename=VerInfoLib.DLL
PrivateBuild=
ProductName=VerInfoLib Dynamic Link Library
ProductVersion=1, 0, 0, 1
SpecialBuild=
------------------------------
The code for updating a given string is very neat (-u <FileName> <StringName> "<Value>"):
if (!_tcscmp(argv[1], _T("-u")))
{
CString strFilePath(argv[2]);
CVersionInfo vi(strFilePath);
vi[argv[3]] = argv[4];
vi.Save();
}
Notes
The code compiles and works in VC6 and 2005 (both MBCS and UNICODE are supported). MFC is used for the container classes, maps etc. The code can be easily ported to be used without MFC.
| You must Sign In to use this message board. |
|
| | Msgs 1 to 25 of 56 (Total in Forum: 56) (Refresh) | FirstPrevNext |
|
 |
|
|
Hi,
The implementation seems to be missing the methods for getting the file & product versions from the fixed structure. Although I can easily implement this in my copy of the code, I'd like to see it public too 
Thanks, Gil.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Hi, as said numerous times, great article!
I am currently using this library in a program I am writing to update the file version information to include an arbitrary build number. It is working fine apart from if the build number is too large it seems an arbitrary number is put in its place. For example,
say "verinfolibtest.exe" is currently 1.0.0.1, now I run my program with a build number of say, a large value 999999999:
%>UpdateBuildNumber.exe -f verinfolibtest.exe -n 999999999
Then it will say the current file version is now 1.0.51711.1
Is there a way of finding out when this will occur so I can catch and handle the error?
Cheers..
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
Keep in mind that VS_FIXEDFILEINFO structure hast two values:
DWORD dwFileVersionMS DWORD dwFileVersionLS
Both are double-words, each their WORD (high and low) are corresponding parts of the four part version number. Internally those two DWORDs make a 64-bit value used for numeric comparisons.
So the limit on each is one word (two bytes). The maximum for each part is 65535 (0xFFFF). So if you are trying to set one of the members to anything higher than that value, it will be mod-ed by 65536 (0x10000) (or AND-ed with 65535 (0xFFFF). In your case 999999999 % 65536 = 51711
For setting arbitrary version numbers (alpha-numeric) try using StringFileInfo values like FileVersion, PrivateBuild, SpecialBuild. Keep in mind that File Version on the top of the Version tab of File Properties always displays standard representation of the version number from VS_FIXEDFILEINFO, and arbitrary (string) version number below (in "Other version information" group).
Regards
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thank you, that answers my question perfectly. However one more question, I am trying to run my program on DLLs which have the resource information, but a assertion is failing where the hasStringTables function is in VersionInfo.cpp, what can I do to resolve this or catch it, etc..
thanks for your time
regards,
Michael
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You can just remove the whole part of the code between
#ifdef _DEBUG and
#endif
As it is just sanity check and should have been removed (I left it there, so if somebody modifies the library, he would have some code sample to perform sanity check. You don't actually need it to create f1.res and f2.res files.
You can also make a release build, it will "solve" the problem (which is BTW caused by some extra translation(s) registered but no string table for it)
-- No project was ever completed on time and within budget. Cheops Law
modified on Friday, January 11, 2008 12:29:55 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Oh I thought it wasn't necessary. In fact I just discovered that a release build gets rid of the problem all together. My program is completed now!
Thank you very much for your time and speedy responses.
Regards,
Michael
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
You might want to add this to the sample code. Updates the fileversion, so you can do VerInfoLibTest.exe -fileversion foobar.dll 1.2.3.4.
else if (argc == 4) { if (!_tcscmp(argv[1], _T("-fileversion"))) { CString filename(argv[2]); CString version(argv[3]); int dwVersion[4]; int i = 0; CString token; int j = 0; do { token = version.Tokenize(".", i); dwVersion[j++] = atoi((LPCSTR) token); } while (token.GetLength() > 0 && j < 4);
_tprintf(_T("Patching %s to version %d.%d.%d.%d\n"), (LPCSTR) filename, dwVersion[0], dwVersion[1], dwVersion[2], dwVersion[3]);
CVersionInfo vi; vi.FromFile(filename); vi.SetFileVersion(dwVersion[0], dwVersion[1], dwVersion[2], dwVersion[3]); vi.ToFile(filename); } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
Thanks. It would probably make sense to implement -productversion in sample code too. I'll update the code when I have more free time.
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
Hi, I got source files and rebuilded it, but i got 2 error: 1.version information resources in compiled binaries\tools\mainline\verinfolib\versioninfo.h(77) : error C2061: syntax error : identifier 'LONG_PTR' 2.version information resources in compiled binaries\tools\mainline\verinfolib\versioninfo.cpp(31) : error C2065: 'IS_INTRESOURCE' : undeclared identifier
I had tried include windows.h, include winuser.h, but it still got there error. My compter has .net2003, VS6. Winserver2000. and i used VC6 to rebuilded project.
Thanks and regards. ColdSun
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi,
when I try the sample program to update the file version of my InstallShiled setup.exe the setup.exe no longer works after changing. It looks like there was omething embedded in the binary before which is stripped of when using the VerInfoLib library. The setup.exe was 4011 kB before and 248 kB after the modification.
Any ideas?
Anyway, very useful tool... 
Bernd
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Wow, that's an odd side effect.
Did you trace it? When exactly does the size change?
Feel free to contact me by e-mail, I really want to see that happening and fix it.
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
It is because you are using a self-extracting file. You need to update before you alter the PE header
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hello, I'm seeing this same problem when trying to update our version information on a Macrovision file. Can you give more detail on how to update before altering the PE header?
thanks,
Charlie
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi,
First, I really appreciate this code!! Thanks! 
And now a question: When using vi.SetFileVersion on a version such as 1.02.0.0, it writes it as 1.2.0.0. In other words,
vi.SetFileVersion(1, 02, 0, 0, TRUE /*Update StringTables*/);
doesn't write the file version a 1.02, but rather 1.2.
I'm really only a beginner C++ programmer (I'm a VB guy)...how can I make it write it with the leading 0? Is this something you could fix in a new version?
Thanks, Chris
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
There are two places for both product version and file version:
1st is in the fixed (basic) version information VS_FIXEDFILEINFO which is all binary (file version and product version are made of two 32 bit values (64 bits for four parts of the version number, thus allowing 16 bits per part).
2nd is in StringFileInfo - that is a collection of name-value pairs, where values are unicode strings.
Notice the parameter TRUE /*Update StringTables*/ in your call to SetFileVersion. It was implemented for convenience, because most of the time you want to update version number in both places, and do it as MS Visual Studio does - no leading zeroes.
Windows explorer is using information from String Tables when you are looking at files' properties, so if you want it to display 1.02 you need to update the file version in string table.
See the sample at the top of the article:
// Set file version in both VS_FIXEDFILEINFO // and in all string tables // FileVersion string will be set // to "4, 5, 0, 3" just like VS does vi.SetFileVersion(4, 5, 0, 3, TRUE /*Update StringTables*/);
// Alternatively, if you need to set // non-numeric version number // like 4.5.0.3452Rus.RelB.MFC7 use // vi["FileVersion"] = "4.5.0.3452Rus.RelB.MFC7"
You need to modify your code - set FileVersion separately. For that replace one line with these two:
vi.SetFileVersion(1, 2, 0, 0, FALSE /*Don't update StringTables, I'll write arbitrary value there*/); vi["FileVersion"] = "1.02"
Don't forget to save update version info.
Regards,
Denis
P.S. This does not guarantee that some applications will not display version as 1.2.0.0 for your module. Such applications may disregard what is in string tables and obtain the version number(s) from VS_FIXEDFILEINFO and format it as it needs, which will in most cases not have trailing zeros
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks so much Denis for your reply to this! 
So what I am hearing you saying is that a fileversion as found in VS_FIXEDFILEINFO can not appear as 1.02.0.0 because of the way it is constructed, however the string fileversion can do this (because it can show whatever you want).
I just always thought that there was a way for VS_FIXEDFILEINFO to do this and that other programs did it, but I guess they don't. I certainly couldn't make it do it.
Anyway, thanks. Chris
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
chrislong2 wrote: So what I am hearing you saying is that a fileversion as found in VS_FIXEDFILEINFO can not appear as 1.02.0.0 because of the way it is constructed
That's an incorrect question
Value vs. Representation!
Your version number will be stored in VS_FIXEDFILEINFO's member variables DWORD dwFileVersionMS; DWORD dwFileVersionLS;
In hex it will look like this: dwFileVersionMS = 0x00010002; dwFileVersionLS = 0x00000000;
One may interprete/format the version number like this strVersion.Format(_T("%d.%02d.%d.%d"), HIWORD(dwFileVersionMS), LOWORD(dwFileVersionMS), HIWORD(dwFileVersionLS), LOWORD(dwFileVersionLS));
strVersion for your number will look like 1.02.0.0
Somebody may use _T("%d.%02d.%04d.%04d") which will result in 1.02.0000.0000.
Microsoft chose %d.%d.%d.%d format when displaying "File version:" on the "Version" tab of file properties (first line). In the "Other version information" you see the list of items, one of them is File Version. The representation/format of a string is string itself (the only formatting is word wrapping).
So if you need to display 1.02.0.0 or 1.02 somewhere from your own code, all you need to do is format binary data from VS_FIXEDFILEINFO to your liking, or get File Version from string table. In later case you have a lot of flexibility - you can have any text in version number (i.e. File Version for XP SP2 package: 5.5.1005.0 (SRV03_QFE.031113-0918)
If you want version number for your DLL/EXE to be displayed as 1.02.0.0 in the first line on the Version tab (Right click/Properties/Version) - you can't do that.
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Denis,
Is there a similar way to update resource strings in STRINGTABLE?
Is there an easy way to port these libraries for use in non-MFC apps?
Thanks!!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi,
Yes, there is a way to update string resources. See this KB article http://support.microsoft.com/kb/q196774/.
As for MFC, you can convert it to non MFC project. Replace/remove CString, and container classes (maps, etc.) with similar from other libraries.
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi Denis,
Not tested yet but I'm planning to use it under Windows98. Any info about it?
Also, what about limited user rights under Windows 2000/XP?
Thanks, Pavel
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Very good tool and I am going to use a modified version (one executable, no DLL, update the FileDescription if it has a particular string).
However, I can't find out if there is a restriction on the length of the FileDescription string. Do you know of one (and any other strings in the Version String Table)??
David
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
According to MSDN (String structure description): This structure is not a true C-language structure because it contains variable-length members. This structure was created solely to depict the organization of data in a version resource and does not appear in any of the header files shipped with the Microsoft® Platform Software Development Kit (SDK).
struct String { WORD wLength; WORD wValueLength; WORD wType; WCHAR szKey[]; WORD Padding[]; WORD Value[]; };
Members wLength - Specifies the length, in bytes, of this String structure. wValueLength - Specifies the size, in words, of the Value member.
Since wLength is a WORD and wValueLength is a WORD maximum String structure size is 64K. I didn't try writing long strings into the version resources, but if I had to I would never have the Value longer than:
65535 - (sizeof(WORD)*3 /* size of fixed size members */ + (wcslen(szKey) + sizeof(WCHAR) /*zero term for szKey*/ + 3 /*max padding*/) & (~3 /*alignment) ) bytes
Which leaves you enough space for about 32740 (or so) unicode characters for the value of FileDescription.
(Keep in mind that as any other modification to executable it will invalidate any strong name digital signatures, if module has been signed)
Best regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Thanks but I knew that.
However, if I change the "File Description" to a string longer than about 37-48 characters (depending on whether it is Windows XP/Windows 2000), it gets truncated in MS's version display (right click file in Windows Explorer, select Properties, select Version tab).
Note MS put "File Description" in the top part of the file's properties display with limited width along with the version and copyright information, rather than under the heading of "Other version information", which seems to allow the string size you mention.
Apart from using your program, I don't know how a "general user" would be able to view this "File Descritpion" except in this properties page and, if it is always truncated, then there is no point in putting anything there longer than can be displayed i.e. ~40 characters.
David
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
I would just put all extra information into Comment. Because of sorting it will show up on the top of the list, and it's value will be displayed in multi-line edit box.
Regards,
Denis
-- No project was ever completed on time and within budget. Cheops Law
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|