|

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
| You must Sign In to use this message board. |
|
| | Msgs 1 to 17 of 17 (Total in Forum: 17) (Refresh) | FirstPrevNext |
|
|
 |
|
|
 |
|
|
This is explain in MSDN that to get the Version Info resources, you should use the following methods : 1) GetFileVersionInfoSize 2) GetFileVersionInfo 3) VerQueryValue
Your code generates an access violation, I think this is not the good way.
Regards. Annie
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
Do we need to care the following exceptions? I'm kind of worried.
1. In VC++6.0
First-chance exception in GetVersion.exe (VERSION.DLL): 0xC0000005: Access Violation.
or In VS .Net 2003
2. First-chance exception at 0x77c015b5 in GetVersion.exe: 0xC0000005: Access violation writing location 0x00412c6e.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi zwu_ca,
sorry, I have no idea at all. I'm using my code in several projects within VC++ 6.0 SP 6 and havn't had any problems.
Actually the application GetVersion.Exe was intended to be just a demo on how to your this class. Further more, I'm not developing with VS.NET.
Sorry again, Luetz
Hartmut Luetz-Hawranke tetronik GmbH AEN http://www.tetronik.com/
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Getting version info with this method seems to be a rather unsafe thing to do. According to this recent article: http://blogs.msdn.com/oldnewthing/archive/2006/12/26/1365215.aspx you shouldn't be using ANYTHING other than GetFileVersionInfo for the parameter to VerQueryValue. Still, I gave it a shot in my project, and while it works in Debug mode, in the Release compilation, the call to VerQueryValue seems to just... disappear. I have a call to log an Event Viewer entry before the call, which I see, and after the call, which I don't see. My program doesn't get an access violation or an exception or otherwise crash, but SOMETHING very odd is going on and I must assume some memory is being overwritten somewhere.
Anyway, I'm going to just stick with calling GetFileVersionInfo once when my DLL is loaded and cache the value.
Using VC++ 7.0.9466
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
For unicode app, you should replace GlobalFunctions.cpp by this source code!
// GlobalFunctions.cpp: Implementierung der Klasse CGlobalFunctions. // //////////////////////////////////////////////////////////////////////
#include "stdafx.h" #include "stdafx.h" #include "GlobalFunctions.h"
#ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif
////////////////////////////////////////////////////////////////////// // Konstruktion/Destruktion //////////////////////////////////////////////////////////////////////
CString CGlobalFunctions::m_csFileVersion; CString CGlobalFunctions::m_csProductVersion;
CGlobalFunctions::CGlobalFunctions() {
}
CGlobalFunctions::~CGlobalFunctions() {
}
/***********************************************************************************/ /* */ /* Class: CGlobalFunctions */ /* Method: GetVersionInfo */ /* */ /* Parameters: */ /* ----------- */ /* HMODULE hLib */ /* Handle to the module that contains the resource (EXE or DLL) */ /* A value of NULL specifies the current applications resources */ /* */ /* CString csEntry */ /* Specifies the name of the resource. For more information, */ /* see the Remarks section. */ /* */ /* Return Values: */ /* -------------- */ /* If the function succeeds, the return value is a string containing the value */ /* of the specified resource. */ /* If the function fails, the returned string is empty. To get extended error */ /* information, call GetLastError. */ /* */ /* Remarks: */ /* -------- */ /* Since the Win32 API resource information is encoded in Unicode, this method */ /* also strips the strings from Unicode. */ /* */ /* The following valid values for csEntry, as specified by Microsoft are: */ /* CompanyName, FileDescription, FileVersion, InternalName, LegalCopyright, */ /* OriginalFilename, ProductName, ProductVersion, Comments, LegalTrademarks, */ /* PrivateBuild, SpecialBuild */ /* */ /* Opening the rc-file as "text" or with a text-editor allows you to add further */ /* entries to your version information structure and it is retrievable using */ /* this same method. */ /* */ /***********************************************************************************/
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; #ifdef _UNICODE static TCHAR fileEntry[256]; wsprintf(fileEntry,_T("\\VarFileInfo\\Translation")); #else static char fileEntry[256]; sprintf(fileEntry,_T("\\VarFileInfo\\Translation")); #endif
retVal = VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen); if (retVal && vLen==4) { memcpy(&langD,retbuf,4); #ifdef _UNICODE wsprintf(fileEntry, _T("\\StringFileInfo\\%02X%02X%02X%02X\\%s"), (langD & 0xff00)>>8,langD & 0xff,(langD & 0xff000000)>>24, (langD & 0xff0000)>>16, csEntry); #else sprintf(fileEntry, _T("\\StringFileInfo\\%02X%02X%02X%02X\\%s"), (langD & 0xff00)>>8,langD & 0xff,(langD & 0xff000000)>>24, (langD & 0xff0000)>>16, csEntry); #endif } else #ifdef _UNICODE wsprintf(fileEntry, L"\\StringFileInfo\\%04X04B0\\%s", GetUserDefaultLangID(), csEntry); #else sprintf(fileEntry, L"\\StringFileInfo\\%04X04B0\\%s", GetUserDefaultLangID(), csEntry); #endif
if (VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen)) #ifdef _UNICODE csRet = (TCHAR*)retbuf; #else csRet = (char*)retbuf; #endif } }
UnlockResource( hGlobal ); FreeResource( hGlobal ); }
return csRet; }
/***********************************************************************************/ /* */ /* Class: CGlobalFunctions */ /* Method: FormatVersion */ /* */ /* Parameters: */ /* ----------- */ /* CString cs */ /* Specifies a version number such as "FileVersion" or */ /* "ProductVersion" in the format "m, n, o, p" */ /* (e.g. "1, 2, 3, a") */ /* */ /* Return Values: */ /* -------------- */ /* If the function succeeds, the return value is a string containing the version */ /* in the format "m.nop" (e.g. "1.23a") */ /* */ /* If the function fails, the returned string is empty. */ /* */ /***********************************************************************************/ CString CGlobalFunctions::FormatVersion(CString cs) { CString csRet; if (!cs.IsEmpty()) { cs.TrimRight(); int iPos = cs.Find(','); if (iPos == -1) return _T(""); cs.TrimLeft(); cs.TrimRight(); csRet.Format(_T("%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; }
/***********************************************************************************/ /* */ /* Class: CGlobalFunctions */ /* Method: GetFileVersionX */ /* */ /* Parameters: */ /* ----------- */ /* */ /* Return Values: */ /* -------------- */ /* If the function succeeds, the return value is a string containing the */ /* "FileVersion" in the format "m.nop" (e.g. "1.23a") */ /* */ /* If the function fails, the returned string is empty. */ /* */ /***********************************************************************************/ CString CGlobalFunctions::GetFileVersionX() { if (m_csFileVersion.IsEmpty()) { CString csVersion = FormatVersion(GetVersionInfo(NULL, "FileVersion")); m_csFileVersion.Format(_T("Version %s (Build %s)"), csVersion, GetVersionInfo(NULL, _T("SpecialBuild"))); }
return m_csFileVersion; }
/***********************************************************************************/ /* */ /* Class: CGlobalFunctions */ /* Method: GetFileVersionX */ /* */ /* Parameters: */ /* ----------- */ /* */ /* Return Values: */ /* -------------- */ /* If the function succeeds, the return value is a string containing the */ /* "ProductVersion" in the format "m.nop" (e.g. "1.23a") */ /* */ /* If the function fails, the returned string is empty. */ /* */ /***********************************************************************************/ CString CGlobalFunctions::GetProductVersionX() { if (m_csProductVersion.IsEmpty()) m_csProductVersion = FormatVersion(GetVersionInfo(NULL, "ProductVersion"));
return m_csProductVersion; }
Nguyen Thien Nhan Entec Software HCM, VietNam
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
Hi,
thanks for posting the Unicode version. 
Cheers Luetz
Hartmut Luetz-Hawranke tetronik GmbH AEN http://www.tetronik.com/
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Hi all,
The second call to VerQueryValue always returns 0 for me, and so the program is unable to retrieve the version information.
if (VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen)) csRet = (char*)retbuf;
The first call however works fine. I am running in Win98, but when I run the program in Win2000 it works fine. Any ideas?
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
You haven't added the logic of removing trailing zeros from version. For Example. 1.110 is the version of exe In this case, No need of trailing 0 at the end since 1.11 is quite appropraite.
Ajay
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
Ajay - Get a life! Can't you say anything more constructive than the above remark!. Reporting 4 numeric values is definitely the correct approach. In your remarked example 1.110 is more precise than 1.11; the unlisted 4th digit, if not printed, becomes ambiguous issue. The user does not know if the last digit is a zero that is not printed or the programmer arbitrarily truncated the version to three digits.....
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
Ohh I'm sorry Sir, I should have given constructive remark. but do u know if you want to improve yourself, then let the other people find out the problems. I personally do the same thing. There is no doubt that I liked this article a lot and I used it in my application, But thought some of the modifications that I did certianly would be useful for others. So I just put that remark.
Ajay
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Wow! |  | 309DA944-69E5-4f18-9E33-56D805BE5BD1 | 10:05 13 Jan '05 |
|
|
 |
|
|
retVal = VerQueryValue(versionInfo,fileEntry,&retbuf,(UINT *)&vLen); is returning a memory fault
I am using your code in a MFC DLL compiled in VS2003.Net
Any Ideas on what may be the problem??
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
vLen should be declared as UINT, not DWORD. As DWORD is a 32 bit memory allocation, but UINT will depend on platform...
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
|
I've been looking for something like this for sometime and even tried to spend some time on this subject. Very helpful. Got my 5 points!!
Rolando
|
| Sign In·View Thread·PermaLink | 5.00/5 (1 vote) |
|
|
|
 |
|
|
General News Question Answer Joke Rant Admin
|