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;