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 ---
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; }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernelbase/version.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/dlls/kernelbase/version.c b/dlls/kernelbase/version.c index dc8cc61..37285e1 100644 --- a/dlls/kernelbase/version.c +++ b/dlls/kernelbase/version.c @@ -1431,11 +1431,15 @@ 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; + DWORD result; + + if (!init_current_version()) return 0; + + result = MAKELONG( MAKEWORD( current_version.dwMajorVersion, current_version.dwMinorVersion ), + (current_version.dwPlatformId ^ 2) << 14 ); + + if (current_version.dwPlatformId == VER_PLATFORM_WIN32_NT) + result |= LOWORD(current_version.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/version.c | 141 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 6 deletions(-)
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; }
/***********************************************************************
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=67366
Your paranoid android.
=== debiant (32 bit report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
=== debiant (32 bit French report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
=== debiant (32 bit Japanese:Japan report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
=== debiant (32 bit Chinese:China report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
=== debiant (32 bit WoW report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
=== debiant (64 bit WoW report) ===
kernel32: version.c:670: Test failed: 2: unexpected return value 0. version.c:672: Test failed: 2: unexpected error code 1150, expected 0. version.c:670: Test failed: 3: unexpected return value 0. version.c:672: Test failed: 3: unexpected error code 1150, expected 0. version.c:670: Test failed: 10: unexpected return value 0. version.c:672: Test failed: 10: unexpected error code 1150, expected 0. version.c:670: Test failed: 12: unexpected return value 0. version.c:672: Test failed: 12: unexpected error code 1150, expected 0. version.c:670: Test failed: 14: unexpected return value 0. version.c:672: Test failed: 14: unexpected error code 1150, expected 0. version.c:670: Test failed: 24: unexpected return value 0. version.c:672: Test failed: 24: unexpected error code 1150, expected 0. version.c:670: Test failed: 39: unexpected return value 0. version.c:672: Test failed: 39: unexpected error code 1150, expected 0. version.c:670: Test failed: 41: unexpected return value 0. version.c:672: Test failed: 41: unexpected error code 1150, expected 0. version.c:670: Test failed: 42: unexpected return value 0. version.c:672: Test failed: 42: unexpected error code 1150, expected 0. version.c:670: Test failed: 43: unexpected return value 0. version.c:672: Test failed: 43: unexpected error code 1150, expected 0. version.c:670: Test failed: 45: unexpected return value 0. version.c:672: Test failed: 45: unexpected error code 1150, expected 0. version.c:670: Test failed: 46: unexpected return value 0. version.c:672: Test failed: 46: unexpected error code 1150, expected 0. version.c:670: Test failed: 47: unexpected return value 0. version.c:672: Test failed: 47: unexpected error code 1150, expected 0. version.c:706: Test failed: VerifyVersionInfoA failed with error 1150
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}}
- }
+};
For that you only need to store 3 values - major/minor/build.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
- /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci;
Why not use proper type pointer?
if (elements[i].Type == ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS &&
IsEqualGUID(&elements[i].Id, &version_data[idx].guid))
Type value name has a typo.
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}} + } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
+ /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci;
Why not use proper type pointer?
I don't know, ntdll does the same thing when filling it, for some reason we don't even have that structure so I couldn't use it, although I did find it in headers. So I was a bit confused and just copied what ntdll does, assuming it had a good reason to.
+ if (elements[i].Type == ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS && + IsEqualGUID(&elements[i].Id, &version_data[idx].guid))
Type value name has a typo.
Right, but that's in our headers, I thought it was intentional. Should I send a fix for this?
On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}}
+ } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
There are functions to create, activate and deactivate contexts. We use that in tests for example.
- /*ACTIVATION_CONTEXT_COMPATIBILITY_INFORMATION*/DWORD *acci;
Why not use proper type pointer?
I don't know, ntdll does the same thing when filling it, for some reason we don't even have that structure so I couldn't use it, although I did find it in headers. So I was a bit confused and just copied what ntdll does, assuming it had a good reason to.
I remember now, unfortunately public type definition is using zero length array.
+ if (elements[i].Type == ACTCX_COMPATIBILITY_ELEMENT_TYPE_OS && + IsEqualGUID(&elements[i].Id, &version_data[idx].guid))
Type value name has a typo.
Right, but that's in our headers, I thought it was intentional. Should I send a fix for this?
On 17/03/2020 19:24, Nikolay Sivov wrote:
On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}}
+ } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
There are functions to create, activate and deactivate contexts. We use that in tests for example.
So, I tested with the ACTCTX_FLAG_SET_PROCESS_DEFAULT flag for CreateActCtx, followed by ActivateActCtx, at the beginning of the test.
Just to make sure, I retrieved the GUIDs with CompatibilityInformationInActivationContext and they seemed in order, so the manifest was loaded correctly and the activation context pushed. i.e. it returned the same GUIDs as if the app itself had the manifest, without pushing it manually on the stack.
However, GetVersionEx reported Windows 8, as if no manifest. So I suspect it gets cached at startup somewhere... and any further activation context changes won't matter.
Any idea of a good place to do that? It must be a point where the default activation context become available but application code hasn't run yet. Would DllMain for kernelbase be a good place to cache it?
Thanks, Gabriel
On 3/18/20 4:09 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 19:24, Nikolay Sivov wrote:
On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}} + } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
There are functions to create, activate and deactivate contexts. We use that in tests for example.
So, I tested with the ACTCTX_FLAG_SET_PROCESS_DEFAULT flag for CreateActCtx, followed by ActivateActCtx, at the beginning of the test.
Just to make sure, I retrieved the GUIDs with CompatibilityInformationInActivationContext and they seemed in order, so the manifest was loaded correctly and the activation context pushed. i.e. it returned the same GUIDs as if the app itself had the manifest, without pushing it manually on the stack.
However, GetVersionEx reported Windows 8, as if no manifest. So I suspect it gets cached at startup somewhere... and any further activation context changes won't matter.
Any idea of a good place to do that? It must be a point where the default activation context become available but application code hasn't run yet. Would DllMain for kernelbase be a good place to cache it?
I'm not sure. It makes sense I guess that user context can't change version set with application default context, so version is consistent.
I don't know if it's safe to assume that kernelbase is always loaded early enough, if for example it's possible to start something that does not load kernel32/kernelbase. But initializing on first GetVersion() using current context is too late, according to your testing results. Maybe default context could (or should) be stashed in PEB somewhere, being accessible at all times.
Thanks, Gabriel
On 18/03/2020 15:52, Nikolay Sivov wrote:
On 3/18/20 4:09 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 19:24, Nikolay Sivov wrote:
On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}} + } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
There are functions to create, activate and deactivate contexts. We use that in tests for example.
So, I tested with the ACTCTX_FLAG_SET_PROCESS_DEFAULT flag for CreateActCtx, followed by ActivateActCtx, at the beginning of the test.
Just to make sure, I retrieved the GUIDs with CompatibilityInformationInActivationContext and they seemed in order, so the manifest was loaded correctly and the activation context pushed. i.e. it returned the same GUIDs as if the app itself had the manifest, without pushing it manually on the stack.
However, GetVersionEx reported Windows 8, as if no manifest. So I suspect it gets cached at startup somewhere... and any further activation context changes won't matter.
Any idea of a good place to do that? It must be a point where the default activation context become available but application code hasn't run yet. Would DllMain for kernelbase be a good place to cache it?
I'm not sure. It makes sense I guess that user context can't change version set with application default context, so version is consistent.
I don't know if it's safe to assume that kernelbase is always loaded early enough, if for example it's possible to start something that does not load kernel32/kernelbase. But initializing on first GetVersion() using current context is too late, according to your testing results. Maybe default context could (or should) be stashed in PEB somewhere, being accessible at all times.
I can't find a pointer-sized reserved/unknown field where to place the default context, they're all LONGs, and if I placed a pointer it would mess up the offsets for 64-bit. Do you have some suggestion?
Though, I could use an internal __wine_* export but I guess that's kind of a last resort right?
On 3/18/20 4:09 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 19:24, Nikolay Sivov wrote:
On 3/17/20 7:50 PM, Gabriel Ivăncescu wrote:
On 17/03/2020 18:28, Nikolay Sivov wrote:
On 3/17/20 7:07 PM, Gabriel Ivăncescu wrote:
+/***********************************************************************
- 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}} + } +};
For that you only need to store 3 values - major/minor/build.
Ah alright, I just copy-pasted the entires from ntdll.
+/***********************************************************************
- 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) +{
Should it actually be static and initialized once? What happens if you activate another context dynamically?
I actually have no idea, didn't know you can do that. I'm somewhat unfamiliar with activation context APIs. What API should I be using to test this?
There are functions to create, activate and deactivate contexts. We use that in tests for example.
So, I tested with the ACTCTX_FLAG_SET_PROCESS_DEFAULT flag for CreateActCtx, followed by ActivateActCtx, at the beginning of the test.
Just to make sure, I retrieved the GUIDs with CompatibilityInformationInActivationContext and they seemed in order, so the manifest was loaded correctly and the activation context pushed. i.e. it returned the same GUIDs as if the app itself had the manifest, without pushing it manually on the stack.
However, GetVersionEx reported Windows 8, as if no manifest. So I suspect it gets cached at startup somewhere... and any further activation context changes won't matter.
Any idea of a good place to do that? It must be a point where the default activation context become available but application code hasn't run yet. Would DllMain for kernelbase be a good place to cache it?
P.S. forgot to mention. This is different from other use cases we have, window classes or COM classes data is dynamic, functions will be using current context intentionally.
Thanks, Gabriel