Figure 2    VersionDlg

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