[User32_wineTest]
[reactos.git] / rostests / winetests / user32 / msg.c
index f6fdb7d..fcb5c4f 100755 (executable)
@@ -1172,51 +1172,6 @@ static const struct message WmDestroyInvisibleChildSeq[] = {
     { WM_NCDESTROY, sent },
     { 0 }
 };
-/* Moving the mouse in nonclient area */
-static const struct message WmMouseMoveInNonClientAreaSeq[] = { /* FIXME: add */
-    { WM_NCHITTEST, sent },
-    { WM_SETCURSOR, sent },
-    { WM_NCMOUSEMOVE, posted },
-    { 0 }
-};
-/* Moving the mouse in client area */
-static const struct message WmMouseMoveInClientAreaSeq[] = { /* FIXME: add */
-    { WM_NCHITTEST, sent },
-    { WM_SETCURSOR, sent },
-    { WM_MOUSEMOVE, posted },
-    { 0 }
-};
-/* Moving by dragging the title bar (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
-static const struct message WmDragTitleBarSeq[] = { /* FIXME: add */
-    { WM_NCLBUTTONDOWN, sent|wparam, HTCAPTION },
-    { WM_SYSCOMMAND, sent|defwinproc|wparam, SC_MOVE+2 },
-    { WM_GETMINMAXINFO, sent|defwinproc },
-    { WM_ENTERSIZEMOVE, sent|defwinproc },
-    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
-    { WM_MOVE, sent|defwinproc },
-    { WM_EXITSIZEMOVE, sent|defwinproc },
-    { 0 }
-};
-/* Sizing by dragging the thick borders (after WM_NCHITTEST and WM_SETCURSOR) (outline move) */
-static const struct message WmDragThickBordersBarSeq[] = { /* FIXME: add */
-    { WM_NCLBUTTONDOWN, sent|wparam, 0xd },
-    { WM_SYSCOMMAND, sent|defwinproc|wparam, 0xf004 },
-    { WM_GETMINMAXINFO, sent|defwinproc },
-    { WM_ENTERSIZEMOVE, sent|defwinproc },
-    { WM_SIZING, sent|defwinproc|wparam, 4}, /* one for each mouse movement */
-    { WM_WINDOWPOSCHANGING, sent|wparam|defwinproc, 0 },
-    { WM_GETMINMAXINFO, sent|defwinproc },
-    { WM_NCCALCSIZE, sent|defwinproc|wparam, 1 },
-    { WM_NCPAINT, sent|defwinproc|wparam, 1 },
-    { WM_GETTEXT, sent|defwinproc },
-    { WM_ERASEBKGND, sent|defwinproc },
-    { WM_WINDOWPOSCHANGED, sent|wparam|defwinproc, 0 },
-    { WM_MOVE, sent|defwinproc },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
-    { WM_EXITSIZEMOVE, sent|defwinproc },
-    { 0 }
-};
 /* Resizing child window with MoveWindow (32) */
 static const struct message WmResizingChildWithMoveWindowSeq[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOACTIVATE },
@@ -1229,41 +1184,6 @@ static const struct message WmResizingChildWithMoveWindowSeq[] = {
     { EVENT_OBJECT_LOCATIONCHANGE, winevent_hook|wparam|lparam, 0, 0 },
     { 0 }
 };
