
Introduction
This application contains a small class supporting access to the version
information stored in YOUR OWN Win32 application.
Background
There is a lot code around the Internet, showing how easy it is to retrieve
version information from any EXE or DLL. I never really liked the idea, opening
my own application's file again (using GetFileVersionInfo()
) simply
to be able to access my local version data.
It took me quite some searching and other people's free source code to
understand how this information may be retrieved from the local application's
resource (equivalent to CString::LoadString()
etc.).
Using the code
First of all:
Be happy to use this code to your convenience
and modify/enhance it as you like.
This is what you have to do
1. Add version.lib to
your project's libraries
2. Add "#include <winver.h>" to your
stdafx.h
(could also be included to CGlobalFunctions.cpp
instead)
3. Add GlobalFunctions.h and
CGlobalFunctions.cpp to your project
4. Call any of the static
functions from anywhere in your application
GlobalFunctions.h:
class CGlobalFunctions : public CObject
{
public:
CGlobalFunctions();
virtual ~CGlobalFunctions();
public:
static CString GetFileVersionX();
static CString GetProductVersionX();
static CString GetVersionInfo(HMODULE hLib, CString csEntry);
static CString FormatVersion(CString cs);
private:
static CString m_csFileVersion;
static CString m_csProductVersion;
};
GlobalFunctions.cpp:
(see source code for more detailed description
of the methods)
String CGlobalFunctions::m_csFileVersion;
CString CGlobalFunctions::m_csProductVersion;
CGlobalFunctions::CGlobalFunctions()
{
}
CGlobalFunctions::~CGlobalFunctions()
{
}
CString CGlobalFunctions::GetVersionInfo(HMODULE hLib, CString csEntry)
{
CString csRet;
if (hLib == NULL)
hLib = AfxGetResourceHandle();
HRSRC hVersion = FindResource( hLib,
MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION );
if (hVersion != NULL)
{
HGLOBAL hGlobal = LoadResource( hLib, hVersion );
if ( hGlobal != NULL)
{
LPVOID versionInfo = LockResource(hGlobal);
if (versionInfo != NULL)
{
DWORD vLen,langD;
BOOL retVal;
LPVOID retbuf=NULL;
static char fileEntry[256];
sprintf(fileEntry,"\\VarFileInfo\\Translation");
retVal = VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen);
if (retVal && vLen==4)
{
memcpy(&langD,retbuf,4);
sprintf(fileEntry, "\\StringFileInfo\\%02X%02X%02X%02X\\%s",
(langD & 0xff00)>>8,langD & 0xff,(langD & 0xff000000)>>24,
(langD & 0xff0000)>>16, csEntry);
}
else
sprintf(fileEntry, "\\StringFileInfo\\%04X04B0\\%s",
GetUserDefaultLangID(), csEntry);
if (VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen))
csRet = (char*)retbuf;
}
}
UnlockResource( hGlobal );
FreeResource( hGlobal );
}
return csRet;
}
CString CGlobalFunctions::FormatVersion(CString cs)
{
CString csRet;
if (!cs.IsEmpty())
{
cs.TrimRight();
int iPos = cs.Find(',');
if (iPos == -1)
return "";
cs.TrimLeft();
cs.TrimRight();
csRet.Format("%s.", cs.Left(iPos));
while (1)
{
cs = cs.Mid(iPos + 1);
cs.TrimLeft();
iPos = cs.Find(',');
if (iPos == -1)
{
csRet +=cs;
break;
}
csRet += cs.Left(iPos);
}
}
return csRet;
}
CString CGlobalFunctions::GetFileVersionX()
{
if (m_csFileVersion.IsEmpty())
{
CString csVersion = FormatVersion(GetVersionInfo(NULL, "FileVersion"));
m_csFileVersion.Format("Version %s (Build %s)",
csVersion, GetVersionInfo(NULL, "SpecialBuild"));
}
return m_csFileVersion;
}
CString CGlobalFunctions::GetProductVersionX()
{
if (m_csProductVersion.IsEmpty())
m_csProductVersion = FormatVersion(GetVersionInfo(NULL, "ProductVersion"));
return m_csProductVersion;
}
Using it anywhere in the application:
(e.g. in
CGetVersionApp::InitInstance()
)
....
CString cs;
cs.Format("FileVersion: %s\nProductVersion: %s\nMyPrivateInfo: %s",
CGlobalFunctions::GetFileVersionX(),
CGlobalFunctions::GetProductVersionX(),
CGlobalFunctions::GetVersionInfo(NULL, "MyPrivateInfo"));
AfxMessageBox(cs);
....
Points of Interest
Well, if you take a close look (while debuging) at fileEntry your will
notify it's containig "\StringFileInfo\040704B0\ProductVersion".
Compare this path with the structure of Version in the
*.rc-file. This makes clear how the access of the version information works:
GetVersion.rc:
...
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "Comments", "\0"
VALUE "CompanyName", "\0"
VALUE "FileDescription", "MFC-Anwendung GetVersion\0"
VALUE "FileVersion", "1, 2, 3, a\0"
VALUE "InternalName", "GetVersion\0"
VALUE "LegalCopyright", "Copyright (C) 2004\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "GetVersion.EXE\0"
VALUE "PrivateBuild", "\0"
VALUE "ProductName", "Anwendung GetVersion\0"
VALUE "ProductVersion", "1, 0, 0, a\0"
VALUE "SpecialBuild", "2\0"
VALUE "MyPrivateInfo",
"This is an privately defined version information\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x407, 1200
END
END
NOTE:
I have extended the version information
structure by an own entry ("MyPrivateInfo").
You may retrieve this
information in the same way as you can using all the predefined
entries.
List of predefined entries within the version information
structure:
Comments
CompanyName
FileDescription
FileVersion
InternalName
LegalCopyright
LegalTrademarks
OriginalFilename
PrivateBuild
ProductName
ProductVersion
SpecialBuild
Further recommendation
As you know from Microsoft products, beside the version information they
specify a build code e.g. (Build 1376).
There is a free add-in for
Visual C++ that automatically increments the "SpecialBuild" entry within your
*.rc file.
It has been developed by Mihai
Filimon and is called IncPrivateBuild.
Click here
for more information.
It comes with the source code an is a really cool
tool.
Cheers and enjoy this code,
Hartmut