I have a version resource in my resources in a C++ project which contains version number, copyright and build details. Is there an easy way to access this at run-time to populate my help/about dialog as I am currently maintaining seperate const values of this information. Ideally, the solution should work for Windows/CE mobile and earlier versions of Visual C++ (6.0 upwards).
-
You should note that using `_get_pgmptr()` or `_get_wpgmptr()` is better than `GetModuleFileName(NULL, szFilename, MAX_PATH)`; saves you from unnecessary allocation and clarify your intention. – q12 Jan 08 '17 at 13:00
7 Answers
This is an edited version of my original answer.
bool GetProductAndVersion(CStringA & strProductName, CStringA & strProductVersion)
{
// get the filename of the executable containing the version resource
TCHAR szFilename[MAX_PATH + 1] = {0};
if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
{
TRACE("GetModuleFileName failed with error %d\n", GetLastError());
return false;
}
// allocate a block of memory for the version info
DWORD dummy;
DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
if (dwSize == 0)
{
TRACE("GetFileVersionInfoSize failed with error %d\n", GetLastError());
return false;
}
std::vector<BYTE> data(dwSize);
// load the version info
if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0]))
{
TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
return false;
}
// get the name and version strings
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
LPVOID pvProductVersion = NULL;
unsigned int iProductVersionLen = 0;
// replace "040904e4" with the language ID of your resources
if (!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductName"), &pvProductName, &iProductNameLen) ||
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
strProductName.SetString((LPCSTR)pvProductName, iProductNameLen);
strProductVersion.SetString((LPCSTR)pvProductVersion, iProductVersionLen);
return true;
}

