Commit a40ce393 authored by Robert Shearman's avatar Robert Shearman Committed by Alexandre Julliard

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.
parent 2e32a425
...@@ -2668,10 +2668,30 @@ BOOL WINAPI PostThreadMessageW( DWORD thread, UINT msg, WPARAM wparam, LPARAM lp ...@@ -2668,10 +2668,30 @@ BOOL WINAPI PostThreadMessageW( DWORD thread, UINT msg, WPARAM wparam, LPARAM lp
/*********************************************************************** /***********************************************************************
* PostQuitMessage (USER32.@) * 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;
} }
......
...@@ -6958,6 +6958,57 @@ todo_wine { ...@@ -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) START_TEST(msg)
{ {
BOOL ret; BOOL ret;
...@@ -7016,6 +7067,7 @@ START_TEST(msg) ...@@ -7016,6 +7067,7 @@ START_TEST(msg)
test_DispatchMessage(); test_DispatchMessage();
test_SendMessageTimeout(); test_SendMessageTimeout();
test_edit_messages(); test_edit_messages();
test_quit_message();
UnhookWindowsHookEx(hCBT_hook); UnhookWindowsHookEx(hCBT_hook);
if (pUnhookWinEvent) if (pUnhookWinEvent)
......
...@@ -2205,6 +2205,16 @@ struct send_message_reply ...@@ -2205,6 +2205,16 @@ struct send_message_reply
struct reply_header __header; 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 enum message_type
{ {
MSG_ASCII, MSG_ASCII,
...@@ -3824,6 +3834,7 @@ enum request ...@@ -3824,6 +3834,7 @@ enum request
REQ_get_queue_status, REQ_get_queue_status,
REQ_wait_input_idle, REQ_wait_input_idle,
REQ_send_message, REQ_send_message,
REQ_post_quit_message,
REQ_get_message, REQ_get_message,
REQ_reply_message, REQ_reply_message,
REQ_accept_hardware_message, REQ_accept_hardware_message,
...@@ -4042,6 +4053,7 @@ union generic_request ...@@ -4042,6 +4053,7 @@ union generic_request
struct get_queue_status_request get_queue_status_request; struct get_queue_status_request get_queue_status_request;
struct wait_input_idle_request wait_input_idle_request; struct wait_input_idle_request wait_input_idle_request;
struct send_message_request send_message_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 get_message_request get_message_request;
struct reply_message_request reply_message_request; struct reply_message_request reply_message_request;
struct accept_hardware_message_request accept_hardware_message_request; struct accept_hardware_message_request accept_hardware_message_request;
...@@ -4258,6 +4270,7 @@ union generic_reply ...@@ -4258,6 +4270,7 @@ union generic_reply
struct get_queue_status_reply get_queue_status_reply; struct get_queue_status_reply get_queue_status_reply;
struct wait_input_idle_reply wait_input_idle_reply; struct wait_input_idle_reply wait_input_idle_reply;
struct send_message_reply send_message_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 get_message_reply get_message_reply;
struct reply_message_reply reply_message_reply; struct reply_message_reply reply_message_reply;
struct accept_hardware_message_reply accept_hardware_message_reply; struct accept_hardware_message_reply accept_hardware_message_reply;
...@@ -4349,6 +4362,6 @@ union generic_reply ...@@ -4349,6 +4362,6 @@ union generic_reply
struct query_symlink_reply query_symlink_reply; struct query_symlink_reply query_symlink_reply;
}; };
#define SERVER_PROTOCOL_VERSION 220 #define SERVER_PROTOCOL_VERSION 221
#endif /* __WINE_WINE_SERVER_PROTOCOL_H */ #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
...@@ -1573,6 +1573,10 @@ enum char_info_mode ...@@ -1573,6 +1573,10 @@ enum char_info_mode
VARARG(data,bytes); /* message data for sent messages */ VARARG(data,bytes); /* message data for sent messages */
@END @END
@REQ(post_quit_message)
int exit_code; /* exit code to return */
@END
enum message_type enum message_type
{ {
MSG_ASCII, /* Ascii message (from SendMessageA) */ MSG_ASCII, /* Ascii message (from SendMessageA) */
......
...@@ -120,6 +120,8 @@ struct msg_queue ...@@ -120,6 +120,8 @@ struct msg_queue
unsigned int changed_bits; /* changed wakeup bits */ unsigned int changed_bits; /* changed wakeup bits */
unsigned int changed_mask; /* changed wakeup mask */ unsigned int changed_mask; /* changed wakeup mask */
int paint_count; /* pending paint messages count */ 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 msg_list[NB_MSG_KINDS]; /* lists of messages */
struct list send_result; /* stack of sent messages waiting for result */ struct list send_result; /* stack of sent messages waiting for result */
struct list callback_result; /* list of callback 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_ ...@@ -244,6 +246,7 @@ static struct msg_queue *create_msg_queue( struct thread *thread, struct thread_
queue->changed_bits = 0; queue->changed_bits = 0;
queue->changed_mask = 0; queue->changed_mask = 0;
queue->paint_count = 0; queue->paint_count = 0;
queue->quit_message = 0;
queue->recv_result = NULL; queue->recv_result = NULL;
queue->next_timer_id = 1; queue->next_timer_id = 1;
queue->timeout = NULL; queue->timeout = NULL;
...@@ -642,7 +645,6 @@ static int get_posted_message( struct msg_queue *queue, user_handle_t win, ...@@ -642,7 +645,6 @@ static int get_posted_message( struct msg_queue *queue, user_handle_t win,
/* check against the filters */ /* check against the filters */
LIST_FOR_EACH_ENTRY( msg, &queue->msg_list[POST_MESSAGE], struct message, entry ) 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 (win && msg->win && msg->win != win && !is_child_window( win, msg->win )) continue;
if (!check_msg_filter( msg->msg, first, last )) continue; if (!check_msg_filter( msg->msg, first, last )) continue;
goto found; /* found one */ goto found; /* found one */
...@@ -682,6 +684,31 @@ found: ...@@ -682,6 +684,31 @@ found:
return 1; 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 */ /* empty a message list and free all the messages */
static void empty_msg_list( struct list *list ) static void empty_msg_list( struct list *list )
{ {
...@@ -1614,6 +1641,18 @@ DECL_HANDLER(send_message) ...@@ -1614,6 +1641,18 @@ DECL_HANDLER(send_message)
if (thread) release_object( thread ); 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 */ /* get a message from the current queue */
DECL_HANDLER(get_message) DECL_HANDLER(get_message)
...@@ -1645,6 +1684,11 @@ 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 )) if (get_posted_message( queue, get_win, req->get_first, req->get_last, req->flags, reply ))
return; 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 */ /* then check for any raw hardware message */
if (filter_contains_hw_range( req->get_first, req->get_last ) && 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 )) get_hardware_message( current, req->hw_id, get_win, req->get_first, req->get_last, reply ))
......
...@@ -232,6 +232,7 @@ DECL_HANDLER(set_queue_mask); ...@@ -232,6 +232,7 @@ DECL_HANDLER(set_queue_mask);
DECL_HANDLER(get_queue_status); DECL_HANDLER(get_queue_status);
DECL_HANDLER(wait_input_idle); DECL_HANDLER(wait_input_idle);
DECL_HANDLER(send_message); DECL_HANDLER(send_message);
DECL_HANDLER(post_quit_message);
DECL_HANDLER(get_message); DECL_HANDLER(get_message);
DECL_HANDLER(reply_message); DECL_HANDLER(reply_message);
DECL_HANDLER(accept_hardware_message); DECL_HANDLER(accept_hardware_message);
...@@ -449,6 +450,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = ...@@ -449,6 +450,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
(req_handler)req_get_queue_status, (req_handler)req_get_queue_status,
(req_handler)req_wait_input_idle, (req_handler)req_wait_input_idle,
(req_handler)req_send_message, (req_handler)req_send_message,
(req_handler)req_post_quit_message,
(req_handler)req_get_message, (req_handler)req_get_message,
(req_handler)req_reply_message, (req_handler)req_reply_message,
(req_handler)req_accept_hardware_message, (req_handler)req_accept_hardware_message,
......
...@@ -2058,6 +2058,11 @@ static void dump_send_message_request( const struct send_message_request *req ) ...@@ -2058,6 +2058,11 @@ static void dump_send_message_request( const struct send_message_request *req )
dump_varargs_bytes( cur_size ); 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 ) static void dump_get_message_request( const struct get_message_request *req )
{ {
fprintf( stderr, " flags=%d,", req->flags ); fprintf( stderr, " flags=%d,", req->flags );
...@@ -3333,6 +3338,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { ...@@ -3333,6 +3338,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_get_queue_status_request, (dump_func)dump_get_queue_status_request,
(dump_func)dump_wait_input_idle_request, (dump_func)dump_wait_input_idle_request,
(dump_func)dump_send_message_request, (dump_func)dump_send_message_request,
(dump_func)dump_post_quit_message_request,
(dump_func)dump_get_message_request, (dump_func)dump_get_message_request,
(dump_func)dump_reply_message_request, (dump_func)dump_reply_message_request,
(dump_func)dump_accept_hardware_message_request, (dump_func)dump_accept_hardware_message_request,
...@@ -3547,6 +3553,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { ...@@ -3547,6 +3553,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
(dump_func)dump_get_queue_status_reply, (dump_func)dump_get_queue_status_reply,
(dump_func)dump_wait_input_idle_reply, (dump_func)dump_wait_input_idle_reply,
(dump_func)0, (dump_func)0,
(dump_func)0,
(dump_func)dump_get_message_reply, (dump_func)dump_get_message_reply,
(dump_func)0, (dump_func)0,
(dump_func)0, (dump_func)0,
...@@ -3761,6 +3768,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { ...@@ -3761,6 +3768,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
"get_queue_status", "get_queue_status",
"wait_input_idle", "wait_input_idle",
"send_message", "send_message",
"post_quit_message",
"get_message", "get_message",
"reply_message", "reply_message",
"accept_hardware_message", "accept_hardware_message",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment