分享
 
 
 

如何获取某个动态链接库的版本信息

王朝vc·作者佚名  2006-05-23
窄屏简体版  字體: |||超大  

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 期刊,可通过当地报摊获得,或者最好是

订阅

本文由 VCKBASE MTT 翻译

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有