- 299,747
- 42
- 398
- 622
-
-
@JeffB I'd start at the MSDN page [Language Identifier Constants and Strings](https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693(v=vs.85).aspx). – Mark Ransom Mar 23 '16 at 20:05
-
Both your last two lines gives me a `'void ATL::CSimpleStringT
::SetString(const char *,int)' : cannot convert argument 1 from 'LPCTSTR' to 'const char *'.`. I've `#include – Patrizio Bertoni Jun 01 '16 at 09:26` -
1
-
The example in the [`VerQueryValue` documentation](https://msdn.microsoft.com/en-us/library/ms647464(v=vs.85).aspx) uses `VerQueryValue` with `lpSubBlock = _T("\\VarFileInfo\\Translation")` to get the translation information. – Bondolin Jul 14 '16 at 13:55
-
Complete language identifier table can be found [here](https://msdn.microsoft.com/pl-pl/library/windows/desktop/aa381049(v=vs.85).aspx). 040904e4 is equivalent to 0409=US and 04e4=multilingual charset (decimal 1252) – ANTARA Nov 07 '16 at 14:57
-
This Code has an error. `VerQueryValue` returns the length of the string **including** the terminating zero. `SetString` appends the terminating zero to the string. This leads to unexpected behaviour. If you append sth. to say `strProductName` it looks like nothing happened, because there now is a zero in the middle of the string. Functions that take a zero-terminated string will take only the part of the original product name. It should be either: `strProductName.SetString((LPCSTR)pvProductName, iProductNameLen -1 );` or `strProductName.SetString((LPCSTR)pvProductName);`. – Holger Böhnke Apr 10 '17 at 17:22
-
@Holger that's not at all obvious from reading the documentation, and the code is no longer available to me for testing. I wouldn't presume to make that correction without testing. – Mark Ransom Apr 10 '17 at 17:52
-
2@Mark: There you are perfectly right. It's not very well documented. I was trying to find, why my string concatenation did not work. It was at a completely different place in the code. Finally I traced it down to the `VerQueryValue` function. There it dawned to me that there might be an additional zero in the string data. Trimmed that off and voila it worked. Maybe you just put a hint in your solution to ask people to look at my comment. If you just use the string by itself you would not notice the difference. As soon as you `+=` another string or use the `+` operator the symptom shows. – Holger Böhnke Apr 12 '17 at 19:48
-
-
When I tried it, I am able to get only the first character. I tried debugging using Visual studio. – Snippy Valson Jul 02 '19 at 07:00
-
If using TCHAR (wchar) instead of single-byte characters, use `std::vector
data(dwSize);` instead of `std::vector – AlainD Jul 18 '22 at 15:26data(dwSize);` and `LPCTSTR` instead of `LPCSTR`. -
@AlainD the return value from `GetFileVersionInfoSize` is in bytes, so the vector should be bytes as well or it will be the wrong size. You might be right about needing better casting to pull out the results though. – Mark Ransom Jul 18 '22 at 16:33
-
@MarkRansom: Ah yes, possibly `...data(dwSize / sizeof(TCHAR))` to be absolutely correct. Most of the returned data is blank so I hadn't spot that bug. Good spot. Using `TCHAR` and casting to `LPCTSTR` worked for me with VS2019 using C++14. My project `Character Set` is set (by default) to `Use Unicode Character Set`. – AlainD Jul 19 '22 at 08:42
To get a language independent result to Mark's answer change :
// replace "040904e4" with the language ID of your resources
!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
TRACE("Can't obtain ProductName and ProductVersion from resources\n");
return false;
}
To
UINT uiVerLen = 0;
VS_FIXEDFILEINFO* pFixedInfo = 0; // pointer to fixed file info structure
// get the fixed file info (language-independent)
if(VerQueryValue(&data[0], TEXT("\\"), (void**)&pFixedInfo, (UINT *)&uiVerLen) == 0)
{
return false;
}
strProductVersion.Format("%u.%u.%u.%u",
HIWORD (pFixedInfo->dwProductVersionMS),
LOWORD (pFixedInfo->dwProductVersionMS),
HIWORD (pFixedInfo->dwProductVersionLS),
LOWORD (pFixedInfo->dwProductVersionLS));
-
4This gets different information from Mark Ransom's answer. The values you retrieve are four integers from the PRODUCTVERSION top-level line in the resource file; however Marks' answer retrieves the string "ProductVersion" from under the "StringFileInfo" block. – M.M Feb 02 '18 at 03:50
Something like might get you started, perhaps:
TCHAR moduleName[MAX_PATH+1];
(void)GetModuleFileName(AfxGetInstanceHandle(), moduleName, MAX_PATH);
DWORD dummyZero;
DWORD versionSize = GetFileVersionInfoSize(moduleName, &dummyZero);
if(versionSize == 0)
{
return NULL;
}
void* pVersion = malloc(versionSize);
if(pVersion == NULL)
{
return NULL;
}
if(!GetFileVersionInfo(moduleName, NULL, versionSize, pVersion))
{
free(pVersion);
return NULL;
}
UINT length;
VS_FIXEDFILEINFO* pFixInfo;
VERIFY(VerQueryValue(pVersionInfo, const_cast<LPTSTR>("\\"), (LPVOID*)&pFixInfo, &length));

- 39,055
- 11
- 90
- 118
Beware! Using FindResource..LockResource is not correct. It will sometimes work (as it did in my small demo program) and sometimes cause access violations (example: the production code I was making the demo for).
The VerQueryValue() documentation states that you should call GetFileVersionInfoSize and GetFileVersionInfo instead. Raymond Chen explains, see http://blogs.msdn.com/oldnewthing/archive/2006/12/26/1365215.aspx

- 81
- 1
- 1
Something like this will give you raw access to the resource data and get you started:
HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(MY_VERSION_ID), RT_VERSION);
DWORD size = ::SizeofResource(NULL, res);
HGLOBAL mem = ::LoadResource(NULL, res);
LPVOID raw_data = ::LockResource(mem);
...
::FreeResource(mem);

- 76,700
- 56
- 158
- 197
Sometimes I receive Access Violation when use VerQueryValueA
. But I never got this error when use VerQueryValueW
. I think something wrong with VerQueryValueA
in version.dll. Therefore I use VerQueryValueW
instead of VerQueryValueA
even in projects Multi-byte Character Encoding. Here is my code of ReadVersion
function

- 29,228
- 8
- 68
- 105

- 1