《关于NOTIFYICONDATA的一些新特性》中,我提到了使用
- HRESULT CALLBACK DllGetVersion(DLLVERSIONINFO *pdvi);
这一函数获取Shell32.dll的版本号,但这一函数不是API,而是Shell32.dll中的一个导出函数。我们需要使用LoadLibrary与GetProcAddress 来获取这个函数的指针,并调用它,才能实现版本的获取。 下面,简单介绍下如何实现这一过程。
首先,DllGetVersion并不是每一个版本的Shell32.dl的导出函数,若函数GetProcAddress在Shell32.dll中找不到此导出函数,返回值为0 ,这时,我们可以确定Shell32.dll为4.7.1之前的版本(4.7.1开始有此导出函数)。
其次,使用 LoadLibrary 加载dll时,错误的操作会有安全风险,我们必须确保dll路径有效并且正确。
再次,函数DllGetVersion的唯一参数是一个指向DLLVERSIONINFO结构的指针,该结构用于接收dll的版本信息(major and minor version numbers, the build number, and a platform ID),在调用之前,我们必须初始化该结构中的cbSize。下面给出的是该结构定义。
- typedef struct _DllVersionInfo {
- DWORD cbSize;
- DWORD dwMajorVersion;
- DWORD dwMinorVersion;
- DWORD dwBuildNumber;
- DWORD dwPlatformID;
- } DLLVERSIONINFO;
另外,在Windows 2000及之后的版本,系统返回的可能是DLLVERSIONINFO2结构,为了保持兼容性,该结构的第一个成员是DLLVERSIONINFO结构。在返回DLLVERSIONINFO2结构时,可以使用宏MAKEDLLVERULL将 versions, build numbers, and service pack releases等信息与该结构中的成员 ullVersion 进行比较。下面给出的是该结构的定义。
- typedef struct _DLLVERSIONINFO2 {
- DLLVERSIONINFO info1;
- DWORD dwFlags;
- ULONGLONG ullVersion;
- } DLLVERSIONINFO2;
最后,该函数的返回值为S_OK则表示成功获取。
下面给出的是MSDN中的一个例子,用于获取指定路径dll的版本信息,版本信息中的major,minor信息组合成DWORD形式返回,若指定Dll不存在,返回0。
- #include "stdafx.h"
- #include "windows.h"
- #include "windef.h"
- #include "winbase.h"
- #include "shlwapi.h"
- #define PACKVERSION(major,minor) MAKELONG(minor,major)
- DWORD GetVersion(LPCTSTR lpszDllName)
- {
- HINSTANCE hinstDll;
- DWORD dwVersion = 0;
- /* For security purposes, LoadLibrary should be provided with a fully-qualified
- path to the DLL. The lpszDllName variable should be tested to ensure that it
- is a fully qualified path before it is used. */
- hinstDll = LoadLibrary(lpszDllName);
- if(hinstDll)
- {
- DLLGETVERSIONPROC pDllGetVersion;
- pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinstDll, "DllGetVersion");
- /* Because some DLLs might not implement this function, you must test for
- it explicitly. Depending on the particular DLL, the lack of a DllGetVersion
- function can be a useful indicator of the version. */
- if(pDllGetVersion)
- {
- DLLVERSIONINFO dvi;
- HRESULT hr;
- ZeroMemory(&dvi, sizeof(dvi));
- dvi.info1.cbSize = sizeof(dvi);
- hr = (*pDllGetVersion)(&dvi);
- if(SUCCEEDED(hr))
- {
- dwVersion = PACKVERSION(dvi.info1.dwMajorVersion, dvi.info1.dwMinorVersion);
- }
- }
- FreeLibrary(hinstDll);
- }
- return dwVersion;
- }
调用例子,判断Shell32.dll版本是否大于6.0
- LPCTSTR lpszDllName = L"C://Windows//System32//Shell32.dll";
- DWORD dwVer = GetVersion(lpszDllName);
- DWORD dwTarget = PACKVERSION(6,0);
- if(dwVer >= dwTarget)
- {
- // This version of Shell32.dll is version 6.0 or later.
- }
- else
- {
- // Proceed knowing that version 6.0 or later additions are not available.
- // Use an alternate approach for older the DLL version.
- }
该例子存在一些问题,函数GetVersion定义了dvi为DLLVERSIONINFO型变量,却引用了不存在的成员info1,应该去掉通过该成员的调用,或者直接定义为DLLVERSIONINFO2型的数据。
info1实际上为DLLVERSIONINFO2类型的成员,我们可以定义该结构变量,并初始化info1中的cbSize为DLLVERSIONINFO2大小,再传递dvi.info1至GetDllVersion,这个函数原意可能是想表明如何使用DLLVERSIONINFO2类型的结构,再使用ullVersion成员进行判断。
下面介绍使用DLLVERSIONINFO2结构进行版本判断,宏MAKEDLLVERULL参数为
- ULONGLONG MAKEDLLVERULL(
- WORD wMajorVersion,
- WORD wMinorVersion,
- WORD wBuild,
- WORD wQFE
- );
调用如下:
- DLLVERSIONINFO2 dvi2;
- HRESULT hr;
- ZeroMemory(&dvi2, sizeof(dvi2));
- dvi2.info1.cbSize = sizeof(dvi2);
- hr = (*pDllGetVersion)(&dvi2.info1);
- if(dvi2.ullVersion >= MAKEDLLVERULL(4, 71, 0,0))
- {
- ...
- }
经测试,在WIN7 64bit与XP下ullVersion都可以接收到正确的版本号信息。
最后补充一点,关于Shell32.dll版本,在MSDN中给出6.0.6版本以上均为Vista及以上操作系统,而XP则是6.0版本的。
劝各位先查看下自己硬盘/windows/system32/shell32.dll的属性中版本信息,再选择具体的判断依据。
在我的电脑上,其中在XPSP3下的版本为6.0.2900.5512,WIN7 64bit下为6.1.7600.16644。
我不知道6.0.6与6.0.2900哪个版本高,但若是利用ullVersion直接判断,那么6.0.6 < 6.0.2900。
Vista正确的版本号是6.0.6000.xxxx,写程序时不要忽略了这点。
具体的信息,可以参看英文原版MSDN,链接:
http://msdn.microsoft.com/en-us/library/bb776779(v=vs.85).aspx