This is required to avoid silencing (potentially fatal) exceptions from timer procedures.
-- v4: win32u: Ignore unhandled info index in NtUserSetObjectInformation. win32u/tests: Add tests for NtUserSetObjectInformation. user32: Implement UOI_TIMERPROC_EXCEPTION_SUPPRESSION. user32/tests: Add tests for UOI_TIMERPROC_EXCEPTION_SUPPRESSION. include: Add definition for UOI_TIMERPROC_EXCEPTION_SUPPRESSION user32/tests: Make test_unicode_wm_char robust against superfluous messages.
From: Jinoh Kang jinoh.kang.kr@gmail.com
Commit e445303ab45 (user32/tests: Make a few more messages optional., 2014-03-20) modified test_unicode_wm_char so that it skips non-essential messages (e.g. WM_DWMNCRENDERINGCHANGED) from GetMessageW.
Extend this for messages from GetMessageA as well.
Also, handle the case where no messages other than WM_QUIT are received at all.
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/tests/msg.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 7d1f796e53f..78e984b88d9 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -16175,6 +16175,16 @@ static void test_dbcs_wm_char(void) DestroyWindow(hwnd2); }
+static BOOL get_next_msg( BOOL (*WINAPI getmessage)(MSG *, HWND, UINT, UINT), + MSG *msg, HWND hwnd ) +{ + while ((*getmessage)( msg, hwnd, 0, 0 )) + { + if (!ignore_message( msg->message )) return TRUE; + } + return FALSE; +} + static void test_unicode_wm_char(void) { HWND hwnd; @@ -16205,11 +16215,7 @@ static void test_unicode_wm_char(void)
PostMessageW( hwnd, WM_CHAR, 0x3b1, 0 );
- while (GetMessageW( &msg, hwnd, 0, 0 )) - { - if (!ignore_message( msg.message )) break; - } - + ok( get_next_msg( GetMessageW, &msg, hwnd ), "expected a recongized message\n" ); ok( msg.hwnd == hwnd, "unexpected hwnd %p\n", msg.hwnd ); ok( msg.message == WM_CHAR, "unexpected message %x\n", msg.message ); ok( msg.wParam == 0x3b1, "bad wparam %Ix\n", msg.wParam ); @@ -16229,7 +16235,7 @@ static void test_unicode_wm_char(void) /* greek alpha -> 'a' in cp1252 */ PostMessageW( hwnd, WM_CHAR, 0x3b1, 0 );
- ok( GetMessageA( &msg, hwnd, 0, 0 ), "no message\n" ); + ok( get_next_msg( GetMessageA, &msg, hwnd ), "expected a recognized message\n" ); ok( msg.hwnd == hwnd, "unexpected hwnd %p\n", msg.hwnd ); ok( msg.message == WM_CHAR, "unexpected message %x\n", msg.message ); ok( msg.wParam == 0x61, "bad wparam %Ix\n", msg.wParam ); @@ -16250,7 +16256,7 @@ static void test_unicode_wm_char(void) /* greek alpha -> 0xe1 in cp1253 */ PostMessageW( hwnd, WM_CHAR, 0x3b1, 0 );
- ok( GetMessageA( &msg, hwnd, 0, 0 ), "no message\n" ); + ok( get_next_msg( GetMessageA, &msg, hwnd ), "expected a recognized message\n" ); ok( msg.hwnd == hwnd, "unexpected hwnd %p\n", msg.hwnd ); ok( msg.message == WM_CHAR, "unexpected message %x\n", msg.message ); ok( msg.wParam == 0xe1, "bad wparam %Ix\n", msg.wParam );
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- include/winuser.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/winuser.h b/include/winuser.h index 453f561e62f..666deeb6cbe 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -99,6 +99,7 @@ typedef void* HPOWERNOTIFY; #define UOI_NAME 2 #define UOI_TYPE 3 #define UOI_USER_SID 4 +#define UOI_TIMERPROC_EXCEPTION_SUPPRESSION 7
#define WSF_VISIBLE 1 #define DF_ALLOWOTHERACCOUNTHOOK 1
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/tests/msg.c | 103 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+)
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 78e984b88d9..9ff92ce2997 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -11405,11 +11405,36 @@ static void CALLBACK callback_count(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWOR count++; }
+enum timer_exception_phase { + TIMER_EXCEPTION_INITIAL, + TIMER_EXCEPTION_RAISED, + TIMER_EXCEPTION_CONTINUE, + TIMER_EXCEPTION_CONTINUE_OK, +}; + static DWORD exception; +static enum timer_exception_phase timer_exc_phase; +static BOOL tproc_exc_no_suppress; static void CALLBACK callback_exception(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { + if (tproc_exc_no_suppress) + { + BOOL ret, value; + + value = FALSE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + tproc_exc_no_suppress = FALSE; + } + count++; + timer_exc_phase = TIMER_EXCEPTION_RAISED; RaiseException(exception, 0, 0, NULL); + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE, timer_exc_phase); + timer_exc_phase = TIMER_EXCEPTION_CONTINUE_OK; }
static DWORD WINAPI timer_thread_proc(LPVOID x) @@ -11570,10 +11595,41 @@ static void test_timers_no_wnd(void) while (i > 0) KillTimer(NULL, ids[--i]); }
+static LONG CALLBACK timer_exception_handler(EXCEPTION_POINTERS *eptr) +{ + if (timer_exc_phase == TIMER_EXCEPTION_RAISED && + eptr->ExceptionRecord->ExceptionCode == exception && + eptr->ExceptionRecord->NumberParameters == 0) + { + if (eptr->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) + { +#if defined(__i386__) + if ((ULONG_PTR)eptr->ExceptionRecord->ExceptionAddress == eptr->ContextRecord->Eip + 1) + eptr->ContextRecord->Eip++; /* cancel EIP rewinding */ +#elif defined(__x86_64__) + if ((ULONG_PTR)eptr->ExceptionRecord->ExceptionAddress == eptr->ContextRecord->Rip + 1) + eptr->ContextRecord->Rip++; /* cancel RIP rewinding */ +#endif + } + timer_exc_phase = TIMER_EXCEPTION_CONTINUE; + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; +} + +static void dispatch_message_ansi_handle_exception(const MSG *msg, PVECTORED_EXCEPTION_HANDLER handler) +{ + void *cookie = AddVectoredExceptionHandler(TRUE, handler); + DispatchMessageA(msg); + RemoveVectoredExceptionHandler(cookie); +} + static void test_timers_exception(DWORD code) { UINT_PTR id; MSG msg; + BOOL ret, value;
exception = code; id = SetTimer(NULL, 0, 1000, callback_exception); @@ -11585,8 +11641,55 @@ static void test_timers_exception(DWORD code) msg.lParam = (LPARAM)callback_exception;
count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; DispatchMessageA(&msg); ok(count == 1, "did not get one count as expected (%i).\n", count); + ok(timer_exc_phase == TIMER_EXCEPTION_RAISED, + "expected phase %d, got %d\n", TIMER_EXCEPTION_RAISED, timer_exc_phase); + + value = FALSE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + if (!ret && GetLastError() == ERROR_INVALID_FUNCTION) + { + win_skip("UOI_TIMERPROC_EXCEPTION_SUPPRESSION not supported on this platform\n"); + } + else + { + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + + count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; + dispatch_message_ansi_handle_exception(&msg, timer_exception_handler); + ok(count == 1, "expected count to be 1, got %d\n", count); + todo_wine + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || + broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); + + value = TRUE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + + tproc_exc_no_suppress = TRUE; + count = 0; + timer_exc_phase = TIMER_EXCEPTION_INITIAL; + dispatch_message_ansi_handle_exception(&msg, timer_exception_handler); + ok(count == 1, "expected count to be 1, got %d\n", count); + todo_wine + ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || + broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, + "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); + + value = TRUE; + ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, + &value, sizeof(value)); + todo_wine + ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); + }
KillTimer(NULL, id); }
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/user32/message.c | 11 ++++++++++- dlls/user32/tests/msg.c | 6 ------ dlls/user32/user32.spec | 2 +- dlls/user32/user_main.c | 1 + dlls/user32/user_private.h | 1 + dlls/user32/winstation.c | 23 ++++++++++++++++++++++- 6 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/dlls/user32/message.c b/dlls/user32/message.c index acab4117cf2..c3b90eeaf70 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -857,6 +857,15 @@ static LRESULT dispatch_message( const MSG *msg, BOOL ansi ) }
+static LONG WINAPI timerproc_exception_filter(EXCEPTION_POINTERS *eptr) +{ + if (suppress_timerproc_exception) + return EXCEPTION_EXECUTE_HANDLER; + + return EXCEPTION_CONTINUE_SEARCH; +} + + /*********************************************************************** * DispatchMessageA (USER32.@) * @@ -874,7 +883,7 @@ LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageA( const MSG* msg ) retval = CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd, msg->message, msg->wParam, GetTickCount() ); } - __EXCEPT_ALL + __EXCEPT(timerproc_exception_filter) { retval = 0; } diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 9ff92ce2997..9cc550a844b 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -11424,7 +11424,6 @@ static void CALLBACK callback_exception(HWND hwnd, UINT uMsg, UINT_PTR idEvent, value = FALSE; ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &value, sizeof(value)); - todo_wine ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); tproc_exc_no_suppress = FALSE; } @@ -11656,14 +11655,12 @@ static void test_timers_exception(DWORD code) } else { - todo_wine ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError());
count = 0; timer_exc_phase = TIMER_EXCEPTION_INITIAL; dispatch_message_ansi_handle_exception(&msg, timer_exception_handler); ok(count == 1, "expected count to be 1, got %d\n", count); - todo_wine ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); @@ -11671,7 +11668,6 @@ static void test_timers_exception(DWORD code) value = TRUE; ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &value, sizeof(value)); - todo_wine ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError());
tproc_exc_no_suppress = TRUE; @@ -11679,7 +11675,6 @@ static void test_timers_exception(DWORD code) timer_exc_phase = TIMER_EXCEPTION_INITIAL; dispatch_message_ansi_handle_exception(&msg, timer_exception_handler); ok(count == 1, "expected count to be 1, got %d\n", count); - todo_wine ok(timer_exc_phase == TIMER_EXCEPTION_CONTINUE_OK || broken(timer_exc_phase == TIMER_EXCEPTION_RAISED) /* < win10 1507 */, "expected phase %d, got %d\n", TIMER_EXCEPTION_CONTINUE_OK, timer_exc_phase); @@ -11687,7 +11682,6 @@ static void test_timers_exception(DWORD code) value = TRUE; ret = SetUserObjectInformationW(GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, &value, sizeof(value)); - todo_wine ok(ret, "SetUserObjectInformationW error %lu\n", GetLastError()); }
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec index 96e6e1a7d6b..6a605ae4216 100644 --- a/dlls/user32/user32.spec +++ b/dlls/user32/user32.spec @@ -721,7 +721,7 @@ @ stdcall SetThreadDpiAwarenessContext(ptr) @ stdcall SetTimer(long long long ptr) @ stdcall SetUserObjectInformationA(long long ptr long) -@ stdcall SetUserObjectInformationW(long long ptr long) NtUserSetObjectInformation +@ stdcall SetUserObjectInformationW(long long ptr long) @ stdcall SetUserObjectSecurity(long ptr ptr) @ stdcall SetWinEventHook(long long long ptr long long long) @ stdcall SetWindowCompositionAttribute(ptr ptr) diff --git a/dlls/user32/user_main.c b/dlls/user32/user_main.c index ef2b94ba698..f375462487b 100644 --- a/dlls/user32/user_main.c +++ b/dlls/user32/user_main.c @@ -29,6 +29,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(graphics); WINE_DECLARE_DEBUG_CHANNEL(message);
HMODULE user32_module = 0; +BOOL suppress_timerproc_exception = TRUE;
extern void WDML_NotifyThreadDetach(void);
diff --git a/dlls/user32/user_private.h b/dlls/user32/user_private.h index 9330174a778..06d09e67381 100644 --- a/dlls/user32/user_private.h +++ b/dlls/user32/user_private.h @@ -44,6 +44,7 @@ struct wm_char_mapping_data };
extern HMODULE user32_module DECLSPEC_HIDDEN; +extern BOOL suppress_timerproc_exception;
extern BOOL post_dde_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid, DWORD type ) DECLSPEC_HIDDEN; diff --git a/dlls/user32/winstation.c b/dlls/user32/winstation.c index 62593ca046f..789e45f8928 100644 --- a/dlls/user32/winstation.c +++ b/dlls/user32/winstation.c @@ -389,12 +389,33 @@ BOOL WINAPI GetUserObjectInformationA( HANDLE handle, INT index, LPVOID info, DW }
+/****************************************************************************** + * SetUserObjectInformationW (USER32.@) + */ +BOOL WINAPI SetUserObjectInformationW( HANDLE handle, INT index, LPVOID info, DWORD len ) +{ + if (index == UOI_TIMERPROC_EXCEPTION_SUPPRESSION) + { + if (handle != GetCurrentProcess() || len != sizeof(BOOL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + suppress_timerproc_exception = *(const BOOL *)info; + return TRUE; + } + + return NtUserSetObjectInformation( handle, index, info, len ); +} + + /****************************************************************************** * SetUserObjectInformationA (USER32.@) */ BOOL WINAPI SetUserObjectInformationA( HANDLE handle, INT index, LPVOID info, DWORD len ) { - return NtUserSetObjectInformation( handle, index, info, len ); + return SetUserObjectInformationW( handle, index, info, len ); }
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/win32u/tests/win32u.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+)
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index 25358c5d0d8..c8d4f776d0c 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -2017,6 +2017,39 @@ static void test_wndproc_hook(void) UnregisterClassW( L"TestLParamClass", NULL ); }
+static void test_NtUserSetObjectInformation(void) +{ + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( GetProcessWindowStation(), UOI_FLAGS, (void *)NULL, sizeof(USEROBJECTFLAGS) ); + todo_wine + ok( GetLastError() == ERROR_NOACCESS, "NtUserSetObjectInformation error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( GetThreadDesktop(GetCurrentThreadId()), UOI_FLAGS, (void *)NULL, sizeof(USEROBJECTFLAGS) ); + todo_wine + ok( GetLastError() == ERROR_NOACCESS, "NtUserSetObjectInformation error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)TRUE, 0 ); + todo_wine + ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)0xdeadbeef, 0xdeadbeef ); + todo_wine + ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)-1, -1 ); + todo_wine + ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() ); + + SetLastError( 0xdeadbeef ); + NtUserSetObjectInformation( NULL, UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)-1, -1 ); + todo_wine + ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() ); +} + START_TEST(win32u) { char **argv; @@ -2060,4 +2093,6 @@ START_TEST(win32u)
test_NtUserEnableMouseInPointer( argv, FALSE ); test_NtUserEnableMouseInPointer( argv, TRUE ); + + test_NtUserSetObjectInformation(); }
From: Jinoh Kang jinoh.kang.kr@gmail.com
Signed-off-by: Jinoh Kang jinoh.kang.kr@gmail.com --- dlls/win32u/tests/win32u.c | 4 ---- dlls/win32u/winstation.c | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c index c8d4f776d0c..5a8acc44d44 100644 --- a/dlls/win32u/tests/win32u.c +++ b/dlls/win32u/tests/win32u.c @@ -2031,22 +2031,18 @@ static void test_NtUserSetObjectInformation(void)
SetLastError( 0xdeadbeef ); NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)TRUE, 0 ); - todo_wine ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() );
SetLastError( 0xdeadbeef ); NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)0xdeadbeef, 0xdeadbeef ); - todo_wine ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() );
SetLastError( 0xdeadbeef ); NtUserSetObjectInformation( GetCurrentProcess(), UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)-1, -1 ); - todo_wine ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() );
SetLastError( 0xdeadbeef ); NtUserSetObjectInformation( NULL, UOI_TIMERPROC_EXCEPTION_SUPPRESSION, (void *)-1, -1 ); - todo_wine ok( GetLastError() == 0xdeadbeef, "NtUserSetObjectInformation error %lu\n", GetLastError() ); }
diff --git a/dlls/win32u/winstation.c b/dlls/win32u/winstation.c index 5d1d5254ae1..28131a0b2d5 100644 --- a/dlls/win32u/winstation.c +++ b/dlls/win32u/winstation.c @@ -395,6 +395,8 @@ BOOL WINAPI NtUserSetObjectInformation( HANDLE handle, INT index, void *info, DW BOOL ret; const USEROBJECTFLAGS *obj_flags = info;
+ if (index == UOI_TIMERPROC_EXCEPTION_SUPPRESSION) return TRUE; + if (index != UOI_FLAGS || !info || len < sizeof(*obj_flags)) { RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=135721
Your paranoid android.
=== w1064_tsign (32 bit report) ===
user32: msg.c:5016: Test failed: ShowWindow(SW_SHOWNA) on visible top level window: 0: the msg 0x0018 was expected, but got msg 0x0088 instead msg.c:5016: Test failed: ShowWindow(SW_SHOWNA) on visible top level window: 1: the msg 0x0046 was expected, but got msg 0x0018 instead msg.c:5016: Test failed: ShowWindow(SW_SHOWNA) on visible top level window: 6: the msg sequence is not complete: expected 0000 - actual 0046
v3: Fix crash in Windows 11 64-bit due to unrecognized `EXCEPTION_SOFTWARE_ORIGINATE` flag in `ExceptionFlags`.