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.
This commit is contained in:
Robert Shearman 2006-01-17 13:14:31 +01:00 committed by Alexandre Julliard
parent 2e32a425db
commit a40ce39340
7 changed files with 147 additions and 4 deletions

View File

@ -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;
}

View File

@ -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)

View File

@ -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 */

View File

@ -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) */

View File

@ -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 ))

View File

@ -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,

View File

@ -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",