[User32_wineTest]
[reactos.git] / rostests / winetests / user32 / msg.c
index 87417da..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,11 @@ static void test_WaitForInputIdle( char *argv0 )
     {
         ResetEvent( start_event );
         ResetEvent( end_event );
+#ifndef __REACTOS__
         sprintf( path, "%s msg %u", argv0, i );
+#else
+        sprintf( path, "%s msg_queue %u", argv0, i );
+#endif
         ret = CreateProcessA( NULL, path, NULL, NULL, TRUE, 0, NULL, NULL, &startup, &pi );
         ok( ret, "CreateProcess '%s' failed err %u.\n", path, GetLastError() );
         if (ret)
@@ -13992,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)
 {
@@ -14110,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();
 
@@ -14470,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");
@@ -14483,6 +14759,7 @@ static void init_funcs(void)
 #undef X
 }
 
+#ifndef __REACTOS__
 START_TEST(msg)
 {
     char **test_argv;
@@ -14547,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();
@@ -14571,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();
@@ -14623,3 +14905,258 @@ START_TEST(msg)
     }
     DeleteCriticalSection( &sequence_cs );
 }
+#endif /* __REACTOS__ */
+
+static void init_tests()
+{
+    HMODULE hModuleImm32;
+    BOOL (WINAPI *pImmDisableIME)(DWORD);
+
+    init_funcs();
+
+    InitializeCriticalSection( &sequence_cs );
+    init_procs();
+
+    hModuleImm32 = LoadLibraryA("imm32.dll");
+    if (hModuleImm32) {
+        pImmDisableIME = (void *)GetProcAddress(hModuleImm32, "ImmDisableIME");
+        if (pImmDisableIME)
+            pImmDisableIME(0);
+    }
+    pImmDisableIME = NULL;
+    FreeLibrary(hModuleImm32);
+
+    if (!RegisterWindowClasses()) assert(0);
+
+    cbt_hook_thread_id = GetCurrentThreadId();
+    hCBT_hook = SetWindowsHookExA(WH_CBT, cbt_hook_proc, 0, GetCurrentThreadId());
+    if (!hCBT_hook) win_skip( "cannot set global hook, will skip hook tests\n" );
+}
+
+static void cleanup_tests()
+{
+    BOOL ret;
+    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 */
+           "unexpected error %d\n", GetLastError());
+    }
+    DeleteCriticalSection( &sequence_cs );
+
+}
+
+START_TEST(msg_queue)
+{
+    int argc;
+    char **test_argv;
+    argc = winetest_get_mainargs( &test_argv );
+    if (argc >= 3)
+    {
+        unsigned int arg;
+        /* Child process. */
+        sscanf (test_argv[2], "%d", (unsigned int *) &arg);
+        do_wait_idle_child( arg );
+        return;
+    }
+
+    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();
+    test_quit_message();
+    test_WaitForInputIdle( test_argv[0] );
+    test_DestroyWindow();
+    cleanup_tests();
+}
+
+START_TEST(msg_messages)
+{
+    init_tests();
+    test_message_conversion();
+    test_messages();
+    test_wmime_keydown_message();
+    test_nullCallback();
+    test_dbcs_wm_char();
+    test_unicode_wm_char();
+    test_defwinproc();
+    cleanup_tests();
+}
+
+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 */
+    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.
+     */
+    test_SetForegroundWindow();
+    cleanup_tests();
+}
+
+START_TEST(msg_winpos)
+{
+    init_tests();
+    test_SetParent();
+    test_ShowWindow();
+    test_setwindowpos();
+    test_showwindow();
+    test_SetWindowRgn();
+    invisible_parent_tests();
+    cleanup_tests();
+}
+
+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();
+}
+
+START_TEST(msg_input)
+{
+    init_tests();
+    test_accelerators();
+    if (!pTrackMouseEvent)
+        win_skip("TrackMouseEvent is not available\n");
+    else
+        test_TrackMouseEvent();
+
+    test_keyflags();
+    test_hotkey();
+    cleanup_tests();
+}
+
+START_TEST(msg_timer)
+{
+    init_tests();
+    test_timers();
+    test_timers_no_wnd();
+    test_timers_exceptions();
+    cleanup_tests();
+}
+
+typedef BOOL (WINAPI *IS_WINEVENT_HOOK_INSTALLED)(DWORD);
+
+START_TEST(msg_hook)
+{
+//    HMODULE user32 = GetModuleHandleA("user32.dll");
+//    IS_WINEVENT_HOOK_INSTALLED pIsWinEventHookInstalled = (IS_WINEVENT_HOOK_INSTALLED)GetProcAddress(user32, "IsWinEventHookInstalled");
+    BOOL (WINAPI *pIsWinEventHookInstalled)(DWORD)= 0;/*GetProcAddress(user32, "IsWinEventHookInstalled");*/
+
+    init_tests();
+
+    if (pSetWinEventHook)
+    {
+        hEvent_hook = pSetWinEventHook(EVENT_MIN, EVENT_MAX,
+                                       GetModuleHandleA(0), win_event_proc,
+                                       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);
+        }
+    }
+    if (!hEvent_hook) win_skip( "no win event hook support\n" );
+
+    test_winevents();
+
+    /* Fix message sequences before removing 4 lines below */
+    if (pUnhookWinEvent && hEvent_hook)
+    {
+        BOOL ret;
+        ret = pUnhookWinEvent(hEvent_hook);
+        ok( ret, "UnhookWinEvent error %d\n", GetLastError());
+        pUnhookWinEvent = 0;
+    }
+    hEvent_hook = 0;
+    if (hCBT_hook) test_set_hook();
+    cleanup_tests();
+}
+
+START_TEST(msg_menu)
+{
+    init_tests();
+    test_sys_menu();
+    test_menu_messages();
+    test_TrackPopupMenu();
+    test_TrackPopupMenuEmpty();
+    cleanup_tests();
+}
+
+START_TEST(msg_mdi)
+{
+    init_tests();
+    test_mdi_messages();
+    cleanup_tests();
+}
+
+START_TEST(msg_controls)
+{
+    init_tests();
+    test_button_messages();
+    test_static_messages();
+    test_listbox_messages();
+    test_combobox_messages();
+    test_edit_messages();
+    cleanup_tests();
+}
+
+START_TEST(msg_layered_window)
+{
+    init_tests();
+    test_layered_window();
+    cleanup_tests();
+}
+
+START_TEST(msg_dialog)
+{
+    init_tests();
+    test_dialog_messages();
+    test_EndDialog();
+    cleanup_tests();
+}
+
+START_TEST(msg_clipboard)
+{
+    init_tests();
+    test_clipboard_viewers();
+    cleanup_tests();
+}