This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


April 1998

Microsoft Systems Journal Homepage

C++ Q&A

Download Apr98CQAcode.exe (22KB)

Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd.

Q How can I tell which version of a particular module (DLL) is installed on my system? I am trying to figure out which version of comctl32.dll is installed. I have seen code that calls GetProcAddress to try getting various functions like InitCommonControlsEx to determine the version based on which functions are present. This seems kludgy to me. What is the right way to get the version?

A Well, there are two ways: the easy way and the hard way. The easy way is to call a special new entry DllGetVersion. The only problem is, while comctl32.dll supports this function, not all DLLs have it. If there's no DllGetVersion, you have to use the hard way—namely, the FileVersion API, which is perhaps one of the most confusing APIs you will ever come across.
      I wrote a class called CModuleVersion that encapsulates both techniques, and a demo program called VersionDlg that shows how to use it. Figure 1 shows VersionDlg in action. You can type the name of any system module into the edit control and VersionDlg displays the version information using both DllGetVersion, if it exists, and the FileVersion API. Figure 2 shows the source code.

Figure 1 VersionDlg in action
Figure 1 VersionDlg in action


      Let's take a look at the easy one first. DllGetVersion fills a DLLVERSIONINFO struct with information about a DLL version. This struct is defined in the latest (November 1997) release of the Win32® SDK in the file shlwapi.h. Many of you may not have this yet, so you may have to define DLLVERSIONINFO yourself, as I did in VersionDlg.

 typedef struct _DllVersionInfo {
   DWORD cbSize;
   DWORD dwMajorVersion;
   DWORD dwMinorVersion;
   DWORD dwBuildNumber;
   DWORD dwPlatformID;
 } DLLVERSIONINFO;
The fields are mostly self-explanatory: dwPlatformID is DLLVER_PLATFORM_WINDOWS (value = 1) for Windows® 95, or DLLVER_PLATFORM_NT (value = 2) for Windows NT®. Once you have DLLVERSIONINFO defined, you can call DllGetVersion, which has the following signature:
 HRESULT DllGetVersion(DLLVERSIONINFO*);
      Since DllGetVersion may not exist in any given DLL, you have to do the standard thing, calling GetProcAddress and handling the case of a NULL return. I wrote a CModuleVersion class with a DllGetVersion function that encapsulates all the details (see ModulVer.cpp in Figure 2). To use CModuleVersion, you can write:
   DLLVERSIONINFO dvi;
   if (CModuleVersion::DllGetVersion(
                "comctl32.dll", dvi)) {
                  // now info is in dvi
   }
      DllGetVersion is a relatively new thing that works fine for comctl32, which implements DllGetVersion—but what about DLLs that don't? For example, shell32.dll doesn't implement DllGetVersion, as Figure 3 shows. For such modules, you have to use the dreaded and bizarre GetFileVersionInfo and VerQueryValue functions defined in winver.h.
Figure 3 No DllGetVersion Info
Figure 3 No DllGetVersion Info


      Most executables and DLLs have a VS_VERSION_INFO resource, as defined in the module's RC file. Figure 4 shows the version information from VersionDlg's own RC file. You can edit this information directly in the resource file using a text editor or with Visual Studio. You can specify the file version, product version, and so on, as well as any fields you like, such as a CompanyName or InternalName. The file version information is the same as what Windows displays on the Version tab in the Properties page for an EXE or DLL file in Explorer (see Figure 5).

Figure 5 Version Tab
Figure 5 Version Tab

      The version APIs are quite confusing, as you will soon find out, but CModuleVersion makes it easy. CModuleVersion is derived from VS_FIXEDFILEINFO (see Figure 6), the structure that contains the "fixed" part of the version info, which includes the major and minor version numbers and other stuff that's in DLLVERSIONINFO. To use CModuleVersion, all you have to do is write:

 CModuleVersion ver;
 if (ver.GetFileVersionInfo(_T("comctl32.dll")) {
 WORD major = HIWORD(ver.dwFileVersionMS);
 WORD minor = LOWORD(ver.dwFileVersionMS);
 	•
 	•
 	•
 }
      To access the variable information like CompanyName and whatever else the author of the module might have put in it, you can use another function, CModuleVersion:: GetValue. For example, after the following code executes, sCompanyName will be something like "XYZ Corp" or "Acme Corporation":

 CString sCompanyName = 
    ver.GetValue(_T("CompanyName"));
CModuleVersion hides all the grungy work of getting the information—and believe me, it's grungy! If all you want to do is use CModuleVersion, you can stop now; if you want to know how it works, read on.
      Assuming CModuleVersion::GetFileVersionInfo can load the module and get an HINSTANCE, it calls ::GetFileVersionInfoSize to find out how big the version info is, then allocates a buffer and calls GetFileVersionInfo to fill it. The raw buffer (CModuleVersion::m_pVersionInfo) is a chunk of data that contains both fixed and variable information. VerQueryValue sets a pointer to the start of whatever particular information you're interested in. For example, to get the fixed info (VS_FIXEDFILEINFO), you must write:

 LPVOID lpvi;
 UINT iLen;
 VerQueryValue(buf, _T("\\"), &lpvi, &iLen);
buf is the full information returned from GetFileVersion-Info. The string "\" ("\\" in C), is the root of the information if you think of it as a directory (shades of the Registry). VerQueryValue sets lpvi to the start of the VS_FIXEDFILEINFO and iLen to its length.
      So much for getting the fixed info. Getting the variable info is even stranger, because first you need to know what the language ID and code page are. In Windows, a code page specifies a character set, which is a mapping between character glyphs and 1 or 2-byte values that represent them. The standard ANSI code page is 1252; Unicode is 1200. Figure 7 lists the language IDs and code pages. The Translation key in the file info in Figure 4 specifies the module's language ID and code page. I use my own TRANSLATION struct to get this information in CModuleVersion.

 // in CModuleVersion
 struct TRANSLATION {
 	WORD langID   // language ID
 	WORD charset; // code page
 } m_translation;
To retrieve the language information, CModuleVersion uses VerQueryValue with \VarFileInfo\Translation as the key.

 if (VerQueryValue(m_pVersionInfo,
     "\\VarFileInfo\\Translation", 
    &lpvi, &iLen) && iLen >= 4) {
 
      m_translation = *(TRANSLATION*)lpvi;
 }
      Once you know the language ID and code page, you can get the variable information like CompanyName and InternalName. To do it, you must build a query in the form

  \StringFileInfo\<langID><codepage>\<keyname> 
where <langID> is the language ID in ASCII hex (0409 for US English), <codepage> is the codepage in similar format (04e4 for 1252, the ANSI code page), and <keyname> is the key you want, like CompanyName. To build the query, you need to use sprintf or CString:: Format to build a string like

  \\StringFileInfo\\040904e4\\CompanyName
then feed this string to VerQueryValue. If it all seems terribly confusing and a bit like witchcraft, don't worry—it is. Fortunately, CModuleVersion::GetValue hides all this nonsense, so all you have to write is:

 CString s = ver.GetValue(_T("CompanyName"));
      With CModuleVersion implemented, VersionDlg is a piece of cake. It's a simple dialog with an edit field for the module name and an ON_EN_CHANGE handler named CVersionDialog::OnChangedModule, which MFC calls whenever the user types something into the module name field. OnChangedModule uses CModuleVersion to get the version info using both GetFileVersionInfo and GetDllVersion, then it displays the information, if any, in two static text fields in the dialog. Easy.
      There's one final trick I should mention. GetFileVersionInfo, VerQueryValue, and the other file version functions live in a library called—what else—version.lib, with which you must link. To avoid annoying "undefined symbol" errors during linking, ModuleVer.h uses the little-known but useful #pragma comment syntax to make your project link with version.lib even if you forget to add it to the Input Libraries list on the Link page in your Project Settings.

 // tell linker to link with 
 // version.lib
 #pragma comment(linker,
            "/defaultlib:version.lib")
      Now, many of you may be wondering why all this is so important and who ever needs to look at this stuff anyway? Typically, you only need to get the variable strings like CompanyName and LegalCopyright if you're writing some kind of tool that displays file properties. But you might find it useful to use CModuleVersion to extract the file info from your own app, for example, to display the version info in an About dialog or splash screen. If you use CModuleVersion, you only have to modify the version info in its one rightful place—the resource file—and the dialog or splash screen will show the current info automatically.
      Another important use of the version info is to find out which language a particular DLL is written for, so your code can behave appropriately. In the rapidly evolving world of Windows-based programming, where new versions of DLLs ship by the day, you'll soon find yourself more and more often writing code that looks like this:

 if (version <= 470)
  // do one thing
 else if (version==471)
  // do something else
 else if (version==472)
  // do a third thing
 else
  // scream
It's just a sad fact of life, and one reason I suspect the Redmondtonians introduced DllGetVersion as a quick way to get the version number without having to confront the dreaded GetFileVersionInfo—let alone deal with language IDs and code pages (which are really only needed for the variable stuff like CompanyName).
      It's no accident you asked in particular about comctl32.dll. This module has become one of the biggest banes to programmers, at least as far as versionitis goes. My poor email box has been flooded with questions lately from readers whose programs have broken because some free download from Microsoft put a new version of comctl32.dll on their customers' machines. Next month, I'll try to explain what's with all the versions of comctl32.dll, some of the new toolbar features, and how to circumvent bugs with MFC's CToolBar. For now, all I have room to say is that in case you didn't notice, there's an even newer version of comctl32.dll than version 4.71 (which shipped with Microsoft® Internet Explorer 4.0)—namely, 4.72. And finally, praise the Lord, Microsoft has a policy for redistributing comctl32.dll with your app! You can't distribute comctl32 by itself, but you can distribute it as part of an update packet that installs other files as well. For the full poop, see http://msdn.microsoft.com/developer/downloads/files/
40comupd.htm
. Read it now, before a new version comes out!


Have a question about programming in C or C++? Send it to askpd@pobox.com

From the April 1998 issue of Microsoft Systems Journal.