Since Windows 8.1, these functions have been deprecated and run in a sort of compatibility mode, reporting Windows 8 unless the application supplies a manifest that specifies compatibility with newer Windows versions explicitly (by listing their GUIDs).
Some applications have bad non-forward-compatible checks based on GetVersionEx, and depend on this behavior (they do not supply a manifest). Currently, they break on Wine if we use a Windows 10 prefix for example, since we always report the real version. One example is the game Rock of Ages.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
v2: Duplicated the tables and implementation of RtlVerifyVersionInfo to avoid extra internal exports from ntdll. Also used goto instead of many nested ifs.
dlls/kernel32/tests/version.c | 2 +- dlls/kernelbase/version.c | 170 +++++++++++++++++++++++++++++++--- 2 files changed, 157 insertions(+), 15 deletions(-)
diff --git a/dlls/kernel32/tests/version.c b/dlls/kernel32/tests/version.c index 1bb3e20..c9e92a9 100644 --- a/dlls/kernel32/tests/version.c +++ b/dlls/kernel32/tests/version.c @@ -610,7 +610,7 @@ static void test_VerifyVersionInfo(void)
if (rtlinfo.dwMajorVersion != 6 || rtlinfo.dwMinorVersion != 2) { - win_skip("GetVersionEx and VerifyVersionInfo are faking values\n"); + skip("GetVersionEx and VerifyVersionInfo are faking values\n"); return; } } diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c index cf46e9c..dc8cc61 100644 --- a/dlls/kernelbase/version.c +++ b/dlls/kernelbase/version.c @@ -28,6 +28,8 @@ #include <stdio.h> #include <sys/types.h>
+#include "ntstatus.h" +#define WIN32_NO_STATUS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" @@ -116,6 +118,132 @@ typedef struct (VS_VERSION_INFO_STRUCT32 *)( (LPBYTE)ver + (((ver)->wLength + 3) & ~3) )
+/*********************************************************************** + * Win8 info, reported if app doesn't provide compat GUID in manifest. + */ +static const RTL_OSVERSIONINFOEXW windows8_version_data = +{ + sizeof(RTL_OSVERSIONINFOEXW), 6, 2, 0x23f0, VER_PLATFORM_WIN32_NT, + {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 +}; + + +/*********************************************************************** + * Windows versions that need compatibility GUID specified in manifest + * in order to be reported by the APIs. + */ +static const struct +{ + RTL_OSVERSIONINFOEXW info; + GUID guid; +} version_data[] = +{ + /* Windows 8.1 */ + { + { + sizeof(RTL_OSVERSIONINFOEXW), 6, 3, 0x2580, VER_PLATFORM_WIN32_NT, + {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 + }, + {0x1f676c76,0x80e1,0x4239,{0x95,0xbb,0x83,0xd0,0xf6,0xd0,0xda,0x78}} + }, + /* Windows 10 */ + { + { + sizeof(RTL_OSVERSIONINFOEXW), 10, 0, 0x42ee, VER_PLATFORM_WIN32_NT, + {0}, 0, 0, VER_SUITE_SINGLEUSERTS, VER_NT_WORKSTATION, 0 + }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } +}; + + +/*********************************************************************** + * Holds the current version (including compatibility mode). + * Call init_current_version before using it. + */ +static RTL_OSVERSIONINFOEXW current_version; + + +/****************************************************************************** + * init_current_version + * + * Initialize the current_version variable. + * + * For compatibility, Windows 8.1 and later report Win8 version unless the app + * has a manifest that confirms its compatibility with newer versions of Windows. + * + */ +static BOOL CALLBACK init_current_version_callback(PINIT_ONCE init_once, PVOID parameter, PVOID *context) +{ + /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci; + RTL_OSVERSIONINFOEXW tmp_version; + const RTL_OSVERSIONINFOEXW *ver; + SIZE_T req; + int idx; + + if (!set_ntstatus(RtlGetVersion(&tmp_version))) return FALSE; + ver = &tmp_version; + + for (idx = ARRAY_SIZE(version_data); idx--;) + if ( tmp_version.dwMajorVersion > version_data[idx].info.dwMajorVersion || + (tmp_version.dwMajorVersion == version_data[idx].info.dwMajorVersion && + tmp_version.dwMinorVersion >= version_data[idx].info.dwMinorVersion)) + break; + + if (idx < 0) goto done; + ver = &windows8_version_data; + + if (RtlQueryInformationActivationContext(0, NULL, NULL, + CompatibilityInformationInActivationContext, NULL, 0, &req) != STATUS_BUFFER_TOO_SMALL + || !req) + goto done; + + if (!(acci = HeapAlloc(GetProcessHeap(), 0, req))) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (RtlQueryInformationActivationContext(0, NULL, NULL, + CompatibilityInformationInActivationContext, acci, req, &req) == STATUS_SUCCESS) + { + do + { + COMPATIBILITY_CONTEXT_ELEMENT *elements = (COMPATIBILITY_CONTEXT_ELEMENT*)(acci + 1); + DWORD i, count = *acci; + + for (i = 0; i < count; i++) + { + if (elements[i].Type == ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS && + IsEqualGUID(&elements[i].Id, &version_data[idx].guid)) + { + ver = &version_data[idx].info; + + if (ver->dwMajorVersion == tmp_version.dwMajorVersion && + ver->dwMinorVersion == tmp_version.dwMinorVersion) + ver = &tmp_version; + + idx = 0; /* break from outer loop */ + break; + } + } + } while(idx--); + } + HeapFree(GetProcessHeap(), 0, acci); + +done: + memcpy(¤t_version, ver, sizeof(current_version)); + return TRUE; +} + +static BOOL init_current_version(void) +{ + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + + return InitOnceExecuteOnce(&init_once, init_current_version_callback, NULL, NULL); +} + + /********************************************************************** * find_entry_by_id * @@ -1317,8 +1445,6 @@ DWORD WINAPI GetVersion(void) */ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) { - RTL_OSVERSIONINFOEXW infoW; - if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) && info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA)) { @@ -1327,23 +1453,22 @@ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) return FALSE; }
- infoW.dwOSVersionInfoSize = sizeof(infoW); - if (!set_ntstatus( RtlGetVersion( &infoW ))) return FALSE; + if (!init_current_version()) return FALSE;
- info->dwMajorVersion = infoW.dwMajorVersion; - info->dwMinorVersion = infoW.dwMinorVersion; - info->dwBuildNumber = infoW.dwBuildNumber; - info->dwPlatformId = infoW.dwPlatformId; - WideCharToMultiByte( CP_ACP, 0, infoW.szCSDVersion, -1, + info->dwMajorVersion = current_version.dwMajorVersion; + info->dwMinorVersion = current_version.dwMinorVersion; + info->dwBuildNumber = current_version.dwBuildNumber; + info->dwPlatformId = current_version.dwPlatformId; + WideCharToMultiByte( CP_ACP, 0, current_version.szCSDVersion, -1, info->szCSDVersion, sizeof(info->szCSDVersion), NULL, NULL );
if (info->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA)) { OSVERSIONINFOEXA *vex = (OSVERSIONINFOEXA *)info; - vex->wServicePackMajor = infoW.wServicePackMajor; - vex->wServicePackMinor = infoW.wServicePackMinor; - vex->wSuiteMask = infoW.wSuiteMask; - vex->wProductType = infoW.wProductType; + vex->wServicePackMajor = current_version.wServicePackMajor; + vex->wServicePackMinor = current_version.wServicePackMinor; + vex->wSuiteMask = current_version.wSuiteMask; + vex->wProductType = current_version.wProductType; } return TRUE; } @@ -1360,5 +1485,22 @@ BOOL WINAPI GetVersionExW( OSVERSIONINFOW *info ) WARN( "wrong OSVERSIONINFO size from app (got: %d)\n", info->dwOSVersionInfoSize ); return FALSE; } - return set_ntstatus( RtlGetVersion( (RTL_OSVERSIONINFOEXW *)info )); + + if (!init_current_version()) return FALSE; + + info->dwMajorVersion = current_version.dwMajorVersion; + info->dwMinorVersion = current_version.dwMinorVersion; + info->dwBuildNumber = current_version.dwBuildNumber; + info->dwPlatformId = current_version.dwPlatformId; + wcscpy( info->szCSDVersion, current_version.szCSDVersion ); + + if (info->dwOSVersionInfoSize == sizeof(RTL_OSVERSIONINFOEXW)) + { + OSVERSIONINFOEXW *vex = (OSVERSIONINFOEXW *)info; + vex->wServicePackMajor = current_version.wServicePackMajor; + vex->wServicePackMinor = current_version.wServicePackMinor; + vex->wSuiteMask = current_version.wSuiteMask; + vex->wProductType = current_version.wProductType; + } + return TRUE; }