diff --git a/dlls/user/message.c b/dlls/user/message.c index c22285e2d30..98b425c6d35 100644 --- a/dlls/user/message.c +++ b/dlls/user/message.c @@ -512,7 +512,6 @@ static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lpara 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 @@ static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lpara /* 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 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa 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 @@ static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lpa /* 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 @@ static void unpack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam, 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 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR } } +/* 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 @@ BOOL MSG_peek_message( MSG *msg, HWND hwnd, UINT first, UINT last, int flags ) /* 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 @@ static BOOL put_message_in_queue( DWORD dest_tid, const struct send_message_info 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 a639ebe2113..150a213defb 100644 --- a/server/queue.c +++ b/server/queue.c @@ -749,7 +749,7 @@ DECL_HANDLER(send_message) 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 @@ DECL_HANDLER(send_message) 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 @@ static void return_message_to_app( struct msg_queue *queue, int flags, 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 @@ static void return_message_to_app( struct msg_queue *queue, int flags, 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; }