Implemented inter-thread SendMessageCallback.

diff --git a/dlls/user/message.c b/dlls/user/message.c
index a7ca483..ff32ec2 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -38,6 +38,7 @@
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(msg);
+WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
 #define WM_NCMOUSELAST  WM_NCMBUTTONDBLCLK
@@ -1469,6 +1470,26 @@
 
 
 /***********************************************************************
+ *           call_sendmsg_callback
+ *
+ * Call the callback function of SendMessageCallback.
+ */
+static inline void call_sendmsg_callback( SENDASYNCPROC callback, HWND hwnd, UINT msg,
+                                          ULONG_PTR data, LRESULT result )
+{
+    if (TRACE_ON(relay))
+        DPRINTF( "%04lx:Call message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
+                 GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
+                 data, result );
+    callback( hwnd, msg, data, result );
+    if (TRACE_ON(relay))
+        DPRINTF( "%04lx:Ret  message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
+                 GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
+                 data, result );
+}
+
+
+/***********************************************************************
  *           MSG_peek_message
  *
  * Peek for a message matching the given parameters. Return FALSE if none available.
@@ -1540,6 +1561,10 @@
         case MSG_CALLBACK:
             info.flags = ISMEX_CALLBACK;
             break;
+        case MSG_CALLBACK_RESULT:
+            call_sendmsg_callback( (SENDASYNCPROC)info.msg.wParam, info.msg.hwnd,
+                                   info.msg.message, extra_info, info.msg.lParam );
+            goto next;
         case MSG_OTHER_PROCESS:
             info.flags = ISMEX_SEND;
             if (!unpack_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
@@ -1690,6 +1715,13 @@
         req->lparam  = info->lparam;
         req->time    = GetCurrentTime();
         req->timeout = timeout;
+
+        if (info->type == MSG_CALLBACK)
+        {
+            req->callback = info->callback;
+            req->info     = info->data;
+        }
+
         if (info->flags & SMTO_ABORTIFHUNG) req->flags |= SEND_MSG_ABORT_IF_HUNG;
         for (i = 0; i < data.count; i++) wine_server_add_data( req, data.data[i], data.size[i] );
         if ((res = wine_server_call( req )))
@@ -2043,7 +2075,7 @@
     if (dest_tid == GetCurrentThreadId())
     {
         result = call_window_proc( hwnd, msg, wparam, lparam, TRUE, TRUE );
-        callback( hwnd, msg, data, result );
+        call_sendmsg_callback( callback, hwnd, msg, data, result );
         return TRUE;
     }
     FIXME( "callback will not be called\n" );
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index fe451ab..38f10e2 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -2214,6 +2214,7 @@
     unsigned int    time;
     unsigned int    info;
     int             timeout;
+    void*           callback;
     /* VARARG(data,bytes); */
 };
 struct send_message_reply
@@ -2227,6 +2228,7 @@
     MSG_UNICODE,
     MSG_NOTIFY,
     MSG_CALLBACK,
+    MSG_CALLBACK_RESULT,
     MSG_OTHER_PROCESS,
     MSG_POSTED,
     MSG_HARDWARE
@@ -3645,6 +3647,6 @@
     struct open_token_reply open_token_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 117
+#define SERVER_PROTOCOL_VERSION 118
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/server/protocol.def b/server/protocol.def
index c3e37cc..bf672b7 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -1574,6 +1574,7 @@
     unsigned int    time;      /* message time */
     unsigned int    info;      /* extra info */
     int             timeout;   /* timeout for reply */
+    void*           callback;  /* callback address */
     VARARG(data,bytes);        /* message data for sent messages */
 @END
 
@@ -1583,6 +1584,7 @@
     MSG_UNICODE,        /* Unicode message (from SendMessageW) */
     MSG_NOTIFY,         /* notify message (from SendNotifyMessageW), always Unicode */
     MSG_CALLBACK,       /* callback message (from SendMessageCallbackW), always Unicode */
+    MSG_CALLBACK_RESULT,/* result of a callback message */
     MSG_OTHER_PROCESS,  /* sent from other process, may include vararg data, always Unicode */
     MSG_POSTED,         /* posted message (from PostMessageW), always Unicode */
     MSG_HARDWARE        /* hardware message */
@@ -1600,12 +1602,12 @@
     int             type;      /* message type */
     user_handle_t   win;       /* window handle */
     unsigned int    msg;       /* message code */
