[PATCH v2 1/3] kernelbase: Implement compatibility mode for GetVersionEx.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Tue Mar 17 11:07:51 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>
---
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;
}
--
2.21.0
More information about the wine-devel
mailing list