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

时间:2017-06-20 16:26:47

标签: c++ c windows dll manifest

假设我正在编写一个多用途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 8的版本正确检索Windows版本,需要嵌入指定支持平台的清单(请参阅详细信息here)。

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

<?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,我创建一个简单的App.exe,它加载dll并调用它的函数:

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

为什么会这样?如果不向可执行文件添加清单,我可以解决这个问题吗?

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

4 个答案:

答案 0 :(得分:2)

确定实际操作系统版本的另一种方法记录在MSDN页面"Getting the System Version"上:

  

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

无论应用程序是否有清单,这都可以在DLL中使用。

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

答案 1 :(得分:2)

补充accepted answer,以下是其他任何想要实现它的人的初码:

#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

答案 2 :(得分:1)

App (executable) manifest MSDN page相当明确地描述了这个问题:

  

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

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

答案 3 :(得分:0)

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

  

Windows中引入的compatibility section of the app (executable) manifest可帮助操作系统确定应用程序旨在定位的Windows版本。

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

如果您真的需要这些信息,只需要做一些工作,GetProcAddress也可用于确定您所使用的版本。查看MSDN上各种kernel32和user32函数的最低操作系统版本......