在Windows 8.1或Windows 10中正确获取系统版本

很多人都知道一个WinAPI函数: GetVersionEx ,这个函数可以获取系统版本的详细信息(信息被放在第一个参数 OSVERSIONINFO OSVERSIONINFOEX 结构体中),它的Unicode形式如下( WINAPI 就是 __stdcall 的意思): 

BOOL WINAPI GetVersionExW(OSVERSIONINFOW* lpVersionInformation);  

其中OSVERINFOEX结构体如下(OSVERINFO结构体只到szCSDVersion就没有了): 

typedef struct _OSVERSIONINFOEXW {
    DWORD dwOSVersionInfoSize;
    DWORD dwMajorVersion;
    DWORD dwMinorVersion;
    DWORD dwBuildNumber;
    DWORD dwPlatformId;
    WCHAR  szCSDVersion[ 128 ];     // Maintenance string for PSS usage
    WORD   wServicePackMajor;
    WORD   wServicePackMinor;
    WORD   wSuiteMask;
    BYTE  wProductType;
    BYTE  wReserved;
} OSVERSIONINFOEXW, *POSVERSIONINFOW, *LPOSVERSIONINFOW, RTL_OSVERSIONINFOW, *PRTL_OSVERSIONINFOW;


但是在 Windows 8.1 Windows 10 中,这个函数不灵了,返回的是如下的信息,即返回系统版本为 Windows 8(6.2.9200) : 

在Windows 8.1或Windows 10中正确获取系统版本_第1张图片  

这是因为微软因为某种原因,在Windows 8.1中废弃了GetVersionEx函数。所有的程序如果使用了GetVersionEx判断系统版本,在Windows 8.1或Windows 10中都会只返回Windows 8的版本信息,而不会返回当前的版本信息。 

一个替代的方法是调用 VerifyVersionInfo 函数,但是这个函数使用比较麻烦。为了方便起见,微软在 <VersionHelpers.h> 中增加了一个非常方便的辅助函数 IsWindowsVersionOrGreater ,这个函数只有三个参数——主版本号、次版本号、服务包版本号: 

BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor);  

但是有时候我们只想返回版本信息,并不想一个个地去验证版本号。在Windows 8及以前版本的操作系统中,kernel32.dll中的GetVersionExW函数会直接调用 ntdll.dll 中一个名为 RtlGetVersion 的函数,这个函数在Windows 8.1以上的系统中返回的仍然是正确的信息。它的函数原型如下( NTAPI 也是 __stdcall 的意思): 

NTSTATUS NTAPI RtlGetVersion(RTL_OSVERSIONINFOW* lpVersionInformation);  

可以看到,这个RtlGetVersion函数跟GetVersionExW的形式基本一致( RTL_OSVERSIONINFOW等于OSVERSIONINFOW ),只有返回值不同。 NTSTATUS 的规则是NTSTATUS>=0成功,NTSTATUS<0失败。 

由于微软并没有对ntdll.dll制作相应的ntdll.lib导入库文件,这个RtlGetVersion函数并不能直接调用。但是我们可以采取另外的办法。由于kernel32.dll一定会调用ntdll.dll,因此每个程序启动时,ntdll.dll其实已经加载到我们的进程上了,我们不需要再用LoadLibrary加载它,只需要先使用以下方法即可获取ntdll.dll的模块句柄: 

HMODULE hNtdll = GetModuleHandleW(L"NTDLL");  

然后使用 GetProcAdderss(hNtdll, "RtlGetVersion") 获取RtlGetVersion函数的指针即可进行调用。为了方便起见,我们可以自己写一个 GetVersionEx2 函数,用以代替被Windows 8.1废掉的GetVersionEx: 

BOOL GetVersionEx2(LPOSVERSIONINFOW lpVersionInformation)
{
    HMODULE hNtDll = GetModuleHandleW(L"NTDLL"); // 获取ntdll.dll的句柄
    typedef NTSTATUS (NTAPI*tRtlGetVersion)(PRTL_OSVERSIONINFOW povi); // RtlGetVersion的原型
    tRtlGetVersion pRtlGetVersion = NULL;
    if (hNtDll)
    {
        pRtlGetVersion = (tRtlGetVersion)GetProcAddress(hNtDll, "RtlGetVersion"); // 获取RtlGetVersion地址
    }
    if (pRtlGetVersion)
    {
        return pRtlGetVersion((PRTL_OSVERSIONINFOW)lpVersionInformation) >= 0; // 调用RtlGetVersion
    }
    return FALSE;
}


用这个RtlGetVersion写成的GetVersionEx2函数获取的信息是正确的: 

在Windows 8.1或Windows 10中正确获取系统版本_第2张图片  

完整的程序: 

#include <windows.h>
#include <stdio.h>
#include <atlbase.h>
                       
BOOL GetVersionEx2(LPOSVERSIONINFOW lpVersionInformation)
{
    HMODULE hNtDll = GetModuleHandleW(L"NTDLL"); // 获取ntdll.dll的句柄
    typedef NTSTATUS (NTAPI*tRtlGetVersion)(PRTL_OSVERSIONINFOW povi); // RtlGetVersion的原型
    tRtlGetVersion pRtlGetVersion = NULL;
    if (hNtDll)
    {
        pRtlGetVersion = (tRtlGetVersion)GetProcAddress(hNtDll, "RtlGetVersion"); // 获取RtlGetVersion地址
    }
    if (pRtlGetVersion)
    {
        return pRtlGetVersion((PRTL_OSVERSIONINFOW)lpVersionInformation) >= 0; // 调用RtlGetVersion
    }
    return FALSE;
}
                       
#define NTDLL_RTL_GET_VERSION
                       
int main(int argc, _TCHAR* argv[])
{
    OSVERSIONINFOEXW ovi = {sizeof ovi};
                       
#ifdef NTDLL_RTL_GET_VERSION
    GetVersionEx2((LPOSVERSIONINFOW)&ovi);
    printf("使用NTDLL->RtlGetVersion获取的信息:\n");
#else
    GetVersionExW((LPOSVERSIONINFOW)&ovi);
    printf("使用KERNEL32->GetVersionExW获取的信息:\n");
#endif
                       
    printf("dwMajorVersion: %08x %d\n", ovi.dwMajorVersion, ovi.dwMajorVersion);
    printf("dwMinorVersion: %08x %d\n", ovi.dwMinorVersion, ovi.dwMinorVersion);
    printf("dwBuildNumber: %08x %d\n", ovi.dwBuildNumber, ovi.dwBuildNumber);
    printf("dwPlatformID: %08x %d\n", ovi.dwPlatformId, ovi.dwPlatformId);
    printf("szCSDVersion: %s\n", (char*)CW2A(ovi.szCSDVersion, 1));
    printf("wServicePackMajor: %04x %d\n", ovi.wServicePackMajor, ovi.wServicePackMajor);
    printf("wServicePackMinor: %04x %d\n", ovi.wServicePackMinor, ovi.wServicePackMinor);
    printf("wSuitMask: %04x %d\n", ovi.wSuiteMask, ovi.wSuiteMask);
    printf("wProductType: %02x %d\n", ovi.wProductType, ovi.wProductType);
    printf("wReserved: %02x %d\n", ovi.wReserved, ovi.wReserved);
                       
    return 0;
}


你可能感兴趣的:(在Windows 8.1或Windows 10中正确获取系统版本)