-    unsigned int    wparam;    /* parameters */
-    unsigned int    lparam;    /* parameters */
+    unsigned int    wparam;    /* parameters (callback function for MSG_CALLBACK_RESULT) */
+    unsigned int    lparam;    /* parameters (result for MSG_CALLBACK_RESULT) */
     int             x;         /* x position */
     int             y;         /* y position */
     unsigned int    time;      /* message time */
-    unsigned int    info;      /* extra info */
+    unsigned int    info;      /* extra info (callback argument for MSG_CALLBACK_RESULT) */
     size_t          total;     /* total size of extra data */
     VARARG(data,bytes);        /* message data for sent messages */
 @END
diff --git a/server/queue.c b/server/queue.c
index eb2c11c..65bb680 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -42,16 +42,17 @@
 
 struct message_result
 {
-    struct message_result *send_next;   /* next in sender list */
-    struct message_result *recv_next;   /* next in receiver list */
-    struct msg_queue      *sender;      /* sender queue */
-    struct msg_queue      *receiver;    /* receiver queue */
-    int                    replied;     /* has it been replied to? */
-    unsigned int           result;      /* reply result */
-    unsigned int           error;       /* error code to pass back to sender */
-    void                  *data;        /* message reply data */
-    unsigned int           data_size;   /* size of message reply data */
-    struct timeout_user   *timeout;     /* result timeout */
+    struct list            sender_entry;  /* entry in sender list */
+    struct message_result *recv_next;     /* next in receiver list */
+    struct msg_queue      *sender;        /* sender queue */
+    struct msg_queue      *receiver;      /* receiver queue */
+    int                    replied;       /* has it been replied to? */
+    unsigned int           result;        /* reply result */
+    unsigned int           error;         /* error code to pass back to sender */
+    struct message        *callback_msg;  /* message to queue for callback */
+    void                  *data;          /* message reply data */
+    unsigned int           data_size;     /* size of message reply data */
+    struct timeout_user   *timeout;       /* result timeout */
 };
 
 struct message
@@ -110,22 +111,23 @@
 
 struct msg_queue
 {
-    struct object          obj;           /* object header */
-    unsigned int           wake_bits;     /* wakeup bits */
-    unsigned int           wake_mask;     /* wakeup mask */
-    unsigned int           changed_bits;  /* changed wakeup bits */
-    unsigned int           changed_mask;  /* changed wakeup mask */
-    int                    paint_count;   /* pending paint messages count */
+    struct object          obj;             /* object header */
+    unsigned int           wake_bits;       /* wakeup bits */
+    unsigned int           wake_mask;       /* wakeup mask */
+    unsigned int           changed_bits;    /* changed wakeup bits */
+    unsigned int           changed_mask;    /* changed wakeup mask */
+    int                    paint_count;     /* pending paint messages count */
     struct message_list    msg_list[NB_MSG_KINDS];  /* lists of messages */
-    struct message_result *send_result;   /* stack of sent messages waiting for result */
-    struct message_result *recv_result;   /* stack of received messages waiting for result */
-    struct timer          *first_timer;   /* head of timer list */
-    struct timer          *last_timer;    /* tail of timer list */
-    struct timer          *next_timer;    /* next timer to expire */
-    struct timeout_user   *timeout;       /* timeout for next timer to expire */
-    struct thread_input   *input;         /* thread input descriptor */
-    struct hook_table     *hooks;         /* hook table */
-    struct timeval         last_get_msg;  /* time of last get message call */
+    struct list            send_result;     /* stack of sent messages waiting for result */
+    struct list            callback_result; /* list of callback messages waiting for result */
+    struct message_result *recv_result;     /* stack of received messages waiting for result */
+    struct timer          *first_timer;     /* head of timer list */
+    struct timer          *last_timer;      /* tail of timer list */
+    struct timer          *next_timer;      /* next timer to expire */
+    struct timeout_user   *timeout;         /* timeout for next timer to expire */
+    struct thread_input   *input;           /* thread input descriptor */
+    struct hook_table     *hooks;           /* hook table */
+    struct timeval         last_get_msg;    /* time of last get message call */
 };
 
 static void msg_queue_dump( struct object *obj, int verbose );
@@ -214,7 +216,6 @@
         queue->changed_bits    = 0;
         queue->changed_mask    = 0;
         queue->paint_count     = 0;
