Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernel32/tests/actctx.c | 2 +- dlls/ntdll/actctx.c | 2 +- include/winnt.h | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/dlls/kernel32/tests/actctx.c b/dlls/kernel32/tests/actctx.c index 01c6c1e..2d942cd 100644 --- a/dlls/kernel32/tests/actctx.c +++ b/dlls/kernel32/tests/actctx.c @@ -2851,7 +2851,7 @@ static void test_with_compat(HANDLE handle, DWORD num_compat, const GUID* expect wine_dbgstr_guid(&compat_info.Elements[n].Id), wine_dbgstr_guid(expected_compat[n]), n); - ok_(__FILE__, line)(compat_info.Elements[n].Type == ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS, + ok_(__FILE__, line)(compat_info.Elements[n].Type == ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS, "Wrong type, got %u for %u\n", (DWORD)compat_info.Elements[n].Type, n); } } diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index c513419..b88e6de 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -2397,7 +2397,7 @@ static void parse_supportedos_elem( xmlbuf_t *xmlbuf, struct assembly *assembly, set_error( xmlbuf ); return; } - compat->Type = ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS; + compat->Type = ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS; compat->Id = compat_id; } else diff --git a/include/winnt.h b/include/winnt.h index 50a6213..7a5b8aa 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -6294,8 +6294,10 @@ typedef struct _ASSEMBLY_FILE_DETAILED_INFORMATION { typedef const ASSEMBLY_FILE_DETAILED_INFORMATION *PCASSEMBLY_FILE_DETAILED_INFORMATION;
typedef enum { - ACTCX_COMPATIBILITY_ELEMENT_TYPE_UNKNOWN = 0, - ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS + ACTCTX_COMPATIBILITY_ELEMENT_TYPE_UNKNOWN = 0, + ACTCTX_COMPATIBILITY_ELEMENT_TYPE_OS, + ACTCTX_COMPATIBILITY_ELEMENT_TYPE_MITIGATION, + ACTCTX_COMPATIBILITY_ELEMENT_TYPE_MAXVERSIONTESTED } ACTCTX_COMPATIBILITY_ELEMENT_TYPE;
typedef struct _COMPATIBILITY_CONTEXT_ELEMENT {
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
We can't get rid of process_actctx; the loader uses RtlFindActivationContextSectionString when it's not initialized, and will segfault if we do that.
dlls/ntdll/actctx.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/ntdll/actctx.c b/dlls/ntdll/actctx.c index b88e6de..17f9cad 100644 --- a/dlls/ntdll/actctx.c +++ b/dlls/ntdll/actctx.c @@ -4942,6 +4942,8 @@ void actctx_init(void) ctx.lpResourceName = (LPCWSTR)CREATEPROCESS_MANIFEST_RESOURCE_ID;
if (!RtlCreateActivationContext( &handle, &ctx )) process_actctx = check_actctx(handle); + + NtCurrentTeb()->Peb->ActivationContextData = process_actctx; }
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 --- 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; }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernelbase/version.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c index a2a0f3c..20c1b35 100644 --- a/dlls/kernelbase/version.c +++ b/dlls/kernelbase/version.c @@ -1420,11 +1420,17 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetProductInfo( DWORD os_major, DWORD os_minor, */ DWORD WINAPI GetVersion(void) { - DWORD result = MAKELONG( MAKEWORD( NtCurrentTeb()->Peb->OSMajorVersion, - NtCurrentTeb()->Peb->OSMinorVersion ), - (NtCurrentTeb()->Peb->OSPlatformId ^ 2) << 14 ); - if (NtCurrentTeb()->Peb->OSPlatformId == VER_PLATFORM_WIN32_NT) - result |= LOWORD(NtCurrentTeb()->Peb->OSBuildNumber) << 16; + OSVERSIONINFOEXW info; + DWORD result; + + info.dwOSVersionInfoSize = sizeof(info); + if (!GetVersionExW( (OSVERSIONINFOW*)&info )) return 0; + + result = MAKELONG( MAKEWORD( info.dwMajorVersion, info.dwMinorVersion ), + (info.dwPlatformId ^ 2) << 14 ); + + if (info.dwPlatformId == VER_PLATFORM_WIN32_NT) + result |= LOWORD(info.dwBuildNumber) << 16; return result; }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
This is mostly copied from RtlVerifyVersionInfo, but using GetVersionEx instead of RtlGetVersion. Also, it has slight tweaks to return values, since this API uses SetLastError and a BOOL return value. The behavior should be identical to before, though, except when the compatibility mode kicks in.
dlls/kernel32/tests/version.c | 5 -- dlls/kernel32/version.c | 141 ++++++++++++++++++++++++++++++++-- 2 files changed, 135 insertions(+), 11 deletions(-)
diff --git a/dlls/kernel32/tests/version.c b/dlls/kernel32/tests/version.c index c9e92a9..5fdc961 100644 --- a/dlls/kernel32/tests/version.c +++ b/dlls/kernel32/tests/version.c @@ -193,8 +193,6 @@ static void test_VerifyVersionInfo(void) DWORD condition3; DWORD typemask4; DWORD condition4; - - BOOL todo; } verify_version_tests[] = { { @@ -665,13 +663,10 @@ static void test_VerifyVersionInfo(void)
SetLastError(0xdeadbeef); ret = VerifyVersionInfoA(&info, test->verifymask, mask); - todo_wine_if(test->todo) - { ok(test->err ? !ret : ret, "%u: unexpected return value %d.\n", i, ret); if (!ret) ok(GetLastError() == test->err, "%u: unexpected error code %d, expected %d.\n", i, GetLastError(), test->err); } - }
/* test handling of version numbers */ /* v3.10 is always less than v4.x even diff --git a/dlls/kernel32/version.c b/dlls/kernel32/version.c index 5f33847..0716c29 100644 --- a/dlls/kernel32/version.c +++ b/dlls/kernel32/version.c @@ -42,6 +42,61 @@ WINE_DEFAULT_DEBUG_CHANNEL(ver);
+static inline UCHAR version_update_condition(UCHAR *last_condition, UCHAR condition) +{ + switch (*last_condition) + { + case 0: + *last_condition = condition; + break; + case VER_EQUAL: + if (condition >= VER_EQUAL && condition <= VER_LESS_EQUAL) + { + *last_condition = condition; + return condition; + } + break; + case VER_GREATER: + case VER_GREATER_EQUAL: + if (condition >= VER_EQUAL && condition <= VER_GREATER_EQUAL) + return condition; + break; + case VER_LESS: + case VER_LESS_EQUAL: + if (condition == VER_EQUAL || (condition >= VER_LESS && condition <= VER_LESS_EQUAL)) + return condition; + break; + } + if (!condition) *last_condition |= 0x10; + return *last_condition & 0xf; +} + +static inline BOOL version_compare_values(ULONG left, ULONG right, UCHAR condition) +{ + switch (condition) + { + case VER_EQUAL: + if (left != right) return FALSE; + break; + case VER_GREATER: + if (left <= right) return FALSE; + break; + case VER_GREATER_EQUAL: + if (left < right) return FALSE; + break; + case VER_LESS: + if (left >= right) return FALSE; + break; + case VER_LESS_EQUAL: + if (left > right) return FALSE; + break; + default: + return FALSE; + } + return TRUE; +} + + /****************************************************************************** * VerifyVersionInfoA (KERNEL32.@) */ @@ -71,16 +126,90 @@ BOOL WINAPI VerifyVersionInfoA( LPOSVERSIONINFOEXA lpVersionInfo, DWORD dwTypeMa BOOL WINAPI VerifyVersionInfoW( LPOSVERSIONINFOEXW lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask) { - switch(RtlVerifyVersionInfo( lpVersionInfo, dwTypeMask, dwlConditionMask )) + OSVERSIONINFOEXW ver; + + TRACE("(%p 0x%x 0x%s)\n", lpVersionInfo, dwTypeMask, wine_dbgstr_longlong(dwlConditionMask)); + + ver.dwOSVersionInfoSize = sizeof(ver); + if (!GetVersionExW((OSVERSIONINFOW*)&ver)) return FALSE; + + if (!dwTypeMask || !dwlConditionMask) { - case STATUS_INVALID_PARAMETER: - SetLastError( ERROR_BAD_ARGUMENTS ); - return FALSE; - case STATUS_REVISION_MISMATCH: - SetLastError( ERROR_OLD_WIN_VERSION ); + SetLastError(ERROR_BAD_ARGUMENTS); return FALSE; } + + if (dwTypeMask & VER_PRODUCT_TYPE) + { + if (!version_compare_values(ver.wProductType, lpVersionInfo->wProductType, dwlConditionMask >> 7*3 & 0x07)) + goto mismatch; + } + if (dwTypeMask & VER_SUITENAME) + switch (dwlConditionMask >> 6*3 & 0x07) + { + case VER_AND: + if ((lpVersionInfo->wSuiteMask & ver.wSuiteMask) != lpVersionInfo->wSuiteMask) + goto mismatch; + break; + case VER_OR: + if (!(lpVersionInfo->wSuiteMask & ver.wSuiteMask) && lpVersionInfo->wSuiteMask) + goto mismatch; + break; + default: + SetLastError(ERROR_BAD_ARGUMENTS); + return FALSE; + } + if (dwTypeMask & VER_PLATFORMID) + { + if (!version_compare_values(ver.dwPlatformId, lpVersionInfo->dwPlatformId, dwlConditionMask >> 3*3 & 0x07)) + goto mismatch; + } + if (dwTypeMask & VER_BUILDNUMBER) + { + if (!version_compare_values(ver.dwBuildNumber, lpVersionInfo->dwBuildNumber, dwlConditionMask >> 2*3 & 0x07)) + goto mismatch; + } + + if (dwTypeMask & (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR)) + { + unsigned char condition, last_condition = 0; + BOOL succeeded = TRUE, do_next_check = TRUE; + + if (dwTypeMask & VER_MAJORVERSION) + { + condition = version_update_condition(&last_condition, dwlConditionMask >> 1*3 & 0x07); + succeeded = version_compare_values(ver.dwMajorVersion, lpVersionInfo->dwMajorVersion, condition); + do_next_check = (ver.dwMajorVersion == lpVersionInfo->dwMajorVersion) && + ((condition >= VER_EQUAL) && (condition <= VER_LESS_EQUAL)); + } + if ((dwTypeMask & VER_MINORVERSION) && do_next_check) + { + condition = version_update_condition(&last_condition, dwlConditionMask >> 0*3 & 0x07); + succeeded = version_compare_values(ver.dwMinorVersion, lpVersionInfo->dwMinorVersion, condition); + do_next_check = (ver.dwMinorVersion == lpVersionInfo->dwMinorVersion) && + ((condition >= VER_EQUAL) && (condition <= VER_LESS_EQUAL)); + } + if ((dwTypeMask & VER_SERVICEPACKMAJOR) && do_next_check) + { + condition = version_update_condition(&last_condition, dwlConditionMask >> 5*3 & 0x07); + succeeded = version_compare_values(ver.wServicePackMajor, lpVersionInfo->wServicePackMajor, condition); + do_next_check = (ver.wServicePackMajor == lpVersionInfo->wServicePackMajor) && + ((condition >= VER_EQUAL) && (condition <= VER_LESS_EQUAL)); + } + if ((dwTypeMask & VER_SERVICEPACKMINOR) && do_next_check) + { + condition = version_update_condition(&last_condition, dwlConditionMask >> 4*3 & 0x07); + succeeded = version_compare_values(ver.wServicePackMinor, lpVersionInfo->wServicePackMinor, condition); + } + + if (!succeeded) goto mismatch; + } + return TRUE; + +mismatch: + SetLastError(ERROR_OLD_WIN_VERSION); + return FALSE; }
/***********************************************************************