[PATCH v3 3/5] kernelbase: Implement compatibility mode for GetVersionEx.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Wed Mar 18 11:31:53 CDT 2020
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 at gmail.com>
---
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 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..a2a0f3c 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"
@@ -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;
}
--
2.21.0
More information about the wine-devel
mailing list