C++ Q&A...
如何获取某个动态链接库的版本信息

原著:Paul DiLascia
翻译:NorthTibet

下载源代码:Apr98CQAcode.exe (22KB)
原文出处:C++ Q&A April 1998



 我如何获得安装在我的系统上的某个特定的 DLL 的版本信息?我尝试着确定系统安装了哪个版本的 comctl32.dll。我见过有些代码调用 GetProcAddress 来获取各种函数,如 InitCommonControlsEx,以确定基于不同版本的函数调用。对于我来说,这是一个坎儿,到底用什么方法获得版本号?

有两种方法:容易的和难的。容易的方法是调用一个专门用于此目的的函数 DllGetVersion。问题是虽然 comctl32.dll 支持该函数,但并不是所有的 DLLs 都具备它。如果不具备 DllGetVersion,那么就得用难的方法——使用 FileVersion API,这可能是你要遭遇到的最为暧昧的 API 之一。我写了一个类 CModuleVersion 来封装两种方法,同时还写了一个Demo程序 VersionDlg 来示范 CModuleVersion 的使用方法。程序画面如 Figure 1 所示。你可以在编辑框中敲入任何系统模块的名字,VersionDlg 将用 DllGetVersion (如果具备这个函数的话)和 FileVersion API 两种方法显示版本信息。源代码参见 Figure 2


Figure 1 运行中的 VersionDlg 程序

  让我们先看容易的方法。DllGetVersion 用 DLL 版本信息填写一个 DLLVERSIONINFO 结构。该结构定义在 Win32 SDK 的 showapi.h 头文件中。许多人可能都没有安装 Platform SDK,那么就得自己定义这个结构了(译者注:实际上,早期的 Developer Studio 不包含这个头文件。后来的 Visual Studio 6.0 安装已经包含该头文件,路经参见:Driver:/Program Files/Microsoft Visual Studio/VC98/Include),就像我在 VersionDlg 所做的那样。

typedef struct _DllVersionInfo {
DWORD cbSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformID;
} DLLVERSIONINFO;

  这个结构中的字段基本不用怎么说明就知道是什么意思:dwPlatformID 为 DLLVER_PLATFORM_WINDOWS (value = 1)指 Windows 9x,而 DLLVER_PLATFORM_NT (value = 2)用于 Windows NT。一旦定义了 DLLVERSIONINFO 结构,就可以调用 DllGetVersion 了,该函数的署名如下:

HRESULT DllGetVersion(DLLVERSIONINFO*);

  因为并不是任何给定的 Dll 都输出 DllGetVersion 函数,你得按照标准套路来调用它,即调用 GetProcAddress 并判断返回值是否为 NULL。我编写的类 CModuleVersion 中含有一个 DllGetVersion 函数,它把所有细节都进行了封装(参见 Figure 2 中的 ModulVer.cpp。)CModuleVersion 类的使用方法如下:

DLLVERSIONINFO dvi;
if (CModuleVersion::DllGetVersion("comctl32.dll", dvi)) 
{
    // now info is in dvi
}

  DllGetVersion 是一个比较新的函数(译者注:在1998年是这样。)对于 comctl32 很好使,因为它实现并输出 DllGetVersion——但是对于那些不输出 DllGetVersion 的 DLLs 来说怎么办呢?例如:shell32.dll 就没有实现 DllGetVersion,如 Figure 3 所示。这时你就得用可怕以及奇怪的 GetFileVersionInfo 和 VerQueryValue 函数,它们在 winver.h 中定义。


Figure 3 No DllGetVersion Info

  大多数可执行程序和 DLLs 都具备 VS_VERSION_INFO 资源,在模块的 RC 文件中定义。Figure 4 是 VersionDlg 的 RC 文件中的版本信息。你可以用文本编辑器或者 Visual Studio 直接编辑资源文件中的这段信息。你可以指定文件版本,产品版本等等,以及任何你想要编辑的字段,如:CompanyName、InternalName。文件版本信息与 Exe 或 DLL 文件在资源管理器“属性”页“版本”标签中显示的信息相同(参见 Figure 5)。


