You would think that retrieving version information from a program would be a basic and simple task. For example if you want to display your version number in an About box. For a long time I’ve stored version numbers in a resource (as they should be) but used a separate #define in the code, which is easy to access and display in an About box. Finally I got tired of this redundancy and dug up some old code I had (circa 1998!) and holy crap, it still works. And holy crap, the API hasn’t changed either.
The code was in C, and ugly, so I made a new class based on the old code. It has a static method to get the fixed portion of the version resource, and another method to get a string. one to get a string. Here is the string version:
CString CMyVersion::GetStringItem (CString modulename, CString itemName)
{
CString result = "";
if (modulename.IsEmpty())
{
modulename = CCoreFile::GetProgramPath();
}
DWORD handle; // not used, will be set to 0.
DWORD size = ::GetFileVersionInfoSize (modulename, &handle);
if (size > 0)
{
VOID* block = ::HeapAlloc (::GetProcessHeap(), HEAP_ZERO_MEMORY, size);
if (block != NULL)
{
if (::GetFileVersionInfo (modulename, 0, size, block))
{
// The pointer returned in buffer is freed automatically when block is freed.
// This assumes the english language block
itemName = "\StringFileInfo\040904E4" + itemName;
LPBYTE buffer;
UINT length;
if (::VerQueryValue (block, itemName, (LPVOID*)&buffer, &length))
{
result = CString(buffer);
}
}
::HeapFree(::GetProcessHeap(), 0, block);
}
}
return result;
}
Note the limitation, this method assumes you want an item in the English language version of the StringFileInfo block. Since I don’t foresee myself needing my version number in Swahili, I think this limitation will be fine.
To get the file or product version using this class:
CString fileVersion = CMyVersion::GetStringItem(modulename, "FileVersion");CString productVersion = CMyVersion::GetStringItem(modulename, "ProductVersion");
Getting the fixed part of the version resource is similar:
BOOL CMyVersion::GetFixedInfo (CString modulename, VS_FIXEDFILEINFO* info)
{
BOOL result = FALSE;
if (modulename.IsEmpty())
{
modulename = CCoreFile::GetProgramPath();
}
DWORD handle; // not used, will be set to 0.
DWORD size = ::GetFileVersionInfoSize (modulename, &handle);
if (size > 0)
{
VOID* block = ::HeapAlloc (::GetProcessHeap(), HEAP_ZERO_MEMORY, size);
if (block != NULL)
{
if (::GetFileVersionInfo (modulename, 0, size, block))
{
// The pointer returned in buffer is freed automatically when block is freed.
LPBYTE buffer;
UINT length;
if (::VerQueryValue (block, “\”, (LPVOID*)&buffer, &length))
{
length = min(length, sizeof(*info));
::CopyMemory(info, buffer, length);
result = TRUE;
}
}
::HeapFree(::GetProcessHeap(), 0, block);
}
}
return result;
}
The Windows Version API certainly is robust, and supportive of multiple languages, but for goodness sake, didn’t the author realize that most of its usefulness would be in retrieving a version number? Why go through all of this nonsense for such a simple task?
Tags: C++, Version Resource, Windows API, Windows programming
feed