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:
parent
e95d0813fd
commit
6719bf2e03
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(¤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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue