From: Zebediah Figura Subject: [PATCH v2 1/5] user32: Fix handling of internal messages. Message-Id: <1489762118-19901-1-git-send-email-z.figura12@gmail.com> Date: Fri, 17 Mar 2017 09:48:34 -0500 This patch adds a new flag and a server reply, allowing GetQueueStatus to ignore internal messages, and MsgWaitForMultipleObjects to process internal messages until a real sent message is found. Signed-off-by: Zebediah Figura --- dlls/user32/message.c | 129 +++++++++++++++++++++++++++++++++++++++-- dlls/user32/tests/msg.c | 8 +-- include/wine/server_protocol.h | 4 +- include/winuser.h | 3 + server/protocol.def | 1 + server/queue.c | 14 ++++- server/request.h | 3 +- server/trace.c | 1 + 8 files changed, 150 insertions(+), 13 deletions(-) diff --git a/dlls/user32/message.c b/dlls/user32/message.c index 9bfb453..92caf64 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -2964,6 +2964,98 @@ static BOOL peek_message( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags /*********************************************************************** + * receive_internal_message + * + * Retrieves and processes a single sent internal message. + */ +static DWORD receive_internal_message(size_t buffer_size) +{ + LRESULT result; + struct user_thread_info *thread_info = get_user_thread_info(); + struct received_message_info info, *old_info; + void *buffer; + size_t size = 0; + + buffer = HeapAlloc(GetProcessHeap(), 0, buffer_size); + + SERVER_START_REQ( get_message ) + { + NTSTATUS res; + + req->flags = PM_REMOVE | PM_QS_SENDMESSAGE; + req->get_win = 0; + req->get_first = 0; + req->get_last = 0; + req->hw_id = 0; + req->wake_mask = 0; + req->changed_mask = 0; + wine_server_set_reply( req, buffer, buffer_size ); + + if ((res = wine_server_call( req )) != STATUS_SUCCESS) + { + HeapFree(GetProcessHeap(), 0, buffer); + if (res == STATUS_BUFFER_OVERFLOW) + return receive_internal_message(reply->total); + + return RtlNtStatusToDosError(res); + } + + size = wine_server_reply_size( reply ); + info.type = reply->type; + info.msg.hwnd = wine_server_ptr_handle( reply->win ); + info.msg.message = reply->msg; + info.msg.wParam = reply->wparam; + info.msg.lParam = reply->lparam; + info.msg.time = reply->time; + info.msg.pt.x = reply->x; + info.msg.pt.y = reply->y; + thread_info->active_hooks = reply->active_hooks; + } + SERVER_END_REQ; + + switch(info.type) + { + case MSG_ASCII: + case MSG_UNICODE: + info.flags = ISMEX_SEND; + break; + case MSG_NOTIFY: + info.flags = ISMEX_NOTIFY; + break; + case MSG_CALLBACK: + info.flags = ISMEX_CALLBACK; + break; + case MSG_HOOK_LL: + info.flags = ISMEX_SEND; + reply_message(&info, 0, TRUE); + return STATUS_SUCCESS; + case MSG_OTHER_PROCESS: + info.flags = ISMEX_SEND; + if (!unpack_message(info.msg.hwnd, info.msg.message, &info.msg.wParam, + &info.msg.lParam, &buffer, size)) + { + /* ignore it */ + reply_message(&info, 0, TRUE); + return ERROR_SUCCESS; + } + break; + default: + ERR("unexpected type %d for internal message %08x\n", info.type, info.msg.message); + } + + old_info = thread_info->receive_info; + thread_info->receive_info = &info; + result = handle_internal_message(info.msg.hwnd, info.msg.message, + info.msg.wParam, info.msg.lParam); + reply_message( &info, result, TRUE ); + thread_info->receive_info = old_info; + + HeapFree(GetProcessHeap(), 0, buffer); + return ERROR_SUCCESS; +} + + +/*********************************************************************** * process_sent_messages * * Process all pending sent messages. @@ -3811,7 +3903,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageA( MSG *msg, HWND hwnd, UINT first, UIN BOOL WINAPI DECLSPEC_HOTPATCH GetMessageW( MSG *msg, HWND hwnd, UINT first, UINT last ) { HANDLE server_queue = get_server_queue_handle(); - unsigned int mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */ + unsigned int mask = QS_POSTMESSAGE | QS_SENDMESSAGE | QS_WINE_SENDMESSAGE; /* Always selected */ USER_CheckNotLock(); check_for_driver_events( 0 ); @@ -3825,7 +3917,7 @@ BOOL WINAPI DECLSPEC_HOTPATCH GetMessageW( MSG *msg, HWND hwnd, UINT first, UINT if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER; if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT; } - else mask = QS_ALLINPUT; + else mask |= QS_ALLINPUT; while (!peek_message( msg, hwnd, first, last, PM_REMOVE | (mask << 16), mask )) { @@ -4142,7 +4234,7 @@ DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *pHandles, DWORD timeout, DWORD mask, DWORD flags ) { HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - DWORD i; + DWORD i, res; if (count > MAXIMUM_WAIT_OBJECTS-1) { @@ -4154,8 +4246,37 @@ DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, const HANDLE *pHandles, for (i = 0; i < count; i++) handles[i] = pHandles[i]; handles[count] = get_server_queue_handle(); - return wait_objects( count+1, handles, timeout, + mask |= QS_WINE_SENDMESSAGE; + + for (;;) + { + res = wait_objects( count+1, handles, timeout, (flags & MWMO_INPUTAVAILABLE) ? mask : 0, mask, flags ); + if (res == WAIT_OBJECT_0+count) + { + NTSTATUS server_res; + UINT msg; + SERVER_START_REQ( get_queue_status ) + { + req->clear_bits = 0; + if ((server_res = wine_server_call( req )) != STATUS_SUCCESS) + { + SetLastError(RtlNtStatusToDosError(server_res)); + return WAIT_FAILED; + } + msg = reply->first_sent_msg; + } + SERVER_END_REQ; + + if (msg & 0x80000000) + { + /* If it's a WINE internal message, process it and keep waiting */ + receive_internal_message(256); + continue; + } + } + return res; + } } diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c index 19a1f31..a682b70 100644 --- a/dlls/user32/tests/msg.c +++ b/dlls/user32/tests/msg.c @@ -16332,7 +16332,7 @@ static void test_SendMessage_other_thread(int thread_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); + ok_sequence(send_message_1, "SendMessage from other thread 1", FALSE); /* intentionally yield */ MsgWaitForMultipleObjects(0, NULL, FALSE, 100, qs_all_input); @@ -16347,7 +16347,7 @@ static void test_SendMessage_other_thread(int thread_n) 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); + ok_sequence(send_message_3, "SendMessage from other thread 3", FALSE); trace("main: call PeekMessage\n"); ok(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE), "PeekMessage should not fail\n"); @@ -16359,13 +16359,11 @@ static void test_SendMessage_other_thread(int thread_n) MsgWaitForMultipleObjects(0, NULL, FALSE, 100, qs_all_input); ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE); - /* FIXME: remove once Wine is fixed */ -todo_wine_if (thread_n == 2) 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); + ok_sequence(WmEmptySeq, "SendMessage from other thread 5", FALSE); ret = GetQueueStatus(QS_SENDMESSAGE|QS_POSTMESSAGE); ok(ret == 0, "wrong status %08x\n", ret); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index ee9fdd1..4417cb6 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -2941,6 +2941,8 @@ struct get_queue_status_reply struct reply_header __header; unsigned int wake_bits; unsigned int changed_bits; + unsigned int first_sent_msg; + char __pad_20[4]; }; @@ -6412,6 +6414,6 @@ union generic_reply struct terminate_job_reply terminate_job_reply; }; -#define SERVER_PROTOCOL_VERSION 524 +#define SERVER_PROTOCOL_VERSION 525 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/include/winuser.h b/include/winuser.h index 2b8331c..d10caba 100644 --- a/include/winuser.h +++ b/include/winuser.h @@ -2898,6 +2898,9 @@ typedef struct tagTRACKMOUSEEVENT { /* Extra (undocumented) queue wake bits - see "Undoc. Windows" */ #define QS_SMRESULT 0x8000 +/* Internal WINE message */ +#define QS_WINE_SENDMESSAGE 0x80000000 + /* InSendMessageEx flags */ #define ISMEX_NOSEND 0x00000000 #define ISMEX_SEND 0x00000001 diff --git a/server/protocol.def b/server/protocol.def index 60865a6..7b797af 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2162,6 +2162,7 @@ enum char_info_mode @REPLY unsigned int wake_bits; /* wake bits */ unsigned int changed_bits; /* changed bits since last time */ + unsigned int first_sent_msg; /* first message in the sent message queue */ @END diff --git a/server/queue.c b/server/queue.c index c479b38..ea6d5e6 100644 --- a/server/queue.c +++ b/server/queue.c @@ -744,7 +744,8 @@ static void receive_message( struct msg_queue *queue, struct message *msg, queue->recv_result = result; } free( msg ); - if (list_empty( &queue->msg_list[SEND_MESSAGE] )) clear_queue_bits( queue, QS_SENDMESSAGE ); + if (list_empty( &queue->msg_list[SEND_MESSAGE] )) + clear_queue_bits( queue, QS_SENDMESSAGE | QS_WINE_SENDMESSAGE ); } /* set the result of the current received message */ @@ -2272,11 +2273,17 @@ DECL_HANDLER(set_queue_mask) DECL_HANDLER(get_queue_status) { struct msg_queue *queue = current->queue; + struct list *ptr; if (queue) { reply->wake_bits = queue->wake_bits; reply->changed_bits = queue->changed_bits; queue->changed_bits &= ~req->clear_bits; + + if ((ptr = list_head( &queue->msg_list[SEND_MESSAGE] ))) + reply->first_sent_msg = LIST_ENTRY( ptr, struct message, entry )->msg; + else + reply->first_sent_msg = 0; } else reply->wake_bits = reply->changed_bits = 0; } @@ -2339,7 +2346,10 @@ DECL_HANDLER(send_message) /* fall through */ case MSG_NOTIFY: list_add_tail( &recv_queue->msg_list[SEND_MESSAGE], &msg->entry ); - set_queue_bits( recv_queue, QS_SENDMESSAGE ); + if (msg->msg & 0x80000000) /* Wine internal message */ + set_queue_bits( recv_queue, QS_WINE_SENDMESSAGE ); + else + set_queue_bits( recv_queue, QS_SENDMESSAGE ); break; case MSG_POSTED: list_add_tail( &recv_queue->msg_list[POST_MESSAGE], &msg->entry ); diff --git a/server/request.h b/server/request.h index cd396ee..374150d 100644 --- a/server/request.h +++ b/server/request.h @@ -1480,7 +1480,8 @@ C_ASSERT( FIELD_OFFSET(struct get_queue_status_request, clear_bits) == 12 ); C_ASSERT( sizeof(struct get_queue_status_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_queue_status_reply, wake_bits) == 8 ); C_ASSERT( FIELD_OFFSET(struct get_queue_status_reply, changed_bits) == 12 ); -C_ASSERT( sizeof(struct get_queue_status_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_queue_status_reply, first_sent_msg) == 16 ); +C_ASSERT( sizeof(struct get_queue_status_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct get_process_idle_event_request, handle) == 12 ); C_ASSERT( sizeof(struct get_process_idle_event_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_process_idle_event_reply, event) == 8 ); diff --git a/server/trace.c b/server/trace.c index e0ce45a..5ba65da 100644 --- a/server/trace.c +++ b/server/trace.c @@ -2698,6 +2698,7 @@ static void dump_get_queue_status_reply( const struct get_queue_status_reply *re { fprintf( stderr, " wake_bits=%08x", req->wake_bits ); fprintf( stderr, ", changed_bits=%08x", req->changed_bits ); + fprintf( stderr, ", first_sent_msg=%08x", req->first_sent_msg ); } static void dump_get_process_idle_event_request( const struct get_process_idle_event_request *req ) -- 2.7.4