[PATCH 1/4] kernelbase: Implement compatibility mode for GetVersionEx.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Mon Mar 16 08:05:58 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>
---
I'm introducing an exported internal ntdll function, since I didn't want to
duplicate the table for kernelbase. I don't know if this is the best way,
so let me know if I should proceed differently.
dlls/kernelbase/version.c | 54 +++++++++++++++++++------
dlls/ntdll/ntdll.spec | 1 +
dlls/ntdll/version.c | 85 +++++++++++++++++++++++++++++++++++++++
3 files changed, 127 insertions(+), 13 deletions(-)
diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c
index cf46e9c..d07c1c9 100644
--- a/dlls/kernelbase/version.c
+++ b/dlls/kernelbase/version.c
@@ -43,6 +43,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(ver);
+extern CDECL const RTL_OSVERSIONINFOEXW *__wine_get_compat_win_version(void);
+
typedef struct
{
WORD offset;
@@ -1317,7 +1319,7 @@ DWORD WINAPI GetVersion(void)
*/
BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info )
{
- RTL_OSVERSIONINFOEXW infoW;
+ const RTL_OSVERSIONINFOEXW *ver;
if (info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA) &&
info->dwOSVersionInfoSize != sizeof(OSVERSIONINFOEXA))
@@ -1327,23 +1329,26 @@ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info )
return FALSE;
}
- infoW.dwOSVersionInfoSize = sizeof(infoW);
- if (!set_ntstatus( RtlGetVersion( &infoW ))) return FALSE;
+ if (!(ver = __wine_get_compat_win_version()))
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ 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 = ver->dwMajorVersion;
+ info->dwMinorVersion = ver->dwMinorVersion;
+ info->dwBuildNumber = ver->dwBuildNumber;
+ info->dwPlatformId = ver->dwPlatformId;
+ WideCharToMultiByte( CP_ACP, 0, ver->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 = ver->wServicePackMajor;
+ vex->wServicePackMinor = ver->wServicePackMinor;
+ vex->wSuiteMask = ver->wSuiteMask;
+ vex->wProductType = ver->wProductType;
}
return TRUE;
}
@@ -1354,11 +1359,34 @@ BOOL WINAPI GetVersionExA( OSVERSIONINFOA *info )
*/
BOOL WINAPI GetVersionExW( OSVERSIONINFOW *info )
{
+ const RTL_OSVERSIONINFOEXW *ver;
+
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 (!(ver = __wine_get_compat_win_version()))
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ info->dwMajorVersion = ver->dwMajorVersion;
+ info->dwMinorVersion = ver->dwMinorVersion;
+ info->dwBuildNumber = ver->dwBuildNumber;
+ info->dwPlatformId = ver->dwPlatformId;
+ wcscpy( info->szCSDVersion, ver->szCSDVersion );
+
+ if(info->dwOSVersionInfoSize == sizeof(RTL_OSVERSIONINFOEXW))
+ {
+ OSVERSIONINFOEXW *vex = (OSVERSIONINFOEXW *)info;
+ vex->wServicePackMajor = ver->wServicePackMajor;
+ vex->wServicePackMinor = ver->wServicePackMinor;
+ vex->wSuiteMask = ver->wSuiteMask;
+ vex->wProductType = ver->wProductType;
+ }
+ return TRUE;
}
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 0ea72e3..5f58e4a 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -1572,6 +1572,7 @@
@ cdecl wine_get_version() NTDLL_wine_get_version
@ cdecl wine_get_build_id() NTDLL_wine_get_build_id
@ cdecl wine_get_host_version(ptr ptr) NTDLL_wine_get_host_version
+@ cdecl __wine_get_compat_win_version()
# Codepages
@ cdecl __wine_get_unix_codepage()
diff --git a/dlls/ntdll/version.c b/dlls/ntdll/version.c
index 61e48f6..f48f725 100644
--- a/dlls/ntdll/version.c
+++ b/dlls/ntdll/version.c
@@ -191,6 +191,13 @@ static const RTL_OSVERSIONINFOEXW VersionData[NB_WINDOWS_VERSIONS] =
};
+/* GUIDs for Windows versions WIN81 and later, needed for compatibility manifest check */
+static const GUID version_compat_guid[NB_WINDOWS_VERSIONS - WIN81] =
+{
+ {0x1f676c76,0x80e1,0x4239,{0x95,0xbb,0x83,0xd0,0xf6,0xd0,0xda,0x78}}, /* WIN81 */
+ {0x8e0f7a12,0xbfb3,0x4fe8,{0xb9,0xa5,0x48,0xfd,0x50,0xa1,0x5a,0x9a}}, /* WIN10 */
+};
+
static const struct { WCHAR name[12]; WINDOWS_VERSION ver; } version_names[] =
{
{ {'w','i','n','2','0',0}, WIN20 },
@@ -781,3 +788,81 @@ NTSTATUS WINAPI RtlVerifyVersionInfo( const RTL_OSVERSIONINFOEXW *info,
return STATUS_SUCCESS;
}
+
+/******************************************************************************
+ * __wine_get_compat_win_version (NTDLL.@)
+ *
+ * Get a pointer to a RTL_OSVERSIONINFOEXW that matches the compatibility mode.
+ *
+ * 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.
+ *
+ * RETURNS
+ * A pointer to a RTL_OSVERSIONINFOEXW containing the version in compatibility
+ * mode, or NULL if out of memory.
+ */
+CDECL const RTL_OSVERSIONINFOEXW *__wine_get_compat_win_version(void)
+{
+ static const RTL_OSVERSIONINFOEXW *compat_ver;
+
+ if (!compat_ver)
+ {
+ /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci;
+ const RTL_OSVERSIONINFOEXW *ver = current_version;
+ SIZE_T req;
+ int idx;
+
+ for (idx = ARRAY_SIZE(version_compat_guid); idx--;)
+ {
+ const RTL_OSVERSIONINFOEXW *v = &VersionData[WIN81 + idx];
+
+ if ( current_version->dwMajorVersion > v->dwMajorVersion ||
+ (current_version->dwMajorVersion == v->dwMajorVersion &&
+ current_version->dwMinorVersion >= v->dwMinorVersion))
+ break;
+ }
+
+ if (idx >= 0)
+ {
+ ver = &VersionData[WIN8];
+
+ if (RtlQueryInformationActivationContext(0, NULL, NULL,
+ CompatibilityInformationInActivationContext, NULL, 0, &req) == STATUS_BUFFER_TOO_SMALL
+ && req)
+ {
+ if (!(acci = RtlAllocateHeap(GetProcessHeap(), 0, req)))
+ return NULL;
+
+ 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_compat_guid[idx]))
+ {
+ ver = &VersionData[WIN81 + idx];
+
+ if (ver->dwMajorVersion == current_version->dwMajorVersion &&
+ ver->dwMinorVersion == current_version->dwMinorVersion)
+ ver = current_version;
+
+ idx = 0; /* break from outer loop */
+ break;
+ }
+ }
+ } while(idx--);
+ }
+ RtlFreeHeap(GetProcessHeap(), 0, acci);
+ }
+ }
+ interlocked_cmpxchg_ptr((void**)&compat_ver, (void*)ver, NULL);
+ }
+
+ return compat_ver;
+}
--
2.21.0
More information about the wine-devel
mailing list