VersionDlg.cpp
//////////////////////////////////////////////////////////////////
// VersionDlg 1998 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// VersionDlg illustrates how to use CModuleVersion and DllGetVersion to
// read the version info for a DLL/EXE. The only interesting function
// for the purpose of CModuleVersion is OnChangedModule, which is called
// when the user enters a new module name into the edit control.
//
#include "stdafx.h"
#include "resource.h"
#include "ModulVer.h"
#include "StatLink.h"
#include "TraceWin.h"
//#include <shlwapi.h> // if you have the Nov 1997 SDK installed
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CVersionDialog dialog
//
class CVersionDialog : public CDialog {
public:
CVersionDialog(CWnd* pParent = NULL);
CString m_sModuleName; // module name typed by user
protected:
CStaticLink m_wndLink1; // web link
CStaticLink m_wndLink2; // web link
CStaticLink m_wndLink3; // web link
virtual void DoDataExchange(CDataExchange* pDX);
virtual BOOL OnInitDialog();
virtual void OnOK();
afx_msg void OnChangedModule();
DECLARE_MESSAGE_MAP()
};
////////////////////////////////////////////////////////////////
// CMyApp
//
class CMyApp : public CWinApp {
public:
CMyApp() { }
virtual BOOL InitInstance();
} theApp;
/////////////////
// Initialize: just run the dialog and quit.
//
BOOL CMyApp::InitInstance()
{
CVersionDialog dlg; // create dialog..
dlg.DoModal(); // ..run it
return FALSE; // ..and quit
}
//////////////////////////////////////////////////////////////////
// CVersionDialog
//
BEGIN_MESSAGE_MAP(CVersionDialog, CDialog)
ON_EN_CHANGE(IDC_EDIT_MODULE, OnChangedModule)
END_MESSAGE_MAP()
CVersionDialog::CVersionDialog(CWnd* pParent) : CDialog(IDD_VERSION, pParent)
{
}
/////////////////
// Initialize dialog: subclass static hyperlinks
//
BOOL CVersionDialog::OnInitDialog()
{
m_wndLink1.SubclassDlgItem(IDC_STATICPD, this,
_T("http://pobox.com/~dilascia"));
m_wndLink2.SubclassDlgItem(IDC_STATICMSJ, this,
_T("http://www.microsoft.com/msj"));
m_wndLink3.SubclassDlgItem(IDC_ICONMSJ, this,
_T("http://www.microsoft.com/msj"));
return CDialog::OnInitDialog();
}
/////////////////
// When user pressed Enter, don't exit
//
void CVersionDialog::OnOK()
{
return; // (don't exit)
}
//////////////////
// Standard MFC DDX data exchange for edit control
//
void CVersionDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT_MODULE, m_sModuleName);
}
//////////////////
// User changed the module name: vet version info if I can.
//
void CVersionDialog::OnChangedModule()
{
UpdateData(TRUE); // get dialog data (module name)
CString s;
CModuleVersion ver;
// 1st get version using File version API
//
if (ver.GetFileVersionInfo(m_sModuleName)) {
// display file version from VS_FIXEDFILEINFO struct
s.Format("Version: %d.%d.%d.%d\n",
HIWORD(ver.dwFileVersionMS), LOWORD(ver.dwFileVersionMS),
HIWORD(ver.dwFileVersionLS), LOWORD(ver.dwFileVersionLS));
// display a bunch of string values
static LPCTSTR Keys[] = {
_T("CompanyName"),
_T("FileDescription"),
_T("FileVersion"),
_T("InternalName"),
_T("LegalCopyright"),
_T("OriginalFilename"),
_T("ProductName"),
_T("ProductVersion"),
NULL
};
for (int i=0; Keys[i]; i++) {
CString temp;
temp.Format("%s:\t%s\n", Keys[i], ver.GetValue(Keys[i]));
s += temp;
}
}
// set static text
GetDlgItem(IDC_STATICINFO)->SetWindowText(s);
// 2nd get version using DllGetVersion API
//
s.Empty();
DLLVERSIONINFO dvi;
if (ver.DllGetVersion(m_sModuleName, dvi)) {
s.Format(_T("DLL Version = %d.%02d\nBuild# = %d\n"),
dvi.dwMajorVersion,
dvi.dwMinorVersion,
dvi.dwBuildNumber);
s +=_T("Platform is ");
if (dvi.dwPlatformID == DLLVER_PLATFORM_WINDOWS)
s +=_T("Windows");
else if (dvi.dwPlatformID == DLLVER_PLATFORM_NT)
s +=_T("Windows NT");
else
s += _T("unrecognized");
} else {
s += _T("This file does not implement DllGetVersion.");
}
// set static text
GetDlgItem(IDC_STATICINFO2)->SetWindowText(s);
}
ModulVer.h
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal
//
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#ifndef __MODULEVER_H
#define __MODULEVER_H
// tell linker to link with version.lib for VerQueryValue, etc.
#pragma comment(linker, "/defaultlib:version.lib")
#ifndef DLLVERSIONINFO
// following is from shlwapi.h, in November 1997 release of the Windows SDK
typedef struct _DllVersionInfo
{
DWORD cbSize;
DWORD dwMajorVersion; // Major version
DWORD dwMinorVersion; // Minor version
DWORD dwBuildNumber; // Build number
DWORD dwPlatformID; // DLLVER_PLATFORM_*
} DLLVERSIONINFO;
// Platform IDs for DLLVERSIONINFO
#define DLLVER_PLATFORM_WINDOWS 0x00000001 // Windows 95
#define DLLVER_PLATFORM_NT 0x00000002 // Windows NT
#endif // DLLVERSIONINFO
//////////////////
// CModuleVersion version info about a module.
// To use:
//
// CModuleVersion ver
// if (ver.GetFileVersionInfo("_T("mymodule))) {
// // info is in ver, you can call GetValue to get variable info like
// CString s = ver.GetValue(_T("CompanyName"));
// }
//
// You can also call the static fn DllGetVersion to get DLLVERSIONINFO.
//
class CModuleVersion : public VS_FIXEDFILEINFO {
protected:
BYTE* m_pVersionInfo; // all version info
struct TRANSLATION {
WORD langID; // language ID
WORD charset; // character set (code page)
} m_translation;
public:
CModuleVersion();
virtual ~CModuleVersion();
BOOL GetFileVersionInfo(LPCTSTR modulename);
CString GetValue(LPCTSTR lpKeyName);
static BOOL DllGetVersion(LPCTSTR modulename, DLLVERSIONINFO& dvi);
};
#endif
ModulVer.cpp
////////////////////////////////////////////////////////////////
// 1998 Microsoft Systems Journal
// If this code works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
// CModuleVersion provides an easy way to get version info
// for a module.(DLL or EXE).
//
#include "StdAfx.h"
#include "ModulVer.h"
CModuleVersion::CModuleVersion()
{
m_pVersionInfo = NULL; // raw version info data
}
//////////////////
// Destroy: delete version info
//
CModuleVersion::~CModuleVersion()
{
delete [] m_pVersionInfo;
}
//////////////////
// Get file version info for a given module
// Allocates storage for all info, fills "this" with
// VS_FIXEDFILEINFO, and sets codepage.
//
BOOL CModuleVersion::GetFileVersionInfo(LPCTSTR modulename)
{
m_translation.charset = 1252; // default = ANSI code page
memset((VS_FIXEDFILEINFO*)this, 0, sizeof(VS_FIXEDFILEINFO));
// get module handle
TCHAR filename[_MAX_PATH];
HMODULE hModule = ::GetModuleHandle(modulename);
if (hModule==NULL && modulename!=NULL)
return FALSE;
// get module file name
DWORD len = GetModuleFileName(hModule, filename,
sizeof(filename)/sizeof(filename[0]));
if (len <= 0)
return FALSE;
// read file version info
DWORD dwDummyHandle; // will always be set to zero
len = GetFileVersionInfoSize(filename, &dwDummyHandle);
if (len <= 0)
return FALSE;
m_pVersionInfo = new BYTE[len]; // allocate version info
if (!::GetFileVersionInfo(filename, 0, len, m_pVersionInfo))
return FALSE;
LPVOID lpvi;
UINT iLen;
if (!VerQueryValue(m_pVersionInfo, _T("\\"), &lpvi, &iLen))
return FALSE;
// copy fixed info to myself, which am derived from VS_FIXEDFILEINFO
*(VS_FIXEDFILEINFO*)this = *(VS_FIXEDFILEINFO*)lpvi;
// Get translation info
if (VerQueryValue(m_pVersionInfo,
"\\VarFileInfo\\Translation", &lpvi, &iLen) && iLen >= 4) {
m_translation = *(TRANSLATION*)lpvi;
TRACE("code page = %d\n", m_translation.charset);
}
return dwSignature == VS_FFI_SIGNATURE;
}
//////////////////
// Get string file info.
// Key name is something like "CompanyName".
// returns the value as a CString.
//
CString CModuleVersion::GetValue(LPCTSTR lpKeyName)
{
CString sVal;
if (m_pVersionInfo) {
// To get a string value must pass query in the form
//
// "\StringFileInfo\<langID><codepage>\keyname"
//
// where <langID><codepage> is the languageID concatenated with the
// code page, in hex. Wow.
//
CString query;
query.Format(_T("\\StringFileInfo\\%04x%04x\\%s"),
m_translation.langID,
m_translation.charset,
lpKeyName);
LPCTSTR pVal;
UINT iLenVal;
if (VerQueryValue(m_pVersionInfo, (LPTSTR)(LPCTSTR)query,
(LPVOID*)&pVal, &iLenVal)) {
sVal = pVal;
}
}
return sVal;
}
// typedef for DllGetVersion proc
typedef HRESULT (CALLBACK* DLLGETVERSIONPROC)(DLLVERSIONINFO *);
/////////////////
// Get DLL Version by calling DLL's DllGetVersion proc
//
BOOL CModuleVersion::DllGetVersion(LPCTSTR modulename, DLLVERSIONINFO& dvi)
{
HINSTANCE hinst = LoadLibrary(modulename);
if (!hinst)
return FALSE;
// Must use GetProcAddress because the DLL might not implement
// DllGetVersion. Depending upon the DLL, the lack of implementation of the
// function may be a version marker in itself.
//
DLLGETVERSIONPROC pDllGetVersion =
(DLLGETVERSIONPROC)GetProcAddress(hinst, _T("DllGetVersion"));
if (!pDllGetVersion)
return FALSE;
memset(&dvi, 0, sizeof(dvi)); // clear
dvi.cbSize = sizeof(dvi); // set size for Windows
return SUCCEEDED((*pDllGetVersion)(&dvi));
}
Figure 4 Ver.rc
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Written by Paul DiLascia\0"
VALUE "FileDescription",
"VersionDlg Application by Paul DiLascia\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "VersionDlg\0"
VALUE "LegalCopyright", "1998 Paul DiLascia\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "VersionDlg.EXE\0"
VALUE "ProductName", "VersionDlg\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
Figure 6 VS_FIXEDFILEINFO
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature; /* e.g. 0xfeef04bd */
DWORD dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
DWORD dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
DWORD dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
DWORD dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */
DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
DWORD dwFileType; /* e.g. VFT_DRIVER */
DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
DWORD dwFileDateMS; /* e.g. 0 */
DWORD dwFileDateLS; /* e.g. 0 */
} VS_FIXEDFILEINFO;
Figure 7 Language IDs and Code Pages
Language ID | Language |
0x0401 | Arabic |
0x0402 | Bulgarian |
0x0403 | Catalan |
0x0404 | Traditional Chinese |
0x0405 | Czech |
0x0406 | Danish |
0x0407 | German |
0x0408 | Greek |
0x0409 | U.S. English |
0x040A | Castilian Spanish |
0x040B | Finnish |
0x040C | French |
0x040D | Hebrew |
0x040E | Hungarian |
0x040F | Icelandic |
0x0410 | Italian |
0x0411 | Japanese |
0x0412 | Korean |
0x0413 | Dutch |
0x0414 | Norwegian - Bokml |
0x0810 | Swiss Italian |
0x0813 | Belgian Dutch |
0x0814 | Norwegian - Nynorsk |
0x0415 | Polish |
0x0416 | Brazilian Portuguese |
0x0417 | Rhaeto-Romanic |
0x0418 | Romanian |
0x0419 | Russian |
0x041A | Croato-Serbian (Latin) |
0x041B | Slovak |
0x041C | Albanian |
0x041D | Swedish |
0x041E | Thai |
0x041F | Turkish |
0x0420 | Urdu |
0x0421 | Bahasa |
0x0804 | Simplified Chinese |
0x0807 | Swiss German |
0x0809 | U.K. English |
0x080A | Mexican Spanish |
0x080C | Belgian French |
0x0C0C | Canadian French |
0x100C | Swiss French |
0x0816 | Portuguese |
0x081A | Serbo-Croatian (Cyrillic) |
Code Page | Character Set |
932 | Windows, Japan (Shift - JIS X-0208) |
949 | Windows, Korea (Shift - KSC 5601) |
950 | Windows, Taiwan (GB5) |
1200 | Unicode |
1250 | Windows, Latin-2 (Eastern European) |
1251 | Windows, Cyrillic |
1252 | Windows, Multilingual |
1253 | Windows, Greek |
1254 | Windows, Turkish |
1255 | Windows, Hebrew |
1256 | Windows, Arabic |