从dll函数调用正确获取Windows版本?

假设我正在写一个多用途的dll,其中包含一个获取操作系统版本的函数:

void get_os_version(DWORD *major, DWORD *minor) { OSVERSIONINFOEX osvi; ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOsVersionInfoSize = sizeof(OSVERSIONINFOEX); // deprecated but easier to use for this example's sake GetVersionEx((OSVERSIONINFO*)&osvi); *major = osvi.dwMajorVersion; *minor = osvi.dwMinorVersion; } 

对于Windows版本高于Windows 8的版本,需要embedded一个指定受支持平台的清单(请参阅此处的详细信息)。

所以我在编译时使用/MANIFEST:NO标志禁用自动生成我的dll文件/MANIFEST:NO ,而是添加下面的清单:

 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- Windows 10 --> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> <!-- Windows 8.1 --> <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> <!-- Windows Vista --> <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> <!-- Windows 7 --> <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> <!-- Windows 8 --> <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> </application> </compatibility> </assembly> 

,使用mt工具:

 mt -manifest GetOsVersion.dll.manifest -outputresource:GetOsVersion.dll;#2 

一切顺利,没有错误。 现在要使用DLL,我创build一个简单的App.exe加载DLL并调用它的function:

 int _tmain(int argc, _TCHAR* argv[]) { DWORD major, minor; get_os_version(&major, &minor); printf("%d.%d\n", major, minor); return 0; } 

但是在Windows 10上运行App.exe时,出乎意料的是,输出结果是:

 6.2 

,这是Windows 8的版本。如果我也将清单应用到App.exe:

 mt -manifest GetOsVersion.dll.manifest -outputresource:App.exe;#1 

,产出是预期的:

 10.0 

为什么发生这种情况? 我可以解决这个问题,而无需向可执行文件添加清单?

我不能控制将使用我的库的应用程序,但我仍然要正确检索操作系统版本。

确定实际操作系统版本的另一种方法记录在MSDN页面“获取系统版本”上 :

若要获取操作系统的完整版本号,请在其中一个系统DLL(如coreel32.dll)上调用GetFileVersionInfo函数,然后调用VerQueryValue以获取\\StringFileInfo\\<lang><codepage>\\ProductVersion子块文件版本信息。

这将从DLL工作,而不管应用程序是否具有清单。

(当然,GetVersionInfo和朋友不会返回实际的操作系统版本是有原因的:程序员有一个恶意的滥用这个信息的倾向,你应该认真考虑在你的DLL中提供这样一个函数是否真的是一个好的理念。)

应用程序(可执行)清单MSDN页面相当明确地描述了这个问题:

Windows中引入的应用程序(可执行)清单的兼容性部分可帮助操作系统确定应用程序旨在针对的Windows版本。

如果清单不是可执行清单,则兼容性(以及其他应用程序设置,如DPI知晓)将被完全忽略。 这是有道理的,因为否则在不同dll的清单之间会出现明显的冲突。

作为接受答案的补充,以下是任何想要实现它的人的一些起始代码:

 #pragma comment(lib, "version") static void print_version() { DWORD buffer_size = GetFileVersionInfoSize(_T("kernel32.dll"), NULL); if (buffer_size == 0) { // get error from GetLastError() return; } VOID *buffer = malloc(buffer_size); if (buffer == NULL) { // out of memory return; } if (!GetFileVersionInfo(_T("kernel32.dll"), 0, buffer_size, buffer)) { goto error; } VS_FIXEDFILEINFO *version = NULL; UINT version_len = 0; if (!VerQueryValue(buffer, _T("\\"), (LPVOID*)&version, &version_len)) { goto error; } _tprintf(_T("Version is: %u.%u\n"), HIWORD(version->dwProductVersionMS), LOWORD(version->dwProductVersionMS)); error: free(buffer); } 

而Windows 10的输出是:

 Version is 10.0 

清单中的大多数节点都适用于整个过程,只能从主.exe模块读取:

Windows中引入的应用程序(可执行)清单的兼容性部分可帮助操作系统确定应用程序旨在针对的Windows版本。

您应该使用GetProcAddressCoCreateInstance来检查您需要的功能是否存在,而不是Windows版本。

通过一些小小的工作, GetProcAddress也可以用来确定你真正需要的信息。 查看MSDN上各种kernel32和user32函数的最低操作系统版本…