Gabriel Ivăncescu : kernelbase: Implement compatibility mode for GetVersionEx.

Alexandre Julliard julliard at winehq.org
Thu Mar 19 16:27:47 CDT 2020


Module: wine
Branch: master
Commit: 6719bf2e037b95b8338fae9e660c43e8fadafc7a
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=6719bf2e037b95b8338fae9e660c43e8fadafc7a

Author: Gabriel Ivăncescu <gabrielopcode at gmail.com>
Date:   Wed Mar 18 18:31:53 2020 +0200

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 at gmail.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 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 1bb3e20978..c9e92a9c03 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 cf46e9cc3a..3e1cef919b 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(&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;
 }




More information about the wine-cvs mailing list