From 6719bf2e037b95b8338fae9e660c43e8fadafc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Iv=C4=83ncescu?= Date: Wed, 18 Mar 2020 18:31:53 +0200 Subject: [PATCH] kernelbase: Implement compatibility mode for GetVersionEx. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Alexandre Julliard --- dlls/kernel32/tests/version.c | 2 +- dlls/kernelbase/version.c | 142 +++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 4 deletions(-) diff --git a/dlls/kernel32/tests/version.c b/dlls/kernel32/tests/version.c index 1bb3e20978d..c9e92a9c036 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 cf46e9cc3af..3e1cef919b6 100644 --- a/dlls/kernelbase/version.c +++ b/dlls/kernelbase/version.c @@ -28,6 +28,8 @@ #include #include +#include "ntstatus.h" +#define WIN32_NO_STATUS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" @@ -60,6 +62,13 @@ typedef struct DWORD resloader; } NE_TYPEINFO; +struct version_info +{ + DWORD major; + DWORD minor; + DWORD build; +}; + /*********************************************************************** * Version Info Structure */ @@ -116,6 +125,114 @@ 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 struct version_info windows8_version_info = { 6, 2, 0x23f0 }; + + +/*********************************************************************** + * Windows versions that need compatibility GUID specified in manifest + * in order to be reported by the APIs. + */ +static const struct +{ + struct version_info info; + GUID guid; +} version_data[] = +{ + /* Windows 8.1 */ + { + { 6, 3, 0x2580 }, + {0x1f676c76,0x80e1,0x4239,{0x95,0xbb,0x83,0xd0,0xf6,0xd0,0xda,0x78}} + }, + /* Windows 10 */ + { + { 10, 0, 0x42ee }, + {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}} + } +}; + + +/****************************************************************************** + * 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 RTL_OSVERSIONINFOEXW current_version; + +static BOOL CALLBACK init_current_version(PINIT_ONCE init_once, PVOID parameter, PVOID *context) +{ + /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci; + const struct version_info *ver; + SIZE_T req; + int idx; + + current_version.dwOSVersionInfoSize = sizeof(current_version); + if (!set_ntstatus( RtlGetVersion(¤t_version) )) return FALSE; + + for (idx = ARRAY_SIZE(version_data); idx--;) + if ( current_version.dwMajorVersion > version_data[idx].info.major || + (current_version.dwMajorVersion == version_data[idx].info.major && + current_version.dwMinorVersion >= version_data[idx].info.minor)) + break; + + if (idx < 0) return TRUE; + ver = &windows8_version_info; + + if (RtlQueryInformationActivationContext(0, NtCurrentTeb()->Peb->ActivationContextData, 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, NtCurrentTeb()->Peb->ActivationContextData, 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 == ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS && + IsEqualGUID(&elements[i].Id, &version_data[idx].guid)) + { + ver = &version_data[idx].info; + + if (ver->major == current_version.dwMajorVersion && + ver->minor == current_version.dwMinorVersion) + ver = NULL; + + idx = 0; /* break from outer loop */ + break; + } + } + } while (idx--); + } + HeapFree(GetProcessHeap(), 0, acci); + +done: + if (ver) + { + current_version.dwMajorVersion = ver->major; + current_version.dwMinorVersion = ver->minor; + current_version.dwBuildNumber = ver->build; + } + return TRUE; +} + + /********************************************************************** * find_entry_by_id * @@ -1317,7 +1434,7 @@ DWORD WINAPI GetVersion(void) */ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) { - RTL_OSVERSIONINFOEXW infoW; + OSVERSIONINFOEXW infoW; if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) && info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA)) @@ -1328,7 +1445,7 @@ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) } infoW.dwOSVersionInfoSize = sizeof(infoW); - if (!set_ntstatus( RtlGetVersion( &infoW ))) return FALSE; + if (!GetVersionExW( (OSVERSIONINFOW *)&infoW )) return FALSE; info->dwMajorVersion = infoW.dwMajorVersion; info->dwMinorVersion = infoW.dwMinorVersion; @@ -1354,11 +1471,30 @@ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info ) */ BOOL WINAPI GetVersionExW( OSVERSIONINFOW *info ) { + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOW) && info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXW)) { WARN( "wrong OSVERSIONINFO size from app (got: %d)\n", info->dwOSVersionInfoSize ); return FALSE; } - return set_ntstatus( RtlGetVersion( (RTL_OSVERSIONINFOEXW *)info )); + + if (!InitOnceExecuteOnce(&init_once, init_current_version, NULL, NULL)) 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(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; }