diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 5f10a89ec29..b014dd9a0f7 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -14598,6 +14598,153 @@ 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 }, + { 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); +todo_wine + 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_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_3, "SendMessage from other thread 3", thread_n == 2); + + /* 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 4", 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"); @@ -14675,6 +14822,9 @@ START_TEST(msg) pUnhookWinEvent = 0; } hEvent_hook = 0; + + test_SendMessage_other_thread(1); + test_SendMessage_other_thread(2); test_SetFocus(); test_SetParent(); test_PostMessage();