-        queue->send_result     = NULL;
         queue->recv_result     = NULL;
         queue->first_timer     = NULL;
         queue->last_timer      = NULL;
@@ -223,6 +224,8 @@
         queue->input           = (struct thread_input *)grab_object( input );
         queue->hooks           = NULL;
         gettimeofday( &queue->last_get_msg, NULL );
+        list_init( &queue->send_result );
+        list_init( &queue->callback_result );
         for (i = 0; i < NB_MSG_KINDS; i++)
             queue->msg_list[i].first = queue->msg_list[i].last = NULL;
 
@@ -359,9 +362,20 @@
 {
     if (result->timeout) remove_timeout_user( result->timeout );
     if (result->data) free( result->data );
+    if (result->callback_msg) free( result->callback_msg );
     free( result );
 }
 
+/* remove the result from the sender list it is on */
+static inline void remove_result_from_sender( struct message_result *result )
+{
+    assert( result->sender );
+
+    list_remove( &result->sender_entry );
+    result->sender = NULL;
+    if (!result->receiver) free_result( result );
+}
+
 /* store the message result in the appropriate structure */
 static void store_message_result( struct message_result *res, unsigned int result,
                                   unsigned int error )
@@ -374,9 +388,25 @@
         remove_timeout_user( res->timeout );
         res->timeout = NULL;
     }
-    /* wake sender queue if waiting on this result */
-    if (res->sender && res->sender->send_result == res)
-        set_queue_bits( res->sender, QS_SMRESULT );
+    if (res->sender)
+    {
+        if (res->callback_msg)
+        {
+            /* queue the callback message in the sender queue */
+            res->callback_msg->lparam = result;
+            append_message( &res->sender->msg_list[SEND_MESSAGE], res->callback_msg );
+            set_queue_bits( res->sender, QS_SENDMESSAGE );
+            res->callback_msg = NULL;
+            remove_result_from_sender( res );
+        }
+        else
+        {
+            /* wake sender queue if waiting on this result */
+            if (list_head(&res->sender->send_result) == &res->sender_entry)
+                set_queue_bits( res->sender, QS_SMRESULT );
+        }
+    }
+
 }
 
 /* free a message when deleting a queue or window */
