From a40ce39340c028618591a73dbc4d2c6fe18ae4c2 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 17 Jan 2006 13:14:31 +0100 Subject: [PATCH] user: Fix WM_QUIT message ordering from PostQuitMessage. Added a new server call as PostQuitMessage should set a flag in the message queue to return the WM_QUIT message when there are no other pending messages, rather than posting a message to the thread queue as it does at the moment. --- dlls/user/message.c | 24 ++++++++++++++-- dlls/user/tests/msg.c | 52 ++++++++++++++++++++++++++++++++++ include/wine/server_protocol.h | 15 +++++++++- server/protocol.def | 4 +++ server/queue.c | 46 +++++++++++++++++++++++++++++- server/request.h | 2 ++ server/trace.c | 8 ++++++ 7 files changed, 147 insertions(+), 4 deletions(-) diff --git a/dlls/user/message.c b/dlls/user/message.c index 4df29908ea5..99d1748298f 100644 --- a/dlls/user/message.c +++ b/dlls/user/message.c @@ -2668,10 +2668,30 @@ BOOL WINAPI PostThreadMessageW( DWORD thread, UINT msg, WPARAM wparam, LPARAM lp /*********************************************************************** * PostQuitMessage (USER32.@) + * + * Posts a quit message to the current thread's message queue. + * + * PARAMS + * exit_code [I] Exit code to return from message loop. + * + * RETURNS + * Nothing. + * + * NOTES + * This function is not the same as calling: + *|PostThreadMessage(GetCurrentThreadId(), WM_QUIT, exit_code, 0); + * It instead sets a flag in the message queue that signals it to generate + * a WM_QUIT message when there are no other pending sent or posted messages + * in the queue. */ -void WINAPI PostQuitMessage( INT exitCode ) +void WINAPI PostQuitMessage( INT exit_code ) { - PostThreadMessageW( GetCurrentThreadId(), WM_QUIT, exitCode, 0 ); + SERVER_START_REQ( post_quit_message ) + { + req->exit_code = exit_code; + wine_server_call( req ); + } + SERVER_END_REQ; } diff --git a/dlls/user/tests/msg.c b/dlls/user/tests/msg.c index 3cdaf5e49be..2495df751c7 100644 --- a/dlls/user/tests/msg.c +++ b/dlls/user/tests/msg.c @@ -6958,6 +6958,57 @@ todo_wine { } +static void test_quit_message(void) +{ + MSG msg; + BOOL ret; + + /* test using PostQuitMessage */ + PostQuitMessage(0xbeef); + + ret = PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); + ok(ret, "PeekMessage failed with error %ld\n", GetLastError()); + ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message); + ok(msg.wParam == 0xbeef, "wParam was 0x%x instead of 0xbeef\n", msg.wParam); + + ret = PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0); + ok(ret, "PostMessage failed with error %ld\n", GetLastError()); + + ret = GetMessage(&msg, NULL, 0, 0); + ok(ret > 0, "GetMessage failed with error %ld\n", GetLastError()); + ok(msg.message == WM_USER, "Received message 0x%04x instead of WM_USER\n", msg.message); + + /* note: WM_QUIT message received after WM_USER message */ + ret = GetMessage(&msg, NULL, 0, 0); + ok(!ret, "GetMessage return %d with error %ld instead of FALSE\n", ret, GetLastError()); + ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message); + ok(msg.wParam == 0xbeef, "wParam was 0x%x instead of 0xbeef\n", msg.wParam); + + ret = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); + ok( !ret || msg.message != WM_QUIT, "Received WM_QUIT again\n" ); + + /* now test with PostThreadMessage - different behaviour! */ + PostThreadMessage(GetCurrentThreadId(), WM_QUIT, 0xdead, 0); + + ret = PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); + ok(ret, "PeekMessage failed with error %ld\n", GetLastError()); + ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message); + ok(msg.wParam == 0xdead, "wParam was 0x%x instead of 0xdead\n", msg.wParam); + + ret = PostThreadMessage(GetCurrentThreadId(), WM_USER, 0, 0); + ok(ret, "PostMessage failed with error %ld\n", GetLastError()); + + /* note: we receive the WM_QUIT message first this time */ + ret = GetMessage(&msg, NULL, 0, 0); + ok(!ret, "GetMessage return %d with error %ld instead of FALSE\n", ret, GetLastError()); + ok(msg.message == WM_QUIT, "Received message 0x%04x instead of WM_QUIT\n", msg.message); + ok(msg.wParam == 0xdead, "wParam was 0x%x instead of 0xdead\n", msg.wParam); + + ret = GetMessage(&msg, NULL, 0, 0); + ok(ret > 0, "GetMessage failed with error %ld\n", GetLastError()); + ok(msg.message == WM_USER, "Received message 0x%04x instead of WM_USER\n", msg.message); +} + START_TEST(msg) { BOOL ret; @@ -7016,6 +7067,7 @@ START_TEST(msg) test_DispatchMessage(); test_SendMessageTimeout(); test_edit_messages(); + test_quit_message(); UnhookWindowsHookEx(hCBT_hook); if (pUnhookWinEvent) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 4ab14677d66..f5562153a3c 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2205,6 +2205,16 @@ struct send_message_reply struct reply_header __header; }; +struct post_quit_message_request +{ + struct request_header __header; + int exit_code; +}; +struct post_quit_message_reply +{ + struct reply_header __header; +}; + enum message_type { MSG_ASCII, @@ -3824,6 +3834,7 @@ enum request REQ_get_queue_status, REQ_wait_input_idle, REQ_send_message, + REQ_post_quit_message, REQ_get_message, REQ_reply_message, REQ_accept_hardware_message, @@ -4042,6 +4053,7 @@ union generic_request struct get_queue_status_request get_queue_status_request; struct wait_input_idle_request wait_input_idle_request; struct send_message_request send_message_request; + struct post_quit_message_request post_quit_message_request; struct get_message_request get_message_request; struct reply_message_request reply_message_request; struct accept_hardware_message_request accept_hardware_message_request; @@ -4258,6 +4270,7 @@ union generic_reply struct get_queue_status_reply get_queue_status_reply; struct wait_input_idle_reply wait_input_idle_reply; struct send_message_reply send_message_reply; + struct post_quit_message_reply post_quit_message_reply; struct get_message_reply get_message_reply; struct reply_message_reply reply_message_reply; struct accept_hardware_message_reply accept_hardware_message_reply; @@ -4349,6 +4362,6 @@ union generic_reply struct query_symlink_reply query_symlink_reply; }; -#define SERVER_PROTOCOL_VERSION 220 +#define SERVER_PROTOCOL_VERSION 221 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/protocol.def b/server/protocol.def index d3878cbe457..e717dd2785d 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1573,6 +1573,10 @@ enum char_info_mode VARARG(data,bytes); /* message data for sent messages */ @END +@REQ(post_quit_message) + int exit_code; /* exit code to return */ +@END + enum message_type { MSG_ASCII, /* Ascii message (from SendMessageA) */ diff --git a/server/queue.c b/server/queue.c index 3bc64d485b9..57b94b82822 100644 --- a/server/queue.c +++ b/server/queue.c @@ -120,6 +120,8 @@ struct msg_queue unsigned int changed_bits; /* changed wakeup bits */ unsigned int changed_mask; /* changed wakeup mask */ int paint_count; /* pending paint messages count */ + int quit_message; /* is there a pending quit message? */ + int exit_code; /* exit code of pending quit message */ struct list msg_list[NB_MSG_KINDS]; /* lists of messages */ struct list send_result; /* stack of sent messages waiting for result */ struct list callback_result; /* list of callback messages waiting for result */ @@ -244,6 +246,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_ queue->changed_bits = 0; queue->changed_mask = 0; queue->paint_count = 0; + queue->quit_message = 0; queue->recv_result = NULL; queue->next_timer_id = 1; queue->timeout = NULL; @@ -642,7 +645,6 @@ static int get_posted_message( struct msg_queue *queue, user_handle_t win, /* check against the filters */ LIST_FOR_EACH_ENTRY( msg, &queue->msg_list[POST_MESSAGE], struct message, entry ) { - if (msg->msg == WM_QUIT) goto found; /* WM_QUIT is never filtered */ if (win && msg->win && msg->win != win && !is_child_window( win, msg->win )) continue; if (!check_msg_filter( msg->msg, first, last )) continue; goto found; /* found one */ @@ -682,6 +684,31 @@ found: return 1; } +static int get_quit_message( struct msg_queue *queue, unsigned int flags, + struct get_message_reply *reply ) +{ + if (queue->quit_message) + { + reply->total = 0; + reply->type = MSG_POSTED; + reply->win = NULL; + reply->msg = WM_QUIT; + reply->wparam = queue->exit_code; + reply->lparam = 0; + reply->x = 0; + reply->y = 0; + reply->time = get_tick_count(); + reply->info = 0; + + if (flags & GET_MSG_REMOVE) + queue->quit_message = 0; + + return 1; + } + else + return 0; +} + /* empty a message list and free all the messages */ static void empty_msg_list( struct list *list ) { @@ -1614,6 +1641,18 @@ DECL_HANDLER(send_message) if (thread) release_object( thread ); } +/* post a quit message to the current queue */ +DECL_HANDLER(post_quit_message) +{ + struct msg_queue *queue = get_current_queue(); + + if (!queue) + return; + + queue->quit_message = 1; + queue->exit_code = req->exit_code; + set_queue_bits( queue, QS_POSTMESSAGE|QS_ALLPOSTMESSAGE ); +} /* get a message from the current queue */ DECL_HANDLER(get_message) @@ -1645,6 +1684,11 @@ DECL_HANDLER(get_message) if (get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply )) return; + /* only check for quit messages if not posted messages pending. + * note: the quit message isn't filtered */ + if (get_quit_message( queue, req->flags, reply )) + return; + /* then check for any raw hardware message */ if (filter_contains_hw_range( req->get_first, req->get_last ) && get_hardware_message( current, req->hw_id, get_win, req->get_first, req->get_last, reply )) diff --git a/server/request.h b/server/request.h index 1fecb0f74c9..91b314fe1b3 100644 --- a/server/request.h +++ b/server/request.h @@ -232,6 +232,7 @@ DECL_HANDLER(set_queue_mask); DECL_HANDLER(get_queue_status); DECL_HANDLER(wait_input_idle); DECL_HANDLER(send_message); +DECL_HANDLER(post_quit_message); DECL_HANDLER(get_message); DECL_HANDLER(reply_message); DECL_HANDLER(accept_hardware_message); @@ -449,6 +450,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_queue_status, (req_handler)req_wait_input_idle, (req_handler)req_send_message, + (req_handler)req_post_quit_message, (req_handler)req_get_message, (req_handler)req_reply_message, (req_handler)req_accept_hardware_message, diff --git a/server/trace.c b/server/trace.c index 1aa964eceae..96cecb145fb 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2058,6 +2058,11 @@ static void dump_send_message_request( const struct send_message_request *req ) dump_varargs_bytes( cur_size ); } +static void dump_post_quit_message_request( const struct post_quit_message_request *req ) +{ + fprintf( stderr, " exit_code=%d", req->exit_code ); +} + static void dump_get_message_request( const struct get_message_request *req ) { fprintf( stderr, " flags=%d,", req->flags ); @@ -3333,6 +3338,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_queue_status_request, (dump_func)dump_wait_input_idle_request, (dump_func)dump_send_message_request, + (dump_func)dump_post_quit_message_request, (dump_func)dump_get_message_request, (dump_func)dump_reply_message_request, (dump_func)dump_accept_hardware_message_request, @@ -3547,6 +3553,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_queue_status_reply, (dump_func)dump_wait_input_idle_reply, (dump_func)0, + (dump_func)0, (dump_func)dump_get_message_reply, (dump_func)0, (dump_func)0, @@ -3761,6 +3768,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_queue_status", "wait_input_idle", "send_message", + "post_quit_message", "get_message", "reply_message", "accept_hardware_message",