kernelbase: Implement compatibility mode for GetVersionEx.

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>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Gabriel Ivăncescu 2020-03-18 18:31:53 +02:00 committed by Alexandre Julliard
parent e95d0813fd
commit 6719bf2e03
2 changed files with 140 additions and 4 deletions

View File

@ -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;
}
}

View File

@ -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"
@ -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(&current_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;
}