Implemented interprocess DDE message posting.
diff --git a/dlls/user/message.c b/dlls/user/message.c
index c22285e..98b425c 100644
--- a/dlls/user/message.c
+++ b/dlls/user/message.c
@@ -512,7 +512,6 @@
case WM_MDIGETACTIVE:
if (lparam) return sizeof(BOOL);
return 0;
-
case WM_WINE_SETWINDOWPOS:
push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
return 0;
@@ -537,13 +536,6 @@
/* these contain an HGLOBAL */
case WM_PAINTCLIPBOARD:
case WM_SIZECLIPBOARD:
- case WM_DDE_INITIATE:
- case WM_DDE_ADVISE:
- case WM_DDE_UNADVISE:
- case WM_DDE_DATA:
- case WM_DDE_REQUEST:
- case WM_DDE_POKE:
- case WM_DDE_EXECUTE:
/* these contain pointers */
case WM_DROPOBJECT:
case WM_QUERYDROPOBJECT:
@@ -763,7 +755,6 @@
if (!*lparam) return TRUE;
if (!get_buffer_space( buffer, sizeof(BOOL) )) return FALSE;
break;
-
/* these contain an HFONT */
case WM_SETFONT:
case WM_GETFONT:
@@ -784,13 +775,6 @@
/* these contain an HGLOBAL */
case WM_PAINTCLIPBOARD:
case WM_SIZECLIPBOARD:
- case WM_DDE_INITIATE:
- case WM_DDE_ADVISE:
- case WM_DDE_UNADVISE:
- case WM_DDE_DATA:
- case WM_DDE_REQUEST:
- case WM_DDE_POKE:
- case WM_DDE_EXECUTE:
/* these contain pointers */
case WM_DROPOBJECT:
case WM_QUERYDROPOBJECT:
@@ -944,7 +928,7 @@
memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
break;
case EM_GETLINE:
- size = min( size, *(WORD *)lparam );
+ size = min( size, (size_t)*(WORD *)lparam );
memcpy( (WCHAR *)lparam, buffer, size );
break;
case LB_GETSELITEMS:
@@ -1065,6 +1049,271 @@
}
}
+/* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
+ * to the memory handle, we keep track (in the server side) of all pairs of handle
+ * used (the client passes its value and the content of the memory handle), and
+ * the server stored both values (the client, and the local one, created after the
+ * content). When a ACK message is generated, the list of pair is searched for a
+ * matching pair, so that the client memory handle can be returned.
+ */
+struct DDE_pair {
+ HGLOBAL client_hMem;
+ HGLOBAL server_hMem;
+};
+
+static struct DDE_pair* dde_pairs;
+static int dde_num_alloc;
+static int dde_num_used;
+static CRITICAL_SECTION dde_crst = CRITICAL_SECTION_INIT("Raw_DDE_CritSect");
+
+static BOOL dde_add_pair(HGLOBAL chm, HGLOBAL shm)
+{
+ int i;
+#define GROWBY 4
+
+ EnterCriticalSection(&dde_crst);
+
+ /* now remember the pair of hMem on both sides */
+ if (dde_num_used == dde_num_alloc)
+ {
+ struct DDE_pair* tmp = HeapReAlloc( GetProcessHeap(), 0, dde_pairs,
+ (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
+ if (!tmp)
+ {
+ LeaveCriticalSection(&dde_crst);
+ return FALSE;
+ }
+ dde_pairs = tmp;
+ /* zero out newly allocated part */
+ memset(&dde_pairs[dde_num_alloc], 0, GROWBY * sizeof(struct DDE_pair));
+ dde_num_alloc += GROWBY;
+ }
+#undef GROWBY
+ for (i = 0; i < dde_num_alloc; i++)
+ {
+ if (dde_pairs[i].server_hMem == 0)
+ {
+ dde_pairs[i].client_hMem = chm;
+ dde_pairs[i].server_hMem = shm;
+ dde_num_used++;
+ break;
+ }
+ }
+ LeaveCriticalSection(&dde_crst);
+ return TRUE;
+}
+
+static HGLOBAL dde_get_pair(HGLOBAL shm)
+{
+ int i;
+ HGLOBAL ret = 0;
+
+ EnterCriticalSection(&dde_crst);
+ for (i = 0; i < dde_num_alloc; i++)
+ {
+ if (dde_pairs[i].server_hMem == shm)
+ {
+ /* free this pair */
+ dde_pairs[i].server_hMem = 0;
+ dde_num_used--;
+ ret = dde_pairs[i].client_hMem;
+ break;
+ }
+ }
+ LeaveCriticalSection(&dde_crst);
+ return ret;
+}
+
+/***********************************************************************
+ * post_dde_message
+ *
+ * Post a DDE messag
+ */
+static BOOL post_dde_message( DWORD dest_tid, struct packed_message *data, const struct send_message_info *info )
+{
+ void* ptr = NULL;
+ int size = 0;
+ UINT uiLo, uiHi;
+ LPARAM lp = 0;
+ HGLOBAL hunlock = 0;
+ int i;
+ DWORD res;
+
+ if (!UnpackDDElParam( info->msg, info->lparam, &uiLo, &uiHi ))
+ return FALSE;
+
+ lp = info->lparam;
+ switch (info->msg)
+ {
+ /* DDE messages which don't require packing are:
+ * WM_DDE_INITIATE
+ * WM_DDE_TERMINATE
+ * WM_DDE_REQUEST
+ * WM_DDE_UNADVISE
+ */
+ case WM_DDE_ACK:
+ if (HIWORD(uiHi))
+ {
+ /* uiHi should contain a hMem from WM_DDE_EXECUTE */
+ HGLOBAL h = dde_get_pair( uiHi );
+ if (h)
+ {
+ /* send back the value of h on the other side */
+ push_data( data, &h, sizeof(HGLOBAL) );
+ lp = uiLo;
+ TRACE( "send dde-ack %x %08x => %08lx\n", uiLo, uiHi, (DWORD)h );
+ }
+ }
+ else
+ {
+ /* uiHi should contain either an atom or 0 */
+ TRACE( "send dde-ack %x atom=%x\n", uiLo, uiHi );
+ lp = MAKELONG( uiLo, uiHi );
+ }
+ break;
+ case WM_DDE_ADVISE:
+ case WM_DDE_DATA:
+ case WM_DDE_POKE:
+ size = 0;
+ if (uiLo)
+ {
+ size = GlobalSize( (HGLOBAL)uiLo ) ;
+ if ((info->msg == WM_DDE_ADVISE && size < sizeof(DDEADVISE)) ||
+ (info->msg == WM_DDE_DATA && size < sizeof(DDEDATA)) ||
+ (info->msg == WM_DDE_POKE && size < sizeof(DDEPOKE))
+ )
+ return FALSE;
+ }
+ else if (info->msg != WM_DDE_DATA) return FALSE;
+
+ lp = uiHi;
+ if (uiLo)
+ {
+ if ((ptr = GlobalLock( (HGLOBAL)uiLo) ))
+ {
+ push_data( data, ptr, size );
+ hunlock = (HGLOBAL)uiLo;
+ }
+ }
+ TRACE( "send ddepack %u %x\n", size, uiHi );
+ break;
+ case WM_DDE_EXECUTE:
+ if (info->lparam)
+ {
+ if ((ptr = GlobalLock( (HGLOBAL)info->lparam) ))
+ {
+ push_data(data, ptr, GlobalSize( (HGLOBAL)info->lparam ));
+ /* so that the other side can send it back on ACK */
+ lp = info->lparam;
+ hunlock = (HGLOBAL)info->lparam;
+ }
+ }
+ break;
+ }
+ SERVER_START_REQ( send_message )
+ {
+ req->id = (void *)dest_tid;
+ req->type = info->type;
+ req->win = info->hwnd;
+ req->msg = info->msg;
+ req->wparam = info->wparam;
+ req->lparam = lp;
+ req->time = GetCurrentTime();
+ req->timeout = -1;
+ for (i = 0; i < data->count; i++)
+ wine_server_add_data( req, data->data[i], data->size[i] );
+ if ((res = wine_server_call( req )))
+ {
+ if (res == STATUS_INVALID_PARAMETER)
+ /* FIXME: find a STATUS_ value for this one */
+ SetLastError( ERROR_INVALID_THREAD_ID );
+ else
+ SetLastError( RtlNtStatusToDosError(res) );
+ }
+ else
+ FreeDDElParam(info->msg, info->lparam);
+ }
+ SERVER_END_REQ;
+ if (hunlock) GlobalUnlock(hunlock);
+
+ return !res;
+}
+
+/***********************************************************************
+ * unpack_dde_message
+ *
+ * Unpack a posted DDE message received from another process.
+ */
+static BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
+ void **buffer, size_t size )
+{
+ UINT uiLo, uiHi;
+ HGLOBAL hMem = 0;
+ void* ptr;
+
+ switch (message)
+ {
+ case WM_DDE_ACK:
+ if (size)
+ {
+ /* hMem is being passed */
+ if (size != sizeof(HGLOBAL)) return FALSE;
+ if (!buffer || !*buffer) return FALSE;
+ uiLo = *lparam;
+ memcpy( &hMem, *buffer, size );
+ uiHi = hMem;
+ TRACE("recv dde-ack %u mem=%x[%lx]\n", uiLo, uiHi, GlobalSize( uiHi ));
+ }
+ else
+ {
+ uiLo = LOWORD( *lparam );
+ uiHi = HIWORD( *lparam );
+ TRACE("recv dde-ack %x atom=%x\n", uiLo, uiHi);
+ }
+ *lparam = PackDDElParam( WM_DDE_ACK, uiLo, uiHi );
+ break;
+ case WM_DDE_ADVISE:
+ case WM_DDE_DATA:
+ case WM_DDE_POKE:
+ if ((!buffer || !*buffer) && message != WM_DDE_DATA) return FALSE;
+ uiHi = *lparam;
+ TRACE( "recv ddepack %u %x\n", size, uiHi );
+ if (size)
+ {
+ hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size );
+ if (hMem && (ptr = GlobalLock( hMem )))
+ {
+ memcpy( ptr, *buffer, size );
+ GlobalUnlock( hMem );
+ }
+ else return FALSE;
+ }
+ uiLo = hMem;
+
+ *lparam = PackDDElParam( message, uiLo, uiHi );
+ break;
+ case WM_DDE_EXECUTE:
+ if (size)
+ {
+ if (!buffer || !*buffer) return FALSE;
+ hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size );
+ if (hMem && (ptr = GlobalLock( hMem )))
+ {
+ memcpy( ptr, *buffer, size );
+ GlobalUnlock( hMem );
+ TRACE( "exec: pairing c=%08lx s=%08lx\n", *lparam, (DWORD)hMem );
+ if (!dde_add_pair( *lparam, hMem ))
+ {
+ GlobalFree( hMem );
+ return FALSE;
+ }
+ }
+ } else return FALSE;
+ *lparam = hMem;
+ break;
+ }
+ return TRUE;
+}
/***********************************************************************
* call_window_proc
@@ -1222,6 +1471,18 @@
/* fall through */
case MSG_POSTED:
queue->GetMessageExtraInfoVal = extra_info;
+ if (info.msg.message >= WM_DDE_FIRST && info.msg.message <= WM_DDE_LAST)
+ {
+ if (!unpack_dde_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
+ &info.msg.lParam, &buffer, size ))
+ {
+ ERR( "invalid packed dde-message %x (%s) hwnd %x wp %x lp %lx size %d\n",
+ info.msg.message, SPY_GetMsgName(info.msg.message, info.msg.hwnd),
+ info.msg.hwnd, info.msg.wParam, info.msg.lParam, size );
+ /* ignore it */
+ continue;
+ }
+ }
*msg = info.msg;
if (buffer) HeapFree( GetProcessHeap(), 0, buffer );
return TRUE;
@@ -1321,6 +1582,10 @@
return FALSE;
}
}
+ else if (info->type == MSG_POSTED && info->msg >= WM_DDE_FIRST && info->msg <= WM_DDE_LAST)
+ {
+ return post_dde_message( dest_tid, &data, info );
+ }
SERVER_START_REQ( send_message )
{
diff --git a/server/queue.c b/server/queue.c
index a639ebe..150a213 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -749,7 +749,7 @@
switch(msg->type)
{
- case MSG_OTHER_PROCESS:
+ case MSG_OTHER_PROCESS:
msg->data_size = get_req_data_size();
if (msg->data_size && !(msg->data = memdup( get_req_data(), msg->data_size )))
{
@@ -771,6 +771,13 @@
set_queue_bits( recv_queue, QS_SENDMESSAGE );
break;
case MSG_POSTED:
+ /* needed for posted DDE messages */
+ msg->data_size = get_req_data_size();
+ if (msg->data_size && !(msg->data = memdup( get_req_data(), msg->data_size )))
+ {
+ free( msg );
+ break;
+ }
append_message( &recv_queue->msg_list[POST_MESSAGE], msg );
set_queue_bits( recv_queue, QS_POSTMESSAGE );
break;
@@ -803,8 +810,12 @@
struct get_message_reply *reply,
struct message *msg, enum message_kind kind )
{
- assert( !msg->data_size ); /* posted messages can't have data */
-
+ reply->total = msg->data_size;
+ if (msg->data_size > get_reply_max_size())
+ {
+ set_error( STATUS_BUFFER_OVERFLOW );
+ return;
+ }
reply->type = msg->type;
reply->win = msg->win;
reply->msg = msg->msg;
@@ -814,16 +825,22 @@
reply->y = msg->y;
reply->time = msg->time;
reply->info = msg->info;
- reply->total = 0;
/* raw messages always get removed */
if ((msg->type == MSG_HARDWARE_RAW) || (flags & GET_MSG_REMOVE))
{
queue->last_msg = NULL;
+ if (msg->data)
+ {
+ set_reply_data_ptr( msg->data, msg->data_size );
+ msg->data = NULL;
+ msg->data_size = 0;
+ }
remove_queue_message( queue, msg, kind );
}
else /* remember it as the last returned message */
{
+ if (msg->data) set_reply_data( msg->data, msg->data_size );
queue->last_msg = msg;
queue->last_msg_kind = kind;
}