win32u: Use platform-independent layout for ntuser_thread_info.
[wine.git] / dlls / user32 / message.c
blob2cbdd761fe97cfb8bc0d432bbe43ab8c437abe92
1 /*
2 * Window messaging support
4 * Copyright 2001 Alexandre Julliard
5 * Copyright 2008 Maarten Lankhorst
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <assert.h>
23 #include <stdarg.h>
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27 #include "ntstatus.h"
28 #define WIN32_NO_STATUS
29 #include "windef.h"
30 #include "winbase.h"
31 #include "wingdi.h"
32 #include "winuser.h"
33 #include "winerror.h"
34 #include "winnls.h"
35 #include "dbt.h"
36 #include "dde.h"
37 #include "imm.h"
38 #include "hidusage.h"
39 #include "ddk/imm.h"
40 #include "wine/server.h"
41 #include "user_private.h"
42 #include "win.h"
43 #include "controls.h"
44 #include "wine/debug.h"
45 #include "wine/exception.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(msg);
50 /* pack a pointer into a 32/64 portable format */
51 static inline ULONGLONG pack_ptr( const void *ptr )
53 return (ULONG_PTR)ptr;
56 /* unpack a potentially 64-bit pointer, returning 0 when truncated */
57 static inline void *unpack_ptr( ULONGLONG ptr64 )
59 if ((ULONG_PTR)ptr64 != ptr64) return 0;
60 return (void *)(ULONG_PTR)ptr64;
63 static struct wm_char_mapping_data *get_wmchar_data(void)
65 return (struct wm_char_mapping_data *)(UINT_PTR)NtUserGetThreadInfo()->wmchar_data;
68 /* check for pending WM_CHAR message with DBCS trailing byte */
69 static inline BOOL get_pending_wmchar( MSG *msg, UINT first, UINT last, BOOL remove )
71 struct wm_char_mapping_data *data = get_wmchar_data();
73 if (!data || !data->get_msg.message) return FALSE;
74 if ((first || last) && (first > WM_CHAR || last < WM_CHAR)) return FALSE;
75 if (!msg) return FALSE;
76 *msg = data->get_msg;
77 if (remove) data->get_msg.message = 0;
78 return TRUE;
82 /***********************************************************************
83 * MessageWndProc
85 * Window procedure for "Message" windows (HWND_MESSAGE parent).
87 LRESULT WINAPI MessageWndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
89 if (message == WM_NCCREATE) return TRUE;
90 return 0; /* all other messages are ignored */
94 DWORD get_input_codepage( void )
96 DWORD cp;
97 int ret;
98 HKL hkl = NtUserGetKeyboardLayout( 0 );
100 ret = GetLocaleInfoW( LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
101 (WCHAR *)&cp, sizeof(cp) / sizeof(WCHAR) );
102 if (!ret) cp = CP_ACP;
103 return cp;
106 /***********************************************************************
107 * map_wparam_AtoW
109 * Convert the wparam of an ASCII message to Unicode.
111 BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping )
113 char ch[2];
114 WCHAR wch[2];
115 DWORD cp;
117 wch[0] = wch[1] = 0;
118 switch(message)
120 case WM_CHAR:
121 /* WM_CHAR is magic: a DBCS char can be sent/posted as two consecutive WM_CHAR
122 * messages, in which case the first char is stored, and the conversion
123 * to Unicode only takes place once the second char is sent/posted.
125 if (mapping != WMCHAR_MAP_NOMAPPING)
127 struct wm_char_mapping_data *data = get_wmchar_data();
128 BYTE low = LOBYTE(*wparam);
129 cp = get_input_codepage();
131 if (HIBYTE(*wparam))
133 ch[0] = low;
134 ch[1] = HIBYTE(*wparam);
135 MultiByteToWideChar( cp, 0, ch, 2, wch, 2 );
136 TRACE( "map %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
137 if (data) data->lead_byte[mapping] = 0;
139 else if (data && data->lead_byte[mapping])
141 ch[0] = data->lead_byte[mapping];
142 ch[1] = low;
143 MultiByteToWideChar( cp, 0, ch, 2, wch, 2 );
144 TRACE( "map stored %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
145 data->lead_byte[mapping] = 0;
147 else if (!IsDBCSLeadByte( low ))
149 ch[0] = low;
150 MultiByteToWideChar( cp, 0, ch, 1, wch, 2 );
151 TRACE( "map %02x -> %04x\n", (BYTE)ch[0], wch[0] );
152 if (data) data->lead_byte[mapping] = 0;
154 else /* store it and wait for trail byte */
156 if (!data)
158 if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )))
159 return FALSE;
160 NtUserGetThreadInfo()->wmchar_data = (UINT_PTR)data;
162 TRACE( "storing lead byte %02x mapping %u\n", low, mapping );
163 data->lead_byte[mapping] = low;
164 return FALSE;
166 *wparam = MAKEWPARAM(wch[0], wch[1]);
167 break;
169 /* else fall through */
170 case WM_CHARTOITEM:
171 case EM_SETPASSWORDCHAR:
172 case WM_DEADCHAR:
173 case WM_SYSCHAR:
174 case WM_SYSDEADCHAR:
175 case WM_MENUCHAR:
176 cp = get_input_codepage();
177 ch[0] = LOBYTE(*wparam);
178 ch[1] = HIBYTE(*wparam);
179 MultiByteToWideChar( cp, 0, ch, 2, wch, 2 );
180 *wparam = MAKEWPARAM(wch[0], wch[1]);
181 break;
182 case WM_IME_CHAR:
183 cp = get_input_codepage();
184 ch[0] = HIBYTE(*wparam);
185 ch[1] = LOBYTE(*wparam);
186 if (ch[0]) MultiByteToWideChar( cp, 0, ch, 2, wch, 2 );
187 else MultiByteToWideChar( cp, 0, ch + 1, 1, wch, 1 );
188 *wparam = MAKEWPARAM(wch[0], HIWORD(*wparam));
189 break;
191 return TRUE;
195 /***********************************************************************
196 * map_wparam_WtoA
198 * Convert the wparam of a Unicode message to ASCII.
200 static void map_wparam_WtoA( MSG *msg, BOOL remove )
202 BYTE ch[4];
203 WCHAR wch[2];
204 DWORD len;
205 DWORD cp;
207 switch(msg->message)
209 case WM_CHAR:
210 if (!HIWORD(msg->wParam))
212 cp = get_input_codepage();
213 wch[0] = LOWORD(msg->wParam);
214 ch[0] = ch[1] = 0;
215 len = WideCharToMultiByte( cp, 0, wch, 1, (LPSTR)ch, 2, NULL, NULL );
216 if (len == 2) /* DBCS char */
218 struct wm_char_mapping_data *data = get_wmchar_data();
219 if (!data)
221 if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) return;
222 NtUserGetThreadInfo()->wmchar_data = (UINT_PTR)data;
224 if (remove)
226 data->get_msg = *msg;
227 data->get_msg.wParam = ch[1];
229 msg->wParam = ch[0];
230 return;
233 /* else fall through */
234 case WM_CHARTOITEM:
235 case EM_SETPASSWORDCHAR:
236 case WM_DEADCHAR:
237 case WM_SYSCHAR:
238 case WM_SYSDEADCHAR:
239 case WM_MENUCHAR:
240 cp = get_input_codepage();
241 wch[0] = LOWORD(msg->wParam);
242 wch[1] = HIWORD(msg->wParam);
243 ch[0] = ch[1] = 0;
244 WideCharToMultiByte( cp, 0, wch, 2, (LPSTR)ch, 4, NULL, NULL );
245 msg->wParam = MAKEWPARAM( ch[0] | (ch[1] << 8), 0 );
246 break;
247 case WM_IME_CHAR:
248 cp = get_input_codepage();
249 wch[0] = LOWORD(msg->wParam);
250 ch[0] = ch[1] = 0;
251 len = WideCharToMultiByte( cp, 0, wch, 1, (LPSTR)ch, 2, NULL, NULL );
252 if (len == 2)
253 msg->wParam = MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(msg->wParam) );
254 else
255 msg->wParam = MAKEWPARAM( ch[0], HIWORD(msg->wParam) );
256 break;
261 /* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
262 * to the memory handle, we keep track (in the server side) of all pairs of handle
263 * used (the client passes its value and the content of the memory handle), and
264 * the server stored both values (the client, and the local one, created after the
265 * content). When a ACK message is generated, the list of pair is searched for a
266 * matching pair, so that the client memory handle can be returned.
268 struct DDE_pair {
269 HGLOBAL client_hMem;
270 HGLOBAL server_hMem;
273 static struct DDE_pair* dde_pairs;
274 static int dde_num_alloc;
275 static int dde_num_used;
277 static CRITICAL_SECTION dde_crst;
278 static CRITICAL_SECTION_DEBUG critsect_debug =
280 0, 0, &dde_crst,
281 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
282 0, 0, { (DWORD_PTR)(__FILE__ ": dde_crst") }
284 static CRITICAL_SECTION dde_crst = { &critsect_debug, -1, 0, 0, 0, 0 };
286 static BOOL dde_add_pair(HGLOBAL chm, HGLOBAL shm)
288 int i;
289 #define GROWBY 4
291 EnterCriticalSection(&dde_crst);
293 /* now remember the pair of hMem on both sides */
294 if (dde_num_used == dde_num_alloc)
296 struct DDE_pair* tmp;
297 if (dde_pairs)
298 tmp = HeapReAlloc( GetProcessHeap(), 0, dde_pairs,
299 (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
300 else
301 tmp = HeapAlloc( GetProcessHeap(), 0,
302 (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
304 if (!tmp)
306 LeaveCriticalSection(&dde_crst);
307 return FALSE;
309 dde_pairs = tmp;
310 /* zero out newly allocated part */
311 memset(&dde_pairs[dde_num_alloc], 0, GROWBY * sizeof(struct DDE_pair));
312 dde_num_alloc += GROWBY;
314 #undef GROWBY
315 for (i = 0; i < dde_num_alloc; i++)
317 if (dde_pairs[i].server_hMem == 0)
319 dde_pairs[i].client_hMem = chm;
320 dde_pairs[i].server_hMem = shm;
321 dde_num_used++;
322 break;
325 LeaveCriticalSection(&dde_crst);
326 return TRUE;
329 static HGLOBAL dde_get_pair(HGLOBAL shm)
331 int i;
332 HGLOBAL ret = 0;
334 EnterCriticalSection(&dde_crst);
335 for (i = 0; i < dde_num_alloc; i++)
337 if (dde_pairs[i].server_hMem == shm)
339 /* free this pair */
340 dde_pairs[i].server_hMem = 0;
341 dde_num_used--;
342 ret = dde_pairs[i].client_hMem;
343 break;
346 LeaveCriticalSection(&dde_crst);
347 return ret;
350 /***********************************************************************
351 * post_dde_message
353 * Post a DDE message
355 BOOL post_dde_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, DWORD dest_tid, DWORD type )
357 void* ptr = NULL;
358 int size = 0;
359 UINT_PTR uiLo, uiHi;
360 LPARAM lp;
361 HGLOBAL hunlock = 0;
362 DWORD res;
363 ULONGLONG hpack;
365 if (!UnpackDDElParam( msg, lparam, &uiLo, &uiHi ))
366 return FALSE;
368 lp = lparam;
369 switch (msg)
371 /* DDE messages which don't require packing are:
372 * WM_DDE_INITIATE
373 * WM_DDE_TERMINATE
374 * WM_DDE_REQUEST
375 * WM_DDE_UNADVISE
377 case WM_DDE_ACK:
378 if (HIWORD(uiHi))
380 /* uiHi should contain a hMem from WM_DDE_EXECUTE */
381 HGLOBAL h = dde_get_pair( (HANDLE)uiHi );
382 if (h)
384 hpack = pack_ptr( h );
385 /* send back the value of h on the other side */
386 ptr = &hpack;
387 size = sizeof(hpack);
388 lp = uiLo;
389 TRACE( "send dde-ack %Ix %08Ix => %p\n", uiLo, uiHi, h );
392 else
394 /* uiHi should contain either an atom or 0 */
395 TRACE( "send dde-ack %Ix atom=%Ix\n", uiLo, uiHi );
396 lp = MAKELONG( uiLo, uiHi );
398 break;
399 case WM_DDE_ADVISE:
400 case WM_DDE_DATA:
401 case WM_DDE_POKE:
402 if (uiLo)
404 size = GlobalSize( (HGLOBAL)uiLo ) ;
405 if ((msg == WM_DDE_ADVISE && size < sizeof(DDEADVISE)) ||
406 (msg == WM_DDE_DATA && size < FIELD_OFFSET(DDEDATA, Value)) ||
407 (msg == WM_DDE_POKE && size < FIELD_OFFSET(DDEPOKE, Value)))
408 return FALSE;
410 else if (msg != WM_DDE_DATA) return FALSE;
412 lp = uiHi;
413 if (uiLo)
415 if ((ptr = GlobalLock( (HGLOBAL)uiLo) ))
417 DDEDATA *dde_data = ptr;
418 TRACE("unused %d, fResponse %d, fRelease %d, fDeferUpd %d, fAckReq %d, cfFormat %d\n",
419 dde_data->unused, dde_data->fResponse, dde_data->fRelease,
420 dde_data->reserved, dde_data->fAckReq, dde_data->cfFormat);
421 hunlock = (HGLOBAL)uiLo;
424 TRACE( "send ddepack %u %Ix\n", size, uiHi );
425 break;
426 case WM_DDE_EXECUTE:
427 if (lparam)
429 if ((ptr = GlobalLock( (HGLOBAL)lparam) ))
431 size = GlobalSize( (HGLOBAL)lparam );
432 /* so that the other side can send it back on ACK */
433 lp = lparam;
434 hunlock = (HGLOBAL)lparam;
437 break;
439 SERVER_START_REQ( send_message )
441 req->id = dest_tid;
442 req->type = type;
443 req->flags = 0;
444 req->win = wine_server_user_handle( hwnd );
445 req->msg = msg;
446 req->wparam = wparam;
447 req->lparam = lp;
448 req->timeout = TIMEOUT_INFINITE;
449 if (size) wine_server_add_data( req, ptr, size );
450 if ((res = wine_server_call( req )))
452 if (res == STATUS_INVALID_PARAMETER)
453 /* FIXME: find a STATUS_ value for this one */
454 SetLastError( ERROR_INVALID_THREAD_ID );
455 else
456 SetLastError( RtlNtStatusToDosError(res) );
458 else
459 FreeDDElParam( msg, lparam );
461 SERVER_END_REQ;
462 if (hunlock) GlobalUnlock(hunlock);
464 return !res;
467 /***********************************************************************
468 * unpack_dde_message
470 * Unpack a posted DDE message received from another process.
472 BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
473 const void *buffer, size_t size )
475 UINT_PTR uiLo, uiHi;
476 HGLOBAL hMem = 0;
477 void* ptr;
479 switch (message)
481 case WM_DDE_ACK:
482 if (size)
484 ULONGLONG hpack;
485 /* hMem is being passed */
486 if (size != sizeof(hpack)) return FALSE;
487 uiLo = *lparam;
488 memcpy( &hpack, buffer, size );
489 hMem = unpack_ptr( hpack );
490 uiHi = (UINT_PTR)hMem;
491 TRACE("recv dde-ack %Ix mem=%Ix[%Ix]\n", uiLo, uiHi, GlobalSize( hMem ));
493 else
495 uiLo = LOWORD( *lparam );
496 uiHi = HIWORD( *lparam );
497 TRACE("recv dde-ack %Ix atom=%Ix\n", uiLo, uiHi);
499 *lparam = PackDDElParam( WM_DDE_ACK, uiLo, uiHi );
500 break;
501 case WM_DDE_ADVISE:
502 case WM_DDE_DATA:
503 case WM_DDE_POKE:
504 if (!size && message != WM_DDE_DATA) return FALSE;
505 uiHi = *lparam;
506 if (size)
508 if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size )))
509 return FALSE;
510 if ((ptr = GlobalLock( hMem )))
512 memcpy( ptr, buffer, size );
513 GlobalUnlock( hMem );
515 else
517 GlobalFree( hMem );
518 return FALSE;
521 uiLo = (UINT_PTR)hMem;
523 *lparam = PackDDElParam( message, uiLo, uiHi );
524 break;
525 case WM_DDE_EXECUTE:
526 if (size)
528 if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size ))) return FALSE;
529 if ((ptr = GlobalLock( hMem )))
531 memcpy( ptr, buffer, size );
532 GlobalUnlock( hMem );
533 TRACE( "exec: pairing c=%08Ix s=%p\n", *lparam, hMem );
534 if (!dde_add_pair( (HGLOBAL)*lparam, hMem ))
536 GlobalFree( hMem );
537 return FALSE;
540 else
542 GlobalFree( hMem );
543 return FALSE;
545 } else return FALSE;
546 *lparam = (LPARAM)hMem;
547 break;
549 return TRUE;
552 /***********************************************************************
553 * SendMessageTimeoutW (USER32.@)
555 LRESULT WINAPI SendMessageTimeoutW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
556 UINT flags, UINT timeout, PDWORD_PTR res_ptr )
558 struct send_message_timeout_params params = { .flags = flags, .timeout = timeout };
559 LRESULT res;
561 res = NtUserMessageCall( hwnd, msg, wparam, lparam, &params, NtUserSendMessageTimeout, FALSE );
562 if (res_ptr) *res_ptr = res;
563 return params.result;
566 /***********************************************************************
567 * SendMessageTimeoutA (USER32.@)
569 LRESULT WINAPI SendMessageTimeoutA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
570 UINT flags, UINT timeout, PDWORD_PTR res_ptr )
572 struct send_message_timeout_params params = { .flags = flags, .timeout = timeout };
573 LRESULT res = 0;
575 if (msg != WM_CHAR || WIN_IsCurrentThread( hwnd ))
577 res = NtUserMessageCall( hwnd, msg, wparam, lparam, &params,
578 NtUserSendMessageTimeout, TRUE );
580 else if (map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_SENDMESSAGE ))
582 res = NtUserMessageCall( hwnd, msg, wparam, lparam, &params,
583 NtUserSendMessageTimeout, FALSE );
586 if (res_ptr) *res_ptr = res;
587 return params.result;
591 static LRESULT dispatch_send_message( struct win_proc_params *params )
593 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
594 INPUT_MESSAGE_SOURCE prev_source = thread_info->msg_source;
595 LRESULT retval = 0;
597 static const INPUT_MESSAGE_SOURCE msg_source_unavailable = { IMDT_UNAVAILABLE, IMO_UNAVAILABLE };
599 thread_info->recursion_count++;
601 params->result = &retval;
602 thread_info->msg_source = msg_source_unavailable;
603 SPY_EnterMessage( SPY_SENDMESSAGE, params->hwnd, params->msg, params->wparam, params->lparam );
605 dispatch_win_proc_params( params );
607 SPY_ExitMessage( SPY_RESULT_OK, params->hwnd, params->msg, retval, params->wparam, params->lparam );
608 thread_info->msg_source = prev_source;
609 thread_info->recursion_count--;
610 return retval;
614 /***********************************************************************
615 * SendMessageW (USER32.@)
617 LRESULT WINAPI SendMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
619 struct win_proc_params params;
620 LRESULT retval;
622 params.hwnd = 0;
623 retval = NtUserMessageCall( hwnd, msg, wparam, lparam, &params, NtUserSendMessage, FALSE );
624 if (params.hwnd) retval = dispatch_send_message( &params );
625 return retval;
629 /***********************************************************************
630 * SendMessageA (USER32.@)
632 LRESULT WINAPI SendMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
634 struct win_proc_params params;
635 LRESULT retval;
637 if (msg == WM_CHAR && !WIN_IsCurrentThread( hwnd ))
639 if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_SENDMESSAGE ))
640 return 0;
641 return NtUserMessageCall( hwnd, msg, wparam, lparam, NULL, NtUserSendMessage, FALSE );
644 params.hwnd = 0;
645 retval = NtUserMessageCall( hwnd, msg, wparam, lparam, &params, NtUserSendMessage, TRUE );
646 if (params.hwnd) retval = dispatch_send_message( &params );
647 return retval;
651 /***********************************************************************
652 * SendNotifyMessageA (USER32.@)
654 BOOL WINAPI SendNotifyMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
656 if (!WIN_IsCurrentThread( hwnd ) && !map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_SENDMESSAGE ))
657 return FALSE;
659 return NtUserMessageCall( hwnd, msg, wparam, lparam, 0, NtUserSendNotifyMessage, TRUE );
663 /***********************************************************************
664 * SendNotifyMessageW (USER32.@)
666 BOOL WINAPI SendNotifyMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
668 return NtUserMessageCall( hwnd, msg, wparam, lparam, 0, NtUserSendNotifyMessage, FALSE );
672 /***********************************************************************
673 * SendMessageCallbackA (USER32.@)
675 BOOL WINAPI SendMessageCallbackA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
676 SENDASYNCPROC callback, ULONG_PTR data )
678 struct send_message_callback_params params = { .callback = callback, .data = data };
680 if (!WIN_IsCurrentThread( hwnd ) && !map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_SENDMESSAGE ))
681 return FALSE;
683 return NtUserMessageCall( hwnd, msg, wparam, lparam, &params, NtUserSendMessageCallback, TRUE );
687 /***********************************************************************
688 * SendMessageCallbackW (USER32.@)
690 BOOL WINAPI SendMessageCallbackW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
691 SENDASYNCPROC callback, ULONG_PTR data )
693 struct send_message_callback_params params = { .callback = callback, .data = data };
694 return NtUserMessageCall( hwnd, msg, wparam, lparam, &params, NtUserSendMessageCallback, FALSE );
698 /***********************************************************************
699 * ReplyMessage (USER32.@)
701 BOOL WINAPI ReplyMessage( LRESULT result )
703 return NtUserReplyMessage( result, NULL );
707 /***********************************************************************
708 * InSendMessage (USER32.@)
710 BOOL WINAPI InSendMessage(void)
712 return (InSendMessageEx( NULL ) & (ISMEX_SEND | ISMEX_NOTIFY | ISMEX_CALLBACK)) != 0;
716 /***********************************************************************
717 * InSendMessageEx (USER32.@)
719 DWORD WINAPI InSendMessageEx( LPVOID reserved )
721 return NtUserGetThreadInfo()->receive_flags;
725 /***********************************************************************
726 * PostMessageA (USER32.@)
728 BOOL WINAPI PostMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
730 if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
731 return PostMessageW( hwnd, msg, wparam, lparam );
735 /***********************************************************************
736 * PostMessageW (USER32.@)
738 BOOL WINAPI PostMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
740 return NtUserPostMessage( hwnd, msg, wparam, lparam );
744 /**********************************************************************
745 * PostThreadMessageA (USER32.@)
747 BOOL WINAPI PostThreadMessageA( DWORD thread, UINT msg, WPARAM wparam, LPARAM lparam )
749 if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
750 return NtUserPostThreadMessage( thread, msg, wparam, lparam );
754 /***********************************************************************
755 * PostQuitMessage (USER32.@)
757 * Posts a quit message to the current thread's message queue.
759 * PARAMS
760 * exit_code [I] Exit code to return from message loop.
762 * RETURNS
763 * Nothing.
765 * NOTES
766 * This function is not the same as calling:
767 *|PostThreadMessage(GetCurrentThreadId(), WM_QUIT, exit_code, 0);
768 * It instead sets a flag in the message queue that signals it to generate
769 * a WM_QUIT message when there are no other pending sent or posted messages
770 * in the queue.
772 void WINAPI PostQuitMessage( INT exit_code )
774 SERVER_START_REQ( post_quit_message )
776 req->exit_code = exit_code;
777 wine_server_call( req );
779 SERVER_END_REQ;
782 /***********************************************************************
783 * PeekMessageW (USER32.@)
785 BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags )
787 return NtUserPeekMessage( msg_out, hwnd, first, last, flags );
791 /***********************************************************************
792 * PeekMessageA (USER32.@)
794 BOOL WINAPI DECLSPEC_HOTPATCH PeekMessageA( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags )
796 if (get_pending_wmchar( msg, first, last, (flags & PM_REMOVE) )) return TRUE;
797 if (!PeekMessageW( msg, hwnd, first, last, flags )) return FALSE;
798 map_wparam_WtoA( msg, (flags & PM_REMOVE) );
799 return TRUE;
803 /***********************************************************************
804 * GetMessageW (USER32.@)
806 BOOL WINAPI DECLSPEC_HOTPATCH GetMessageW( MSG *msg, HWND hwnd, UINT first, UINT last )
808 return NtUserGetMessage( msg, hwnd, first, last );
812 /***********************************************************************
813 * GetMessageA (USER32.@)
815 BOOL WINAPI DECLSPEC_HOTPATCH GetMessageA( MSG *msg, HWND hwnd, UINT first, UINT last )
817 if (get_pending_wmchar( msg, first, last, TRUE )) return TRUE;
818 if (GetMessageW( msg, hwnd, first, last ) < 0) return -1;
819 map_wparam_WtoA( msg, TRUE );
820 return (msg->message != WM_QUIT);
824 /***********************************************************************
825 * IsDialogMessageA (USER32.@)
826 * IsDialogMessage (USER32.@)
828 BOOL WINAPI IsDialogMessageA( HWND hwndDlg, LPMSG pmsg )
830 MSG msg = *pmsg;
831 map_wparam_AtoW( msg.message, &msg.wParam, WMCHAR_MAP_NOMAPPING );
832 return IsDialogMessageW( hwndDlg, &msg );
836 /***********************************************************************
837 * TranslateMessage (USER32.@)
839 * Implementation of TranslateMessage.
841 * TranslateMessage translates virtual-key messages into character-messages,
842 * as follows :
843 * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
844 * ditto replacing WM_* with WM_SYS*
845 * This produces WM_CHAR messages only for keys mapped to ASCII characters
846 * by the keyboard driver.
848 * If the message is WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP, the
849 * return value is nonzero, regardless of the translation.
852 BOOL WINAPI TranslateMessage( const MSG *msg )
854 return NtUserTranslateMessage( msg, 0 );
858 static LRESULT dispatch_message( const MSG *msg, BOOL ansi )
860 struct win_proc_params params;
861 LRESULT retval = 0;
863 if (!NtUserMessageCall( msg->hwnd, msg->message, msg->wParam, msg->lParam,
864 &params, NtUserGetDispatchParams, ansi )) return 0;
865 params.result = &retval;
867 SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message, msg->wParam, msg->lParam );
868 dispatch_win_proc_params( &params );
869 SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval, msg->wParam, msg->lParam );
870 return retval;
874 /***********************************************************************
875 * DispatchMessageA (USER32.@)
877 * See DispatchMessageW.
879 LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageA( const MSG* msg )
881 LRESULT retval;
883 /* Process timer messages */
884 if (msg->lParam && msg->message == WM_TIMER)
886 __TRY
888 retval = CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd,
889 msg->message, msg->wParam, GetTickCount() );
891 __EXCEPT_ALL
893 retval = 0;
895 __ENDTRY
896 return retval;
899 /* whenever possible, avoid using NtUserDispatchMessage to make the call unwindable */
900 if (msg->message != WM_SYSTIMER && msg->message != WM_PAINT)
901 return dispatch_message( msg, TRUE );
903 return NtUserDispatchMessage( msg );
907 /***********************************************************************
908 * DispatchMessageW (USER32.@) Process a message
910 * Process the message specified in the structure *_msg_.
912 * If the lpMsg parameter points to a WM_TIMER message and the
913 * parameter of the WM_TIMER message is not NULL, the lParam parameter
914 * points to the function that is called instead of the window
915 * procedure. The function stored in lParam (timer callback) is protected
916 * from causing page-faults.
918 * The message must be valid.
920 * RETURNS
922 * DispatchMessage() returns the result of the window procedure invoked.
924 * CONFORMANCE
926 * ECMA-234, Win32
929 LRESULT WINAPI DECLSPEC_HOTPATCH DispatchMessageW( const MSG* msg )
931 LRESULT retval;
933 /* Process timer messages */
934 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
936 if (msg->lParam)
938 __TRY
940 retval = CallWindowProcW( (WNDPROC)msg->lParam, msg->hwnd,
941 msg->message, msg->wParam, GetTickCount() );
943 __EXCEPT_ALL
945 retval = 0;
947 __ENDTRY
948 return retval;
952 /* whenever possible, avoid using NtUserDispatchMessage to make the call unwindable */
953 if (msg->message != WM_SYSTIMER && msg->message != WM_PAINT)
954 return dispatch_message( msg, FALSE );
956 return NtUserDispatchMessage( msg );
960 /***********************************************************************
961 * GetMessagePos (USER.119)
962 * GetMessagePos (USER32.@)
964 * The GetMessagePos() function returns a long value representing a
965 * cursor position, in screen coordinates, when the last message
966 * retrieved by the GetMessage() function occurs. The x-coordinate is
967 * in the low-order word of the return value, the y-coordinate is in
968 * the high-order word. The application can use the MAKEPOINT()
969 * macro to obtain a POINT structure from the return value.
971 * For the current cursor position, use GetCursorPos().
973 * RETURNS
975 * Cursor position of last message on success, zero on failure.
977 * CONFORMANCE
979 * ECMA-234, Win32
982 DWORD WINAPI GetMessagePos(void)
984 return NtUserGetThreadInfo()->message_pos;
988 /***********************************************************************
989 * GetMessageTime (USER.120)
990 * GetMessageTime (USER32.@)
992 * GetMessageTime() returns the message time for the last message
993 * retrieved by the function. The time is measured in milliseconds with
994 * the same offset as GetTickCount().
996 * Since the tick count wraps, this is only useful for moderately short
997 * relative time comparisons.
999 * RETURNS
1001 * Time of last message on success, zero on failure.
1003 LONG WINAPI GetMessageTime(void)
1005 return NtUserGetThreadInfo()->message_time;
1009 /***********************************************************************
1010 * GetMessageExtraInfo (USER.288)
1011 * GetMessageExtraInfo (USER32.@)
1013 LPARAM WINAPI GetMessageExtraInfo(void)
1015 return NtUserGetThreadInfo()->message_extra;
1019 /***********************************************************************
1020 * SetMessageExtraInfo (USER32.@)
1022 LPARAM WINAPI SetMessageExtraInfo(LPARAM lParam)
1024 struct ntuser_thread_info *thread_info = NtUserGetThreadInfo();
1025 LONG old_value = thread_info->message_extra;
1026 thread_info->message_extra = lParam;
1027 return old_value;
1031 /***********************************************************************
1032 * GetCurrentInputMessageSource (USER32.@)
1034 BOOL WINAPI GetCurrentInputMessageSource( INPUT_MESSAGE_SOURCE *source )
1036 *source = NtUserGetThreadInfo()->msg_source;
1037 return TRUE;
1041 /***********************************************************************
1042 * WaitMessage (USER.112) Suspend thread pending messages
1043 * WaitMessage (USER32.@) Suspend thread pending messages
1045 * WaitMessage() suspends a thread until events appear in the thread's
1046 * queue.
1048 BOOL WINAPI WaitMessage(void)
1050 return NtUserMsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 ) != WAIT_FAILED;
1054 /***********************************************************************
1055 * MsgWaitForMultipleObjects (USER32.@)
1057 DWORD WINAPI MsgWaitForMultipleObjects( DWORD count, const HANDLE *handles,
1058 BOOL wait_all, DWORD timeout, DWORD mask )
1060 return NtUserMsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
1061 wait_all ? MWMO_WAITALL : 0 );
1065 /***********************************************************************
1066 * WaitForInputIdle (USER32.@)
1068 DWORD WINAPI WaitForInputIdle( HANDLE process, DWORD timeout )
1070 return NtUserWaitForInputIdle( process, timeout, FALSE );
1074 /***********************************************************************
1075 * RegisterWindowMessageA (USER32.@)
1076 * RegisterWindowMessage (USER.118)
1078 UINT WINAPI RegisterWindowMessageA( LPCSTR str )
1080 UINT ret = GlobalAddAtomA(str);
1081 TRACE("%s, ret=%x\n", str, ret);
1082 return ret;
1086 /***********************************************************************
1087 * RegisterWindowMessageW (USER32.@)
1089 UINT WINAPI RegisterWindowMessageW( LPCWSTR str )
1091 UINT ret = GlobalAddAtomW(str);
1092 TRACE("%s ret=%x\n", debugstr_w(str), ret);
1093 return ret;
1096 typedef struct BroadcastParm
1098 DWORD flags;
1099 LPDWORD recipients;
1100 UINT msg;
1101 WPARAM wp;
1102 LPARAM lp;
1103 BOOL success;
1104 HWINSTA winsta;
1105 } BroadcastParm;
1107 static BOOL CALLBACK bcast_childwindow( HWND hw, LPARAM lp )
1109 BroadcastParm *parm = (BroadcastParm*)lp;
1110 DWORD_PTR retval = 0;
1111 LRESULT lresult;
1113 if (parm->flags & BSF_IGNORECURRENTTASK && WIN_IsCurrentProcess(hw))
1115 TRACE("Not telling myself %p\n", hw);
1116 return TRUE;
1119 /* I don't know 100% for sure if this is what Windows does, but it fits the tests */
1120 if (parm->flags & BSF_QUERY)
1122 TRACE("Telling window %p using SendMessageTimeout\n", hw);
1124 /* Not tested for conflicting flags */
1125 if (parm->flags & BSF_FORCEIFHUNG || parm->flags & BSF_NOHANG)
1126 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_ABORTIFHUNG, 2000, &retval );
1127 else if (parm->flags & BSF_NOTIMEOUTIFNOTHUNG)
1128 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NOTIMEOUTIFNOTHUNG, 2000, &retval );
1129 else
1130 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NORMAL, 2000, &retval );
1132 if (!lresult && GetLastError() == ERROR_TIMEOUT)
1134 WARN("Timed out!\n");
1135 if (!(parm->flags & BSF_FORCEIFHUNG))
1136 goto fail;
1138 if (retval == BROADCAST_QUERY_DENY)
1139 goto fail;
1141 return TRUE;
1143 fail:
1144 parm->success = FALSE;
1145 return FALSE;
1147 else if (parm->flags & BSF_POSTMESSAGE)
1149 TRACE("Telling window %p using PostMessage\n", hw);
1150 PostMessageW( hw, parm->msg, parm->wp, parm->lp );
1152 else
1154 TRACE("Telling window %p using SendNotifyMessage\n", hw);
1155 SendNotifyMessageW( hw, parm->msg, parm->wp, parm->lp );
1158 return TRUE;
1161 static BOOL CALLBACK bcast_desktop( LPWSTR desktop, LPARAM lp )
1163 BOOL ret;
1164 HDESK hdesktop;
1165 BroadcastParm *parm = (BroadcastParm*)lp;
1167 TRACE("desktop: %s\n", debugstr_w( desktop ));
1169 hdesktop = open_winstation_desktop( parm->winsta, desktop, 0, FALSE, DESKTOP_ENUMERATE|DESKTOP_WRITEOBJECTS|STANDARD_RIGHTS_WRITE );
1170 if (!hdesktop)
1172 FIXME("Could not open desktop %s\n", debugstr_w(desktop));
1173 return TRUE;
1176 ret = EnumDesktopWindows( hdesktop, bcast_childwindow, lp );
1177 NtUserCloseDesktop( hdesktop );
1178 TRACE("-->%d\n", ret);
1179 return parm->success;
1182 static BOOL CALLBACK bcast_winsta( LPWSTR winsta, LPARAM lp )
1184 BOOL ret;
1185 HWINSTA hwinsta = OpenWindowStationW( winsta, FALSE, WINSTA_ENUMDESKTOPS );
1186 TRACE("hwinsta: %p/%s/%08lx\n", hwinsta, debugstr_w( winsta ), GetLastError());
1187 if (!hwinsta)
1188 return TRUE;
1189 ((BroadcastParm *)lp)->winsta = hwinsta;
1190 ret = EnumDesktopsW( hwinsta, bcast_desktop, lp );
1191 NtUserCloseWindowStation( hwinsta );
1192 TRACE("-->%d\n", ret);
1193 return ret;
1196 /***********************************************************************
1197 * BroadcastSystemMessageA (USER32.@)
1198 * BroadcastSystemMessage (USER32.@)
1200 LONG WINAPI BroadcastSystemMessageA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
1202 return BroadcastSystemMessageExA( flags, recipients, msg, wp, lp, NULL );
1206 /***********************************************************************
1207 * BroadcastSystemMessageW (USER32.@)
1209 LONG WINAPI BroadcastSystemMessageW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
1211 return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
1214 /***********************************************************************
1215 * BroadcastSystemMessageExA (USER32.@)
1217 LONG WINAPI BroadcastSystemMessageExA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
1219 map_wparam_AtoW( msg, &wp, WMCHAR_MAP_NOMAPPING );
1220 return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
1224 /***********************************************************************
1225 * BroadcastSystemMessageExW (USER32.@)
1227 LONG WINAPI BroadcastSystemMessageExW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
1229 BroadcastParm parm;
1230 DWORD recips = BSM_ALLCOMPONENTS;
1231 BOOL ret = TRUE;
1232 static const DWORD all_flags = ( BSF_QUERY | BSF_IGNORECURRENTTASK | BSF_FLUSHDISK | BSF_NOHANG
1233 | BSF_POSTMESSAGE | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG
1234 | BSF_ALLOWSFW | BSF_SENDNOTIFYMESSAGE | BSF_RETURNHDESK | BSF_LUID );
1236 TRACE("Flags: %08lx, recipients: %p(0x%lx), msg: %04x, wparam: %08Ix, lparam: %08Ix\n", flags, recipients,
1237 (recipients ? *recipients : recips), msg, wp, lp);
1239 if (flags & ~all_flags)
1241 SetLastError(ERROR_INVALID_PARAMETER);
1242 return 0;
1245 if (!recipients)
1246 recipients = &recips;
1248 if ( pinfo && flags & BSF_QUERY )
1249 FIXME("Not returning PBSMINFO information yet\n");
1251 parm.flags = flags;
1252 parm.recipients = recipients;
1253 parm.msg = msg;
1254 parm.wp = wp;
1255 parm.lp = lp;
1256 parm.success = TRUE;
1258 if (*recipients & BSM_ALLDESKTOPS || *recipients == BSM_ALLCOMPONENTS)
1259 ret = EnumWindowStationsW(bcast_winsta, (LONG_PTR)&parm);
1260 else if (*recipients & BSM_APPLICATIONS)
1262 EnumWindows(bcast_childwindow, (LONG_PTR)&parm);
1263 ret = parm.success;
1265 else
1266 FIXME("Recipients %08lx not supported!\n", *recipients);
1268 return ret;
1271 /***********************************************************************
1272 * SetMessageQueue (USER32.@)
1274 BOOL WINAPI SetMessageQueue( INT size )
1276 /* now obsolete the message queue will be expanded dynamically as necessary */
1277 return TRUE;
1281 /***********************************************************************
1282 * MessageBeep (USER32.@)
1284 BOOL WINAPI MessageBeep( UINT i )
1286 return NtUserMessageBeep( i );
1290 /******************************************************************
1291 * SetTimer (USER32.@)
1293 UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
1295 return NtUserSetTimer( hwnd, id, timeout, proc, TIMERV_DEFAULT_COALESCING );
1299 /******************************************************************
1300 * SetSystemTimer (USER32.@)
1302 UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout, void *unknown )
1304 if (unknown) FIXME( "ignoring unknown parameter %p\n", unknown );
1306 return NtUserSetSystemTimer( hwnd, id, timeout );
1310 /***********************************************************************
1311 * KillSystemTimer (USER32.@)
1313 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT_PTR id )
1315 return NtUserKillSystemTimer( hwnd, id );
1319 /**********************************************************************
1320 * IsGUIThread (USER32.@)
1322 BOOL WINAPI IsGUIThread( BOOL convert )
1324 FIXME( "%u: stub\n", convert );
1325 return TRUE;
1329 /******************************************************************
1330 * IsHungAppWindow (USER32.@)
1333 BOOL WINAPI IsHungAppWindow( HWND hWnd )
1335 BOOL ret;
1337 SERVER_START_REQ( is_window_hung )
1339 req->win = wine_server_user_handle( hWnd );
1340 ret = !wine_server_call_err( req ) && reply->is_hung;
1342 SERVER_END_REQ;
1343 return ret;
1346 /******************************************************************
1347 * ChangeWindowMessageFilter (USER32.@)
1349 BOOL WINAPI ChangeWindowMessageFilter( UINT message, DWORD flag )
1351 FIXME( "%x %08lx\n", message, flag );
1352 return TRUE;
1355 /******************************************************************
1356 * ChangeWindowMessageFilterEx (USER32.@)
1358 BOOL WINAPI ChangeWindowMessageFilterEx( HWND hwnd, UINT message, DWORD action, CHANGEFILTERSTRUCT *changefilter )
1360 FIXME( "%p %x %ld %p\n", hwnd, message, action, changefilter );
1361 return TRUE;