@@ -427,20 +457,49 @@
 /* allocate and fill a message result structure */
 static struct message_result *alloc_message_result( struct msg_queue *send_queue,
                                                     struct msg_queue *recv_queue,
-                                                    unsigned int timeout )
+                                                    struct message *msg, unsigned int timeout,
+                                                    void *callback, unsigned int callback_data )
 {
     struct message_result *result = mem_alloc( sizeof(*result) );
     if (result)
     {
-        /* put the result on the sender result stack */
         result->sender    = send_queue;
         result->receiver  = recv_queue;
         result->replied   = 0;
         result->data      = NULL;
         result->data_size = 0;
         result->timeout   = NULL;
-        result->send_next = send_queue->send_result;
-        send_queue->send_result = result;
+
+        if (msg->type == MSG_CALLBACK)
+        {
+            struct message *callback_msg = mem_alloc( sizeof(*callback_msg) );
+            if (!callback_msg)
+            {
+                free( result );
+                return NULL;
+            }
+            callback_msg->type      = MSG_CALLBACK_RESULT;
+            callback_msg->win       = msg->win;
+            callback_msg->msg       = msg->msg;
+            callback_msg->wparam    = (unsigned int)callback;
+            callback_msg->lparam    = 0;
+            callback_msg->time      = get_tick_count();
+            callback_msg->x         = 0;
+            callback_msg->y         = 0;
+            callback_msg->info      = callback_data;
+            callback_msg->result    = NULL;
+            callback_msg->data      = NULL;
+            callback_msg->data_size = 0;
+
+            result->callback_msg = callback_msg;
+            list_add_head( &send_queue->callback_result, &result->sender_entry );
+        }
+        else
+        {
+            result->callback_msg = NULL;
+            list_add_head( &send_queue->send_result, &result->sender_entry );
+        }
+
         if (timeout != -1)
         {
             struct timeval when;
@@ -577,15 +636,16 @@
 /* cleanup all pending results when deleting a queue */
 static void cleanup_results( struct msg_queue *queue )
 {
-    struct message_result *result, *next;
+    struct list *entry;
 
-    result = queue->send_result;
-    while (result)
+    while ((entry = list_head( &queue->send_result )) != NULL)
     {
-        next = result->send_next;
-        result->sender = NULL;
-        if (!result->receiver) free_result( result );
-        result = next;
+        remove_result_from_sender( LIST_ENTRY( entry, struct message_result, sender_entry ) );
+    }
+
+    while ((entry = list_head( &queue->callback_result )) != NULL)
+    {
+        remove_result_from_sender( LIST_ENTRY( entry, struct message_result, sender_entry ) );
     }
 
     while (queue->recv_result)
@@ -1309,9 +1369,10 @@
         case MSG_ASCII:
         case MSG_UNICODE:
         case MSG_CALLBACK:
-            if (!(msg->result = alloc_message_result( send_queue, recv_queue, req->timeout )))
+            if (!(msg->result = alloc_message_result( send_queue, recv_queue, msg,
+                                                      req->timeout, req->callback, req->info )))
             {
-                free( msg );
+                free_message( msg );
                 break;
             }
             /* fall through */
@@ -1333,6 +1394,7 @@
         case MSG_HARDWARE:
             queue_hardware_message( recv_queue, msg );
             break;
+        case MSG_CALLBACK_RESULT:  /* cannot send this one */
         default:
             set_error( STATUS_INVALID_PARAMETER );
             free( msg );
@@ -1442,16 +1504,19 @@
 /* retrieve the reply for the last message sent */
 DECL_HANDLER(get_message_reply)
 {
+    struct message_result *result;
+    struct list *entry;
     struct msg_queue *queue = current->queue;
 
     if (queue)
     {
-        struct message_result *result = queue->send_result;
-
         set_error( STATUS_PENDING );
         reply->result = 0;
 
-        if (result && (result->replied || req->cancel))
+        if (!(entry = list_head( &queue->send_result ))) return;  /* no reply ready */
+
+        result = LIST_ENTRY( entry, struct message_result, sender_entry );
+        if (result->replied || req->cancel)
         {
             if (result->replied)
             {
@@ -1465,11 +1530,15 @@
                     result->data_size = 0;
                 }
             }
-            queue->send_result = result->send_next;
-            result->sender = NULL;
-            if (!result->receiver) free_result( result );
-            if (!queue->send_result || !queue->send_result->replied)
-                clear_queue_bits( queue, QS_SMRESULT );
+            remove_result_from_sender( result );
+
+            entry = list_head( &queue->send_result );
+            if (!entry) clear_queue_bits( queue, QS_SMRESULT );
+            else
+            {
+                result = LIST_ENTRY( entry, struct message_result, sender_entry );
+                if (!result->replied) clear_queue_bits( queue, QS_SMRESULT );
+            }
         }
     }
     else set_error( STATUS_ACCESS_DENIED );
diff --git a/server/trace.c b/server/trace.c
index d9f6833..22b542a 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -1844,6 +1844,7 @@
     fprintf( stderr, " time=%08x,", req->time );
     fprintf( stderr, " info=%08x,", req->info );
     fprintf( stderr, " timeout=%d,", req->timeout );
+    fprintf( stderr, " callback=%p,", req->callback );
     fprintf( stderr, " data=" );
     dump_varargs_bytes( cur_size );
 }
diff --git a/windows/input.c b/windows/input.c
index 3326129..2cca587 100644
--- a/windows/input.c
+++ b/windows/input.c
@@ -112,17 +112,19 @@
 {
     SERVER_START_REQ( send_message )
     {
-        req->id     = GetCurrentThreadId();
-        req->type   = MSG_HARDWARE;
-        req->win    = hwnd;
-        req->msg    = message;
-        req->wparam = wParam;
-        req->lparam = lParam;
-        req->x      = xPos;
-        req->y      = yPos;
-        req->time   = time;
-        req->info   = extraInfo;
-        req->timeout = 0;
+        req->id       = GetCurrentThreadId();
+        req->type     = MSG_HARDWARE;
+        req->flags    = 0;
+        req->win      = hwnd;
+        req->msg      = message;
+        req->wparam   = wParam;
+        req->lparam   = lParam;
+        req->x        = xPos;
+        req->y        = yPos;
+        req->time     = time;
+        req->info     = extraInfo;
+        req->timeout  = -1;
+        req->callback = NULL;
         wine_server_call( req );
     }
     SERVER_END_REQ;