user32: Implement BroadcastSystemMessage.
[wine.git] / dlls / user32 / message.c
blobce102de1cd4eff53fc5d6360056dd5955b343204
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 "config.h"
23 #include "wine/port.h"
25 #include <assert.h>
26 #include <stdarg.h>
28 #include "ntstatus.h"
29 #define WIN32_NO_STATUS
30 #include "windef.h"
31 #include "winbase.h"
32 #include "wingdi.h"
33 #include "winuser.h"
34 #include "winerror.h"
35 #include "winnls.h"
36 #include "dbt.h"
37 #include "dde.h"
38 #include "wine/unicode.h"
39 #include "wine/server.h"
40 #include "user_private.h"
41 #include "win.h"
42 #include "controls.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(msg);
46 WINE_DECLARE_DEBUG_CHANNEL(relay);
47 WINE_DECLARE_DEBUG_CHANNEL(key);
49 #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
50 #define WM_NCMOUSELAST (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST))
52 #define MAX_PACK_COUNT 4
53 #define MAX_SENDMSG_RECURSION 64
55 #define SYS_TIMER_RATE 55 /* min. timer rate in ms (actually 54.925)*/
57 /* description of the data fields that need to be packed along with a sent message */
58 struct packed_message
60 int count;
61 const void *data[MAX_PACK_COUNT];
62 size_t size[MAX_PACK_COUNT];
65 /* info about the message currently being received by the current thread */
66 struct received_message_info
68 enum message_type type;
69 MSG msg;
70 UINT flags; /* InSendMessageEx return flags */
73 /* structure to group all parameters for sent messages of the various kinds */
74 struct send_message_info
76 enum message_type type;
77 DWORD dest_tid;
78 HWND hwnd;
79 UINT msg;
80 WPARAM wparam;
81 LPARAM lparam;
82 UINT flags; /* flags for SendMessageTimeout */
83 UINT timeout; /* timeout for SendMessageTimeout */
84 SENDASYNCPROC callback; /* callback function for SendMessageCallback */
85 ULONG_PTR data; /* callback data */
86 enum wm_char_mapping wm_char;
90 /* flag for messages that contain pointers */
91 /* 32 messages per entry, messages 0..31 map to bits 0..31 */
93 #define SET(msg) (1 << ((msg) & 31))
95 static const unsigned int message_pointer_flags[] =
97 /* 0x00 - 0x1f */
98 SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) |
99 SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
100 /* 0x20 - 0x3f */
101 SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) |
102 SET(WM_COMPAREITEM),
103 /* 0x40 - 0x5f */
104 SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) |
105 SET(WM_NOTIFY) | SET(WM_HELP),
106 /* 0x60 - 0x7f */
107 SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED),
108 /* 0x80 - 0x9f */
109 SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE),
110 /* 0xa0 - 0xbf */
111 SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP),
112 /* 0xc0 - 0xdf */
113 SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS),
114 /* 0xe0 - 0xff */
115 SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO),
116 /* 0x100 - 0x11f */
118 /* 0x120 - 0x13f */
120 /* 0x140 - 0x15f */
121 SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) |
122 SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) |
123 SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT),
124 /* 0x160 - 0x17f */
126 /* 0x180 - 0x19f */
127 SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) |
128 SET(LB_DIR) | SET(LB_FINDSTRING) |
129 SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT),
130 /* 0x1a0 - 0x1bf */
131 SET(LB_FINDSTRINGEXACT),
132 /* 0x1c0 - 0x1df */
134 /* 0x1e0 - 0x1ff */
136 /* 0x200 - 0x21f */
137 SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE),
138 /* 0x220 - 0x23f */
139 SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) |
140 SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE),
141 /* 0x240 - 0x25f */
143 /* 0x260 - 0x27f */
145 /* 0x280 - 0x29f */
147 /* 0x2a0 - 0x2bf */
149 /* 0x2c0 - 0x2df */
151 /* 0x2e0 - 0x2ff */
153 /* 0x300 - 0x31f */
154 SET(WM_ASKCBFORMATNAME)
157 /* flags for messages that contain Unicode strings */
158 static const unsigned int message_unicode_flags[] =
160 /* 0x00 - 0x1f */
161 SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) | SET(WM_GETTEXTLENGTH) |
162 SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
163 /* 0x20 - 0x3f */
164 SET(WM_CHARTOITEM),
165 /* 0x40 - 0x5f */
167 /* 0x60 - 0x7f */
169 /* 0x80 - 0x9f */
170 SET(WM_NCCREATE),
171 /* 0xa0 - 0xbf */
173 /* 0xc0 - 0xdf */
174 SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETPASSWORDCHAR),
175 /* 0xe0 - 0xff */
177 /* 0x100 - 0x11f */
178 SET(WM_CHAR) | SET(WM_DEADCHAR) | SET(WM_SYSCHAR) | SET(WM_SYSDEADCHAR),
179 /* 0x120 - 0x13f */
180 SET(WM_MENUCHAR),
181 /* 0x140 - 0x15f */
182 SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) | SET(CB_GETLBTEXTLEN) |
183 SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) | SET(CB_FINDSTRINGEXACT),
184 /* 0x160 - 0x17f */
186 /* 0x180 - 0x19f */
187 SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_GETTEXTLEN) |
188 SET(LB_SELECTSTRING) | SET(LB_DIR) | SET(LB_FINDSTRING) | SET(LB_ADDFILE),
189 /* 0x1a0 - 0x1bf */
190 SET(LB_FINDSTRINGEXACT),
191 /* 0x1c0 - 0x1df */
193 /* 0x1e0 - 0x1ff */
195 /* 0x200 - 0x21f */
197 /* 0x220 - 0x23f */
198 SET(WM_MDICREATE),
199 /* 0x240 - 0x25f */
201 /* 0x260 - 0x27f */
203 /* 0x280 - 0x29f */
204 SET(WM_IME_CHAR),
205 /* 0x2a0 - 0x2bf */
207 /* 0x2c0 - 0x2df */
209 /* 0x2e0 - 0x2ff */
211 /* 0x300 - 0x31f */
212 SET(WM_PAINTCLIPBOARD) | SET(WM_SIZECLIPBOARD) | SET(WM_ASKCBFORMATNAME)
215 /* check whether a given message type includes pointers */
216 static inline int is_pointer_message( UINT message )
218 if (message >= 8*sizeof(message_pointer_flags)) return FALSE;
219 return (message_pointer_flags[message / 32] & SET(message)) != 0;
222 /* check whether a given message type contains Unicode (or ASCII) chars */
223 static inline int is_unicode_message( UINT message )
225 if (message >= 8*sizeof(message_unicode_flags)) return FALSE;
226 return (message_unicode_flags[message / 32] & SET(message)) != 0;
229 #undef SET
231 /* add a data field to a packed message */
232 static inline void push_data( struct packed_message *data, const void *ptr, size_t size )
234 data->data[data->count] = ptr;
235 data->size[data->count] = size;
236 data->count++;
239 /* add a string to a packed message */
240 static inline void push_string( struct packed_message *data, LPCWSTR str )
242 push_data( data, str, (strlenW(str) + 1) * sizeof(WCHAR) );
245 /* retrieve a pointer to data from a packed message and increment the buffer pointer */
246 static inline void *get_data( void **buffer, size_t size )
248 void *ret = *buffer;
249 *buffer = (char *)*buffer + size;
250 return ret;
253 /* make sure that the buffer contains a valid null-terminated Unicode string */
254 static inline BOOL check_string( LPCWSTR str, size_t size )
256 for (size /= sizeof(WCHAR); size; size--, str++)
257 if (!*str) return TRUE;
258 return FALSE;
261 /* make sure that there is space for 'size' bytes in buffer, growing it if needed */
262 static inline void *get_buffer_space( void **buffer, size_t size )
264 void *ret;
266 if (*buffer)
268 if (!(ret = HeapReAlloc( GetProcessHeap(), 0, *buffer, size )))
269 HeapFree( GetProcessHeap(), 0, *buffer );
271 else ret = HeapAlloc( GetProcessHeap(), 0, size );
273 *buffer = ret;
274 return ret;
277 /* check whether a combobox expects strings or ids in CB_ADDSTRING/CB_INSERTSTRING */
278 static inline BOOL combobox_has_strings( HWND hwnd )
280 DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
281 return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS));
284 /* check whether a listbox expects strings or ids in LB_ADDSTRING/LB_INSERTSTRING */
285 static inline BOOL listbox_has_strings( HWND hwnd )
287 DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
288 return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS));
291 /* check whether message is in the range of keyboard messages */
292 static inline BOOL is_keyboard_message( UINT message )
294 return (message >= WM_KEYFIRST && message <= WM_KEYLAST);
297 /* check whether message is in the range of mouse messages */
298 static inline BOOL is_mouse_message( UINT message )
300 return ((message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST) ||
301 (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST));
304 /* check whether message matches the specified hwnd filter */
305 static inline BOOL check_hwnd_filter( const MSG *msg, HWND hwnd_filter )
307 if (!hwnd_filter || hwnd_filter == GetDesktopWindow()) return TRUE;
308 return (msg->hwnd == hwnd_filter || IsChild( hwnd_filter, msg->hwnd ));
311 /* check for pending WM_CHAR message with DBCS trailing byte */
312 static inline BOOL get_pending_wmchar( MSG *msg, UINT first, UINT last, BOOL remove )
314 struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
316 if (!data || !data->get_msg.message) return FALSE;
317 if ((first || last) && (first > WM_CHAR || last < WM_CHAR)) return FALSE;
318 if (!msg) return FALSE;
319 *msg = data->get_msg;
320 if (remove) data->get_msg.message = 0;
321 return TRUE;
324 /***********************************************************************
325 * broadcast_message_callback
327 * Helper callback for broadcasting messages.
329 static BOOL CALLBACK broadcast_message_callback( HWND hwnd, LPARAM lparam )
331 struct send_message_info *info = (struct send_message_info *)lparam;
332 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & (WS_POPUP|WS_CAPTION))) return TRUE;
333 switch(info->type)
335 case MSG_UNICODE:
336 SendMessageTimeoutW( hwnd, info->msg, info->wparam, info->lparam,
337 info->flags, info->timeout, NULL );
338 break;
339 case MSG_ASCII:
340 SendMessageTimeoutA( hwnd, info->msg, info->wparam, info->lparam,
341 info->flags, info->timeout, NULL );
342 break;
343 case MSG_NOTIFY:
344 SendNotifyMessageW( hwnd, info->msg, info->wparam, info->lparam );
345 break;
346 case MSG_CALLBACK:
347 SendMessageCallbackW( hwnd, info->msg, info->wparam, info->lparam,
348 info->callback, info->data );
349 break;
350 case MSG_POSTED:
351 PostMessageW( hwnd, info->msg, info->wparam, info->lparam );
352 break;
353 default:
354 ERR( "bad type %d\n", info->type );
355 break;
357 return TRUE;
361 /***********************************************************************
362 * map_wparam_AtoW
364 * Convert the wparam of an ASCII message to Unicode.
366 BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping )
368 char ch[2];
369 WCHAR wch[2];
371 wch[0] = wch[1] = 0;
372 switch(message)
374 case WM_CHAR:
375 /* WM_CHAR is magic: a DBCS char can be sent/posted as two consecutive WM_CHAR
376 * messages, in which case the first char is stored, and the conversion
377 * to Unicode only takes place once the second char is sent/posted.
379 if (mapping != WMCHAR_MAP_NOMAPPING)
381 struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
382 BYTE low = LOBYTE(*wparam);
384 if (HIBYTE(*wparam))
386 ch[0] = low;
387 ch[1] = HIBYTE(*wparam);
388 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
389 TRACE( "map %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
390 if (data) data->lead_byte[mapping] = 0;
392 else if (data && data->lead_byte[mapping])
394 ch[0] = data->lead_byte[mapping];
395 ch[1] = low;
396 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
397 TRACE( "map stored %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
398 data->lead_byte[mapping] = 0;
400 else if (!IsDBCSLeadByte( low ))
402 ch[0] = low;
403 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 1 );
404 TRACE( "map %02x -> %04x\n", (BYTE)ch[0], wch[0] );
405 if (data) data->lead_byte[mapping] = 0;
407 else /* store it and wait for trail byte */
409 if (!data)
411 if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )))
412 return FALSE;
413 get_user_thread_info()->wmchar_data = data;
415 TRACE( "storing lead byte %02x mapping %u\n", low, mapping );
416 data->lead_byte[mapping] = low;
417 return FALSE;
419 *wparam = MAKEWPARAM(wch[0], wch[1]);
420 break;
422 /* else fall through */
423 case WM_CHARTOITEM:
424 case EM_SETPASSWORDCHAR:
425 case WM_DEADCHAR:
426 case WM_SYSCHAR:
427 case WM_SYSDEADCHAR:
428 case WM_MENUCHAR:
429 ch[0] = LOBYTE(*wparam);
430 ch[1] = HIBYTE(*wparam);
431 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
432 *wparam = MAKEWPARAM(wch[0], wch[1]);
433 break;
434 case WM_IME_CHAR:
435 ch[0] = HIBYTE(*wparam);
436 ch[1] = LOBYTE(*wparam);
437 if (ch[0]) RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch, 2 );
438 else RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch + 1, 1 );
439 *wparam = MAKEWPARAM(wch[0], HIWORD(*wparam));
440 break;
442 return TRUE;
446 /***********************************************************************
447 * map_wparam_WtoA
449 * Convert the wparam of a Unicode message to ASCII.
451 static void map_wparam_WtoA( MSG *msg, BOOL remove )
453 BYTE ch[2];
454 WCHAR wch[2];
455 DWORD len;
457 switch(msg->message)
459 case WM_CHAR:
460 if (!HIWORD(msg->wParam))
462 wch[0] = LOWORD(msg->wParam);
463 ch[0] = ch[1] = 0;
464 RtlUnicodeToMultiByteN( (LPSTR)ch, 2, &len, wch, sizeof(wch[0]) );
465 if (len == 2) /* DBCS char */
467 struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
468 if (!data)
470 if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) return;
471 get_user_thread_info()->wmchar_data = data;
473 if (remove)
475 data->get_msg = *msg;
476 data->get_msg.wParam = ch[1];
478 msg->wParam = ch[0];
479 return;
482 /* else fall through */
483 case WM_CHARTOITEM:
484 case EM_SETPASSWORDCHAR:
485 case WM_DEADCHAR:
486 case WM_SYSCHAR:
487 case WM_SYSDEADCHAR:
488 case WM_MENUCHAR:
489 wch[0] = LOWORD(msg->wParam);
490 wch[1] = HIWORD(msg->wParam);
491 ch[0] = ch[1] = 0;
492 RtlUnicodeToMultiByteN( (LPSTR)ch, 2, NULL, wch, sizeof(wch) );
493 msg->wParam = MAKEWPARAM( ch[0] | (ch[1] << 8), 0 );
494 break;
495 case WM_IME_CHAR:
496 wch[0] = LOWORD(msg->wParam);
497 ch[0] = ch[1] = 0;
498 RtlUnicodeToMultiByteN( (LPSTR)ch, 2, &len, wch, sizeof(wch[0]) );
499 if (len == 2)
500 msg->wParam = MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(msg->wParam) );
501 else
502 msg->wParam = MAKEWPARAM( ch[0], HIWORD(msg->wParam) );
503 break;
508 /***********************************************************************
509 * pack_message
511 * Pack a message for sending to another process.
512 * Return the size of the data we expect in the message reply.
513 * Set data->count to -1 if there is an error.
515 static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
516 struct packed_message *data )
518 data->count = 0;
519 switch(message)
521 case WM_NCCREATE:
522 case WM_CREATE:
524 CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
525 push_data( data, cs, sizeof(*cs) );
526 if (HIWORD(cs->lpszName)) push_string( data, cs->lpszName );
527 if (HIWORD(cs->lpszClass)) push_string( data, cs->lpszClass );
528 return sizeof(*cs);
530 case WM_GETTEXT:
531 case WM_ASKCBFORMATNAME:
532 return wparam * sizeof(WCHAR);
533 case WM_WININICHANGE:
534 if (lparam) push_string(data, (LPWSTR)lparam );
535 return 0;
536 case WM_SETTEXT:
537 case WM_DEVMODECHANGE:
538 case CB_DIR:
539 case LB_DIR:
540 case LB_ADDFILE:
541 case EM_REPLACESEL:
542 push_string( data, (LPWSTR)lparam );
543 return 0;
544 case WM_GETMINMAXINFO:
545 push_data( data, (MINMAXINFO *)lparam, sizeof(MINMAXINFO) );
546 return sizeof(MINMAXINFO);
547 case WM_DRAWITEM:
548 push_data( data, (DRAWITEMSTRUCT *)lparam, sizeof(DRAWITEMSTRUCT) );
549 return 0;
550 case WM_MEASUREITEM:
551 push_data( data, (MEASUREITEMSTRUCT *)lparam, sizeof(MEASUREITEMSTRUCT) );
552 return sizeof(MEASUREITEMSTRUCT);
553 case WM_DELETEITEM:
554 push_data( data, (DELETEITEMSTRUCT *)lparam, sizeof(DELETEITEMSTRUCT) );
555 return 0;
556 case WM_COMPAREITEM:
557 push_data( data, (COMPAREITEMSTRUCT *)lparam, sizeof(COMPAREITEMSTRUCT) );
558 return 0;
559 case WM_WINDOWPOSCHANGING:
560 case WM_WINDOWPOSCHANGED:
561 push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
562 return sizeof(WINDOWPOS);
563 case WM_COPYDATA:
565 COPYDATASTRUCT *cp = (COPYDATASTRUCT *)lparam;
566 push_data( data, cp, sizeof(*cp) );
567 if (cp->lpData) push_data( data, cp->lpData, cp->cbData );
568 return 0;
570 case WM_NOTIFY:
571 /* WM_NOTIFY cannot be sent across processes (MSDN) */
572 data->count = -1;
573 return 0;
574 case WM_HELP:
575 push_data( data, (HELPINFO *)lparam, sizeof(HELPINFO) );
576 return 0;
577 case WM_STYLECHANGING:
578 case WM_STYLECHANGED:
579 push_data( data, (STYLESTRUCT *)lparam, sizeof(STYLESTRUCT) );
580 return 0;
581 case WM_NCCALCSIZE:
582 if (!wparam)
584 push_data( data, (RECT *)lparam, sizeof(RECT) );
585 return sizeof(RECT);
587 else
589 NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
590 push_data( data, nc, sizeof(*nc) );
591 push_data( data, nc->lppos, sizeof(*nc->lppos) );
592 return sizeof(*nc) + sizeof(*nc->lppos);
594 case WM_GETDLGCODE:
595 if (lparam) push_data( data, (MSG *)lparam, sizeof(MSG) );
596 return sizeof(MSG);
597 case SBM_SETSCROLLINFO:
598 push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
599 return 0;
600 case SBM_GETSCROLLINFO:
601 push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
602 return sizeof(SCROLLINFO);
603 case SBM_GETSCROLLBARINFO:
605 const SCROLLBARINFO *info = (const SCROLLBARINFO *)lparam;
606 size_t size = min( info->cbSize, sizeof(SCROLLBARINFO) );
607 push_data( data, info, size );
608 return size;
610 case EM_GETSEL:
611 case SBM_GETRANGE:
612 case CB_GETEDITSEL:
614 size_t size = 0;
615 if (wparam) size += sizeof(DWORD);
616 if (lparam) size += sizeof(DWORD);
617 return size;
619 case EM_GETRECT:
620 case LB_GETITEMRECT:
621 case CB_GETDROPPEDCONTROLRECT:
622 return sizeof(RECT);
623 case EM_SETRECT:
624 case EM_SETRECTNP:
625 push_data( data, (RECT *)lparam, sizeof(RECT) );
626 return 0;
627 case EM_GETLINE:
629 WORD *pw = (WORD *)lparam;
630 push_data( data, pw, sizeof(*pw) );
631 return *pw * sizeof(WCHAR);
633 case EM_SETTABSTOPS:
634 case LB_SETTABSTOPS:
635 if (wparam) push_data( data, (UINT *)lparam, sizeof(UINT) * wparam );
636 return 0;
637 case CB_ADDSTRING:
638 case CB_INSERTSTRING:
639 case CB_FINDSTRING:
640 case CB_FINDSTRINGEXACT:
641 case CB_SELECTSTRING:
642 if (combobox_has_strings( hwnd )) push_string( data, (LPWSTR)lparam );
643 return 0;
644 case CB_GETLBTEXT:
645 if (!combobox_has_strings( hwnd )) return sizeof(ULONG_PTR);
646 return (SendMessageW( hwnd, CB_GETLBTEXTLEN, wparam, 0 ) + 1) * sizeof(WCHAR);
647 case LB_ADDSTRING:
648 case LB_INSERTSTRING:
649 case LB_FINDSTRING:
650 case LB_FINDSTRINGEXACT:
651 case LB_SELECTSTRING:
652 if (listbox_has_strings( hwnd )) push_string( data, (LPWSTR)lparam );
653 return 0;
654 case LB_GETTEXT:
655 if (!listbox_has_strings( hwnd )) return sizeof(ULONG_PTR);
656 return (SendMessageW( hwnd, LB_GETTEXTLEN, wparam, 0 ) + 1) * sizeof(WCHAR);
657 case LB_GETSELITEMS:
658 return wparam * sizeof(UINT);
659 case WM_NEXTMENU:
660 push_data( data, (MDINEXTMENU *)lparam, sizeof(MDINEXTMENU) );
661 return sizeof(MDINEXTMENU);
662 case WM_SIZING:
663 case WM_MOVING:
664 push_data( data, (RECT *)lparam, sizeof(RECT) );
665 return sizeof(RECT);
666 case WM_MDICREATE:
668 MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)lparam;
669 push_data( data, cs, sizeof(*cs) );
670 if (HIWORD(cs->szTitle)) push_string( data, cs->szTitle );
671 if (HIWORD(cs->szClass)) push_string( data, cs->szClass );
672 return sizeof(*cs);
674 case WM_MDIGETACTIVE:
675 if (lparam) return sizeof(BOOL);
676 return 0;
677 case WM_DEVICECHANGE:
679 DEV_BROADCAST_HDR *header = (DEV_BROADCAST_HDR *)lparam;
680 push_data( data, header, header->dbch_size );
681 return 0;
683 case WM_WINE_SETWINDOWPOS:
684 push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
685 return 0;
686 case WM_WINE_KEYBOARD_LL_HOOK:
688 struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
689 push_data( data, h_extra, sizeof(*h_extra) );
690 push_data( data, (LPVOID)h_extra->lparam, sizeof(KBDLLHOOKSTRUCT) );
691 return 0;
693 case WM_WINE_MOUSE_LL_HOOK:
695 struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
696 push_data( data, h_extra, sizeof(*h_extra) );
697 push_data( data, (LPVOID)h_extra->lparam, sizeof(MSLLHOOKSTRUCT) );
698 return 0;
700 case WM_NCPAINT:
701 if (wparam <= 1) return 0;
702 FIXME( "WM_NCPAINT hdc packing not supported yet\n" );
703 data->count = -1;
704 return 0;
705 case WM_PAINT:
706 if (!wparam) return 0;
707 /* fall through */
709 /* these contain an HFONT */
710 case WM_SETFONT:
711 case WM_GETFONT:
712 /* these contain an HDC */
713 case WM_ERASEBKGND:
714 case WM_ICONERASEBKGND:
715 case WM_CTLCOLORMSGBOX:
716 case WM_CTLCOLOREDIT:
717 case WM_CTLCOLORLISTBOX:
718 case WM_CTLCOLORBTN:
719 case WM_CTLCOLORDLG:
720 case WM_CTLCOLORSCROLLBAR:
721 case WM_CTLCOLORSTATIC:
722 case WM_PRINT:
723 case WM_PRINTCLIENT:
724 /* these contain an HGLOBAL */
725 case WM_PAINTCLIPBOARD:
726 case WM_SIZECLIPBOARD:
727 /* these contain HICON */
728 case WM_GETICON:
729 case WM_SETICON:
730 case WM_QUERYDRAGICON:
731 case WM_QUERYPARKICON:
732 /* these contain pointers */
733 case WM_DROPOBJECT:
734 case WM_QUERYDROPOBJECT:
735 case WM_DRAGLOOP:
736 case WM_DRAGSELECT:
737 case WM_DRAGMOVE:
738 FIXME( "msg %x (%s) not supported yet\n", message, SPY_GetMsgName(message, hwnd) );
739 data->count = -1;
740 return 0;
742 return 0;
746 /***********************************************************************
747 * unpack_message
749 * Unpack a message received from another process.
751 static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
752 void **buffer, size_t size )
754 size_t minsize = 0;
756 switch(message)
758 case WM_NCCREATE:
759 case WM_CREATE:
761 CREATESTRUCTW *cs = *buffer;
762 WCHAR *str = (WCHAR *)(cs + 1);
763 if (size < sizeof(*cs)) return FALSE;
764 size -= sizeof(*cs);
765 if (HIWORD(cs->lpszName))
767 if (!check_string( str, size )) return FALSE;
768 cs->lpszName = str;
769 size -= (strlenW(str) + 1) * sizeof(WCHAR);
770 str += strlenW(str) + 1;
772 if (HIWORD(cs->lpszClass))
774 if (!check_string( str, size )) return FALSE;
775 cs->lpszClass = str;
777 break;
779 case WM_GETTEXT:
780 case WM_ASKCBFORMATNAME:
781 if (!get_buffer_space( buffer, (*wparam * sizeof(WCHAR)) )) return FALSE;
782 break;
783 case WM_WININICHANGE:
784 if (!*lparam) return TRUE;
785 /* fall through */
786 case WM_SETTEXT:
787 case WM_DEVMODECHANGE:
788 case CB_DIR:
789 case LB_DIR:
790 case LB_ADDFILE:
791 case EM_REPLACESEL:
792 if (!check_string( *buffer, size )) return FALSE;
793 break;
794 case WM_GETMINMAXINFO:
795 minsize = sizeof(MINMAXINFO);
796 break;
797 case WM_DRAWITEM:
798 minsize = sizeof(DRAWITEMSTRUCT);
799 break;
800 case WM_MEASUREITEM:
801 minsize = sizeof(MEASUREITEMSTRUCT);
802 break;
803 case WM_DELETEITEM:
804 minsize = sizeof(DELETEITEMSTRUCT);
805 break;
806 case WM_COMPAREITEM:
807 minsize = sizeof(COMPAREITEMSTRUCT);
808 break;
809 case WM_WINDOWPOSCHANGING:
810 case WM_WINDOWPOSCHANGED:
811 case WM_WINE_SETWINDOWPOS:
812 minsize = sizeof(WINDOWPOS);
813 break;
814 case WM_COPYDATA:
816 COPYDATASTRUCT *cp = *buffer;
817 if (size < sizeof(*cp)) return FALSE;
818 if (cp->lpData)
820 minsize = sizeof(*cp) + cp->cbData;
821 cp->lpData = cp + 1;
823 break;
825 case WM_NOTIFY:
826 /* WM_NOTIFY cannot be sent across processes (MSDN) */
827 return FALSE;
828 case WM_HELP:
829 minsize = sizeof(HELPINFO);
830 break;
831 case WM_STYLECHANGING:
832 case WM_STYLECHANGED:
833 minsize = sizeof(STYLESTRUCT);
834 break;
835 case WM_NCCALCSIZE:
836 if (!*wparam) minsize = sizeof(RECT);
837 else
839 NCCALCSIZE_PARAMS *nc = *buffer;
840 if (size < sizeof(*nc) + sizeof(*nc->lppos)) return FALSE;
841 nc->lppos = (WINDOWPOS *)(nc + 1);
843 break;
844 case WM_GETDLGCODE:
845 if (!*lparam) return TRUE;
846 minsize = sizeof(MSG);
847 break;
848 case SBM_SETSCROLLINFO:
849 minsize = sizeof(SCROLLINFO);
850 break;
851 case SBM_GETSCROLLINFO:
852 if (!get_buffer_space( buffer, sizeof(SCROLLINFO ))) return FALSE;
853 break;
854 case SBM_GETSCROLLBARINFO:
855 if (!get_buffer_space( buffer, sizeof(SCROLLBARINFO ))) return FALSE;
856 break;
857 case EM_GETSEL:
858 case SBM_GETRANGE:
859 case CB_GETEDITSEL:
860 if (*wparam || *lparam)
862 if (!get_buffer_space( buffer, 2*sizeof(DWORD) )) return FALSE;
863 if (*wparam) *wparam = (WPARAM)*buffer;
864 if (*lparam) *lparam = (LPARAM)((DWORD *)*buffer + 1);
866 return TRUE;
867 case EM_GETRECT:
868 case LB_GETITEMRECT:
869 case CB_GETDROPPEDCONTROLRECT:
870 if (!get_buffer_space( buffer, sizeof(RECT) )) return FALSE;
871 break;
872 case EM_SETRECT:
873 case EM_SETRECTNP:
874 minsize = sizeof(RECT);
875 break;
876 case EM_GETLINE:
878 WORD len;
879 if (size < sizeof(WORD)) return FALSE;
880 len = *(WORD *)*buffer;
881 if (!get_buffer_space( buffer, (len + 1) * sizeof(WCHAR) )) return FALSE;
882 *lparam = (LPARAM)*buffer + sizeof(WORD); /* don't erase WORD at start of buffer */
883 return TRUE;
885 case EM_SETTABSTOPS:
886 case LB_SETTABSTOPS:
887 if (!*wparam) return TRUE;
888 minsize = *wparam * sizeof(UINT);
889 break;
890 case CB_ADDSTRING:
891 case CB_INSERTSTRING:
892 case CB_FINDSTRING:
893 case CB_FINDSTRINGEXACT:
894 case CB_SELECTSTRING:
895 case LB_ADDSTRING:
896 case LB_INSERTSTRING:
897 case LB_FINDSTRING:
898 case LB_FINDSTRINGEXACT:
899 case LB_SELECTSTRING:
900 if (!*buffer) return TRUE;
901 if (!check_string( *buffer, size )) return FALSE;
902 break;
903 case CB_GETLBTEXT:
905 size = sizeof(ULONG_PTR);
906 if (combobox_has_strings( hwnd ))
907 size = (SendMessageW( hwnd, CB_GETLBTEXTLEN, *wparam, 0 ) + 1) * sizeof(WCHAR);
908 if (!get_buffer_space( buffer, size )) return FALSE;
909 break;
911 case LB_GETTEXT:
913 size = sizeof(ULONG_PTR);
914 if (listbox_has_strings( hwnd ))
915 size = (SendMessageW( hwnd, LB_GETTEXTLEN, *wparam, 0 ) + 1) * sizeof(WCHAR);
916 if (!get_buffer_space( buffer, size )) return FALSE;
917 break;
919 case LB_GETSELITEMS:
920 if (!get_buffer_space( buffer, *wparam * sizeof(UINT) )) return FALSE;
921 break;
922 case WM_NEXTMENU:
923 minsize = sizeof(MDINEXTMENU);
924 if (!get_buffer_space( buffer, sizeof(MDINEXTMENU) )) return FALSE;
925 break;
926 case WM_SIZING:
927 case WM_MOVING:
928 minsize = sizeof(RECT);
929 if (!get_buffer_space( buffer, sizeof(RECT) )) return FALSE;
930 break;
931 case WM_MDICREATE:
933 MDICREATESTRUCTW *cs = *buffer;
934 WCHAR *str = (WCHAR *)(cs + 1);
935 if (size < sizeof(*cs)) return FALSE;
936 size -= sizeof(*cs);
937 if (HIWORD(cs->szTitle))
939 if (!check_string( str, size )) return FALSE;
940 cs->szTitle = str;
941 size -= (strlenW(str) + 1) * sizeof(WCHAR);
942 str += strlenW(str) + 1;
944 if (HIWORD(cs->szClass))
946 if (!check_string( str, size )) return FALSE;
947 cs->szClass = str;
949 break;
951 case WM_MDIGETACTIVE:
952 if (!*lparam) return TRUE;
953 if (!get_buffer_space( buffer, sizeof(BOOL) )) return FALSE;
954 break;
955 case WM_DEVICECHANGE:
956 minsize = sizeof(DEV_BROADCAST_HDR);
957 break;
958 case WM_WINE_KEYBOARD_LL_HOOK:
959 case WM_WINE_MOUSE_LL_HOOK:
961 struct hook_extra_info *h_extra = (struct hook_extra_info *)*buffer;
963 minsize = sizeof(struct hook_extra_info) +
964 (message == WM_WINE_KEYBOARD_LL_HOOK ? sizeof(KBDLLHOOKSTRUCT)
965 : sizeof(MSLLHOOKSTRUCT));
966 if (size < minsize) return FALSE;
967 h_extra->lparam = (LPARAM)(h_extra + 1);
968 break;
970 case WM_NCPAINT:
971 if (*wparam <= 1) return TRUE;
972 FIXME( "WM_NCPAINT hdc unpacking not supported\n" );
973 return FALSE;
974 case WM_PAINT:
975 if (!*wparam) return TRUE;
976 /* fall through */
978 /* these contain an HFONT */
979 case WM_SETFONT:
980 case WM_GETFONT:
981 /* these contain an HDC */
982 case WM_ERASEBKGND:
983 case WM_ICONERASEBKGND:
984 case WM_CTLCOLORMSGBOX:
985 case WM_CTLCOLOREDIT:
986 case WM_CTLCOLORLISTBOX:
987 case WM_CTLCOLORBTN:
988 case WM_CTLCOLORDLG:
989 case WM_CTLCOLORSCROLLBAR:
990 case WM_CTLCOLORSTATIC:
991 case WM_PRINT:
992 case WM_PRINTCLIENT:
993 /* these contain an HGLOBAL */
994 case WM_PAINTCLIPBOARD:
995 case WM_SIZECLIPBOARD:
996 /* these contain HICON */
997 case WM_GETICON:
998 case WM_SETICON:
999 case WM_QUERYDRAGICON:
1000 case WM_QUERYPARKICON:
1001 /* these contain pointers */
1002 case WM_DROPOBJECT:
1003 case WM_QUERYDROPOBJECT:
1004 case WM_DRAGLOOP:
1005 case WM_DRAGSELECT:
1006 case WM_DRAGMOVE:
1007 FIXME( "msg %x (%s) not supported yet\n", message, SPY_GetMsgName(message, hwnd) );
1008 return FALSE;
1010 default:
1011 return TRUE; /* message doesn't need any unpacking */
1014 /* default exit for most messages: check minsize and store buffer in lparam */
1015 if (size < minsize) return FALSE;
1016 *lparam = (LPARAM)*buffer;
1017 return TRUE;
1021 /***********************************************************************
1022 * pack_reply
1024 * Pack a reply to a message for sending to another process.
1026 static void pack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
1027 LRESULT res, struct packed_message *data )
1029 data->count = 0;
1030 switch(message)
1032 case WM_NCCREATE:
1033 case WM_CREATE:
1034 push_data( data, (CREATESTRUCTW *)lparam, sizeof(CREATESTRUCTW) );
1035 break;
1036 case WM_GETTEXT:
1037 case CB_GETLBTEXT:
1038 case LB_GETTEXT:
1039 push_data( data, (WCHAR *)lparam, (res + 1) * sizeof(WCHAR) );
1040 break;
1041 case WM_GETMINMAXINFO:
1042 push_data( data, (MINMAXINFO *)lparam, sizeof(MINMAXINFO) );
1043 break;
1044 case WM_MEASUREITEM:
1045 push_data( data, (MEASUREITEMSTRUCT *)lparam, sizeof(MEASUREITEMSTRUCT) );
1046 break;
1047 case WM_WINDOWPOSCHANGING:
1048 case WM_WINDOWPOSCHANGED:
1049 push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
1050 break;
1051 case WM_GETDLGCODE:
1052 if (lparam) push_data( data, (MSG *)lparam, sizeof(MSG) );
1053 break;
1054 case SBM_GETSCROLLINFO:
1055 push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
1056 break;
1057 case EM_GETRECT:
1058 case LB_GETITEMRECT:
1059 case CB_GETDROPPEDCONTROLRECT:
1060 case WM_SIZING:
1061 case WM_MOVING:
1062 push_data( data, (RECT *)lparam, sizeof(RECT) );
1063 break;
1064 case EM_GETLINE:
1066 WORD *ptr = (WORD *)lparam;
1067 push_data( data, ptr, ptr[-1] * sizeof(WCHAR) );
1068 break;
1070 case LB_GETSELITEMS:
1071 push_data( data, (UINT *)lparam, wparam * sizeof(UINT) );
1072 break;
1073 case WM_MDIGETACTIVE:
1074 if (lparam) push_data( data, (BOOL *)lparam, sizeof(BOOL) );
1075 break;
1076 case WM_NCCALCSIZE:
1077 if (!wparam)
1078 push_data( data, (RECT *)lparam, sizeof(RECT) );
1079 else
1081 NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
1082 push_data( data, nc, sizeof(*nc) );
1083 push_data( data, nc->lppos, sizeof(*nc->lppos) );
1085 break;
1086 case EM_GETSEL:
1087 case SBM_GETRANGE:
1088 case CB_GETEDITSEL:
1089 if (wparam) push_data( data, (DWORD *)wparam, sizeof(DWORD) );
1090 if (lparam) push_data( data, (DWORD *)lparam, sizeof(DWORD) );
1091 break;
1092 case WM_NEXTMENU:
1093 push_data( data, (MDINEXTMENU *)lparam, sizeof(MDINEXTMENU) );
1094 break;
1095 case WM_MDICREATE:
1096 push_data( data, (MDICREATESTRUCTW *)lparam, sizeof(MDICREATESTRUCTW) );
1097 break;
1098 case WM_ASKCBFORMATNAME:
1099 push_data( data, (WCHAR *)lparam, (strlenW((WCHAR *)lparam) + 1) * sizeof(WCHAR) );
1100 break;
1105 /***********************************************************************
1106 * unpack_reply
1108 * Unpack a message reply received from another process.
1110 static void unpack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
1111 void *buffer, size_t size )
1113 switch(message)
1115 case WM_NCCREATE:
1116 case WM_CREATE:
1118 CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
1119 LPCWSTR name = cs->lpszName, class = cs->lpszClass;
1120 memcpy( cs, buffer, min( sizeof(*cs), size ));
1121 cs->lpszName = name; /* restore the original pointers */
1122 cs->lpszClass = class;
1123 break;
1125 case WM_GETTEXT:
1126 case WM_ASKCBFORMATNAME:
1127 memcpy( (WCHAR *)lparam, buffer, min( wparam*sizeof(WCHAR), size ));
1128 break;
1129 case WM_GETMINMAXINFO:
1130 memcpy( (MINMAXINFO *)lparam, buffer, min( sizeof(MINMAXINFO), size ));
1131 break;
1132 case WM_MEASUREITEM:
1133 memcpy( (MEASUREITEMSTRUCT *)lparam, buffer, min( sizeof(MEASUREITEMSTRUCT), size ));
1134 break;
1135 case WM_WINDOWPOSCHANGING:
1136 case WM_WINDOWPOSCHANGED:
1137 memcpy( (WINDOWPOS *)lparam, buffer, min( sizeof(WINDOWPOS), size ));
1138 break;
1139 case WM_GETDLGCODE:
1140 if (lparam) memcpy( (MSG *)lparam, buffer, min( sizeof(MSG), size ));
1141 break;
1142 case SBM_GETSCROLLINFO:
1143 memcpy( (SCROLLINFO *)lparam, buffer, min( sizeof(SCROLLINFO), size ));
1144 break;
1145 case SBM_GETSCROLLBARINFO:
1146 memcpy( (SCROLLBARINFO *)lparam, buffer, min( sizeof(SCROLLBARINFO), size ));
1147 break;
1148 case EM_GETRECT:
1149 case CB_GETDROPPEDCONTROLRECT:
1150 case LB_GETITEMRECT:
1151 case WM_SIZING:
1152 case WM_MOVING:
1153 memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
1154 break;
1155 case EM_GETLINE:
1156 size = min( size, (size_t)*(WORD *)lparam );
1157 memcpy( (WCHAR *)lparam, buffer, size );
1158 break;
1159 case LB_GETSELITEMS:
1160 memcpy( (UINT *)lparam, buffer, min( wparam*sizeof(UINT), size ));
1161 break;
1162 case LB_GETTEXT:
1163 case CB_GETLBTEXT:
1164 memcpy( (WCHAR *)lparam, buffer, size );
1165 break;
1166 case WM_NEXTMENU:
1167 memcpy( (MDINEXTMENU *)lparam, buffer, min( sizeof(MDINEXTMENU), size ));
1168 break;
1169 case WM_MDIGETACTIVE:
1170 if (lparam) memcpy( (BOOL *)lparam, buffer, min( sizeof(BOOL), size ));
1171 break;
1172 case WM_NCCALCSIZE:
1173 if (!wparam)
1174 memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
1175 else
1177 NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
1178 WINDOWPOS *wp = nc->lppos;
1179 memcpy( nc, buffer, min( sizeof(*nc), size ));
1180 if (size > sizeof(*nc))
1182 size -= sizeof(*nc);
1183 memcpy( wp, (NCCALCSIZE_PARAMS*)buffer + 1, min( sizeof(*wp), size ));
1185 nc->lppos = wp; /* restore the original pointer */
1187 break;
1188 case EM_GETSEL:
1189 case SBM_GETRANGE:
1190 case CB_GETEDITSEL:
1191 if (wparam)
1193 memcpy( (DWORD *)wparam, buffer, min( sizeof(DWORD), size ));
1194 if (size <= sizeof(DWORD)) break;
1195 size -= sizeof(DWORD);
1196 buffer = (DWORD *)buffer + 1;
1198 if (lparam) memcpy( (DWORD *)lparam, buffer, min( sizeof(DWORD), size ));
1199 break;
1200 case WM_MDICREATE:
1202 MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)lparam;
1203 LPCWSTR title = cs->szTitle, class = cs->szClass;
1204 memcpy( cs, buffer, min( sizeof(*cs), size ));
1205 cs->szTitle = title; /* restore the original pointers */
1206 cs->szClass = class;
1207 break;
1209 default:
1210 ERR( "should not happen: unexpected message %x\n", message );
1211 break;
1216 /***********************************************************************
1217 * reply_message
1219 * Send a reply to a sent message.
1221 static void reply_message( struct received_message_info *info, LRESULT result, BOOL remove )
1223 struct packed_message data;
1224 int i, replied = info->flags & ISMEX_REPLIED;
1226 if (info->flags & ISMEX_NOTIFY) return; /* notify messages don't get replies */
1227 if (!remove && replied) return; /* replied already */
1229 data.count = 0;
1230 info->flags |= ISMEX_REPLIED;
1232 if (info->type == MSG_OTHER_PROCESS && !replied)
1234 pack_reply( info->msg.hwnd, info->msg.message, info->msg.wParam,
1235 info->msg.lParam, result, &data );
1238 SERVER_START_REQ( reply_message )
1240 req->result = result;
1241 req->remove = remove;
1242 for (i = 0; i < data.count; i++) wine_server_add_data( req, data.data[i], data.size[i] );
1243 wine_server_call( req );
1245 SERVER_END_REQ;
1249 /***********************************************************************
1250 * handle_internal_message
1252 * Handle an internal Wine message instead of calling the window proc.
1254 static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
1256 switch(msg)
1258 case WM_WINE_DESTROYWINDOW:
1259 return WIN_DestroyWindow( hwnd );
1260 case WM_WINE_SETWINDOWPOS:
1261 if (hwnd == GetDesktopWindow()) return 0;
1262 return USER_SetWindowPos( (WINDOWPOS *)lparam );
1263 case WM_WINE_SHOWWINDOW:
1264 if (hwnd == GetDesktopWindow()) return 0;
1265 return ShowWindow( hwnd, wparam );
1266 case WM_WINE_SETPARENT:
1267 if (hwnd == GetDesktopWindow()) return 0;
1268 return (LRESULT)SetParent( hwnd, (HWND)wparam );
1269 case WM_WINE_SETWINDOWLONG:
1270 return WIN_SetWindowLong( hwnd, (short)LOWORD(wparam), HIWORD(wparam), lparam, TRUE );
1271 case WM_WINE_ENABLEWINDOW:
1272 if (hwnd == GetDesktopWindow()) return 0;
1273 return EnableWindow( hwnd, wparam );
1274 case WM_WINE_SETACTIVEWINDOW:
1275 if (hwnd == GetDesktopWindow()) return 0;
1276 return (LRESULT)SetActiveWindow( (HWND)wparam );
1277 case WM_WINE_KEYBOARD_LL_HOOK:
1278 case WM_WINE_MOUSE_LL_HOOK:
1280 struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
1282 return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam );
1284 default:
1285 if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG)
1286 return USER_Driver->pWindowMessage( hwnd, msg, wparam, lparam );
1287 FIXME( "unknown internal message %x\n", msg );
1288 return 0;
1292 /* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
1293 * to the memory handle, we keep track (in the server side) of all pairs of handle
1294 * used (the client passes its value and the content of the memory handle), and
1295 * the server stored both values (the client, and the local one, created after the
1296 * content). When a ACK message is generated, the list of pair is searched for a
1297 * matching pair, so that the client memory handle can be returned.
1299 struct DDE_pair {
1300 HGLOBAL client_hMem;
1301 HGLOBAL server_hMem;
1304 static struct DDE_pair* dde_pairs;
1305 static int dde_num_alloc;
1306 static int dde_num_used;
1308 static CRITICAL_SECTION dde_crst;
1309 static CRITICAL_SECTION_DEBUG critsect_debug =
1311 0, 0, &dde_crst,
1312 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
1313 0, 0, { (DWORD_PTR)(__FILE__ ": dde_crst") }
1315 static CRITICAL_SECTION dde_crst = { &critsect_debug, -1, 0, 0, 0, 0 };
1317 static BOOL dde_add_pair(HGLOBAL chm, HGLOBAL shm)
1319 int i;
1320 #define GROWBY 4
1322 EnterCriticalSection(&dde_crst);
1324 /* now remember the pair of hMem on both sides */
1325 if (dde_num_used == dde_num_alloc)
1327 struct DDE_pair* tmp;
1328 if (dde_pairs)
1329 tmp = HeapReAlloc( GetProcessHeap(), 0, dde_pairs,
1330 (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
1331 else
1332 tmp = HeapAlloc( GetProcessHeap(), 0,
1333 (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
1335 if (!tmp)
1337 LeaveCriticalSection(&dde_crst);
1338 return FALSE;
1340 dde_pairs = tmp;
1341 /* zero out newly allocated part */
1342 memset(&dde_pairs[dde_num_alloc], 0, GROWBY * sizeof(struct DDE_pair));
1343 dde_num_alloc += GROWBY;
1345 #undef GROWBY
1346 for (i = 0; i < dde_num_alloc; i++)
1348 if (dde_pairs[i].server_hMem == 0)
1350 dde_pairs[i].client_hMem = chm;
1351 dde_pairs[i].server_hMem = shm;
1352 dde_num_used++;
1353 break;
1356 LeaveCriticalSection(&dde_crst);
1357 return TRUE;
1360 static HGLOBAL dde_get_pair(HGLOBAL shm)
1362 int i;
1363 HGLOBAL ret = 0;
1365 EnterCriticalSection(&dde_crst);
1366 for (i = 0; i < dde_num_alloc; i++)
1368 if (dde_pairs[i].server_hMem == shm)
1370 /* free this pair */
1371 dde_pairs[i].server_hMem = 0;
1372 dde_num_used--;
1373 ret = dde_pairs[i].client_hMem;
1374 break;
1377 LeaveCriticalSection(&dde_crst);
1378 return ret;
1381 /***********************************************************************
1382 * post_dde_message
1384 * Post a DDE message
1386 static BOOL post_dde_message( struct packed_message *data, const struct send_message_info *info )
1388 void* ptr = NULL;
1389 int size = 0;
1390 UINT_PTR uiLo, uiHi;
1391 LPARAM lp = 0;
1392 HGLOBAL hunlock = 0;
1393 int i;
1394 DWORD res;
1396 if (!UnpackDDElParam( info->msg, info->lparam, &uiLo, &uiHi ))
1397 return FALSE;
1399 lp = info->lparam;
1400 switch (info->msg)
1402 /* DDE messages which don't require packing are:
1403 * WM_DDE_INITIATE
1404 * WM_DDE_TERMINATE
1405 * WM_DDE_REQUEST
1406 * WM_DDE_UNADVISE
1408 case WM_DDE_ACK:
1409 if (HIWORD(uiHi))
1411 /* uiHi should contain a hMem from WM_DDE_EXECUTE */
1412 HGLOBAL h = dde_get_pair( (HANDLE)uiHi );
1413 if (h)
1415 /* send back the value of h on the other side */
1416 push_data( data, &h, sizeof(HGLOBAL) );
1417 lp = uiLo;
1418 TRACE( "send dde-ack %lx %08lx => %p\n", uiLo, uiHi, h );
1421 else
1423 /* uiHi should contain either an atom or 0 */
1424 TRACE( "send dde-ack %lx atom=%lx\n", uiLo, uiHi );
1425 lp = MAKELONG( uiLo, uiHi );
1427 break;
1428 case WM_DDE_ADVISE:
1429 case WM_DDE_DATA:
1430 case WM_DDE_POKE:
1431 size = 0;
1432 if (uiLo)
1434 size = GlobalSize( (HGLOBAL)uiLo ) ;
1435 if ((info->msg == WM_DDE_ADVISE && size < sizeof(DDEADVISE)) ||
1436 (info->msg == WM_DDE_DATA && size < sizeof(DDEDATA)) ||
1437 (info->msg == WM_DDE_POKE && size < sizeof(DDEPOKE))
1439 return FALSE;
1441 else if (info->msg != WM_DDE_DATA) return FALSE;
1443 lp = uiHi;
1444 if (uiLo)
1446 if ((ptr = GlobalLock( (HGLOBAL)uiLo) ))
1448 DDEDATA *dde_data = (DDEDATA *)ptr;
1449 TRACE("unused %d, fResponse %d, fRelease %d, fDeferUpd %d, fAckReq %d, cfFormat %d\n",
1450 dde_data->unused, dde_data->fResponse, dde_data->fRelease,
1451 dde_data->reserved, dde_data->fAckReq, dde_data->cfFormat);
1452 push_data( data, ptr, size );
1453 hunlock = (HGLOBAL)uiLo;
1456 TRACE( "send ddepack %u %lx\n", size, uiHi );
1457 break;
1458 case WM_DDE_EXECUTE:
1459 if (info->lparam)
1461 if ((ptr = GlobalLock( (HGLOBAL)info->lparam) ))
1463 push_data(data, ptr, GlobalSize( (HGLOBAL)info->lparam ));
1464 /* so that the other side can send it back on ACK */
1465 lp = info->lparam;
1466 hunlock = (HGLOBAL)info->lparam;
1469 break;
1471 SERVER_START_REQ( send_message )
1473 req->id = info->dest_tid;
1474 req->type = info->type;
1475 req->flags = 0;
1476 req->win = info->hwnd;
1477 req->msg = info->msg;
1478 req->wparam = info->wparam;
1479 req->lparam = lp;
1480 req->timeout = TIMEOUT_INFINITE;
1481 for (i = 0; i < data->count; i++)
1482 wine_server_add_data( req, data->data[i], data->size[i] );
1483 if ((res = wine_server_call( req )))
1485 if (res == STATUS_INVALID_PARAMETER)
1486 /* FIXME: find a STATUS_ value for this one */
1487 SetLastError( ERROR_INVALID_THREAD_ID );
1488 else
1489 SetLastError( RtlNtStatusToDosError(res) );
1491 else
1492 FreeDDElParam(info->msg, info->lparam);
1494 SERVER_END_REQ;
1495 if (hunlock) GlobalUnlock(hunlock);
1497 return !res;
1500 /***********************************************************************
1501 * unpack_dde_message
1503 * Unpack a posted DDE message received from another process.
1505 static BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
1506 void **buffer, size_t size )
1508 UINT_PTR uiLo, uiHi;
1509 HGLOBAL hMem = 0;
1510 void* ptr;
1512 switch (message)
1514 case WM_DDE_ACK:
1515 if (size)
1517 /* hMem is being passed */
1518 if (size != sizeof(HGLOBAL)) return FALSE;
1519 if (!buffer || !*buffer) return FALSE;
1520 uiLo = *lparam;
1521 memcpy( &hMem, *buffer, size );
1522 uiHi = (UINT_PTR)hMem;
1523 TRACE("recv dde-ack %lx mem=%lx[%lx]\n", uiLo, uiHi, GlobalSize( hMem ));
1525 else
1527 uiLo = LOWORD( *lparam );
1528 uiHi = HIWORD( *lparam );
1529 TRACE("recv dde-ack %lx atom=%lx\n", uiLo, uiHi);
1531 *lparam = PackDDElParam( WM_DDE_ACK, uiLo, uiHi );
1532 break;
1533 case WM_DDE_ADVISE:
1534 case WM_DDE_DATA:
1535 case WM_DDE_POKE:
1536 if ((!buffer || !*buffer) && message != WM_DDE_DATA) return FALSE;
1537 uiHi = *lparam;
1538 if (size)
1540 if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size )))
1541 return FALSE;
1542 if ((ptr = GlobalLock( hMem )))
1544 memcpy( ptr, *buffer, size );
1545 GlobalUnlock( hMem );
1547 else
1549 GlobalFree( hMem );
1550 return FALSE;
1553 uiLo = (UINT_PTR)hMem;
1555 *lparam = PackDDElParam( message, uiLo, uiHi );
1556 break;
1557 case WM_DDE_EXECUTE:
1558 if (size)
1560 if (!buffer || !*buffer) return FALSE;
1561 if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size ))) return FALSE;
1562 if ((ptr = GlobalLock( hMem )))
1564 memcpy( ptr, *buffer, size );
1565 GlobalUnlock( hMem );
1566 TRACE( "exec: pairing c=%08lx s=%p\n", *lparam, hMem );
1567 if (!dde_add_pair( (HGLOBAL)*lparam, hMem ))
1569 GlobalFree( hMem );
1570 return FALSE;
1573 else
1575 GlobalFree( hMem );
1576 return FALSE;
1578 } else return FALSE;
1579 *lparam = (LPARAM)hMem;
1580 break;
1582 return TRUE;
1585 /***********************************************************************
1586 * call_window_proc
1588 * Call a window procedure and the corresponding hooks.
1590 static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
1591 BOOL unicode, BOOL same_thread, enum wm_char_mapping mapping )
1593 struct user_thread_info *thread_info = get_user_thread_info();
1594 LRESULT result = 0;
1595 CWPSTRUCT cwp;
1596 CWPRETSTRUCT cwpret;
1598 if (thread_info->recursion_count > MAX_SENDMSG_RECURSION) return 0;
1599 thread_info->recursion_count++;
1601 if (msg & 0x80000000)
1603 result = handle_internal_message( hwnd, msg, wparam, lparam );
1604 goto done;
1607 /* first the WH_CALLWNDPROC hook */
1608 hwnd = WIN_GetFullHandle( hwnd );
1609 cwp.lParam = lparam;
1610 cwp.wParam = wparam;
1611 cwp.message = msg;
1612 cwp.hwnd = hwnd;
1613 HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, same_thread, (LPARAM)&cwp, unicode );
1615 /* now call the window procedure */
1616 if (!WINPROC_call_window( hwnd, msg, wparam, lparam, &result, unicode, mapping )) goto done;
1618 /* and finally the WH_CALLWNDPROCRET hook */
1619 cwpret.lResult = result;
1620 cwpret.lParam = lparam;
1621 cwpret.wParam = wparam;
1622 cwpret.message = msg;
1623 cwpret.hwnd = hwnd;
1624 HOOK_CallHooks( WH_CALLWNDPROCRET, HC_ACTION, same_thread, (LPARAM)&cwpret, unicode );
1625 done:
1626 thread_info->recursion_count--;
1627 return result;
1631 /***********************************************************************
1632 * send_parent_notify
1634 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1635 * the window has the WS_EX_NOPARENTNOTIFY style.
1637 static void send_parent_notify( HWND hwnd, WORD event, WORD idChild, POINT pt )
1639 /* pt has to be in the client coordinates of the parent window */
1640 MapWindowPoints( 0, hwnd, &pt, 1 );
1641 for (;;)
1643 HWND parent;
1645 if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)) break;
1646 if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) break;
1647 if (!(parent = GetParent(hwnd))) break;
1648 if (parent == GetDesktopWindow()) break;
1649 MapWindowPoints( hwnd, parent, &pt, 1 );
1650 hwnd = parent;
1651 SendMessageW( hwnd, WM_PARENTNOTIFY,
1652 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1657 /***********************************************************************
1658 * accept_hardware_message
1660 * Tell the server we have passed the message to the app
1661 * (even though we may end up dropping it later on)
1663 static void accept_hardware_message( UINT hw_id, BOOL remove, HWND new_hwnd )
1665 SERVER_START_REQ( accept_hardware_message )
1667 req->hw_id = hw_id;
1668 req->remove = remove;
1669 req->new_win = new_hwnd;
1670 if (wine_server_call( req ))
1671 FIXME("Failed to reply to MSG_HARDWARE message. Message may not be removed from queue.\n");
1673 SERVER_END_REQ;
1677 /***********************************************************************
1678 * process_keyboard_message
1680 * returns TRUE if the contents of 'msg' should be passed to the application
1682 static BOOL process_keyboard_message( MSG *msg, UINT hw_id, HWND hwnd_filter,
1683 UINT first, UINT last, BOOL remove )
1685 EVENTMSG event;
1687 if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ||
1688 msg->message == WM_KEYUP || msg->message == WM_SYSKEYUP)
1689 switch (msg->wParam)
1691 case VK_LSHIFT: case VK_RSHIFT:
1692 msg->wParam = VK_SHIFT;
1693 break;
1694 case VK_LCONTROL: case VK_RCONTROL:
1695 msg->wParam = VK_CONTROL;
1696 break;
1697 case VK_LMENU: case VK_RMENU:
1698 msg->wParam = VK_MENU;
1699 break;
1702 /* FIXME: is this really the right place for this hook? */
1703 event.message = msg->message;
1704 event.hwnd = msg->hwnd;
1705 event.time = msg->time;
1706 event.paramL = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
1707 event.paramH = msg->lParam & 0x7FFF;
1708 if (HIWORD(msg->lParam) & 0x0100) event.paramH |= 0x8000; /* special_key - bit */
1709 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event, TRUE );
1711 /* check message filters */
1712 if (msg->message < first || msg->message > last) return FALSE;
1713 if (!check_hwnd_filter( msg, hwnd_filter )) return FALSE;
1715 if (remove)
1717 if((msg->message == WM_KEYDOWN) &&
1718 (msg->hwnd != GetDesktopWindow()))
1720 /* Handle F1 key by sending out WM_HELP message */
1721 if (msg->wParam == VK_F1)
1723 PostMessageW( msg->hwnd, WM_KEYF1, 0, 0 );
1725 else if(msg->wParam >= VK_BROWSER_BACK &&
1726 msg->wParam <= VK_LAUNCH_APP2)
1728 /* FIXME: Process keystate */
1729 SendMessageW(msg->hwnd, WM_APPCOMMAND, (WPARAM)msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (msg->wParam - VK_BROWSER_BACK + 1))));
1732 else if (msg->message == WM_KEYUP)
1734 /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1735 if (msg->wParam == VK_APPS && !MENU_IsMenuActive())
1736 PostMessageW(msg->hwnd, WM_CONTEXTMENU, (WPARAM)msg->hwnd, (LPARAM)-1);
1740 if (HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
1741 LOWORD(msg->wParam), msg->lParam, TRUE ))
1743 /* skip this message */
1744 HOOK_CallHooks( WH_CBT, HCBT_KEYSKIPPED, LOWORD(msg->wParam), msg->lParam, TRUE );
1745 accept_hardware_message( hw_id, TRUE, 0 );
1746 return FALSE;
1748 accept_hardware_message( hw_id, remove, 0 );
1749 return TRUE;
1753 /***********************************************************************
1754 * process_mouse_message
1756 * returns TRUE if the contents of 'msg' should be passed to the application
1758 static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, HWND hwnd_filter,
1759 UINT first, UINT last, BOOL remove )
1761 static MSG clk_msg;
1763 POINT pt;
1764 UINT message;
1765 INT hittest;
1766 EVENTMSG event;
1767 GUITHREADINFO info;
1768 MOUSEHOOKSTRUCT hook;
1769 BOOL eatMsg;
1771 /* find the window to dispatch this mouse message to */
1773 GetGUIThreadInfo( GetCurrentThreadId(), &info );
1774 if (info.hwndCapture)
1776 hittest = HTCLIENT;
1777 msg->hwnd = info.hwndCapture;
1779 else
1781 msg->hwnd = WINPOS_WindowFromPoint( msg->hwnd, msg->pt, &hittest );
1784 if (!msg->hwnd || !WIN_IsCurrentThread( msg->hwnd ))
1786 accept_hardware_message( hw_id, TRUE, msg->hwnd );
1787 return FALSE;
1790 /* FIXME: is this really the right place for this hook? */
1791 event.message = msg->message;
1792 event.time = msg->time;
1793 event.hwnd = msg->hwnd;
1794 event.paramL = msg->pt.x;
1795 event.paramH = msg->pt.y;
1796 HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event, TRUE );
1798 if (!check_hwnd_filter( msg, hwnd_filter )) return FALSE;
1800 pt = msg->pt;
1801 message = msg->message;
1802 /* Note: windows has no concept of a non-client wheel message */
1803 if (message != WM_MOUSEWHEEL)
1805 if (hittest != HTCLIENT)
1807 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1808 msg->wParam = hittest;
1810 else
1812 /* coordinates don't get translated while tracking a menu */
1813 /* FIXME: should differentiate popups and top-level menus */
1814 if (!(info.flags & GUI_INMENUMODE))
1815 ScreenToClient( msg->hwnd, &pt );
1818 msg->lParam = MAKELONG( pt.x, pt.y );
1820 /* translate double clicks */
1822 if ((msg->message == WM_LBUTTONDOWN) ||
1823 (msg->message == WM_RBUTTONDOWN) ||
1824 (msg->message == WM_MBUTTONDOWN) ||
1825 (msg->message == WM_XBUTTONDOWN))
1827 BOOL update = remove;
1829 /* translate double clicks -
1830 * note that ...MOUSEMOVEs can slip in between
1831 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1833 if ((info.flags & (GUI_INMENUMODE|GUI_INMOVESIZE)) ||
1834 hittest != HTCLIENT ||
1835 (GetClassLongA( msg->hwnd, GCL_STYLE ) & CS_DBLCLKS))
1837 if ((msg->message == clk_msg.message) &&
1838 (msg->hwnd == clk_msg.hwnd) &&
1839 (msg->wParam == clk_msg.wParam) &&
1840 (msg->time - clk_msg.time < GetDoubleClickTime()) &&
1841 (abs(msg->pt.x - clk_msg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1842 (abs(msg->pt.y - clk_msg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
1844 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1845 if (update)
1847 clk_msg.message = 0; /* clear the double click conditions */
1848 update = FALSE;
1852 if (message < first || message > last) return FALSE;
1853 /* update static double click conditions */
1854 if (update) clk_msg = *msg;
1856 else
1858 if (message < first || message > last) return FALSE;
1861 /* message is accepted now (but may still get dropped) */
1863 hook.pt = msg->pt;
1864 hook.hwnd = msg->hwnd;
1865 hook.wHitTestCode = hittest;
1866 hook.dwExtraInfo = extra_info;
1867 if (HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
1868 message, (LPARAM)&hook, TRUE ))
1870 hook.pt = msg->pt;
1871 hook.hwnd = msg->hwnd;
1872 hook.wHitTestCode = hittest;
1873 hook.dwExtraInfo = extra_info;
1874 HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook, TRUE );
1875 accept_hardware_message( hw_id, TRUE, 0 );
1876 return FALSE;
1879 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1881 SendMessageW( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1882 MAKELONG( hittest, msg->message ));
1883 accept_hardware_message( hw_id, TRUE, 0 );
1884 return FALSE;
1887 accept_hardware_message( hw_id, remove, 0 );
1889 if (!remove || info.hwndCapture)
1891 msg->message = message;
1892 return TRUE;
1895 eatMsg = FALSE;
1897 if ((msg->message == WM_LBUTTONDOWN) ||
1898 (msg->message == WM_RBUTTONDOWN) ||
1899 (msg->message == WM_MBUTTONDOWN) ||
1900 (msg->message == WM_XBUTTONDOWN))
1902 /* Send the WM_PARENTNOTIFY,
1903 * note that even for double/nonclient clicks
1904 * notification message is still WM_L/M/RBUTTONDOWN.
1906 send_parent_notify( msg->hwnd, msg->message, 0, msg->pt );
1908 /* Activate the window if needed */
1910 if (msg->hwnd != info.hwndActive)
1912 HWND hwndTop = msg->hwnd;
1913 while (hwndTop)
1915 if ((GetWindowLongW( hwndTop, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
1916 hwndTop = GetParent( hwndTop );
1919 if (hwndTop && hwndTop != GetDesktopWindow())
1921 LONG ret = SendMessageW( msg->hwnd, WM_MOUSEACTIVATE, (WPARAM)hwndTop,
1922 MAKELONG( hittest, msg->message ) );
1923 switch(ret)
1925 case MA_NOACTIVATEANDEAT:
1926 eatMsg = TRUE;
1927 /* fall through */
1928 case MA_NOACTIVATE:
1929 break;
1930 case MA_ACTIVATEANDEAT:
1931 eatMsg = TRUE;
1932 /* fall through */
1933 case MA_ACTIVATE:
1934 case 0:
1935 if (!FOCUS_MouseActivate( hwndTop )) eatMsg = TRUE;
1936 break;
1937 default:
1938 WARN( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1939 break;
1945 /* send the WM_SETCURSOR message */
1947 /* Windows sends the normal mouse message as the message parameter
1948 in the WM_SETCURSOR message even if it's non-client mouse message */
1949 SendMessageW( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1951 msg->message = message;
1952 return !eatMsg;
1956 /***********************************************************************
1957 * process_hardware_message
1959 * Process a hardware message; return TRUE if message should be passed on to the app
1961 static BOOL process_hardware_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, HWND hwnd_filter,
1962 UINT first, UINT last, BOOL remove )
1964 if (is_keyboard_message( msg->message ))
1965 return process_keyboard_message( msg, hw_id, hwnd_filter, first, last, remove );
1967 if (is_mouse_message( msg->message ))
1968 return process_mouse_message( msg, hw_id, extra_info, hwnd_filter, first, last, remove );
1970 ERR( "unknown message type %x\n", msg->message );
1971 return FALSE;
1975 /***********************************************************************
1976 * call_sendmsg_callback
1978 * Call the callback function of SendMessageCallback.
1980 static inline void call_sendmsg_callback( SENDASYNCPROC callback, HWND hwnd, UINT msg,
1981 ULONG_PTR data, LRESULT result )
1983 if (!callback) return;
1985 if (TRACE_ON(relay))
1986 DPRINTF( "%04x:Call message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
1987 GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
1988 data, result );
1989 callback( hwnd, msg, data, result );
1990 if (TRACE_ON(relay))
1991 DPRINTF( "%04x:Ret message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
1992 GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
1993 data, result );
1997 /***********************************************************************
1998 * peek_message
2000 * Peek for a message matching the given parameters. Return FALSE if none available.
2001 * All pending sent messages are processed before returning.
2003 static BOOL peek_message( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags )
2005 LRESULT result;
2006 ULONG_PTR extra_info = 0;
2007 struct user_thread_info *thread_info = get_user_thread_info();
2008 struct received_message_info info, *old_info;
2009 unsigned int wake_mask, changed_mask = HIWORD(flags);
2010 unsigned int hw_id = 0; /* id of previous hardware message */
2012 if (!first && !last) last = ~0;
2013 if (!changed_mask) changed_mask = QS_ALLINPUT;
2014 wake_mask = changed_mask & (QS_SENDMESSAGE | QS_SMRESULT);
2016 for (;;)
2018 NTSTATUS res;
2019 void *buffer = NULL;
2020 size_t size = 0, buffer_size = 0;
2022 do /* loop while buffer is too small */
2024 if (buffer_size && !(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size )))
2025 return FALSE;
2026 SERVER_START_REQ( get_message )
2028 req->flags = flags;
2029 req->get_win = hwnd;
2030 req->get_first = first;
2031 req->get_last = last;
2032 req->hw_id = hw_id;
2033 req->wake_mask = wake_mask;
2034 req->changed_mask = changed_mask;
2035 if (buffer_size) wine_server_set_reply( req, buffer, buffer_size );
2036 if (!(res = wine_server_call( req )))
2038 size = wine_server_reply_size( reply );
2039 info.type = reply->type;
2040 info.msg.hwnd = reply->win;
2041 info.msg.message = reply->msg;
2042 info.msg.wParam = reply->wparam;
2043 info.msg.lParam = reply->lparam;
2044 info.msg.time = reply->time;
2045 info.msg.pt.x = reply->x;
2046 info.msg.pt.y = reply->y;
2047 hw_id = reply->hw_id;
2048 extra_info = reply->info;
2049 thread_info->active_hooks = reply->active_hooks;
2051 else
2053 HeapFree( GetProcessHeap(), 0, buffer );
2054 buffer_size = reply->total;
2057 SERVER_END_REQ;
2058 } while (res == STATUS_BUFFER_OVERFLOW);
2060 if (res) return FALSE;
2062 TRACE( "got type %d msg %x (%s) hwnd %p wp %lx lp %lx\n",
2063 info.type, info.msg.message,
2064 (info.type == MSG_WINEVENT) ? "MSG_WINEVENT" : SPY_GetMsgName(info.msg.message, info.msg.hwnd),
2065 info.msg.hwnd, info.msg.wParam, info.msg.lParam );
2067 switch(info.type)
2069 case MSG_ASCII:
2070 case MSG_UNICODE:
2071 info.flags = ISMEX_SEND;
2072 break;
2073 case MSG_NOTIFY:
2074 info.flags = ISMEX_NOTIFY;
2075 break;
2076 case MSG_CALLBACK:
2077 info.flags = ISMEX_CALLBACK;
2078 break;
2079 case MSG_CALLBACK_RESULT:
2080 if (size >= sizeof(struct callback_msg_data))
2082 const struct callback_msg_data *data = (const struct callback_msg_data *)buffer;
2083 call_sendmsg_callback( data->callback, info.msg.hwnd,
2084 info.msg.message, data->data, data->result );
2086 goto next;
2087 case MSG_WINEVENT:
2088 if (size >= sizeof(struct winevent_msg_data))
2090 WINEVENTPROC hook_proc;
2091 const struct winevent_msg_data *data = (const struct winevent_msg_data *)buffer;
2093 hook_proc = data->hook_proc;
2094 size -= sizeof(*data);
2095 if (size)
2097 WCHAR module[MAX_PATH];
2099 size = min( size, (MAX_PATH - 1) * sizeof(WCHAR) );
2100 memcpy( module, buffer, size );
2101 module[size / sizeof(WCHAR)] = 0;
2102 if (!(hook_proc = get_hook_proc( hook_proc, module )))
2104 ERR( "invalid winevent hook module name %s\n", debugstr_w(module) );
2105 goto next;
2109 if (TRACE_ON(relay))
2110 DPRINTF( "%04x:Call winevent proc %p (hook=%p,event=%x,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04x,time=%x)\n",
2111 GetCurrentThreadId(), hook_proc,
2112 data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2113 info.msg.lParam, data->tid, info.msg.time);
2115 hook_proc( data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2116 info.msg.lParam, data->tid, info.msg.time );
2118 if (TRACE_ON(relay))
2119 DPRINTF( "%04x:Ret winevent proc %p (hook=%p,event=%x,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04x,time=%x)\n",
2120 GetCurrentThreadId(), hook_proc,
2121 data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2122 info.msg.lParam, data->tid, info.msg.time);
2124 goto next;
2125 case MSG_OTHER_PROCESS:
2126 info.flags = ISMEX_SEND;
2127 if (!unpack_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
2128 &info.msg.lParam, &buffer, size ))
2130 /* ignore it */
2131 reply_message( &info, 0, TRUE );
2132 goto next;
2134 break;
2135 case MSG_HARDWARE:
2136 if (!process_hardware_message( &info.msg, hw_id, extra_info,
2137 hwnd, first, last, flags & PM_REMOVE ))
2139 TRACE("dropping msg %x\n", info.msg.message );
2140 goto next; /* ignore it */
2142 thread_info->GetMessagePosVal = MAKELONG( info.msg.pt.x, info.msg.pt.y );
2143 /* fall through */
2144 case MSG_POSTED:
2145 thread_info->GetMessageExtraInfoVal = extra_info;
2146 if (info.msg.message >= WM_DDE_FIRST && info.msg.message <= WM_DDE_LAST)
2148 if (!unpack_dde_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
2149 &info.msg.lParam, &buffer, size ))
2150 goto next; /* ignore it */
2152 *msg = info.msg;
2153 HeapFree( GetProcessHeap(), 0, buffer );
2154 return TRUE;
2157 /* if we get here, we have a sent message; call the window procedure */
2158 old_info = thread_info->receive_info;
2159 thread_info->receive_info = &info;
2160 result = call_window_proc( info.msg.hwnd, info.msg.message, info.msg.wParam,
2161 info.msg.lParam, (info.type != MSG_ASCII), FALSE,
2162 WMCHAR_MAP_RECVMESSAGE );
2163 reply_message( &info, result, TRUE );
2164 thread_info->receive_info = old_info;
2166 /* if some PM_QS* flags were specified, only handle sent messages from now on */
2167 if (HIWORD(flags)) flags = PM_QS_SENDMESSAGE | LOWORD(flags);
2168 next:
2169 HeapFree( GetProcessHeap(), 0, buffer );
2174 /***********************************************************************
2175 * process_sent_messages
2177 * Process all pending sent messages.
2179 static inline void process_sent_messages(void)
2181 MSG msg;
2182 peek_message( &msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE );
2186 /***********************************************************************
2187 * get_server_queue_handle
2189 * Get a handle to the server message queue for the current thread.
2191 static HANDLE get_server_queue_handle(void)
2193 struct user_thread_info *thread_info = get_user_thread_info();
2194 HANDLE ret;
2196 if (!(ret = thread_info->server_queue))
2198 SERVER_START_REQ( get_msg_queue )
2200 wine_server_call( req );
2201 ret = reply->handle;
2203 SERVER_END_REQ;
2204 thread_info->server_queue = ret;
2205 if (!ret) ERR( "Cannot get server thread queue\n" );
2207 return ret;
2211 /***********************************************************************
2212 * wait_message_reply
2214 * Wait until a sent message gets replied to.
2216 static void wait_message_reply( UINT flags )
2218 HANDLE server_queue = get_server_queue_handle();
2220 for (;;)
2222 unsigned int wake_bits = 0, changed_bits = 0;
2223 DWORD dwlc, res;
2225 SERVER_START_REQ( set_queue_mask )
2227 req->wake_mask = QS_SMRESULT | ((flags & SMTO_BLOCK) ? 0 : QS_SENDMESSAGE);
2228 req->changed_mask = req->wake_mask;
2229 req->skip_wait = 1;
2230 if (!wine_server_call( req ))
2232 wake_bits = reply->wake_bits;
2233 changed_bits = reply->changed_bits;
2236 SERVER_END_REQ;
2238 if (wake_bits & QS_SMRESULT) return; /* got a result */
2239 if (wake_bits & QS_SENDMESSAGE)
2241 /* Process the sent message immediately */
2242 process_sent_messages();
2243 continue;
2246 /* now wait for it */
2248 ReleaseThunkLock( &dwlc );
2249 res = USER_Driver->pMsgWaitForMultipleObjectsEx( 1, &server_queue,
2250 INFINITE, QS_SENDMESSAGE, 0 );
2251 if (dwlc) RestoreThunkLock( dwlc );
2255 /***********************************************************************
2256 * put_message_in_queue
2258 * Put a sent message into the destination queue.
2259 * For inter-process message, reply_size is set to expected size of reply data.
2261 static BOOL put_message_in_queue( const struct send_message_info *info, size_t *reply_size )
2263 struct packed_message data;
2264 message_data_t msg_data;
2265 unsigned int res;
2266 int i;
2267 timeout_t timeout = TIMEOUT_INFINITE;
2269 /* Check for INFINITE timeout for compatibility with Win9x,
2270 * although Windows >= NT does not do so
2272 if (info->type != MSG_NOTIFY &&
2273 info->type != MSG_CALLBACK &&
2274 info->type != MSG_POSTED &&
2275 info->timeout &&
2276 info->timeout != INFINITE)
2278 /* timeout is signed despite the prototype */
2279 timeout = (timeout_t)max( 0, (int)info->timeout ) * -10000;
2282 data.count = 0;
2283 if (info->type == MSG_OTHER_PROCESS)
2285 *reply_size = pack_message( info->hwnd, info->msg, info->wparam, info->lparam, &data );
2286 if (data.count == -1)
2288 WARN( "cannot pack message %x\n", info->msg );
2289 return FALSE;
2292 else if (info->type == MSG_CALLBACK)
2294 msg_data.callback.callback = info->callback;
2295 msg_data.callback.data = info->data;
2296 msg_data.callback.result = 0;
2297 data.data[0] = &msg_data;
2298 data.size[0] = sizeof(msg_data.callback);
2299 data.count = 1;
2301 else if (info->type == MSG_POSTED && info->msg >= WM_DDE_FIRST && info->msg <= WM_DDE_LAST)
2303 return post_dde_message( &data, info );
2306 SERVER_START_REQ( send_message )
2308 req->id = info->dest_tid;
2309 req->type = info->type;
2310 req->flags = 0;
2311 req->win = info->hwnd;
2312 req->msg = info->msg;
2313 req->wparam = info->wparam;
2314 req->lparam = info->lparam;
2315 req->timeout = timeout;
2317 if (info->flags & SMTO_ABORTIFHUNG) req->flags |= SEND_MSG_ABORT_IF_HUNG;
2318 for (i = 0; i < data.count; i++) wine_server_add_data( req, data.data[i], data.size[i] );
2319 if ((res = wine_server_call( req )))
2321 if (res == STATUS_INVALID_PARAMETER)
2322 /* FIXME: find a STATUS_ value for this one */
2323 SetLastError( ERROR_INVALID_THREAD_ID );
2324 else
2325 SetLastError( RtlNtStatusToDosError(res) );
2328 SERVER_END_REQ;
2329 return !res;
2333 /***********************************************************************
2334 * retrieve_reply
2336 * Retrieve a message reply from the server.
2338 static LRESULT retrieve_reply( const struct send_message_info *info,
2339 size_t reply_size, LRESULT *result )
2341 NTSTATUS status;
2342 void *reply_data = NULL;
2344 if (reply_size)
2346 if (!(reply_data = HeapAlloc( GetProcessHeap(), 0, reply_size )))
2348 WARN( "no memory for reply, will be truncated\n" );
2349 reply_size = 0;
2352 SERVER_START_REQ( get_message_reply )
2354 req->cancel = 1;
2355 if (reply_size) wine_server_set_reply( req, reply_data, reply_size );
2356 if (!(status = wine_server_call( req ))) *result = reply->result;
2357 reply_size = wine_server_reply_size( reply );
2359 SERVER_END_REQ;
2360 if (!status && reply_size)
2361 unpack_reply( info->hwnd, info->msg, info->wparam, info->lparam, reply_data, reply_size );
2363 HeapFree( GetProcessHeap(), 0, reply_data );
2365 TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx got reply %lx (err=%d)\n",
2366 info->hwnd, info->msg, SPY_GetMsgName(info->msg, info->hwnd), info->wparam,
2367 info->lparam, *result, status );
2369 /* MSDN states that last error is 0 on timeout, but at least NT4 returns ERROR_TIMEOUT */
2370 if (status) SetLastError( RtlNtStatusToDosError(status) );
2371 return !status;
2375 /***********************************************************************
2376 * send_inter_thread_message
2378 static LRESULT send_inter_thread_message( const struct send_message_info *info, LRESULT *res_ptr )
2380 size_t reply_size = 0;
2382 TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx\n",
2383 info->hwnd, info->msg, SPY_GetMsgName(info->msg, info->hwnd), info->wparam, info->lparam );
2385 USER_CheckNotLock();
2387 if (!put_message_in_queue( info, &reply_size )) return 0;
2389 /* there's no reply to wait for on notify/callback messages */
2390 if (info->type == MSG_NOTIFY || info->type == MSG_CALLBACK) return 1;
2392 wait_message_reply( info->flags );
2393 return retrieve_reply( info, reply_size, res_ptr );
2397 /***********************************************************************
2398 * send_inter_thread_callback
2400 static LRESULT send_inter_thread_callback( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
2401 LRESULT *result, void *arg )
2403 struct send_message_info *info = arg;
2404 info->hwnd = hwnd;
2405 info->msg = msg;
2406 info->wparam = wp;
2407 info->lparam = lp;
2408 return send_inter_thread_message( info, result );
2412 /***********************************************************************
2413 * send_message
2415 * Backend implementation of the various SendMessage functions.
2417 static BOOL send_message( struct send_message_info *info, DWORD_PTR *res_ptr, BOOL unicode )
2419 DWORD dest_pid;
2420 BOOL ret;
2421 LRESULT result;
2423 if (is_broadcast(info->hwnd))
2425 EnumWindows( broadcast_message_callback, (LPARAM)info );
2426 if (res_ptr) *res_ptr = 1;
2427 return TRUE;
2430 if (!(info->dest_tid = GetWindowThreadProcessId( info->hwnd, &dest_pid ))) return FALSE;
2432 if (USER_IsExitingThread( info->dest_tid )) return FALSE;
2434 SPY_EnterMessage( SPY_SENDMESSAGE, info->hwnd, info->msg, info->wparam, info->lparam );
2436 if (info->dest_tid == GetCurrentThreadId())
2438 result = call_window_proc( info->hwnd, info->msg, info->wparam, info->lparam,
2439 unicode, TRUE, info->wm_char );
2440 if (info->type == MSG_CALLBACK)
2441 call_sendmsg_callback( info->callback, info->hwnd, info->msg, info->data, result );
2442 ret = TRUE;
2444 else
2446 if (dest_pid != GetCurrentProcessId() && (info->type == MSG_ASCII || info->type == MSG_UNICODE))
2447 info->type = MSG_OTHER_PROCESS;
2449 /* MSG_ASCII can be sent unconverted except for WM_CHAR; everything else needs to be Unicode */
2450 if (!unicode && is_unicode_message( info->msg ) &&
2451 (info->type != MSG_ASCII || info->msg == WM_CHAR))
2452 ret = WINPROC_CallProcAtoW( send_inter_thread_callback, info->hwnd, info->msg,
2453 info->wparam, info->lparam, &result, info, info->wm_char );
2454 else
2455 ret = send_inter_thread_message( info, &result );
2458 SPY_ExitMessage( SPY_RESULT_OK, info->hwnd, info->msg, result, info->wparam, info->lparam );
2459 if (ret && res_ptr) *res_ptr = result;
2460 return ret;
2464 /***********************************************************************
2465 * MSG_SendInternalMessageTimeout
2467 * Same as SendMessageTimeoutW but sends the message to a specific thread
2468 * without requiring a window handle. Only works for internal Wine messages.
2470 LRESULT MSG_SendInternalMessageTimeout( DWORD dest_pid, DWORD dest_tid,
2471 UINT msg, WPARAM wparam, LPARAM lparam,
2472 UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2474 struct send_message_info info;
2475 LRESULT ret, result;
2477 assert( msg & 0x80000000 ); /* must be an internal Wine message */
2479 info.type = MSG_UNICODE;
2480 info.dest_tid = dest_tid;
2481 info.hwnd = 0;
2482 info.msg = msg;
2483 info.wparam = wparam;
2484 info.lparam = lparam;
2485 info.flags = flags;
2486 info.timeout = timeout;
2488 if (USER_IsExitingThread( dest_tid )) return 0;
2490 if (dest_tid == GetCurrentThreadId())
2492 result = handle_internal_message( 0, msg, wparam, lparam );
2493 ret = 1;
2495 else
2497 if (dest_pid != GetCurrentProcessId()) info.type = MSG_OTHER_PROCESS;
2498 ret = send_inter_thread_message( &info, &result );
2500 if (ret && res_ptr) *res_ptr = result;
2501 return ret;
2505 /***********************************************************************
2506 * SendMessageTimeoutW (USER32.@)
2508 LRESULT WINAPI SendMessageTimeoutW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2509 UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2511 struct send_message_info info;
2513 info.type = MSG_UNICODE;
2514 info.hwnd = hwnd;
2515 info.msg = msg;
2516 info.wparam = wparam;
2517 info.lparam = lparam;
2518 info.flags = flags;
2519 info.timeout = timeout;
2521 return send_message( &info, res_ptr, TRUE );
2524 /***********************************************************************
2525 * SendMessageTimeoutA (USER32.@)
2527 LRESULT WINAPI SendMessageTimeoutA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2528 UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2530 struct send_message_info info;
2532 info.type = MSG_ASCII;
2533 info.hwnd = hwnd;
2534 info.msg = msg;
2535 info.wparam = wparam;
2536 info.lparam = lparam;
2537 info.flags = flags;
2538 info.timeout = timeout;
2539 info.wm_char = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2541 return send_message( &info, res_ptr, FALSE );
2545 /***********************************************************************
2546 * SendMessageW (USER32.@)
2548 LRESULT WINAPI SendMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2550 DWORD_PTR res = 0;
2551 struct send_message_info info;
2553 info.type = MSG_UNICODE;
2554 info.hwnd = hwnd;
2555 info.msg = msg;
2556 info.wparam = wparam;
2557 info.lparam = lparam;
2558 info.flags = SMTO_NORMAL;
2559 info.timeout = 0;
2561 send_message( &info, &res, TRUE );
2562 return res;
2566 /***********************************************************************
2567 * SendMessageA (USER32.@)
2569 LRESULT WINAPI SendMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2571 DWORD_PTR res = 0;
2572 struct send_message_info info;
2574 info.type = MSG_ASCII;
2575 info.hwnd = hwnd;
2576 info.msg = msg;
2577 info.wparam = wparam;
2578 info.lparam = lparam;
2579 info.flags = SMTO_NORMAL;
2580 info.timeout = 0;
2581 info.wm_char = WMCHAR_MAP_SENDMESSAGE;
2583 send_message( &info, &res, FALSE );
2584 return res;
2588 /***********************************************************************
2589 * SendNotifyMessageA (USER32.@)
2591 BOOL WINAPI SendNotifyMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2593 struct send_message_info info;
2595 if (is_pointer_message(msg))
2597 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2598 return FALSE;
2601 info.type = MSG_NOTIFY;
2602 info.hwnd = hwnd;
2603 info.msg = msg;
2604 info.wparam = wparam;
2605 info.lparam = lparam;
2606 info.flags = 0;
2607 info.wm_char = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2609 return send_message( &info, NULL, FALSE );
2613 /***********************************************************************
2614 * SendNotifyMessageW (USER32.@)
2616 BOOL WINAPI SendNotifyMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2618 struct send_message_info info;
2620 if (is_pointer_message(msg))
2622 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2623 return FALSE;
2626 info.type = MSG_NOTIFY;
2627 info.hwnd = hwnd;
2628 info.msg = msg;
2629 info.wparam = wparam;
2630 info.lparam = lparam;
2631 info.flags = 0;
2633 return send_message( &info, NULL, TRUE );
2637 /***********************************************************************
2638 * SendMessageCallbackA (USER32.@)
2640 BOOL WINAPI SendMessageCallbackA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2641 SENDASYNCPROC callback, ULONG_PTR data )
2643 struct send_message_info info;
2645 if (is_pointer_message(msg))
2647 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2648 return FALSE;
2651 info.type = MSG_CALLBACK;
2652 info.hwnd = hwnd;
2653 info.msg = msg;
2654 info.wparam = wparam;
2655 info.lparam = lparam;
2656 info.callback = callback;
2657 info.data = data;
2658 info.flags = 0;
2659 info.wm_char = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2661 return send_message( &info, NULL, FALSE );
2665 /***********************************************************************
2666 * SendMessageCallbackW (USER32.@)
2668 BOOL WINAPI SendMessageCallbackW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2669 SENDASYNCPROC callback, ULONG_PTR data )
2671 struct send_message_info info;
2673 if (is_pointer_message(msg))
2675 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2676 return FALSE;
2679 info.type = MSG_CALLBACK;
2680 info.hwnd = hwnd;
2681 info.msg = msg;
2682 info.wparam = wparam;
2683 info.lparam = lparam;
2684 info.callback = callback;
2685 info.data = data;
2686 info.flags = 0;
2688 return send_message( &info, NULL, TRUE );
2692 /***********************************************************************
2693 * ReplyMessage (USER32.@)
2695 BOOL WINAPI ReplyMessage( LRESULT result )
2697 struct received_message_info *info = get_user_thread_info()->receive_info;
2699 if (!info) return FALSE;
2700 reply_message( info, result, FALSE );
2701 return TRUE;
2705 /***********************************************************************
2706 * InSendMessage (USER32.@)
2708 BOOL WINAPI InSendMessage(void)
2710 return (InSendMessageEx(NULL) & (ISMEX_SEND|ISMEX_REPLIED)) == ISMEX_SEND;
2714 /***********************************************************************
2715 * InSendMessageEx (USER32.@)
2717 DWORD WINAPI InSendMessageEx( LPVOID reserved )
2719 struct received_message_info *info = get_user_thread_info()->receive_info;
2721 if (info) return info->flags;
2722 return ISMEX_NOSEND;
2726 /***********************************************************************
2727 * PostMessageA (USER32.@)
2729 BOOL WINAPI PostMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2731 if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
2732 return PostMessageW( hwnd, msg, wparam, lparam );
2736 /***********************************************************************
2737 * PostMessageW (USER32.@)
2739 BOOL WINAPI PostMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2741 struct send_message_info info;
2743 if (is_pointer_message( msg ))
2745 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2746 return FALSE;
2749 TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx\n",
2750 hwnd, msg, SPY_GetMsgName(msg, hwnd), wparam, lparam );
2752 info.type = MSG_POSTED;
2753 info.hwnd = hwnd;
2754 info.msg = msg;
2755 info.wparam = wparam;
2756 info.lparam = lparam;
2757 info.flags = 0;
2759 if (is_broadcast(hwnd))
2761 EnumWindows( broadcast_message_callback, (LPARAM)&info );
2762 return TRUE;
2765 if (!hwnd) return PostThreadMessageW( GetCurrentThreadId(), msg, wparam, lparam );
2767 if (!(info.dest_tid = GetWindowThreadProcessId( hwnd, NULL ))) return FALSE;
2769 if (USER_IsExitingThread( info.dest_tid )) return TRUE;
2771 return put_message_in_queue( &info, NULL );
2775 /**********************************************************************
2776 * PostThreadMessageA (USER32.@)
2778 BOOL WINAPI PostThreadMessageA( DWORD thread, UINT msg, WPARAM wparam, LPARAM lparam )
2780 if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
2781 return PostThreadMessageW( thread, msg, wparam, lparam );
2785 /**********************************************************************
2786 * PostThreadMessageW (USER32.@)
2788 BOOL WINAPI PostThreadMessageW( DWORD thread, UINT msg, WPARAM wparam, LPARAM lparam )
2790 struct send_message_info info;
2792 if (is_pointer_message( msg ))
2794 SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2795 return FALSE;
2797 if (USER_IsExitingThread( thread )) return TRUE;
2799 info.type = MSG_POSTED;
2800 info.dest_tid = thread;
2801 info.hwnd = 0;
2802 info.msg = msg;
2803 info.wparam = wparam;
2804 info.lparam = lparam;
2805 info.flags = 0;
2806 return put_message_in_queue( &info, NULL );
2810 /***********************************************************************
2811 * PostQuitMessage (USER32.@)
2813 * Posts a quit message to the current thread's message queue.
2815 * PARAMS
2816 * exit_code [I] Exit code to return from message loop.
2818 * RETURNS
2819 * Nothing.
2821 * NOTES
2822 * This function is not the same as calling:
2823 *|PostThreadMessage(GetCurrentThreadId(), WM_QUIT, exit_code, 0);
2824 * It instead sets a flag in the message queue that signals it to generate
2825 * a WM_QUIT message when there are no other pending sent or posted messages
2826 * in the queue.
2828 void WINAPI PostQuitMessage( INT exit_code )
2830 SERVER_START_REQ( post_quit_message )
2832 req->exit_code = exit_code;
2833 wine_server_call( req );
2835 SERVER_END_REQ;
2839 /***********************************************************************
2840 * PeekMessageW (USER32.@)
2842 BOOL WINAPI PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags )
2844 struct user_thread_info *thread_info = get_user_thread_info();
2845 MSG msg;
2847 USER_CheckNotLock();
2849 /* check for graphics events */
2850 USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_ALLINPUT, 0 );
2852 hwnd = WIN_GetFullHandle( hwnd );
2854 for (;;)
2856 if (!peek_message( &msg, hwnd, first, last, flags ))
2858 if (!(flags & PM_NOYIELD))
2860 DWORD count;
2861 ReleaseThunkLock(&count);
2862 NtYieldExecution();
2863 if (count) RestoreThunkLock(count);
2865 return FALSE;
2867 if (msg.message & 0x80000000)
2869 if (!(flags & PM_REMOVE))
2871 /* Have to remove the message explicitly.
2872 Do this before handling it, because the message handler may
2873 call PeekMessage again */
2874 peek_message( &msg, msg.hwnd, msg.message, msg.message, flags | PM_REMOVE );
2876 handle_internal_message( msg.hwnd, msg.message, msg.wParam, msg.lParam );
2878 else break;
2881 thread_info->GetMessageTimeVal = msg.time;
2882 msg.pt.x = (short)LOWORD( thread_info->GetMessagePosVal );
2883 msg.pt.y = (short)HIWORD( thread_info->GetMessagePosVal );
2885 HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, flags & PM_REMOVE, (LPARAM)&msg, TRUE );
2887 /* copy back our internal safe copy of message data to msg_out.
2888 * msg_out is a variable from the *program*, so it can't be used
2889 * internally as it can get "corrupted" by our use of SendMessage()
2890 * (back to the program) inside the message handling itself. */
2891 if (!msg_out)
2893 SetLastError( ERROR_NOACCESS );
2894 return FALSE;
2896 *msg_out = msg;
2897 return TRUE;
2901 /***********************************************************************
2902 * PeekMessageA (USER32.@)
2904 BOOL WINAPI PeekMessageA( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags )
2906 if (get_pending_wmchar( msg, first, last, (flags & PM_REMOVE) )) return TRUE;
2907 if (!PeekMessageW( msg, hwnd, first, last, flags )) return FALSE;
2908 map_wparam_WtoA( msg, (flags & PM_REMOVE) );
2909 return TRUE;
2913 /***********************************************************************
2914 * GetMessageW (USER32.@)
2916 BOOL WINAPI GetMessageW( MSG *msg, HWND hwnd, UINT first, UINT last )
2918 HANDLE server_queue = get_server_queue_handle();
2919 int mask = QS_POSTMESSAGE | QS_SENDMESSAGE; /* Always selected */
2921 if (first || last)
2923 if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
2924 if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
2925 ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
2926 if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
2927 if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
2928 if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
2930 else mask = QS_ALLINPUT;
2932 while (!PeekMessageW( msg, hwnd, first, last, PM_REMOVE | PM_NOYIELD | (mask << 16) ))
2934 DWORD dwlc;
2936 ReleaseThunkLock( &dwlc );
2937 USER_Driver->pMsgWaitForMultipleObjectsEx( 1, &server_queue, INFINITE, mask, 0 );
2938 if (dwlc) RestoreThunkLock( dwlc );
2941 return (msg->message != WM_QUIT);
2945 /***********************************************************************
2946 * GetMessageA (USER32.@)
2948 BOOL WINAPI GetMessageA( MSG *msg, HWND hwnd, UINT first, UINT last )
2950 if (get_pending_wmchar( msg, first, last, TRUE )) return TRUE;
2951 GetMessageW( msg, hwnd, first, last );
2952 map_wparam_WtoA( msg, TRUE );
2953 return (msg->message != WM_QUIT);
2957 /***********************************************************************
2958 * IsDialogMessageA (USER32.@)
2959 * IsDialogMessage (USER32.@)
2961 BOOL WINAPI IsDialogMessageA( HWND hwndDlg, LPMSG pmsg )
2963 MSG msg = *pmsg;
2964 map_wparam_AtoW( msg.message, &msg.wParam, WMCHAR_MAP_NOMAPPING );
2965 return IsDialogMessageW( hwndDlg, &msg );
2969 /***********************************************************************
2970 * TranslateMessage (USER32.@)
2972 * Implementation of TranslateMessage.
2974 * TranslateMessage translates virtual-key messages into character-messages,
2975 * as follows :
2976 * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
2977 * ditto replacing WM_* with WM_SYS*
2978 * This produces WM_CHAR messages only for keys mapped to ASCII characters
2979 * by the keyboard driver.
2981 * If the message is WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP, the
2982 * return value is nonzero, regardless of the translation.
2985 BOOL WINAPI TranslateMessage( const MSG *msg )
2987 UINT message;
2988 WCHAR wp[2];
2989 BYTE state[256];
2991 if (msg->message < WM_KEYFIRST || msg->message > WM_KEYLAST) return FALSE;
2992 if (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYDOWN) return TRUE;
2994 TRACE_(key)("Translating key %s (%04lx), scancode %02x\n",
2995 SPY_GetVKeyName(msg->wParam), msg->wParam, LOBYTE(HIWORD(msg->lParam)));
2997 GetKeyboardState( state );
2998 /* FIXME : should handle ToUnicode yielding 2 */
2999 switch (ToUnicode(msg->wParam, HIWORD(msg->lParam), state, wp, 2, 0))
3001 case 1:
3002 message = (msg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
3003 TRACE_(key)("1 -> PostMessageW(%p,%s,%04x,%08lx)\n",
3004 msg->hwnd, SPY_GetMsgName(message, msg->hwnd), wp[0], msg->lParam);
3005 PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
3006 break;
3008 case -1:
3009 message = (msg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
3010 TRACE_(key)("-1 -> PostMessageW(%p,%s,%04x,%08lx)\n",
3011 msg->hwnd, SPY_GetMsgName(message, msg->hwnd), wp[0], msg->lParam);
3012 PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
3013 break;
3015 return TRUE;
3019 /***********************************************************************
3020 * DispatchMessageA (USER32.@)
3022 * See DispatchMessageW.
3024 LRESULT WINAPI DispatchMessageA( const MSG* msg )
3026 LRESULT retval;
3028 /* Process timer messages */
3029 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
3031 if (msg->lParam) return CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd,
3032 msg->message, msg->wParam, GetTickCount() );
3034 if (!msg->hwnd) return 0;
3036 SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
3037 msg->wParam, msg->lParam );
3039 if (!WINPROC_call_window( msg->hwnd, msg->message, msg->wParam, msg->lParam,
3040 &retval, FALSE, WMCHAR_MAP_DISPATCHMESSAGE ))
3042 if (!IsWindow( msg->hwnd )) SetLastError( ERROR_INVALID_WINDOW_HANDLE );
3043 else SetLastError( ERROR_MESSAGE_SYNC_ONLY );
3044 retval = 0;
3047 SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
3048 msg->wParam, msg->lParam );
3050 if (msg->message == WM_PAINT)
3052 /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */
3053 HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
3054 GetUpdateRgn( msg->hwnd, hrgn, TRUE );
3055 DeleteObject( hrgn );
3057 return retval;
3061 /***********************************************************************
3062 * DispatchMessageW (USER32.@) Process a message
3064 * Process the message specified in the structure *_msg_.
3066 * If the lpMsg parameter points to a WM_TIMER message and the
3067 * parameter of the WM_TIMER message is not NULL, the lParam parameter
3068 * points to the function that is called instead of the window
3069 * procedure.
3071 * The message must be valid.
3073 * RETURNS
3075 * DispatchMessage() returns the result of the window procedure invoked.
3077 * CONFORMANCE
3079 * ECMA-234, Win32
3082 LRESULT WINAPI DispatchMessageW( const MSG* msg )
3084 LRESULT retval;
3086 /* Process timer messages */
3087 if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
3089 if (msg->lParam) return CallWindowProcW( (WNDPROC)msg->lParam, msg->hwnd,
3090 msg->message, msg->wParam, GetTickCount() );
3092 if (!msg->hwnd) return 0;
3094 SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
3095 msg->wParam, msg->lParam );
3097 if (!WINPROC_call_window( msg->hwnd, msg->message, msg->wParam, msg->lParam,
3098 &retval, TRUE, WMCHAR_MAP_DISPATCHMESSAGE ))
3100 if (!IsWindow( msg->hwnd )) SetLastError( ERROR_INVALID_WINDOW_HANDLE );
3101 else SetLastError( ERROR_MESSAGE_SYNC_ONLY );
3102 retval = 0;
3105 SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
3106 msg->wParam, msg->lParam );
3108 if (msg->message == WM_PAINT)
3110 /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */
3111 HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
3112 GetUpdateRgn( msg->hwnd, hrgn, TRUE );
3113 DeleteObject( hrgn );
3115 return retval;
3119 /***********************************************************************
3120 * GetMessagePos (USER.119)
3121 * GetMessagePos (USER32.@)
3123 * The GetMessagePos() function returns a long value representing a
3124 * cursor position, in screen coordinates, when the last message
3125 * retrieved by the GetMessage() function occurs. The x-coordinate is
3126 * in the low-order word of the return value, the y-coordinate is in
3127 * the high-order word. The application can use the MAKEPOINT()
3128 * macro to obtain a POINT structure from the return value.
3130 * For the current cursor position, use GetCursorPos().
3132 * RETURNS
3134 * Cursor position of last message on success, zero on failure.
3136 * CONFORMANCE
3138 * ECMA-234, Win32
3141 DWORD WINAPI GetMessagePos(void)
3143 return get_user_thread_info()->GetMessagePosVal;
3147 /***********************************************************************
3148 * GetMessageTime (USER.120)
3149 * GetMessageTime (USER32.@)
3151 * GetMessageTime() returns the message time for the last message
3152 * retrieved by the function. The time is measured in milliseconds with
3153 * the same offset as GetTickCount().
3155 * Since the tick count wraps, this is only useful for moderately short
3156 * relative time comparisons.
3158 * RETURNS
3160 * Time of last message on success, zero on failure.
3162 LONG WINAPI GetMessageTime(void)
3164 return get_user_thread_info()->GetMessageTimeVal;
3168 /***********************************************************************
3169 * GetMessageExtraInfo (USER.288)
3170 * GetMessageExtraInfo (USER32.@)
3172 LPARAM WINAPI GetMessageExtraInfo(void)
3174 return get_user_thread_info()->GetMessageExtraInfoVal;
3178 /***********************************************************************
3179 * SetMessageExtraInfo (USER32.@)
3181 LPARAM WINAPI SetMessageExtraInfo(LPARAM lParam)
3183 struct user_thread_info *thread_info = get_user_thread_info();
3184 LONG old_value = thread_info->GetMessageExtraInfoVal;
3185 thread_info->GetMessageExtraInfoVal = lParam;
3186 return old_value;
3190 /***********************************************************************
3191 * WaitMessage (USER.112) Suspend thread pending messages
3192 * WaitMessage (USER32.@) Suspend thread pending messages
3194 * WaitMessage() suspends a thread until events appear in the thread's
3195 * queue.
3197 BOOL WINAPI WaitMessage(void)
3199 return (MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 ) != WAIT_FAILED);
3203 /***********************************************************************
3204 * MsgWaitForMultipleObjectsEx (USER32.@)
3206 DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, CONST HANDLE *pHandles,
3207 DWORD timeout, DWORD mask, DWORD flags )
3209 HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3210 DWORD i, ret, lock;
3212 if (count > MAXIMUM_WAIT_OBJECTS-1)
3214 SetLastError( ERROR_INVALID_PARAMETER );
3215 return WAIT_FAILED;
3218 /* set the queue mask */
3219 SERVER_START_REQ( set_queue_mask )
3221 req->wake_mask = (flags & MWMO_INPUTAVAILABLE) ? mask : 0;
3222 req->changed_mask = mask;
3223 req->skip_wait = 0;
3224 wine_server_call( req );
3226 SERVER_END_REQ;
3228 /* add the queue to the handle list */
3229 for (i = 0; i < count; i++) handles[i] = pHandles[i];
3230 handles[count] = get_server_queue_handle();
3232 ReleaseThunkLock( &lock );
3233 ret = USER_Driver->pMsgWaitForMultipleObjectsEx( count+1, handles, timeout, mask, flags );
3234 if (lock) RestoreThunkLock( lock );
3235 return ret;
3239 /***********************************************************************
3240 * MsgWaitForMultipleObjects (USER32.@)
3242 DWORD WINAPI MsgWaitForMultipleObjects( DWORD count, CONST HANDLE *handles,
3243 BOOL wait_all, DWORD timeout, DWORD mask )
3245 return MsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
3246 wait_all ? MWMO_WAITALL : 0 );
3250 /***********************************************************************
3251 * WaitForInputIdle (USER32.@)
3253 DWORD WINAPI WaitForInputIdle( HANDLE hProcess, DWORD dwTimeOut )
3255 DWORD start_time, elapsed, ret;
3256 HANDLE handles[2];
3258 handles[0] = hProcess;
3259 SERVER_START_REQ( get_process_idle_event )
3261 req->handle = hProcess;
3262 if (!(ret = wine_server_call_err( req ))) handles[1] = reply->event;
3264 SERVER_END_REQ;
3265 if (ret) return WAIT_FAILED; /* error */
3266 if (!handles[1]) return 0; /* no event to wait on */
3268 start_time = GetTickCount();
3269 elapsed = 0;
3271 TRACE("waiting for %p\n", handles[1] );
3274 ret = MsgWaitForMultipleObjects ( 2, handles, FALSE, dwTimeOut - elapsed, QS_SENDMESSAGE );
3275 switch (ret)
3277 case WAIT_OBJECT_0:
3278 return WAIT_FAILED;
3279 case WAIT_OBJECT_0+2:
3280 process_sent_messages();
3281 break;
3282 case WAIT_TIMEOUT:
3283 case WAIT_FAILED:
3284 TRACE("timeout or error\n");
3285 return ret;
3286 default:
3287 TRACE("finished\n");
3288 return 0;
3290 if (dwTimeOut != INFINITE)
3292 elapsed = GetTickCount() - start_time;
3293 if (elapsed > dwTimeOut)
3294 break;
3297 while (1);
3299 return WAIT_TIMEOUT;
3303 /***********************************************************************
3304 * UserYield (USER.332)
3306 void WINAPI UserYield16(void)
3308 DWORD count;
3310 /* Handle sent messages */
3311 process_sent_messages();
3313 /* Yield */
3314 ReleaseThunkLock(&count);
3316 if (count)
3318 RestoreThunkLock(count);
3319 /* Handle sent messages again */
3320 process_sent_messages();
3325 /***********************************************************************
3326 * RegisterWindowMessageA (USER32.@)
3327 * RegisterWindowMessage (USER.118)
3329 UINT WINAPI RegisterWindowMessageA( LPCSTR str )
3331 UINT ret = GlobalAddAtomA(str);
3332 TRACE("%s, ret=%x\n", str, ret);
3333 return ret;
3337 /***********************************************************************
3338 * RegisterWindowMessageW (USER32.@)
3340 UINT WINAPI RegisterWindowMessageW( LPCWSTR str )
3342 UINT ret = GlobalAddAtomW(str);
3343 TRACE("%s ret=%x\n", debugstr_w(str), ret);
3344 return ret;
3347 typedef struct BroadcastParm
3349 DWORD flags;
3350 LPDWORD recipients;
3351 UINT msg;
3352 WPARAM wp;
3353 LPARAM lp;
3354 DWORD success;
3355 HWINSTA winsta;
3356 } BroadcastParm;
3358 static BOOL CALLBACK bcast_childwindow( HWND hw, LPARAM lp )
3360 BroadcastParm *parm = (BroadcastParm*)lp;
3361 DWORD_PTR retval = 0;
3362 LONG lresult;
3364 if (parm->flags & BSF_IGNORECURRENTTASK && WIN_IsCurrentProcess(hw))
3366 TRACE("Not telling myself %p\n", hw);
3367 return TRUE;
3370 /* I don't know 100% for sure if this is what Windows does, but it fits the tests */
3371 if (parm->flags & BSF_QUERY)
3373 TRACE("Telling window %p using SendMessageTimeout\n", hw);
3375 /* Not tested for conflicting flags */
3376 if (parm->flags & BSF_FORCEIFHUNG || parm->flags & BSF_NOHANG)
3377 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_ABORTIFHUNG, 2000, &retval );
3378 else if (parm->flags & BSF_NOTIMEOUTIFNOTHUNG)
3379 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NOTIMEOUTIFNOTHUNG, 2000, &retval );
3380 else
3381 lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NORMAL, 2000, &retval );
3383 if (!lresult && GetLastError() == ERROR_TIMEOUT)
3385 WARN("Timed out!\n");
3386 if (!(parm->flags & BSF_FORCEIFHUNG))
3387 goto fail;
3389 if (retval == BROADCAST_QUERY_DENY)
3390 goto fail;
3392 return TRUE;
3394 fail:
3395 parm->success = 0;
3396 return FALSE;
3398 else if (parm->flags & BSF_POSTMESSAGE)
3400 TRACE("Telling window %p using PostMessage\n", hw);
3401 PostMessageW( hw, parm->msg, parm->wp, parm->lp );
3403 else
3405 TRACE("Telling window %p using SendNotifyMessage\n", hw);
3406 SendNotifyMessageW( hw, parm->msg, parm->wp, parm->lp );
3409 return TRUE;
3412 static BOOL CALLBACK bcast_desktop( LPWSTR desktop, LPARAM lp )
3414 BOOL ret;
3415 HDESK hdesktop;
3416 BroadcastParm *parm = (BroadcastParm*)lp;
3418 TRACE("desktop: %s\n", debugstr_w( desktop ));
3420 hdesktop = open_winstation_desktop( parm->winsta, desktop, 0, FALSE, DESKTOP_ENUMERATE|DESKTOP_WRITEOBJECTS|STANDARD_RIGHTS_WRITE );
3421 if (!hdesktop)
3423 FIXME("Could not open desktop %s\n", debugstr_w(desktop));
3424 return TRUE;
3427 ret = EnumDesktopWindows( hdesktop, bcast_childwindow, lp );
3428 CloseDesktop(hdesktop);
3429 TRACE("-->%d\n", ret);
3430 return parm->success;
3433 static BOOL CALLBACK bcast_winsta( LPWSTR winsta, LPARAM lp )
3435 BOOL ret;
3436 HWINSTA hwinsta = OpenWindowStationW( winsta, FALSE, WINSTA_ENUMDESKTOPS );
3437 TRACE("hwinsta: %p/%s/%08x\n", hwinsta, debugstr_w( winsta ), GetLastError());
3438 if (!hwinsta)
3439 return TRUE;
3440 ((BroadcastParm *)lp)->winsta = hwinsta;
3441 ret = EnumDesktopsW( hwinsta, bcast_desktop, lp );
3442 CloseWindowStation( hwinsta );
3443 TRACE("-->%d\n", ret);
3444 return ret;
3447 /***********************************************************************
3448 * BroadcastSystemMessageA (USER32.@)
3449 * BroadcastSystemMessage (USER32.@)
3451 LONG WINAPI BroadcastSystemMessageA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
3453 return BroadcastSystemMessageExA( flags, recipients, msg, wp, lp, NULL );
3457 /***********************************************************************
3458 * BroadcastSystemMessageW (USER32.@)
3460 LONG WINAPI BroadcastSystemMessageW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
3462 return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
3465 /***********************************************************************
3466 * BroadcastSystemMessageExA (USER32.@)
3468 LONG WINAPI BroadcastSystemMessageExA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
3470 map_wparam_AtoW( msg, &wp, WMCHAR_MAP_NOMAPPING );
3471 return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
3475 /***********************************************************************
3476 * BroadcastSystemMessageExW (USER32.@)
3478 LONG WINAPI BroadcastSystemMessageExW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
3480 BroadcastParm parm;
3481 DWORD recips = BSM_ALLCOMPONENTS;
3482 BOOL ret = TRUE;
3483 static const DWORD all_flags = ( BSF_QUERY | BSF_IGNORECURRENTTASK | BSF_FLUSHDISK | BSF_NOHANG
3484 | BSF_POSTMESSAGE | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG
3485 | BSF_ALLOWSFW | BSF_SENDNOTIFYMESSAGE | BSF_RETURNHDESK | BSF_LUID );
3487 TRACE("Flags: %08x, recipients: %p(0x%x), msg: %04x, wparam: %08lx, lparam: %08lx\n", flags, recipients,
3488 (recipients ? *recipients : recips), msg, wp, lp);
3490 if (flags & ~all_flags)
3492 SetLastError(ERROR_INVALID_PARAMETER);
3493 return 0;
3496 if (!recipients)
3497 recipients = &recips;
3499 if ( pinfo && flags & BSF_QUERY )
3500 FIXME("Not returning PBSMINFO information yet\n");
3502 parm.flags = flags;
3503 parm.recipients = recipients;
3504 parm.msg = msg;
3505 parm.wp = wp;
3506 parm.lp = lp;
3507 parm.success = TRUE;
3509 if (*recipients & BSM_ALLDESKTOPS || *recipients == BSM_ALLCOMPONENTS)
3510 ret = EnumWindowStationsW(bcast_winsta, (LONG_PTR)&parm);
3511 else if (*recipients & BSM_APPLICATIONS)
3513 EnumWindows(bcast_childwindow, (LONG_PTR)&parm);
3514 ret = parm.success;
3516 else
3517 FIXME("Recipients %08x not supported!\n", *recipients);
3519 return ret;
3522 /***********************************************************************
3523 * SetMessageQueue (USER32.@)
3525 BOOL WINAPI SetMessageQueue( INT size )
3527 /* now obsolete the message queue will be expanded dynamically as necessary */
3528 return TRUE;
3532 /***********************************************************************
3533 * MessageBeep (USER32.@)
3535 BOOL WINAPI MessageBeep( UINT i )
3537 BOOL active = TRUE;
3538 SystemParametersInfoA( SPI_GETBEEP, 0, &active, FALSE );
3539 if (active) USER_Driver->pBeep();
3540 return TRUE;
3544 /***********************************************************************
3545 * SetTimer (USER32.@)
3547 UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
3549 UINT_PTR ret;
3550 WNDPROC winproc = 0;
3552 if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, NULL );
3554 SERVER_START_REQ( set_win_timer )
3556 req->win = hwnd;
3557 req->msg = WM_TIMER;
3558 req->id = id;
3559 req->rate = max( timeout, SYS_TIMER_RATE );
3560 req->lparam = (unsigned long)winproc;
3561 if (!wine_server_call_err( req ))
3563 ret = reply->id;
3564 if (!ret) ret = TRUE;
3566 else ret = 0;
3568 SERVER_END_REQ;
3570 TRACE("Added %p %lx %p timeout %d\n", hwnd, id, winproc, timeout );
3571 return ret;
3575 /***********************************************************************
3576 * SetSystemTimer (USER32.@)
3578 UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
3580 UINT_PTR ret;
3581 WNDPROC winproc = 0;
3583 if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, NULL );
3585 SERVER_START_REQ( set_win_timer )
3587 req->win = hwnd;
3588 req->msg = WM_SYSTIMER;
3589 req->id = id;
3590 req->rate = max( timeout, SYS_TIMER_RATE );
3591 req->lparam = (unsigned long)winproc;
3592 if (!wine_server_call_err( req ))
3594 ret = reply->id;
3595 if (!ret) ret = TRUE;
3597 else ret = 0;
3599 SERVER_END_REQ;
3601 TRACE("Added %p %lx %p timeout %d\n", hwnd, id, winproc, timeout );
3602 return ret;
3606 /***********************************************************************
3607 * KillTimer (USER32.@)
3609 BOOL WINAPI KillTimer( HWND hwnd, UINT_PTR id )
3611 BOOL ret;
3613 SERVER_START_REQ( kill_win_timer )
3615 req->win = hwnd;
3616 req->msg = WM_TIMER;
3617 req->id = id;
3618 ret = !wine_server_call_err( req );
3620 SERVER_END_REQ;
3621 return ret;
3625 /***********************************************************************
3626 * KillSystemTimer (USER32.@)
3628 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT_PTR id )
3630 BOOL ret;
3632 SERVER_START_REQ( kill_win_timer )
3634 req->win = hwnd;
3635 req->msg = WM_SYSTIMER;
3636 req->id = id;
3637 ret = !wine_server_call_err( req );
3639 SERVER_END_REQ;
3640 return ret;
3644 /**********************************************************************
3645 * GetGUIThreadInfo (USER32.@)
3647 BOOL WINAPI GetGUIThreadInfo( DWORD id, GUITHREADINFO *info )
3649 BOOL ret;
3651 SERVER_START_REQ( get_thread_input )
3653 req->tid = id;
3654 if ((ret = !wine_server_call_err( req )))
3656 info->flags = 0;
3657 info->hwndActive = reply->active;
3658 info->hwndFocus = reply->focus;
3659 info->hwndCapture = reply->capture;
3660 info->hwndMenuOwner = reply->menu_owner;
3661 info->hwndMoveSize = reply->move_size;
3662 info->hwndCaret = reply->caret;
3663 info->rcCaret.left = reply->rect.left;
3664 info->rcCaret.top = reply->rect.top;
3665 info->rcCaret.right = reply->rect.right;
3666 info->rcCaret.bottom = reply->rect.bottom;
3667 if (reply->menu_owner) info->flags |= GUI_INMENUMODE;
3668 if (reply->move_size) info->flags |= GUI_INMOVESIZE;
3669 if (reply->caret) info->flags |= GUI_CARETBLINKING;
3672 SERVER_END_REQ;
3673 return ret;
3677 /******************************************************************
3678 * IsHungAppWindow (USER32.@)
3681 BOOL WINAPI IsHungAppWindow( HWND hWnd )
3683 BOOL ret;
3685 SERVER_START_REQ( is_window_hung )
3687 req->win = hWnd;
3688 ret = !wine_server_call_err( req ) && reply->is_hung;
3690 SERVER_END_REQ;
3691 return ret;