[PATCH] user32: Fix handling of internal messages.

Zebediah Figura z.figura12 at gmail.com
Wed Mar 15 11:42:51 CDT 2017


I'm submitting this to wine-devel because the code is probably messy and
I'm not sure if this is the right approach to solving the problem.

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 <z.figura12 at gmail.com>
---
 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




More information about the wine-devel mailing list