Figure 5 Version Tab

  等一会儿你就会发现,这些版本 APIs 十分暧昧,很容易把人搞晕菜,但 CModuleVersion 使一切都变得简单明了。这个类派生于 VS_FIXEDFILEINFO(参见 Figure 6),此结构包含“固定的”版本信息,其中有主版本号和次版本号,还有一些 DLLVERSIONINFO 里的东西。使用 CModuleVersion 时,只要像下面这样写即可:

CModuleVersion ver;
if (ver.GetFileVersionInfo(_T("comctl32.dll")) 
{
    WORD major = HIWORD(ver.dwFileVersionMS);
    WORD minor = LOWORD(ver.dwFileVersionMS);
    ...
}

  为了存取 CompanyName 这样的可变信息以及内涵的模块创建信息,你可以用另外一个函数 CModuleVersion:: GetValue,例如,下面代码段执行之后,sCompanyName 的值将类似“XYZ”或“Acme Corporation”这样的公司名称:

CString sCompanyName = 
ver.GetValue(_T("CompanyName"));

  CModuleVersion 隐藏了获取信息所要做的所有邋遢细节——相信我,都是些邋遢细节!如果你只是想使用 CModuleVersion,那么看到这里就可以打住了;如果你想要了解 CModuleVersion 的工作原理,那就继续往下看。
  假设 CModuleVersion::GetFileVersionInfo 能加载模块并获取 HINSTANCE,它调用 ::GetFileVersionInfoSize 来获取版本信息的大小,然后分配一个缓冲并调用 GetFileVersionInfo 来填充该缓冲。原始缓冲(CModuleVersion::m_pVersionInfo)是一个数据块,它包含固定的信息和可变信息。VerQueryValue 将一个指针指向你感兴趣的特定信息的起始位置。例如,为了得到固定的信息(VS_FIXEDFILEINFO),你得这样写:

LPVOID lpvi;
UINT iLen;
VerQueryValue(buf, _T("//"), &lpvi, &iLen);

  此处 buf 是从 GetFileVersionInfo 返回的完整信息。字符串“/”(在 C 中用“//”),你如果把它看作是一个目录,那它就是根信息(有一点像注册表)。VerQueryValue 将 lpvi 置到 VS_FIXEDFILEINFO 的起始处,iLen 为其长度。
  以上是获取固定信息的方法,可变信息获取更奇怪,因为你必须首先知道语言 ID 和代码页是什么。在 Winidows 里,代码页指定了一个字符集,它是字符文字与表示它们的 1 或 2 字节值之间映射。标准的 ANSI 代码页是 1252;Unicode 是 1200。Figure 7 是语言ID和代码页的清单。Figure 4 中文件信息里的 Translation 键指定模块的语言ID和代码页。在 CModuleVersion 中,我使用自己的 Translation 结构来获取这个信息。

// in CModuleVersion
struct TRANSLATION {
WORD langID // language ID
WORD charset; // code page
} m_translation;

为了获取语言信息,CModuleVersion 用 VerQueryValue 函数以 /VarFileInfo/Translation 作为键。

if (VerQueryValue(m_pVersionInfo,"//VarFileInfo//Translation", &lpvi, &iLen) && iLen >= 4) 
{
    m_translation = *(TRANSLATION*)lpvi;
}

一旦你知道了语言ID和代码页,你就可以得到 CompanyName 和 InternalName 这样的可变信息。实现方法是构造一个如下形式的查询:

/StringFileInfo/<langID><codepage>/<keyname> 

  这里 <langID> 是十六进制 ASCI 形式的语言ID(中文是 0804;US English 是 0409),<codepage> 是代码页,格式为(1252 即 ANSI 的代码页是04e4),<keyname> 是你想要的键,如:CompanyName。为了构造这个查询,你得用 sprintf 或者 CString::Format 来构造字符串:

//StringFileInfo//040904e4//CompanyName

  然后将这个字符串传给 VerQueryValue。如果你对这些繁琐的细节感到晕菜,不用担心——很幸运,CModuleVersion::GetValue 对所有邋遢细节都进行了封装,所以你只要像下面这样写即可:

CString s = ver.GetValue(_T("CompanyName"));

  实现了 CModuleVersion,VersionDlg 就简单多了。 它实际上就是一个对话框,这个对话框带有一个编辑框,用于输入模块名称,每当用户在编辑框中敲入模块名称时,MFC 便调用 ON_EN_CHANGE 消息处理例程 CVersionDialog::OnChangedModule。OnChangedModule 例程通过 CModuleVersion 对象及其 GetFileVersionInfo 和 GetDllVersion 函数来获得版本信息,然后将信息显示在对话框的两个静态文本控件中。这个过程很简单。
  最后还有个技巧我得提一下。GetFileVersionInfo,VerQueryValue 以及其它有关文件版本函数在一个叫做 version.lib 的库中,你必须将它链接到你程序中。从而避免链接时出现烦人的“undefined symbol”(未定义符号)错误,ModuleVer.h 使用了一个鲜为人知但特别有用的 #pragma comment 语法,即使你忘记在 Project|Settings 的 Link 属性页中添加 Input ==〉Libraries 也没关系,#pragma comment 会告诉链接器与 version.lib 链接。

// 告诉链接器与 version.lib 进行链接
#pragma comment(linker,
"/defaultlib:version.lib")

  现在,有人可能会问,为什么这些东西如此重要?以及谁会需要这些东西呢?一般来说,如果你编写的是显示文件属性之类的工具程序,那你只是需要获取诸如 CompanyName 和 LegalCopyright 之类的变量。但你也许发现用 CModuleVersion 从自己的应用程序中吸取文件信息很有用,例如,为了在“关于”对话框和启动屏幕中显示版本信息。如果你使用 CModuleVersion,你只需修改资源文件中相应位置的版本信息即可,“关于”对话框和启动屏幕会自动显示当前最新版本信息。
  版本信息另一个重要的用途是确定某个DLL是针对哪种语言编写的,这样你代码能与之对应。随着当今基于 Windows 的编程技术迅猛发展,DLLs 的新版本也随之日新月异,你很快就会发现下面这样的代码越来越多:

if (version <= 470)
// do one thing
else if (version==471)
// do something else
else if (version==472)
// do a third thing
else
// scream

  这是一件很郁闷的事情,我敢说这也是微软的大佬们引入 DllGetVersion 来快速获取版本号的一个原因,从而避免了面对让人恐惧的 GetFileVersionInfo 函数,只用它来获取语言 IDs 和代码页(仅在需要获取诸如 CompanyName 这样的信息时使用)。
  comctl32.dll 的与众不同也没有什么意外的,这个模块版本问题已经程序员最大的祸害之一,我可怜的邮箱曾被读者关于 comctl32.dll 这个模块的问题撑爆,很多问题都是客户下载了微软最新版本的 comctl32.dll 到机器上之后,应用程序就无法运行了。我会在以后的文章中解释 comctl32.dll 的版本问题,以及新的 toolbar 特性,如何解决 MFC 中 CToolBar 的 bug。现在,由于篇幅所限,我只能点到为止,目前 comctl32.dll 最新的版本为 6.00(随 IE 一起发布)。
  最后,感谢上帝,微软已经出台关于可以随你的应用程序一起分发 comctl32.dll!但不是单独分发 comctl32.dll,而是可以随你程序的更新包及其它文件一起分发。详情参见:http://msdn.microsoft.com/developer/downloads/files/40comupd.htm,请在你的新版本出炉之前仔细阅读。


编程愉快!


您的提问和评论可发送到 Paul 的信箱:cppqa@microsoft.com.
 

作者简介
  Paul DiLascia
是一名自由作家,软件咨询顾问以及大型 Web/UI 的设计师。他是《Writing Reusable Windows Code in C++》书(Addison-Wesley, 1992)的作者。业余时间他开发 PixeLib,这是一个 MFC 类库,从 Paul 的网站 http://www.dilascia.com 可以获得这个类库。
.

本文出自 Microsoft System Journal (MSJ)April 1998 期刊,可通过当地报摊获得,或者最好是 订阅

 

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 4Ver.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 6VS_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


 posted on 2010-10-21 09:46  chingliuyu  阅读(1482)  评论(0编辑  收藏  举报