{ 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 },
{ 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 },
{ 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 },
{ 0 }
};
+static void CALLBACK apc_test_proc(ULONG_PTR param)
+{
+ /* nothing */
+}
+
static void test_MsgWaitForMultipleObjects(HWND hwnd)
{
DWORD ret;
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();
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);
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();
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;
/* 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 },
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;
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);
{
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)
{ HCBT_SETFOCUS, hook }, /* child */
{ 0 }
};
-static const struct message WmSetFocus_4[] = {
- { 0 }
-};
static void test_SetFocus(void)
{
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();
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");
#undef X
}
+#ifndef __REACTOS__
START_TEST(msg)
{
char **test_argv;
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();
test_accelerators();
test_timers();
test_timers_no_wnd();
+ test_timers_exceptions();
if (hCBT_hook) test_set_hook();
test_DestroyWindow();
test_DispatchMessage();
}
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();
+}