-/* Clicking on inactive button */
-static const struct message WmClickInactiveButtonSeq[] = { /* FIXME: add */
-    { WM_NCHITTEST, sent },
-    { WM_PARENTNOTIFY, sent|parent|wparam, WM_LBUTTONDOWN },
-    { WM_MOUSEACTIVATE, sent },
-    { WM_MOUSEACTIVATE, sent|parent|defwinproc },
-    { WM_SETCURSOR, sent },
-    { WM_SETCURSOR, sent|parent|defwinproc },
-    { WM_LBUTTONDOWN, posted },
-    { WM_KILLFOCUS, posted|parent },
-    { WM_SETFOCUS, posted },
-    { WM_CTLCOLORBTN, posted|parent },
-    { BM_SETSTATE, posted },
-    { WM_CTLCOLORBTN, posted|parent },
-    { WM_LBUTTONUP, posted },
-    { BM_SETSTATE, posted },
-    { WM_CTLCOLORBTN, posted|parent },
-    { WM_COMMAND, posted|parent },
-    { 0 }
-};
-/* Reparenting a button (16/32) */
-/* The last child (button) reparented gets topmost for its new parent. */
-static const struct message WmReparentButtonSeq[] = { /* FIXME: add */
-    { WM_SHOWWINDOW, sent|wparam, 0 },
-    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE },
-    { EVENT_OBJECT_HIDE, winevent_hook|wparam|lparam, 0, 0 },
-    { WM_ERASEBKGND, sent|parent },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_HIDEWINDOW|SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOSIZE },
-    { WM_WINDOWPOSCHANGING, sent|wparam, SWP_NOSIZE },
-    { WM_CHILDACTIVATE, sent },
-    { WM_WINDOWPOSCHANGED, sent|wparam, SWP_NOSIZE|SWP_NOREDRAW },
-    { WM_MOVE, sent|defwinproc },
-    { WM_SHOWWINDOW, sent|wparam, 1 },
-    { 0 }
-};
 /* Creation of a custom dialog (32) */
 static const struct message WmCreateCustomDialogSeq[] = {
     { HCBT_CREATEWND, hook },
@@ -1464,35 +1384,6 @@ static const struct message WmModalDialogSeq[] = {
     { WM_NCDESTROY, sent },
     { 0 }
 };
-/* Creation of a modal dialog that is resized inside WM_INITDIALOG (32) */
-static const struct message WmCreateModalDialogResizeSeq[] = { /* FIXME: add */
-    /* (inside dialog proc, handling WM_INITDIALOG) */
-    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
-    { WM_NCCALCSIZE, sent },
-    { WM_NCACTIVATE, sent|parent|wparam, 0 },
-    { WM_GETTEXT, sent|defwinproc },
-    { WM_ACTIVATE, sent|parent|wparam, 0 },
-    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
-    { WM_WINDOWPOSCHANGING, sent|parent },
-    { WM_NCACTIVATE, sent|wparam, 1 },
-    { WM_ACTIVATE, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
-    { WM_SIZE, sent|defwinproc|wparam, SIZE_RESTORED },
-    /* (setting focus) */
-    { WM_SHOWWINDOW, sent|wparam, 1 },
-    { WM_WINDOWPOSCHANGING, sent|wparam, 0 },
-    { WM_NCPAINT, sent },
-    { WM_GETTEXT, sent|defwinproc },
-    { WM_ERASEBKGND, sent },
-    { WM_CTLCOLORDLG, sent|defwinproc },
-    { WM_WINDOWPOSCHANGED, sent|wparam, 0 },
-    { WM_PAINT, sent },
-    /* (bunch of WM_CTLCOLOR* for each control) */
-    { WM_PAINT, sent|parent },
-    { WM_ENTERIDLE, sent|parent|wparam, 0 },
-    { WM_SETCURSOR, sent|parent },
-    { 0 }
-};
 /* SetMenu for NonVisible windows with size change*/
 static const struct message WmSetMenuNonVisibleSizeChangeSeq[] = {
     { WM_WINDOWPOSCHANGING, sent|wparam, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOMOVE },
@@ -4542,6 +4433,11 @@ static const struct message WmZOrder[] = {
     { 0 }
 };
 
+static void CALLBACK apc_test_proc(ULONG_PTR param)
+{
+    /* nothing */
+}
+
 static void test_MsgWaitForMultipleObjects(HWND hwnd)
 {
     DWORD ret;
@@ -4583,17 +4479,61 @@ static void test_MsgWaitForMultipleObjects(HWND hwnd)
     ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
     ok(PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n");
     ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+    /* MWMO_INPUTAVAILABLE should succeed even if the message was already seen */
+    PostMessageA( hwnd, WM_USER, 0, 0 );
+    ok(PeekMessageA( &msg, 0, 0, 0, PM_NOREMOVE ), "PeekMessage should succeed\n");
+    ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+    ret = MsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_POSTMESSAGE, MWMO_INPUTAVAILABLE );
+    ok(ret == WAIT_OBJECT_0, "MsgWaitForMultipleObjectsEx returned %x\n", ret);
+
+    ok(PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n");
+    ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+    /* without MWMO_ALERTABLE the result is never WAIT_IO_COMPLETION */
+    ret = QueueUserAPC( apc_test_proc, GetCurrentThread(), 0 );
+    ok(ret, "QueueUserAPC failed %u\n", GetLastError());
+
+    ret = MsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_POSTMESSAGE, 0 );
+    ok(ret == WAIT_TIMEOUT, "MsgWaitForMultipleObjectsEx returned %x\n", ret);
+
+    /* but even with MWMO_ALERTABLE window events are preferred */
+    PostMessageA( hwnd, WM_USER, 0, 0 );
+
+    ret = MsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_POSTMESSAGE, MWMO_ALERTABLE );
+    ok(ret == WAIT_OBJECT_0, "MsgWaitForMultipleObjectsEx returned %x\n", ret);
+
+    ok(PeekMessageA( &msg, 0, 0, 0, PM_REMOVE ), "PeekMessage should succeed\n");
+    ok(msg.message == WM_USER, "got %04x instead of WM_USER\n", msg.message);
+
+    /* the APC call is still queued */
+    ret = MsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_POSTMESSAGE, MWMO_ALERTABLE );
+    ok(ret == WAIT_IO_COMPLETION, "MsgWaitForMultipleObjectsEx returned %x\n", ret);
+}
+
+static DWORD CALLBACK show_window_thread(LPVOID arg)
+{
+   HWND hwnd = arg;
+
+   /* function will not return if ShowWindow(SW_HIDE) calls SendMessage() */
+   ok(ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow(SW_HIDE) expected FALSE\n");
+
+   return 0;
 }
 
 /* test if we receive the right sequence of messages */
 static void test_messages(void)
 {
+    DWORD tid;
+    HANDLE hthread;
     HWND hwnd, hparent, hchild;
     HWND hchild2, hbutton;
     HMENU hmenu;
     MSG msg;
     LRESULT res;
     POINT pos;
+    BOOL ret;
 
     flush_sequence();
 
@@ -4620,6 +4560,19 @@ static void test_messages(void)
     flush_events();
     ok_sequence(WmHideOverlappedSeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
 
+    /* test ShowWindow(SW_HIDE) on a hidden window - single threaded */
+    ok(ShowWindow(hwnd, SW_HIDE) == FALSE, "ShowWindow(SW_HIDE) expected FALSE\n");
+    flush_events();
+    ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
+
+    /* test ShowWindow(SW_HIDE) on a hidden window -  multi-threaded */
+    hthread = CreateThread(NULL, 0, show_window_thread, hwnd, 0, &tid);
+    ok(hthread != NULL, "CreateThread failed, error %d\n", GetLastError());
+    ok(WaitForSingleObject(hthread, INFINITE) == WAIT_OBJECT_0, "WaitForSingleObject failed\n");
+    CloseHandle(hthread);
+    flush_events();
+    ok_sequence(WmEmptySeq, "ShowWindow(SW_HIDE):overlapped", FALSE);
+
     ShowWindow(hwnd, SW_SHOW);
     flush_events();
     ok_sequence(WmShowOverlappedSeq, "ShowWindow(SW_SHOW):overlapped", TRUE);
@@ -4867,10 +4820,24 @@ static void test_messages(void)
 
     flush_events();
     flush_sequence();
-    ok(DrawMenuBar(hwnd), "DrawMenuBar failed: %d\n", GetLastError());
+    ret = DrawMenuBar(hwnd);
+    ok(ret, "DrawMenuBar failed: %d\n", GetLastError());
     flush_events();
     ok_sequence(WmDrawMenuBarSeq, "DrawMenuBar", FALSE);
     ok(SetCursorPos(pos.x, pos.y), "SetCursorPos failed\n");
+
+    DestroyWindow(hwnd);
+
+    hwnd = CreateWindowExA(0, "TestDialogClass", NULL, WS_CHILD|WS_VISIBLE,
+            0, 0, 100, 100, hparent, 0, GetModuleHandleA(0), NULL);
+    ok(hwnd != 0, "Failed to create custom dialog window\n");
+    flush_events();
+    flush_sequence();
+    ret = DrawMenuBar(hwnd);
+    ok(ret, "DrawMenuBar failed: %d\n", GetLastError());
+    flush_events();
+    ok_sequence(WmEmptySeq, "DrawMenuBar for a child window", FALSE);
+
     DestroyWindow(hwnd);
 
     flush_sequence();
@@ -8400,6 +8367,13 @@ static void CALLBACK callback_count(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWOR
     count++;
 }
 
+static DWORD exception;
+static void CALLBACK callback_exception(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+    count++;
+    RaiseException(exception, 0, 0, NULL);
+}
+
 static DWORD WINAPI timer_thread_proc(LPVOID x)
 {
     struct timer_info *info = x;
@@ -8530,6 +8504,41 @@ static void test_timers_no_wnd(void)
     /* Note: SetSystemTimer doesn't support a NULL window, see test_timers */
 }
 
+static void test_timers_exception(DWORD code)
+{
+    UINT_PTR id;
+    MSG msg;
+
+    exception = code;
+    id = SetTimer(NULL, 0, 1000, callback_exception);
+    ok(id != 0, "did not get id from SetTimer.\n");
+
+    memset(&msg, 0, sizeof(msg));
+    msg.message = WM_TIMER;
+    msg.wParam = id;
+    msg.lParam = (LPARAM)callback_exception;
+
+    count = 0;
+    DispatchMessageA(&msg);
+    ok(count == 1, "did not get one count as expected (%i).\n", count);
+
+    KillTimer(NULL, id);
+}
+
+static void test_timers_exceptions(void)
+{
+    test_timers_exception(EXCEPTION_ACCESS_VIOLATION);
+    test_timers_exception(EXCEPTION_DATATYPE_MISALIGNMENT);
+    test_timers_exception(EXCEPTION_BREAKPOINT);
+    test_timers_exception(EXCEPTION_SINGLE_STEP);
+    test_timers_exception(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
+    test_timers_exception(EXCEPTION_FLT_DENORMAL_OPERAND);
+    test_timers_exception(EXCEPTION_FLT_DIVIDE_BY_ZERO);
+    test_timers_exception(EXCEPTION_FLT_INEXACT_RESULT);
+    test_timers_exception(EXCEPTION_ILLEGAL_INSTRUCTION);
+    test_timers_exception(0xE000BEEF); /* customer exception */
+}
+
 /* Various win events with arbitrary parameters */
 static const struct message WmWinEventsSeq[] = {
     { EVENT_SYSTEM_SOUND, winevent_hook|wparam|lparam, OBJID_WINDOW, 0 },
@@ -10431,6 +10440,115 @@ done:
     flush_events();
 }
 
+static void test_PeekMessage3(void)
+{
+    HWND hwnd;
+    BOOL ret;
+    MSG msg;
+
+    hwnd = CreateWindowA("TestWindowClass", "PeekMessage3", WS_OVERLAPPEDWINDOW,
+                         10, 10, 800, 800, NULL, NULL, NULL, NULL);
+    ok(hwnd != NULL, "expected hwnd != NULL\n");
+    flush_events();
+
+    /* GetMessage() and PeekMessage(..., PM_REMOVE) should prefer messages which
+     * were already seen. */
+
+    SetTimer(hwnd, 1, 0, NULL);
+    while (!PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    ret = PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    SetTimer(hwnd, 1, 0, NULL);
+    while (!PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    ret = PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    /* It doesn't matter if a message range is specified or not. */
+
+    SetTimer(hwnd, 1, 0, NULL);
+    while (!PeekMessageA(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    /* But not if the post messages were added before the PeekMessage() call. */
+
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    SetTimer(hwnd, 1, 0, NULL);
+    while (!PeekMessageA(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    /* More complicated test with multiple messages. */
+
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    SetTimer(hwnd, 1, 0, NULL);
+    while (!PeekMessageA(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    PostMessageA(hwnd, WM_USER + 1, 0, 0);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER + 1, "msg.message = %u instead of WM_USER + 1\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    /* Also works for posted messages, but the situation is a bit different,
+     * because both messages are in the same queue. */
+
+    PostMessageA(hwnd, WM_TIMER, 0, 0);
+    while (!PeekMessageA(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    PostMessageA(hwnd, WM_USER, 0, 0);
+    PostMessageA(hwnd, WM_TIMER, 0, 0);
+    while (!PeekMessageA(&msg, NULL, WM_TIMER, WM_TIMER, PM_NOREMOVE));
+    ok(msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_USER, "msg.message = %u instead of WM_USER\n", msg.message);
+    ret = GetMessageA(&msg, NULL, 0, 0);
+    ok(ret && msg.message == WM_TIMER, "msg.message = %u instead of WM_TIMER\n", msg.message);
+    ret = PeekMessageA(&msg, NULL, 0, 0, 0);
+    ok(!ret, "expected PeekMessage to return FALSE, got %u\n", ret);
+
+    DestroyWindow(hwnd);
+    flush_events();
+}
+
 static INT_PTR CALLBACK wm_quit_dlg_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp)
 {
     struct recvd_message msg;
@@ -10491,6 +10609,10 @@ static void test_quit_message(void)
     flush_events();
     PostQuitMessage(0xbeef);
 
+    msg.message = 0;
+    ret = PeekMessageA(&msg, 0, 0, 0, PM_QS_SENDMESSAGE);
+    ok(!ret, "got %x message\n", msg.message);
+
     ret = PeekMessageA(&msg, NULL, 0, 0, PM_NOREMOVE);
     ok(ret, "PeekMessage failed with error %d\n", GetLastError());
     ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message);
@@ -13324,7 +13446,7 @@ static void test_WaitForInputIdle( char *argv0 )
     {
         ResetEvent( start_event );
         ResetEvent( end_event );
-#if 0
+#ifndef __REACTOS__
         sprintf( path, "%s msg %u", argv0, i );
 #else
         sprintf( path, "%s msg_queue %u", argv0, i );
@@ -13996,9 +14118,6 @@ static const struct message WmSetFocus_3[] = {
     { HCBT_SETFOCUS, hook }, /* child */
     { 0 }
 };
-static const struct message WmSetFocus_4[] = {
-    { 0 }
-};
 
 static void test_SetFocus(void)
 {
@@ -14114,10 +14233,9 @@ todo_wine
     ret = AttachThreadInput(GetCurrentThreadId(), tid, TRUE);
     ok(ret, "AttachThreadInput error %d\n", GetLastError());
 
-todo_wine {
     ok(GetActiveWindow() == parent, "expected active %p, got %p\n", parent, GetActiveWindow());
     ok(GetFocus() == parent, "expected focus %p, got %p\n", parent, GetFocus());
-}
+
     flush_events();
     flush_sequence();
 
@@ -14474,6 +14592,160 @@ static void test_TrackPopupMenuEmpty(void)
     DestroyWindow(hwnd);
 }
 
+static const struct message send_message_1[] = {
+    { WM_USER+2, sent|wparam|lparam, 0, 0 },
+    { WM_USER, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message send_message_2[] = {
+    { WM_USER+4, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message send_message_3[] = {
+    { WM_USER+3, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+static const struct message send_message_4[] = {
+    { WM_USER+1, sent|wparam|lparam, 0, 0 },
+    { 0 }
+};
+
+static DWORD WINAPI SendMessage_thread_1(void *param)
+{
+    struct wnd_event *wnd_event = param;
+
+    trace("thread: starting\n");
+    WaitForSingleObject(wnd_event->start_event, INFINITE);
+
+    trace("thread: call PostMessage\n");
+    PostMessageA(wnd_event->hwnd, WM_USER, 0, 0);
+
+    trace("thread: call PostMessage\n");
+    PostMessageA(wnd_event->hwnd, WM_USER+1, 0, 0);
+
+    trace("thread: call SendMessage\n");
+    SendMessageA(wnd_event->hwnd, WM_USER+2, 0, 0);
+
+    trace("thread: call SendMessage\n");
+    SendMessageA(wnd_event->hwnd, WM_USER+3, 0, 0);
+
+    return 0;
+}
+
+static DWORD WINAPI SendMessage_thread_2(void *param)
+{
+    struct wnd_event *wnd_event = param;
+
+    trace("thread: starting\n");
+    WaitForSingleObject(wnd_event->start_event, INFINITE);
+
+    trace("thread: call PostMessage\n");
+    PostMessageA(wnd_event->hwnd, WM_USER, 0, 0);
+
+    trace("thread: call PostMessage\n");
+    PostMessageA(wnd_event->hwnd, WM_USER+1, 0, 0);
+
+    /* this leads to sending an internal message under Wine */
+    trace("thread: call EnableWindow\n");
+    EnableWindow(wnd_event->hwnd, TRUE);
+
+    trace("thread: call SendMessage\n");
+    SendMessageA(wnd_event->hwnd, WM_USER+2, 0, 0);
+
+    trace("thread: call SendMessage\n");
+    SendMessageA(wnd_event->hwnd, WM_USER+3, 0, 0);
+
+    return 0;
+}
+
+static void test_SendMessage_other_thread(int thread_n)
+{
+    DWORD qs_all_input = QS_ALLINPUT & ~QS_RAWINPUT;
+    HANDLE hthread;
+    struct wnd_event wnd_event;
+    DWORD tid, ret;
+    MSG msg;
+
+    wnd_event.start_event = CreateEventA(NULL, 0, 0, NULL);
+
+    wnd_event.hwnd = CreateWindowExA(0, "TestWindowClass", NULL, WS_OVERLAPPEDWINDOW,
+                                     100, 100, 200, 200, 0, 0, 0, NULL);
+    ok(wnd_event.hwnd != 0, "CreateWindowEx failed\n");
+
+    hthread = CreateThread(NULL, 0, thread_n == 1 ? SendMessage_thread_1 : SendMessage_thread_2, &wnd_event, 0, &tid);
+    ok(hthread != NULL, "CreateThread failed, error %d\n", GetLastError());
+    CloseHandle(hthread);
+
+    flush_events();
+    flush_sequence();
+
+    ret = GetQueueStatus(QS_SENDMESSAGE);
+    ok(ret == 0, "wrong status %08x\n", ret);
+
+    SetEvent(wnd_event.start_event);
+
+    /* wait for other thread's SendMessage */
+    for (;;)
+    {
+        ret = GetQueueStatus(QS_SENDMESSAGE);
+        if (ret == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE)) break;
+        Sleep(50);
+    }
+
+    ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE);
+    ok(ret == MAKELONG(QS_POSTMESSAGE, QS_SENDMESSAGE|QS_POSTMESSAGE), "wrong status %08x\n", ret);
+
+    trace("main: call GetMessage\n");
+    GetMessageA(&msg, 0, 0, 0);
+    ok(msg.message == WM_USER, "expected WM_USER, got %04x\n", msg.message);
+    DispatchMessageA(&msg);
+    ok_sequence(send_message_1, "SendMessage from other thread 1", thread_n == 2);
+
+    /* intentionally yield */
+    MsgWaitForMultipleObjects(0, NULL, FALSE, 100, qs_all_input);
+
+    trace("main: call SendMessage\n");
+    SendMessageA(wnd_event.hwnd, WM_USER+4, 0, 0);
+    ok_sequence(send_message_2, "SendMessage from other thread 2", FALSE);
+
+    ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE);
+    ok(ret == MAKELONG(QS_SENDMESSAGE, QS_SENDMESSAGE|QS_POSTMESSAGE), "wrong status %08x\n", ret);
+
+    trace("main: call PeekMessage\n");
+    ok(PeekMessageA(&msg, 0, 0, 0, PM_NOREMOVE), "PeekMessage should not fail\n");
+    ok(msg.message == WM_USER+1, "expected WM_USER+1, got %04x\n", msg.message);
+    ok_sequence(send_message_3, "SendMessage from other thread 3", thread_n == 2);
+
+    trace("main: call PeekMessage\n");
+    ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "PeekMessage should not fail\n");
+    ok(msg.message == WM_USER+1, "expected WM_USER+1, got %04x\n", msg.message);
+    DispatchMessageA(&msg);
+    ok_sequence(send_message_4, "SendMessage from other thread 4", FALSE);
+
+    /* intentionally yield */
+    MsgWaitForMultipleObjects(0, NULL, FALSE, 100, qs_all_input);
+
+    ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE);
+    /* FIXME: remove once Wine is fixed */
+if (thread_n == 2) todo_wine
+    ok(ret == 0, "wrong status %08x\n", ret);
+else
+    ok(ret == 0, "wrong status %08x\n", ret);
+
+    trace("main: call PeekMessage\n");
+    ok(!PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "PeekMessage should fail\n");
+    ok_sequence(WmEmptySeq, "SendMessage from other thread 5", thread_n == 2);
+
+    ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE);
+    ok(ret == 0, "wrong status %08x\n", ret);
+
+    trace("main: call DestroyWindow\n");
+    DestroyWindow(msg.hwnd);
+
+    flush_events();
+    flush_sequence();
+}
+
 static void init_funcs(void)
 {
     HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
@@ -14487,7 +14759,7 @@ static void init_funcs(void)
 #undef X
 }
 
-#if 0
+#ifndef __REACTOS__
 START_TEST(msg)
 {
     char **test_argv;
@@ -14552,12 +14824,16 @@ START_TEST(msg)
         pUnhookWinEvent = 0;
     }
     hEvent_hook = 0;
+
+    test_SendMessage_other_thread(1);
+    test_SendMessage_other_thread(2);
     test_SetFocus();
     test_SetParent();
     test_PostMessage();
     test_ShowWindow();
     test_PeekMessage();
     test_PeekMessage2();
+    test_PeekMessage3();
     test_WaitForInputIdle( test_argv[0] );
     test_scrollwindowex();
     test_messages();
@@ -14576,6 +14852,7 @@ START_TEST(msg)
     test_accelerators();
     test_timers();
     test_timers_no_wnd();
+    test_timers_exceptions();
     if (hCBT_hook) test_set_hook();
     test_DestroyWindow();
     test_DispatchMessage();
@@ -14628,7 +14905,7 @@ START_TEST(msg)
     }
     DeleteCriticalSection( &sequence_cs );
 }
-#endif
+#endif /* __REACTOS__ */
 
 static void init_tests()
 {
@@ -14662,12 +14939,12 @@ static void cleanup_tests()
     UnhookWindowsHookEx(hCBT_hook);
     if (pUnhookWinEvent && hEvent_hook)
     {
-       ret = pUnhookWinEvent(hEvent_hook);
-       ok( ret, "UnhookWinEvent error %d\n", GetLastError());
-       SetLastError(0xdeadbeef);
-       ok(!pUnhookWinEvent(hEvent_hook), "UnhookWinEvent succeeded\n");
-       ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */
-          GetLastError() == 0xdeadbeef, /* Win9x */
+        ret = pUnhookWinEvent(hEvent_hook);
+        ok( ret, "UnhookWinEvent error %d\n", GetLastError());
+        SetLastError(0xdeadbeef);
+        ok(!pUnhookWinEvent(hEvent_hook), "UnhookWinEvent succeeded\n");
+        ok(GetLastError() == ERROR_INVALID_HANDLE || /* Win2k */
+           GetLastError() == 0xdeadbeef, /* Win9x */
            "unexpected error %d\n", GetLastError());
     }
     DeleteCriticalSection( &sequence_cs );
@@ -14689,9 +14966,12 @@ START_TEST(msg_queue)
     }
 
     init_tests();
+    test_SendMessage_other_thread(1);
+    test_SendMessage_other_thread(2);
     test_PostMessage();
     test_PeekMessage();
     test_PeekMessage2();
+    test_PeekMessage3();
     test_interthread_messages();
     test_DispatchMessage();
     test_SendMessageTimeout();
@@ -14717,12 +14997,21 @@ START_TEST(msg_messages)
 START_TEST(msg_focus)
 {
     init_tests();
+
     test_SetFocus();
+
+    /* HACK: For some reason the tests fail on Windows if run consecutively.
+     * Putting these in between helps, and is essentially what happens in the
+     * "normal" msg test. */
+    keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    flush_events();
+
     test_SetActiveWindow();
 
-    /* HACK: For some reason test_SetForegroundWindow fails on Windows unless
-     * we do this */
+    /* HACK */
     keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
+    flush_events();
+
     /* keep it the last test, under Windows it tends to break the tests
      * which rely on active/foreground windows being correct.
      */
@@ -14747,6 +15036,14 @@ START_TEST(msg_paint)
     init_tests();
     test_scrollwindowex();
     test_paint_messages();
+#ifdef __REACTOS__
+    if (!winetest_interactive &&
+        !strcmp(winetest_platform, "windows"))
+    {
+        skip("ROSTESTS-185: Skipping user32_winetest:msg_paint test_paintingloop because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n");
+    }
+    else
+#endif
     test_paintingloop();
     cleanup_tests();
 }
@@ -14770,6 +15067,7 @@ START_TEST(msg_timer)
     init_tests();
     test_timers();
     test_timers_no_wnd();
+    test_timers_exceptions();
     cleanup_tests();
 }
 
@@ -14790,11 +15088,11 @@ START_TEST(msg_hook)
                                        0, GetCurrentThreadId(),
                                        WINEVENT_INCONTEXT);
         if (pIsWinEventHookInstalled && hEvent_hook)
-       {
-           UINT event;
-           for (event = EVENT_MIN; event <= EVENT_MAX; event++)
-               ok(pIsWinEventHookInstalled(event), "IsWinEventHookInstalled(%u) failed\n", event);
-       }
+        {
+            UINT event;
+            for (event = EVENT_MIN; event <= EVENT_MAX; event++)
+                ok(pIsWinEventHookInstalled(event), "IsWinEventHookInstalled(%u) failed\n", event);
+        }
     }
     if (!hEvent_hook) win_skip( "no win event hook support\n" );
 
@@ -14818,13 +15116,8 @@ START_TEST(msg_menu)
     init_tests();
     test_sys_menu();
     test_menu_messages();
-    if(!winetest_interactive)
-       skip("CORE-8299 : Skip Tracking popup menu tests.\n");
-    else
-    {
     test_TrackPopupMenu();
     test_TrackPopupMenuEmpty();
-    }
     cleanup_tests();
 }