mshtml: Added IHTMLWindow2::get_self implementation.
[wine] / dlls / user32 / message.c
1 /*
2  * Window messaging support
3  *
4  * Copyright 2001 Alexandre Julliard
5  * Copyright 2008 Maarten Lankhorst
6  *
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.
11  *
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.
16  *
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
20  */
21
22 #include "config.h"
23 #include "wine/port.h"
24
25 #include <assert.h>
26 #include <stdarg.h>
27
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 "imm.h"
39 #include "ddk/imm.h"
40 #include "wine/unicode.h"
41 #include "wine/server.h"
42 #include "user_private.h"
43 #include "win.h"
44 #include "controls.h"
45 #include "wine/debug.h"
46
47 WINE_DEFAULT_DEBUG_CHANNEL(msg);
48 WINE_DECLARE_DEBUG_CHANNEL(relay);
49 WINE_DECLARE_DEBUG_CHANNEL(key);
50
51 #define WM_NCMOUSEFIRST WM_NCMOUSEMOVE
52 #define WM_NCMOUSELAST  (WM_NCMOUSEFIRST+(WM_MOUSELAST-WM_MOUSEFIRST))
53
54 #define MAX_PACK_COUNT 4
55 #define MAX_SENDMSG_RECURSION  64
56
57 #define SYS_TIMER_RATE  55   /* min. timer rate in ms (actually 54.925)*/
58
59 /* description of the data fields that need to be packed along with a sent message */
60 struct packed_message
61 {
62     int         count;
63     const void *data[MAX_PACK_COUNT];
64     size_t      size[MAX_PACK_COUNT];
65 };
66
67 /* info about the message currently being received by the current thread */
68 struct received_message_info
69 {
70     enum message_type type;
71     MSG               msg;
72     UINT              flags;  /* InSendMessageEx return flags */
73 };
74
75 /* structure to group all parameters for sent messages of the various kinds */
76 struct send_message_info
77 {
78     enum message_type type;
79     DWORD             dest_tid;
80     HWND              hwnd;
81     UINT              msg;
82     WPARAM            wparam;
83     LPARAM            lparam;
84     UINT              flags;      /* flags for SendMessageTimeout */
85     UINT              timeout;    /* timeout for SendMessageTimeout */
86     SENDASYNCPROC     callback;   /* callback function for SendMessageCallback */
87     ULONG_PTR         data;       /* callback data */
88     enum wm_char_mapping wm_char;
89 };
90
91
92 /* Message class descriptor */
93 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
94 static LRESULT WINAPI message_winproc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
95 const struct builtin_class_descr MESSAGE_builtin_class =
96 {
97     messageW,             /* name */
98     0,                    /* style */
99     NULL,                 /* procA (winproc is Unicode only) */
100     message_winproc,      /* procW */
101     0,                    /* extra */
102     IDC_ARROW,            /* cursor */
103     0                     /* brush */
104 };
105
106
107
108 /* flag for messages that contain pointers */
109 /* 32 messages per entry, messages 0..31 map to bits 0..31 */
110
111 #define SET(msg) (1 << ((msg) & 31))
112
113 static const unsigned int message_pointer_flags[] =
114 {
115     /* 0x00 - 0x1f */
116     SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) |
117     SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
118     /* 0x20 - 0x3f */
119     SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) |
120     SET(WM_COMPAREITEM),
121     /* 0x40 - 0x5f */
122     SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) |
123     SET(WM_NOTIFY) | SET(WM_HELP),
124     /* 0x60 - 0x7f */
125     SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED),
126     /* 0x80 - 0x9f */
127     SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE),
128     /* 0xa0 - 0xbf */
129     SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP),
130     /* 0xc0 - 0xdf */
131     SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS),
132     /* 0xe0 - 0xff */
133     SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO),
134     /* 0x100 - 0x11f */
135     0,
136     /* 0x120 - 0x13f */
137     0,
138     /* 0x140 - 0x15f */
139     SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) |
140     SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) |
141     SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT),
142     /* 0x160 - 0x17f */
143     0,
144     /* 0x180 - 0x19f */
145     SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) |
146     SET(LB_DIR) | SET(LB_FINDSTRING) |
147     SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT),
148     /* 0x1a0 - 0x1bf */
149     SET(LB_FINDSTRINGEXACT),
150     /* 0x1c0 - 0x1df */
151     0,
152     /* 0x1e0 - 0x1ff */
153     0,
154     /* 0x200 - 0x21f */
155     SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE),
156     /* 0x220 - 0x23f */
157     SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) |
158     SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE),
159     /* 0x240 - 0x25f */
160     0,
161     /* 0x260 - 0x27f */
162     0,
163     /* 0x280 - 0x29f */
164     0,
165     /* 0x2a0 - 0x2bf */
166     0,
167     /* 0x2c0 - 0x2df */
168     0,
169     /* 0x2e0 - 0x2ff */
170     0,
171     /* 0x300 - 0x31f */
172     SET(WM_ASKCBFORMATNAME)
173 };
174
175 /* flags for messages that contain Unicode strings */
176 static const unsigned int message_unicode_flags[] =
177 {
178     /* 0x00 - 0x1f */
179     SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) | SET(WM_GETTEXTLENGTH) |
180     SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
181     /* 0x20 - 0x3f */
182     SET(WM_CHARTOITEM),
183     /* 0x40 - 0x5f */
184     0,
185     /* 0x60 - 0x7f */
186     0,
187     /* 0x80 - 0x9f */
188     SET(WM_NCCREATE),
189     /* 0xa0 - 0xbf */
190     0,
191     /* 0xc0 - 0xdf */
192     SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETPASSWORDCHAR),
193     /* 0xe0 - 0xff */
194     0,
195     /* 0x100 - 0x11f */
196     SET(WM_CHAR) | SET(WM_DEADCHAR) | SET(WM_SYSCHAR) | SET(WM_SYSDEADCHAR),
197     /* 0x120 - 0x13f */
198     SET(WM_MENUCHAR),
199     /* 0x140 - 0x15f */
200     SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) | SET(CB_GETLBTEXTLEN) |
201     SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) | SET(CB_FINDSTRINGEXACT),
202     /* 0x160 - 0x17f */
203     0,
204     /* 0x180 - 0x19f */
205     SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_GETTEXTLEN) |
206     SET(LB_SELECTSTRING) | SET(LB_DIR) | SET(LB_FINDSTRING) | SET(LB_ADDFILE),
207     /* 0x1a0 - 0x1bf */
208     SET(LB_FINDSTRINGEXACT),
209     /* 0x1c0 - 0x1df */
210     0,
211     /* 0x1e0 - 0x1ff */
212     0,
213     /* 0x200 - 0x21f */
214     0,
215     /* 0x220 - 0x23f */
216     SET(WM_MDICREATE),
217     /* 0x240 - 0x25f */
218     0,
219     /* 0x260 - 0x27f */
220     0,
221     /* 0x280 - 0x29f */
222     SET(WM_IME_CHAR),
223     /* 0x2a0 - 0x2bf */
224     0,
225     /* 0x2c0 - 0x2df */
226     0,
227     /* 0x2e0 - 0x2ff */
228     0,
229     /* 0x300 - 0x31f */
230     SET(WM_PAINTCLIPBOARD) | SET(WM_SIZECLIPBOARD) | SET(WM_ASKCBFORMATNAME)
231 };
232
233 /* check whether a given message type includes pointers */
234 static inline int is_pointer_message( UINT message )
235 {
236     if (message >= 8*sizeof(message_pointer_flags)) return FALSE;
237     return (message_pointer_flags[message / 32] & SET(message)) != 0;
238 }
239
240 /* check whether a given message type contains Unicode (or ASCII) chars */
241 static inline int is_unicode_message( UINT message )
242 {
243     if (message >= 8*sizeof(message_unicode_flags)) return FALSE;
244     return (message_unicode_flags[message / 32] & SET(message)) != 0;
245 }
246
247 #undef SET
248
249 /* add a data field to a packed message */
250 static inline void push_data( struct packed_message *data, const void *ptr, size_t size )
251 {
252     data->data[data->count] = ptr;
253     data->size[data->count] = size;
254     data->count++;
255 }
256
257 /* add a string to a packed message */
258 static inline void push_string( struct packed_message *data, LPCWSTR str )
259 {
260     push_data( data, str, (strlenW(str) + 1) * sizeof(WCHAR) );
261 }
262
263 /* make sure that the buffer contains a valid null-terminated Unicode string */
264 static inline BOOL check_string( LPCWSTR str, size_t size )
265 {
266     for (size /= sizeof(WCHAR); size; size--, str++)
267         if (!*str) return TRUE;
268     return FALSE;
269 }
270
271 /* make sure that there is space for 'size' bytes in buffer, growing it if needed */
272 static inline void *get_buffer_space( void **buffer, size_t size )
273 {
274     void *ret;
275
276     if (*buffer)
277     {
278         if (!(ret = HeapReAlloc( GetProcessHeap(), 0, *buffer, size )))
279             HeapFree( GetProcessHeap(), 0, *buffer );
280     }
281     else ret = HeapAlloc( GetProcessHeap(), 0, size );
282
283     *buffer = ret;
284     return ret;
285 }
286
287 /* check whether a combobox expects strings or ids in CB_ADDSTRING/CB_INSERTSTRING */
288 static inline BOOL combobox_has_strings( HWND hwnd )
289 {
290     DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
291     return (!(style & (CBS_OWNERDRAWFIXED | CBS_OWNERDRAWVARIABLE)) || (style & CBS_HASSTRINGS));
292 }
293
294 /* check whether a listbox expects strings or ids in LB_ADDSTRING/LB_INSERTSTRING */
295 static inline BOOL listbox_has_strings( HWND hwnd )
296 {
297     DWORD style = GetWindowLongA( hwnd, GWL_STYLE );
298     return (!(style & (LBS_OWNERDRAWFIXED | LBS_OWNERDRAWVARIABLE)) || (style & LBS_HASSTRINGS));
299 }
300
301 /* check whether message is in the range of keyboard messages */
302 static inline BOOL is_keyboard_message( UINT message )
303 {
304     return (message >= WM_KEYFIRST && message <= WM_KEYLAST);
305 }
306
307 /* check whether message is in the range of mouse messages */
308 static inline BOOL is_mouse_message( UINT message )
309 {
310     return ((message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST) ||
311             (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST));
312 }
313
314 /* check whether message matches the specified hwnd filter */
315 static inline BOOL check_hwnd_filter( const MSG *msg, HWND hwnd_filter )
316 {
317     if (!hwnd_filter || hwnd_filter == GetDesktopWindow()) return TRUE;
318     return (msg->hwnd == hwnd_filter || IsChild( hwnd_filter, msg->hwnd ));
319 }
320
321 /* check for pending WM_CHAR message with DBCS trailing byte */
322 static inline BOOL get_pending_wmchar( MSG *msg, UINT first, UINT last, BOOL remove )
323 {
324     struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
325
326     if (!data || !data->get_msg.message) return FALSE;
327     if ((first || last) && (first > WM_CHAR || last < WM_CHAR)) return FALSE;
328     if (!msg) return FALSE;
329     *msg = data->get_msg;
330     if (remove) data->get_msg.message = 0;
331     return TRUE;
332 }
333
334
335 /***********************************************************************
336  *           message_winproc
337  *
338  * Window procedure for "Message" windows (HWND_MESSAGE parent).
339  */
340 static LRESULT WINAPI message_winproc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
341 {
342     if (message == WM_NCCREATE) return TRUE;
343     return 0;  /* all other messages are ignored */
344 }
345
346
347 /***********************************************************************
348  *              broadcast_message_callback
349  *
350  * Helper callback for broadcasting messages.
351  */
352 static BOOL CALLBACK broadcast_message_callback( HWND hwnd, LPARAM lparam )
353 {
354     struct send_message_info *info = (struct send_message_info *)lparam;
355     if (!(GetWindowLongW( hwnd, GWL_STYLE ) & (WS_POPUP|WS_CAPTION))) return TRUE;
356     switch(info->type)
357     {
358     case MSG_UNICODE:
359         SendMessageTimeoutW( hwnd, info->msg, info->wparam, info->lparam,
360                              info->flags, info->timeout, NULL );
361         break;
362     case MSG_ASCII:
363         SendMessageTimeoutA( hwnd, info->msg, info->wparam, info->lparam,
364                              info->flags, info->timeout, NULL );
365         break;
366     case MSG_NOTIFY:
367         SendNotifyMessageW( hwnd, info->msg, info->wparam, info->lparam );
368         break;
369     case MSG_CALLBACK:
370         SendMessageCallbackW( hwnd, info->msg, info->wparam, info->lparam,
371                               info->callback, info->data );
372         break;
373     case MSG_POSTED:
374         PostMessageW( hwnd, info->msg, info->wparam, info->lparam );
375         break;
376     default:
377         ERR( "bad type %d\n", info->type );
378         break;
379     }
380     return TRUE;
381 }
382
383
384 /***********************************************************************
385  *              map_wparam_AtoW
386  *
387  * Convert the wparam of an ASCII message to Unicode.
388  */
389 BOOL map_wparam_AtoW( UINT message, WPARAM *wparam, enum wm_char_mapping mapping )
390 {
391     char ch[2];
392     WCHAR wch[2];
393
394     wch[0] = wch[1] = 0;
395     switch(message)
396     {
397     case WM_CHAR:
398         /* WM_CHAR is magic: a DBCS char can be sent/posted as two consecutive WM_CHAR
399          * messages, in which case the first char is stored, and the conversion
400          * to Unicode only takes place once the second char is sent/posted.
401          */
402         if (mapping != WMCHAR_MAP_NOMAPPING)
403         {
404             struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
405             BYTE low = LOBYTE(*wparam);
406
407             if (HIBYTE(*wparam))
408             {
409                 ch[0] = low;
410                 ch[1] = HIBYTE(*wparam);
411                 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
412                 TRACE( "map %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
413                 if (data) data->lead_byte[mapping] = 0;
414             }
415             else if (data && data->lead_byte[mapping])
416             {
417                 ch[0] = data->lead_byte[mapping];
418                 ch[1] = low;
419                 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
420                 TRACE( "map stored %02x,%02x -> %04x mapping %u\n", (BYTE)ch[0], (BYTE)ch[1], wch[0], mapping );
421                 data->lead_byte[mapping] = 0;
422             }
423             else if (!IsDBCSLeadByte( low ))
424             {
425                 ch[0] = low;
426                 RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 1 );
427                 TRACE( "map %02x -> %04x\n", (BYTE)ch[0], wch[0] );
428                 if (data) data->lead_byte[mapping] = 0;
429             }
430             else  /* store it and wait for trail byte */
431             {
432                 if (!data)
433                 {
434                     if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) )))
435                         return FALSE;
436                     get_user_thread_info()->wmchar_data = data;
437                 }
438                 TRACE( "storing lead byte %02x mapping %u\n", low, mapping );
439                 data->lead_byte[mapping] = low;
440                 return FALSE;
441             }
442             *wparam = MAKEWPARAM(wch[0], wch[1]);
443             break;
444         }
445         /* else fall through */
446     case WM_CHARTOITEM:
447     case EM_SETPASSWORDCHAR:
448     case WM_DEADCHAR:
449     case WM_SYSCHAR:
450     case WM_SYSDEADCHAR:
451     case WM_MENUCHAR:
452         ch[0] = LOBYTE(*wparam);
453         ch[1] = HIBYTE(*wparam);
454         RtlMultiByteToUnicodeN( wch, sizeof(wch), NULL, ch, 2 );
455         *wparam = MAKEWPARAM(wch[0], wch[1]);
456         break;
457     case WM_IME_CHAR:
458         ch[0] = HIBYTE(*wparam);
459         ch[1] = LOBYTE(*wparam);
460         if (ch[0]) RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch, 2 );
461         else RtlMultiByteToUnicodeN( wch, sizeof(wch[0]), NULL, ch + 1, 1 );
462         *wparam = MAKEWPARAM(wch[0], HIWORD(*wparam));
463         break;
464     }
465     return TRUE;
466 }
467
468
469 /***********************************************************************
470  *              map_wparam_WtoA
471  *
472  * Convert the wparam of a Unicode message to ASCII.
473  */
474 static void map_wparam_WtoA( MSG *msg, BOOL remove )
475 {
476     BYTE ch[2];
477     WCHAR wch[2];
478     DWORD len;
479
480     switch(msg->message)
481     {
482     case WM_CHAR:
483         if (!HIWORD(msg->wParam))
484         {
485             wch[0] = LOWORD(msg->wParam);
486             ch[0] = ch[1] = 0;
487             RtlUnicodeToMultiByteN( (LPSTR)ch, 2, &len, wch, sizeof(wch[0]) );
488             if (len == 2)  /* DBCS char */
489             {
490                 struct wm_char_mapping_data *data = get_user_thread_info()->wmchar_data;
491                 if (!data)
492                 {
493                     if (!(data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data) ))) return;
494                     get_user_thread_info()->wmchar_data = data;
495                 }
496                 if (remove)
497                 {
498                     data->get_msg = *msg;
499                     data->get_msg.wParam = ch[1];
500                 }
501                 msg->wParam = ch[0];
502                 return;
503             }
504         }
505         /* else fall through */
506     case WM_CHARTOITEM:
507     case EM_SETPASSWORDCHAR:
508     case WM_DEADCHAR:
509     case WM_SYSCHAR:
510     case WM_SYSDEADCHAR:
511     case WM_MENUCHAR:
512         wch[0] = LOWORD(msg->wParam);
513         wch[1] = HIWORD(msg->wParam);
514         ch[0] = ch[1] = 0;
515         RtlUnicodeToMultiByteN( (LPSTR)ch, 2, NULL, wch, sizeof(wch) );
516         msg->wParam = MAKEWPARAM( ch[0] | (ch[1] << 8), 0 );
517         break;
518     case WM_IME_CHAR:
519         wch[0] = LOWORD(msg->wParam);
520         ch[0] = ch[1] = 0;
521         RtlUnicodeToMultiByteN( (LPSTR)ch, 2, &len, wch, sizeof(wch[0]) );
522         if (len == 2)
523             msg->wParam = MAKEWPARAM( (ch[0] << 8) | ch[1], HIWORD(msg->wParam) );
524         else
525             msg->wParam = MAKEWPARAM( ch[0], HIWORD(msg->wParam) );
526         break;
527     }
528 }
529
530
531 /***********************************************************************
532  *              pack_message
533  *
534  * Pack a message for sending to another process.
535  * Return the size of the data we expect in the message reply.
536  * Set data->count to -1 if there is an error.
537  */
538 static size_t pack_message( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
539                             struct packed_message *data )
540 {
541     data->count = 0;
542     switch(message)
543     {
544     case WM_NCCREATE:
545     case WM_CREATE:
546     {
547         CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
548         push_data( data, cs, sizeof(*cs) );
549         if (HIWORD(cs->lpszName)) push_string( data, cs->lpszName );
550         if (HIWORD(cs->lpszClass)) push_string( data, cs->lpszClass );
551         return sizeof(*cs);
552     }
553     case WM_GETTEXT:
554     case WM_ASKCBFORMATNAME:
555         return wparam * sizeof(WCHAR);
556     case WM_WININICHANGE:
557         if (lparam) push_string(data, (LPWSTR)lparam );
558         return 0;
559     case WM_SETTEXT:
560     case WM_DEVMODECHANGE:
561     case CB_DIR:
562     case LB_DIR:
563     case LB_ADDFILE:
564     case EM_REPLACESEL:
565         push_string( data, (LPWSTR)lparam );
566         return 0;
567     case WM_GETMINMAXINFO:
568         push_data( data, (MINMAXINFO *)lparam, sizeof(MINMAXINFO) );
569         return sizeof(MINMAXINFO);
570     case WM_DRAWITEM:
571         push_data( data, (DRAWITEMSTRUCT *)lparam, sizeof(DRAWITEMSTRUCT) );
572         return 0;
573     case WM_MEASUREITEM:
574         push_data( data, (MEASUREITEMSTRUCT *)lparam, sizeof(MEASUREITEMSTRUCT) );
575         return sizeof(MEASUREITEMSTRUCT);
576     case WM_DELETEITEM:
577         push_data( data, (DELETEITEMSTRUCT *)lparam, sizeof(DELETEITEMSTRUCT) );
578         return 0;
579     case WM_COMPAREITEM:
580         push_data( data, (COMPAREITEMSTRUCT *)lparam, sizeof(COMPAREITEMSTRUCT) );
581         return 0;
582     case WM_WINDOWPOSCHANGING:
583     case WM_WINDOWPOSCHANGED:
584         push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
585         return sizeof(WINDOWPOS);
586     case WM_COPYDATA:
587     {
588         COPYDATASTRUCT *cp = (COPYDATASTRUCT *)lparam;
589         push_data( data, cp, sizeof(*cp) );
590         if (cp->lpData) push_data( data, cp->lpData, cp->cbData );
591         return 0;
592     }
593     case WM_NOTIFY:
594         /* WM_NOTIFY cannot be sent across processes (MSDN) */
595         data->count = -1;
596         return 0;
597     case WM_HELP:
598         push_data( data, (HELPINFO *)lparam, sizeof(HELPINFO) );
599         return 0;
600     case WM_STYLECHANGING:
601     case WM_STYLECHANGED:
602         push_data( data, (STYLESTRUCT *)lparam, sizeof(STYLESTRUCT) );
603         return 0;
604     case WM_NCCALCSIZE:
605         if (!wparam)
606         {
607             push_data( data, (RECT *)lparam, sizeof(RECT) );
608             return sizeof(RECT);
609         }
610         else
611         {
612             NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
613             push_data( data, nc, sizeof(*nc) );
614             push_data( data, nc->lppos, sizeof(*nc->lppos) );
615             return sizeof(*nc) + sizeof(*nc->lppos);
616         }
617     case WM_GETDLGCODE:
618         if (lparam) push_data( data, (MSG *)lparam, sizeof(MSG) );
619         return sizeof(MSG);
620     case SBM_SETSCROLLINFO:
621         push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
622         return 0;
623     case SBM_GETSCROLLINFO:
624         push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
625         return sizeof(SCROLLINFO);
626     case SBM_GETSCROLLBARINFO:
627     {
628         const SCROLLBARINFO *info = (const SCROLLBARINFO *)lparam;
629         size_t size = min( info->cbSize, sizeof(SCROLLBARINFO) );
630         push_data( data, info, size );
631         return size;
632     }
633     case EM_GETSEL:
634     case SBM_GETRANGE:
635     case CB_GETEDITSEL:
636     {
637         size_t size = 0;
638         if (wparam) size += sizeof(DWORD);
639         if (lparam) size += sizeof(DWORD);
640         return size;
641     }
642     case EM_GETRECT:
643     case LB_GETITEMRECT:
644     case CB_GETDROPPEDCONTROLRECT:
645         return sizeof(RECT);
646     case EM_SETRECT:
647     case EM_SETRECTNP:
648         push_data( data, (RECT *)lparam, sizeof(RECT) );
649         return 0;
650     case EM_GETLINE:
651     {
652         WORD *pw = (WORD *)lparam;
653         push_data( data, pw, sizeof(*pw) );
654         return *pw * sizeof(WCHAR);
655     }
656     case EM_SETTABSTOPS:
657     case LB_SETTABSTOPS:
658         if (wparam) push_data( data, (UINT *)lparam, sizeof(UINT) * wparam );
659         return 0;
660     case CB_ADDSTRING:
661     case CB_INSERTSTRING:
662     case CB_FINDSTRING:
663     case CB_FINDSTRINGEXACT:
664     case CB_SELECTSTRING:
665         if (combobox_has_strings( hwnd )) push_string( data, (LPWSTR)lparam );
666         return 0;
667     case CB_GETLBTEXT:
668         if (!combobox_has_strings( hwnd )) return sizeof(ULONG_PTR);
669         return (SendMessageW( hwnd, CB_GETLBTEXTLEN, wparam, 0 ) + 1) * sizeof(WCHAR);
670     case LB_ADDSTRING:
671     case LB_INSERTSTRING:
672     case LB_FINDSTRING:
673     case LB_FINDSTRINGEXACT:
674     case LB_SELECTSTRING:
675         if (listbox_has_strings( hwnd )) push_string( data, (LPWSTR)lparam );
676         return 0;
677     case LB_GETTEXT:
678         if (!listbox_has_strings( hwnd )) return sizeof(ULONG_PTR);
679         return (SendMessageW( hwnd, LB_GETTEXTLEN, wparam, 0 ) + 1) * sizeof(WCHAR);
680     case LB_GETSELITEMS:
681         return wparam * sizeof(UINT);
682     case WM_NEXTMENU:
683         push_data( data, (MDINEXTMENU *)lparam, sizeof(MDINEXTMENU) );
684         return sizeof(MDINEXTMENU);
685     case WM_SIZING:
686     case WM_MOVING:
687         push_data( data, (RECT *)lparam, sizeof(RECT) );
688         return sizeof(RECT);
689     case WM_MDICREATE:
690     {
691         MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)lparam;
692         push_data( data, cs, sizeof(*cs) );
693         if (HIWORD(cs->szTitle)) push_string( data, cs->szTitle );
694         if (HIWORD(cs->szClass)) push_string( data, cs->szClass );
695         return sizeof(*cs);
696     }
697     case WM_MDIGETACTIVE:
698         if (lparam) return sizeof(BOOL);
699         return 0;
700     case WM_DEVICECHANGE:
701     {
702         DEV_BROADCAST_HDR *header = (DEV_BROADCAST_HDR *)lparam;
703         push_data( data, header, header->dbch_size );
704         return 0;
705     }
706     case WM_WINE_SETWINDOWPOS:
707         push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
708         return 0;
709     case WM_WINE_KEYBOARD_LL_HOOK:
710     {
711         struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
712         push_data( data, h_extra, sizeof(*h_extra) );
713         push_data( data, (LPVOID)h_extra->lparam, sizeof(KBDLLHOOKSTRUCT) );
714         return 0;
715     }
716     case WM_WINE_MOUSE_LL_HOOK:
717     {
718         struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
719         push_data( data, h_extra, sizeof(*h_extra) );
720         push_data( data, (LPVOID)h_extra->lparam, sizeof(MSLLHOOKSTRUCT) );
721         return 0;
722     }
723     case WM_NCPAINT:
724         if (wparam <= 1) return 0;
725         FIXME( "WM_NCPAINT hdc packing not supported yet\n" );
726         data->count = -1;
727         return 0;
728     case WM_PAINT:
729         if (!wparam) return 0;
730         /* fall through */
731
732     /* these contain an HFONT */
733     case WM_SETFONT:
734     case WM_GETFONT:
735     /* these contain an HDC */
736     case WM_ERASEBKGND:
737     case WM_ICONERASEBKGND:
738     case WM_CTLCOLORMSGBOX:
739     case WM_CTLCOLOREDIT:
740     case WM_CTLCOLORLISTBOX:
741     case WM_CTLCOLORBTN:
742     case WM_CTLCOLORDLG:
743     case WM_CTLCOLORSCROLLBAR:
744     case WM_CTLCOLORSTATIC:
745     case WM_PRINT:
746     case WM_PRINTCLIENT:
747     /* these contain an HGLOBAL */
748     case WM_PAINTCLIPBOARD:
749     case WM_SIZECLIPBOARD:
750     /* these contain HICON */
751     case WM_GETICON:
752     case WM_SETICON:
753     case WM_QUERYDRAGICON:
754     case WM_QUERYPARKICON:
755     /* these contain pointers */
756     case WM_DROPOBJECT:
757     case WM_QUERYDROPOBJECT:
758     case WM_DRAGLOOP:
759     case WM_DRAGSELECT:
760     case WM_DRAGMOVE:
761         FIXME( "msg %x (%s) not supported yet\n", message, SPY_GetMsgName(message, hwnd) );
762         data->count = -1;
763         return 0;
764     }
765     return 0;
766 }
767
768
769 /***********************************************************************
770  *              unpack_message
771  *
772  * Unpack a message received from another process.
773  */
774 static BOOL unpack_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
775                             void **buffer, size_t size )
776 {
777     size_t minsize = 0;
778
779     switch(message)
780     {
781     case WM_NCCREATE:
782     case WM_CREATE:
783     {
784         CREATESTRUCTW *cs = *buffer;
785         WCHAR *str = (WCHAR *)(cs + 1);
786         if (size < sizeof(*cs)) return FALSE;
787         size -= sizeof(*cs);
788         if (HIWORD(cs->lpszName))
789         {
790             if (!check_string( str, size )) return FALSE;
791             cs->lpszName = str;
792             size -= (strlenW(str) + 1) * sizeof(WCHAR);
793             str += strlenW(str) + 1;
794         }
795         if (HIWORD(cs->lpszClass))
796         {
797             if (!check_string( str, size )) return FALSE;
798             cs->lpszClass = str;
799         }
800         break;
801     }
802     case WM_GETTEXT:
803     case WM_ASKCBFORMATNAME:
804         if (!get_buffer_space( buffer, (*wparam * sizeof(WCHAR)) )) return FALSE;
805         break;
806     case WM_WININICHANGE:
807         if (!*lparam) return TRUE;
808         /* fall through */
809     case WM_SETTEXT:
810     case WM_DEVMODECHANGE:
811     case CB_DIR:
812     case LB_DIR:
813     case LB_ADDFILE:
814     case EM_REPLACESEL:
815         if (!check_string( *buffer, size )) return FALSE;
816         break;
817     case WM_GETMINMAXINFO:
818         minsize = sizeof(MINMAXINFO);
819         break;
820     case WM_DRAWITEM:
821         minsize = sizeof(DRAWITEMSTRUCT);
822         break;
823     case WM_MEASUREITEM:
824         minsize = sizeof(MEASUREITEMSTRUCT);
825         break;
826     case WM_DELETEITEM:
827         minsize = sizeof(DELETEITEMSTRUCT);
828         break;
829     case WM_COMPAREITEM:
830         minsize = sizeof(COMPAREITEMSTRUCT);
831         break;
832     case WM_WINDOWPOSCHANGING:
833     case WM_WINDOWPOSCHANGED:
834     case WM_WINE_SETWINDOWPOS:
835         minsize = sizeof(WINDOWPOS);
836         break;
837     case WM_COPYDATA:
838     {
839         COPYDATASTRUCT *cp = *buffer;
840         if (size < sizeof(*cp)) return FALSE;
841         if (cp->lpData)
842         {
843             minsize = sizeof(*cp) + cp->cbData;
844             cp->lpData = cp + 1;
845         }
846         break;
847     }
848     case WM_NOTIFY:
849         /* WM_NOTIFY cannot be sent across processes (MSDN) */
850         return FALSE;
851     case WM_HELP:
852         minsize = sizeof(HELPINFO);
853         break;
854     case WM_STYLECHANGING:
855     case WM_STYLECHANGED:
856         minsize = sizeof(STYLESTRUCT);
857         break;
858     case WM_NCCALCSIZE:
859         if (!*wparam) minsize = sizeof(RECT);
860         else
861         {
862             NCCALCSIZE_PARAMS *nc = *buffer;
863             if (size < sizeof(*nc) + sizeof(*nc->lppos)) return FALSE;
864             nc->lppos = (WINDOWPOS *)(nc + 1);
865         }
866         break;
867     case WM_GETDLGCODE:
868         if (!*lparam) return TRUE;
869         minsize = sizeof(MSG);
870         break;
871     case SBM_SETSCROLLINFO:
872         minsize = sizeof(SCROLLINFO);
873         break;
874     case SBM_GETSCROLLINFO:
875         if (!get_buffer_space( buffer, sizeof(SCROLLINFO ))) return FALSE;
876         break;
877     case SBM_GETSCROLLBARINFO:
878         if (!get_buffer_space( buffer, sizeof(SCROLLBARINFO ))) return FALSE;
879         break;
880     case EM_GETSEL:
881     case SBM_GETRANGE:
882     case CB_GETEDITSEL:
883         if (*wparam || *lparam)
884         {
885             if (!get_buffer_space( buffer, 2*sizeof(DWORD) )) return FALSE;
886             if (*wparam) *wparam = (WPARAM)*buffer;
887             if (*lparam) *lparam = (LPARAM)((DWORD *)*buffer + 1);
888         }
889         return TRUE;
890     case EM_GETRECT:
891     case LB_GETITEMRECT:
892     case CB_GETDROPPEDCONTROLRECT:
893         if (!get_buffer_space( buffer, sizeof(RECT) )) return FALSE;
894         break;
895     case EM_SETRECT:
896     case EM_SETRECTNP:
897         minsize = sizeof(RECT);
898         break;
899     case EM_GETLINE:
900     {
901         WORD len;
902         if (size < sizeof(WORD)) return FALSE;
903         len = *(WORD *)*buffer;
904         if (!get_buffer_space( buffer, (len + 1) * sizeof(WCHAR) )) return FALSE;
905         *lparam = (LPARAM)*buffer + sizeof(WORD);  /* don't erase WORD at start of buffer */
906         return TRUE;
907     }
908     case EM_SETTABSTOPS:
909     case LB_SETTABSTOPS:
910         if (!*wparam) return TRUE;
911         minsize = *wparam * sizeof(UINT);
912         break;
913     case CB_ADDSTRING:
914     case CB_INSERTSTRING:
915     case CB_FINDSTRING:
916     case CB_FINDSTRINGEXACT:
917     case CB_SELECTSTRING:
918     case LB_ADDSTRING:
919     case LB_INSERTSTRING:
920     case LB_FINDSTRING:
921     case LB_FINDSTRINGEXACT:
922     case LB_SELECTSTRING:
923         if (!*buffer) return TRUE;
924         if (!check_string( *buffer, size )) return FALSE;
925         break;
926     case CB_GETLBTEXT:
927     {
928         size = sizeof(ULONG_PTR);
929         if (combobox_has_strings( hwnd ))
930             size = (SendMessageW( hwnd, CB_GETLBTEXTLEN, *wparam, 0 ) + 1) * sizeof(WCHAR);
931         if (!get_buffer_space( buffer, size )) return FALSE;
932         break;
933     }
934     case LB_GETTEXT:
935     {
936         size = sizeof(ULONG_PTR);
937         if (listbox_has_strings( hwnd ))
938             size = (SendMessageW( hwnd, LB_GETTEXTLEN, *wparam, 0 ) + 1) * sizeof(WCHAR);
939         if (!get_buffer_space( buffer, size )) return FALSE;
940         break;
941     }
942     case LB_GETSELITEMS:
943         if (!get_buffer_space( buffer, *wparam * sizeof(UINT) )) return FALSE;
944         break;
945     case WM_NEXTMENU:
946         minsize = sizeof(MDINEXTMENU);
947         if (!get_buffer_space( buffer, sizeof(MDINEXTMENU) )) return FALSE;
948         break;
949     case WM_SIZING:
950     case WM_MOVING:
951         minsize = sizeof(RECT);
952         if (!get_buffer_space( buffer, sizeof(RECT) )) return FALSE;
953         break;
954     case WM_MDICREATE:
955     {
956         MDICREATESTRUCTW *cs = *buffer;
957         WCHAR *str = (WCHAR *)(cs + 1);
958         if (size < sizeof(*cs)) return FALSE;
959         size -= sizeof(*cs);
960         if (HIWORD(cs->szTitle))
961         {
962             if (!check_string( str, size )) return FALSE;
963             cs->szTitle = str;
964             size -= (strlenW(str) + 1) * sizeof(WCHAR);
965             str += strlenW(str) + 1;
966         }
967         if (HIWORD(cs->szClass))
968         {
969             if (!check_string( str, size )) return FALSE;
970             cs->szClass = str;
971         }
972         break;
973     }
974     case WM_MDIGETACTIVE:
975         if (!*lparam) return TRUE;
976         if (!get_buffer_space( buffer, sizeof(BOOL) )) return FALSE;
977         break;
978     case WM_DEVICECHANGE:
979         minsize = sizeof(DEV_BROADCAST_HDR);
980         break;
981     case WM_WINE_KEYBOARD_LL_HOOK:
982     case WM_WINE_MOUSE_LL_HOOK:
983     {
984         struct hook_extra_info *h_extra = (struct hook_extra_info *)*buffer;
985
986         minsize = sizeof(struct hook_extra_info) +
987                   (message == WM_WINE_KEYBOARD_LL_HOOK ? sizeof(KBDLLHOOKSTRUCT)
988                                                        : sizeof(MSLLHOOKSTRUCT));
989         if (size < minsize) return FALSE;
990         h_extra->lparam = (LPARAM)(h_extra + 1);
991         break;
992     }
993     case WM_NCPAINT:
994         if (*wparam <= 1) return TRUE;
995         FIXME( "WM_NCPAINT hdc unpacking not supported\n" );
996         return FALSE;
997     case WM_PAINT:
998         if (!*wparam) return TRUE;
999         /* fall through */
1000
1001     /* these contain an HFONT */
1002     case WM_SETFONT:
1003     case WM_GETFONT:
1004     /* these contain an HDC */
1005     case WM_ERASEBKGND:
1006     case WM_ICONERASEBKGND:
1007     case WM_CTLCOLORMSGBOX:
1008     case WM_CTLCOLOREDIT:
1009     case WM_CTLCOLORLISTBOX:
1010     case WM_CTLCOLORBTN:
1011     case WM_CTLCOLORDLG:
1012     case WM_CTLCOLORSCROLLBAR:
1013     case WM_CTLCOLORSTATIC:
1014     case WM_PRINT:
1015     case WM_PRINTCLIENT:
1016     /* these contain an HGLOBAL */
1017     case WM_PAINTCLIPBOARD:
1018     case WM_SIZECLIPBOARD:
1019     /* these contain HICON */
1020     case WM_GETICON:
1021     case WM_SETICON:
1022     case WM_QUERYDRAGICON:
1023     case WM_QUERYPARKICON:
1024     /* these contain pointers */
1025     case WM_DROPOBJECT:
1026     case WM_QUERYDROPOBJECT:
1027     case WM_DRAGLOOP:
1028     case WM_DRAGSELECT:
1029     case WM_DRAGMOVE:
1030         FIXME( "msg %x (%s) not supported yet\n", message, SPY_GetMsgName(message, hwnd) );
1031         return FALSE;
1032
1033     default:
1034         return TRUE; /* message doesn't need any unpacking */
1035     }
1036
1037     /* default exit for most messages: check minsize and store buffer in lparam */
1038     if (size < minsize) return FALSE;
1039     *lparam = (LPARAM)*buffer;
1040     return TRUE;
1041 }
1042
1043
1044 /***********************************************************************
1045  *              pack_reply
1046  *
1047  * Pack a reply to a message for sending to another process.
1048  */
1049 static void pack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
1050                         LRESULT res, struct packed_message *data )
1051 {
1052     data->count = 0;
1053     switch(message)
1054     {
1055     case WM_NCCREATE:
1056     case WM_CREATE:
1057         push_data( data, (CREATESTRUCTW *)lparam, sizeof(CREATESTRUCTW) );
1058         break;
1059     case WM_GETTEXT:
1060     case CB_GETLBTEXT:
1061     case LB_GETTEXT:
1062         push_data( data, (WCHAR *)lparam, (res + 1) * sizeof(WCHAR) );
1063         break;
1064     case WM_GETMINMAXINFO:
1065         push_data( data, (MINMAXINFO *)lparam, sizeof(MINMAXINFO) );
1066         break;
1067     case WM_MEASUREITEM:
1068         push_data( data, (MEASUREITEMSTRUCT *)lparam, sizeof(MEASUREITEMSTRUCT) );
1069         break;
1070     case WM_WINDOWPOSCHANGING:
1071     case WM_WINDOWPOSCHANGED:
1072         push_data( data, (WINDOWPOS *)lparam, sizeof(WINDOWPOS) );
1073         break;
1074     case WM_GETDLGCODE:
1075         if (lparam) push_data( data, (MSG *)lparam, sizeof(MSG) );
1076         break;
1077     case SBM_GETSCROLLINFO:
1078         push_data( data, (SCROLLINFO *)lparam, sizeof(SCROLLINFO) );
1079         break;
1080     case EM_GETRECT:
1081     case LB_GETITEMRECT:
1082     case CB_GETDROPPEDCONTROLRECT:
1083     case WM_SIZING:
1084     case WM_MOVING:
1085         push_data( data, (RECT *)lparam, sizeof(RECT) );
1086         break;
1087     case EM_GETLINE:
1088     {
1089         WORD *ptr = (WORD *)lparam;
1090         push_data( data, ptr, ptr[-1] * sizeof(WCHAR) );
1091         break;
1092     }
1093     case LB_GETSELITEMS:
1094         push_data( data, (UINT *)lparam, wparam * sizeof(UINT) );
1095         break;
1096     case WM_MDIGETACTIVE:
1097         if (lparam) push_data( data, (BOOL *)lparam, sizeof(BOOL) );
1098         break;
1099     case WM_NCCALCSIZE:
1100         if (!wparam)
1101             push_data( data, (RECT *)lparam, sizeof(RECT) );
1102         else
1103         {
1104             NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
1105             push_data( data, nc, sizeof(*nc) );
1106             push_data( data, nc->lppos, sizeof(*nc->lppos) );
1107         }
1108         break;
1109     case EM_GETSEL:
1110     case SBM_GETRANGE:
1111     case CB_GETEDITSEL:
1112         if (wparam) push_data( data, (DWORD *)wparam, sizeof(DWORD) );
1113         if (lparam) push_data( data, (DWORD *)lparam, sizeof(DWORD) );
1114         break;
1115     case WM_NEXTMENU:
1116         push_data( data, (MDINEXTMENU *)lparam, sizeof(MDINEXTMENU) );
1117         break;
1118     case WM_MDICREATE:
1119         push_data( data, (MDICREATESTRUCTW *)lparam, sizeof(MDICREATESTRUCTW) );
1120         break;
1121     case WM_ASKCBFORMATNAME:
1122         push_data( data, (WCHAR *)lparam, (strlenW((WCHAR *)lparam) + 1) * sizeof(WCHAR) );
1123         break;
1124     }
1125 }
1126
1127
1128 /***********************************************************************
1129  *              unpack_reply
1130  *
1131  * Unpack a message reply received from another process.
1132  */
1133 static void unpack_reply( HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam,
1134                           void *buffer, size_t size )
1135 {
1136     switch(message)
1137     {
1138     case WM_NCCREATE:
1139     case WM_CREATE:
1140     {
1141         CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
1142         LPCWSTR name = cs->lpszName, class = cs->lpszClass;
1143         memcpy( cs, buffer, min( sizeof(*cs), size ));
1144         cs->lpszName = name;  /* restore the original pointers */
1145         cs->lpszClass = class;
1146         break;
1147     }
1148     case WM_GETTEXT:
1149     case WM_ASKCBFORMATNAME:
1150         memcpy( (WCHAR *)lparam, buffer, min( wparam*sizeof(WCHAR), size ));
1151         break;
1152     case WM_GETMINMAXINFO:
1153         memcpy( (MINMAXINFO *)lparam, buffer, min( sizeof(MINMAXINFO), size ));
1154         break;
1155     case WM_MEASUREITEM:
1156         memcpy( (MEASUREITEMSTRUCT *)lparam, buffer, min( sizeof(MEASUREITEMSTRUCT), size ));
1157         break;
1158     case WM_WINDOWPOSCHANGING:
1159     case WM_WINDOWPOSCHANGED:
1160         memcpy( (WINDOWPOS *)lparam, buffer, min( sizeof(WINDOWPOS), size ));
1161         break;
1162     case WM_GETDLGCODE:
1163         if (lparam) memcpy( (MSG *)lparam, buffer, min( sizeof(MSG), size ));
1164         break;
1165     case SBM_GETSCROLLINFO:
1166         memcpy( (SCROLLINFO *)lparam, buffer, min( sizeof(SCROLLINFO), size ));
1167         break;
1168     case SBM_GETSCROLLBARINFO:
1169         memcpy( (SCROLLBARINFO *)lparam, buffer, min( sizeof(SCROLLBARINFO), size ));
1170         break;
1171     case EM_GETRECT:
1172     case CB_GETDROPPEDCONTROLRECT:
1173     case LB_GETITEMRECT:
1174     case WM_SIZING:
1175     case WM_MOVING:
1176         memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
1177         break;
1178     case EM_GETLINE:
1179         size = min( size, (size_t)*(WORD *)lparam );
1180         memcpy( (WCHAR *)lparam, buffer, size );
1181         break;
1182     case LB_GETSELITEMS:
1183         memcpy( (UINT *)lparam, buffer, min( wparam*sizeof(UINT), size ));
1184         break;
1185     case LB_GETTEXT:
1186     case CB_GETLBTEXT:
1187         memcpy( (WCHAR *)lparam, buffer, size );
1188         break;
1189     case WM_NEXTMENU:
1190         memcpy( (MDINEXTMENU *)lparam, buffer, min( sizeof(MDINEXTMENU), size ));
1191         break;
1192     case WM_MDIGETACTIVE:
1193         if (lparam) memcpy( (BOOL *)lparam, buffer, min( sizeof(BOOL), size ));
1194         break;
1195     case WM_NCCALCSIZE:
1196         if (!wparam)
1197             memcpy( (RECT *)lparam, buffer, min( sizeof(RECT), size ));
1198         else
1199         {
1200             NCCALCSIZE_PARAMS *nc = (NCCALCSIZE_PARAMS *)lparam;
1201             WINDOWPOS *wp = nc->lppos;
1202             memcpy( nc, buffer, min( sizeof(*nc), size ));
1203             if (size > sizeof(*nc))
1204             {
1205                 size -= sizeof(*nc);
1206                 memcpy( wp, (NCCALCSIZE_PARAMS*)buffer + 1, min( sizeof(*wp), size ));
1207             }
1208             nc->lppos = wp;  /* restore the original pointer */
1209         }
1210         break;
1211     case EM_GETSEL:
1212     case SBM_GETRANGE:
1213     case CB_GETEDITSEL:
1214         if (wparam)
1215         {
1216             memcpy( (DWORD *)wparam, buffer, min( sizeof(DWORD), size ));
1217             if (size <= sizeof(DWORD)) break;
1218             size -= sizeof(DWORD);
1219             buffer = (DWORD *)buffer + 1;
1220         }
1221         if (lparam) memcpy( (DWORD *)lparam, buffer, min( sizeof(DWORD), size ));
1222         break;
1223     case WM_MDICREATE:
1224     {
1225         MDICREATESTRUCTW *cs = (MDICREATESTRUCTW *)lparam;
1226         LPCWSTR title = cs->szTitle, class = cs->szClass;
1227         memcpy( cs, buffer, min( sizeof(*cs), size ));
1228         cs->szTitle = title;  /* restore the original pointers */
1229         cs->szClass = class;
1230         break;
1231     }
1232     default:
1233         ERR( "should not happen: unexpected message %x\n", message );
1234         break;
1235     }
1236 }
1237
1238
1239 /***********************************************************************
1240  *           reply_message
1241  *
1242  * Send a reply to a sent message.
1243  */
1244 static void reply_message( struct received_message_info *info, LRESULT result, BOOL remove )
1245 {
1246     struct packed_message data;
1247     int i, replied = info->flags & ISMEX_REPLIED;
1248
1249     if (info->flags & ISMEX_NOTIFY) return;  /* notify messages don't get replies */
1250     if (!remove && replied) return;  /* replied already */
1251
1252     data.count = 0;
1253     info->flags |= ISMEX_REPLIED;
1254
1255     if (info->type == MSG_OTHER_PROCESS && !replied)
1256     {
1257         pack_reply( info->msg.hwnd, info->msg.message, info->msg.wParam,
1258                     info->msg.lParam, result, &data );
1259     }
1260
1261     SERVER_START_REQ( reply_message )
1262     {
1263         req->result = result;
1264         req->remove = remove;
1265         for (i = 0; i < data.count; i++) wine_server_add_data( req, data.data[i], data.size[i] );
1266         wine_server_call( req );
1267     }
1268     SERVER_END_REQ;
1269 }
1270
1271
1272 /***********************************************************************
1273  *           handle_internal_message
1274  *
1275  * Handle an internal Wine message instead of calling the window proc.
1276  */
1277 static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
1278 {
1279     switch(msg)
1280     {
1281     case WM_WINE_DESTROYWINDOW:
1282         return WIN_DestroyWindow( hwnd );
1283     case WM_WINE_SETWINDOWPOS:
1284         if (is_desktop_window( hwnd )) return 0;
1285         return USER_SetWindowPos( (WINDOWPOS *)lparam );
1286     case WM_WINE_SHOWWINDOW:
1287         if (is_desktop_window( hwnd )) return 0;
1288         return ShowWindow( hwnd, wparam );
1289     case WM_WINE_SETPARENT:
1290         if (is_desktop_window( hwnd )) return 0;
1291         return (LRESULT)SetParent( hwnd, (HWND)wparam );
1292     case WM_WINE_SETWINDOWLONG:
1293         return WIN_SetWindowLong( hwnd, (short)LOWORD(wparam), HIWORD(wparam), lparam, TRUE );
1294     case WM_WINE_ENABLEWINDOW:
1295         if (is_desktop_window( hwnd )) return 0;
1296         return EnableWindow( hwnd, wparam );
1297     case WM_WINE_SETACTIVEWINDOW:
1298         if (is_desktop_window( hwnd )) return 0;
1299         return (LRESULT)SetActiveWindow( (HWND)wparam );
1300     case WM_WINE_KEYBOARD_LL_HOOK:
1301     case WM_WINE_MOUSE_LL_HOOK:
1302     {
1303         struct hook_extra_info *h_extra = (struct hook_extra_info *)lparam;
1304
1305         return call_current_hook( h_extra->handle, HC_ACTION, wparam, h_extra->lparam );
1306     }
1307     default:
1308         if (msg >= WM_WINE_FIRST_DRIVER_MSG && msg <= WM_WINE_LAST_DRIVER_MSG)
1309             return USER_Driver->pWindowMessage( hwnd, msg, wparam, lparam );
1310         FIXME( "unknown internal message %x\n", msg );
1311         return 0;
1312     }
1313 }
1314
1315 /* since the WM_DDE_ACK response to a WM_DDE_EXECUTE message should contain the handle
1316  * to the memory handle, we keep track (in the server side) of all pairs of handle
1317  * used (the client passes its value and the content of the memory handle), and
1318  * the server stored both values (the client, and the local one, created after the
1319  * content). When a ACK message is generated, the list of pair is searched for a
1320  * matching pair, so that the client memory handle can be returned.
1321  */
1322 struct DDE_pair {
1323     HGLOBAL     client_hMem;
1324     HGLOBAL     server_hMem;
1325 };
1326
1327 static      struct DDE_pair*    dde_pairs;
1328 static      int                 dde_num_alloc;
1329 static      int                 dde_num_used;
1330
1331 static CRITICAL_SECTION dde_crst;
1332 static CRITICAL_SECTION_DEBUG critsect_debug =
1333 {
1334     0, 0, &dde_crst,
1335     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
1336       0, 0, { (DWORD_PTR)(__FILE__ ": dde_crst") }
1337 };
1338 static CRITICAL_SECTION dde_crst = { &critsect_debug, -1, 0, 0, 0, 0 };
1339
1340 static BOOL dde_add_pair(HGLOBAL chm, HGLOBAL shm)
1341 {
1342     int  i;
1343 #define GROWBY  4
1344
1345     EnterCriticalSection(&dde_crst);
1346
1347     /* now remember the pair of hMem on both sides */
1348     if (dde_num_used == dde_num_alloc)
1349     {
1350         struct DDE_pair* tmp;
1351         if (dde_pairs)
1352             tmp  = HeapReAlloc( GetProcessHeap(), 0, dde_pairs,
1353                                             (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
1354         else
1355             tmp  = HeapAlloc( GetProcessHeap(), 0, 
1356                                             (dde_num_alloc + GROWBY) * sizeof(struct DDE_pair));
1357
1358         if (!tmp)
1359         {
1360             LeaveCriticalSection(&dde_crst);
1361             return FALSE;
1362         }
1363         dde_pairs = tmp;
1364         /* zero out newly allocated part */
1365         memset(&dde_pairs[dde_num_alloc], 0, GROWBY * sizeof(struct DDE_pair));
1366         dde_num_alloc += GROWBY;
1367     }
1368 #undef GROWBY
1369     for (i = 0; i < dde_num_alloc; i++)
1370     {
1371         if (dde_pairs[i].server_hMem == 0)
1372         {
1373             dde_pairs[i].client_hMem = chm;
1374             dde_pairs[i].server_hMem = shm;
1375             dde_num_used++;
1376             break;
1377         }
1378     }
1379     LeaveCriticalSection(&dde_crst);
1380     return TRUE;
1381 }
1382
1383 static HGLOBAL dde_get_pair(HGLOBAL shm)
1384 {
1385     int  i;
1386     HGLOBAL     ret = 0;
1387
1388     EnterCriticalSection(&dde_crst);
1389     for (i = 0; i < dde_num_alloc; i++)
1390     {
1391         if (dde_pairs[i].server_hMem == shm)
1392         {
1393             /* free this pair */
1394             dde_pairs[i].server_hMem = 0;
1395             dde_num_used--;
1396             ret = dde_pairs[i].client_hMem;
1397             break;
1398         }
1399     }
1400     LeaveCriticalSection(&dde_crst);
1401     return ret;
1402 }
1403
1404 /***********************************************************************
1405  *              post_dde_message
1406  *
1407  * Post a DDE message
1408  */
1409 static BOOL post_dde_message( struct packed_message *data, const struct send_message_info *info )
1410 {
1411     void*       ptr = NULL;
1412     int         size = 0;
1413     UINT_PTR    uiLo, uiHi;
1414     LPARAM      lp = 0;
1415     HGLOBAL     hunlock = 0;
1416     int         i;
1417     DWORD       res;
1418
1419     if (!UnpackDDElParam( info->msg, info->lparam, &uiLo, &uiHi ))
1420         return FALSE;
1421
1422     lp = info->lparam;
1423     switch (info->msg)
1424     {
1425         /* DDE messages which don't require packing are:
1426          * WM_DDE_INITIATE
1427          * WM_DDE_TERMINATE
1428          * WM_DDE_REQUEST
1429          * WM_DDE_UNADVISE
1430          */
1431     case WM_DDE_ACK:
1432         if (HIWORD(uiHi))
1433         {
1434             /* uiHi should contain a hMem from WM_DDE_EXECUTE */
1435             HGLOBAL h = dde_get_pair( (HANDLE)uiHi );
1436             if (h)
1437             {
1438                 /* send back the value of h on the other side */
1439                 push_data( data, &h, sizeof(HGLOBAL) );
1440                 lp = uiLo;
1441                 TRACE( "send dde-ack %lx %08lx => %p\n", uiLo, uiHi, h );
1442             }
1443         }
1444         else
1445         {
1446             /* uiHi should contain either an atom or 0 */
1447             TRACE( "send dde-ack %lx atom=%lx\n", uiLo, uiHi );
1448             lp = MAKELONG( uiLo, uiHi );
1449         }
1450         break;
1451     case WM_DDE_ADVISE:
1452     case WM_DDE_DATA:
1453     case WM_DDE_POKE:
1454         size = 0;
1455         if (uiLo)
1456         {
1457             size = GlobalSize( (HGLOBAL)uiLo ) ;
1458             if ((info->msg == WM_DDE_ADVISE && size < sizeof(DDEADVISE)) ||
1459                 (info->msg == WM_DDE_DATA   && size < sizeof(DDEDATA))   ||
1460                 (info->msg == WM_DDE_POKE   && size < sizeof(DDEPOKE))
1461                 )
1462             return FALSE;
1463         }
1464         else if (info->msg != WM_DDE_DATA) return FALSE;
1465
1466         lp = uiHi;
1467         if (uiLo)
1468         {
1469             if ((ptr = GlobalLock( (HGLOBAL)uiLo) ))
1470             {
1471                 DDEDATA *dde_data = (DDEDATA *)ptr;
1472                 TRACE("unused %d, fResponse %d, fRelease %d, fDeferUpd %d, fAckReq %d, cfFormat %d\n",
1473                        dde_data->unused, dde_data->fResponse, dde_data->fRelease,
1474                        dde_data->reserved, dde_data->fAckReq, dde_data->cfFormat);
1475                 push_data( data, ptr, size );
1476                 hunlock = (HGLOBAL)uiLo;
1477             }
1478         }
1479         TRACE( "send ddepack %u %lx\n", size, uiHi );
1480         break;
1481     case WM_DDE_EXECUTE:
1482         if (info->lparam)
1483         {
1484             if ((ptr = GlobalLock( (HGLOBAL)info->lparam) ))
1485             {
1486                 push_data(data, ptr, GlobalSize( (HGLOBAL)info->lparam ));
1487                 /* so that the other side can send it back on ACK */
1488                 lp = info->lparam;
1489                 hunlock = (HGLOBAL)info->lparam;
1490             }
1491         }
1492         break;
1493     }
1494     SERVER_START_REQ( send_message )
1495     {
1496         req->id      = info->dest_tid;
1497         req->type    = info->type;
1498         req->flags   = 0;
1499         req->win     = info->hwnd;
1500         req->msg     = info->msg;
1501         req->wparam  = info->wparam;
1502         req->lparam  = lp;
1503         req->timeout = TIMEOUT_INFINITE;
1504         for (i = 0; i < data->count; i++)
1505             wine_server_add_data( req, data->data[i], data->size[i] );
1506         if ((res = wine_server_call( req )))
1507         {
1508             if (res == STATUS_INVALID_PARAMETER)
1509                 /* FIXME: find a STATUS_ value for this one */
1510                 SetLastError( ERROR_INVALID_THREAD_ID );
1511             else
1512                 SetLastError( RtlNtStatusToDosError(res) );
1513         }
1514         else
1515             FreeDDElParam(info->msg, info->lparam);
1516     }
1517     SERVER_END_REQ;
1518     if (hunlock) GlobalUnlock(hunlock);
1519
1520     return !res;
1521 }
1522
1523 /***********************************************************************
1524  *              unpack_dde_message
1525  *
1526  * Unpack a posted DDE message received from another process.
1527  */
1528 static BOOL unpack_dde_message( HWND hwnd, UINT message, WPARAM *wparam, LPARAM *lparam,
1529                                 void **buffer, size_t size )
1530 {
1531     UINT_PTR    uiLo, uiHi;
1532     HGLOBAL     hMem = 0;
1533     void*       ptr;
1534
1535     switch (message)
1536     {
1537     case WM_DDE_ACK:
1538         if (size)
1539         {
1540             /* hMem is being passed */
1541             if (size != sizeof(HGLOBAL)) return FALSE;
1542             if (!buffer || !*buffer) return FALSE;
1543             uiLo = *lparam;
1544             memcpy( &hMem, *buffer, size );
1545             uiHi = (UINT_PTR)hMem;
1546             TRACE("recv dde-ack %lx mem=%lx[%lx]\n", uiLo, uiHi, GlobalSize( hMem ));
1547         }
1548         else
1549         {
1550             uiLo = LOWORD( *lparam );
1551             uiHi = HIWORD( *lparam );
1552             TRACE("recv dde-ack %lx atom=%lx\n", uiLo, uiHi);
1553         }
1554         *lparam = PackDDElParam( WM_DDE_ACK, uiLo, uiHi );
1555         break;
1556     case WM_DDE_ADVISE:
1557     case WM_DDE_DATA:
1558     case WM_DDE_POKE:
1559         if ((!buffer || !*buffer) && message != WM_DDE_DATA) return FALSE;
1560         uiHi = *lparam;
1561         if (size)
1562         {
1563             if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size )))
1564                 return FALSE;
1565             if ((ptr = GlobalLock( hMem )))
1566             {
1567                 memcpy( ptr, *buffer, size );
1568                 GlobalUnlock( hMem );
1569             }
1570             else
1571             {
1572                 GlobalFree( hMem );
1573                 return FALSE;
1574             }
1575         }
1576         uiLo = (UINT_PTR)hMem;
1577
1578         *lparam = PackDDElParam( message, uiLo, uiHi );
1579         break;
1580     case WM_DDE_EXECUTE:
1581         if (size)
1582         {
1583             if (!buffer || !*buffer) return FALSE;
1584             if (!(hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, size ))) return FALSE;
1585             if ((ptr = GlobalLock( hMem )))
1586             {
1587                 memcpy( ptr, *buffer, size );
1588                 GlobalUnlock( hMem );
1589                 TRACE( "exec: pairing c=%08lx s=%p\n", *lparam, hMem );
1590                 if (!dde_add_pair( (HGLOBAL)*lparam, hMem ))
1591                 {
1592                     GlobalFree( hMem );
1593                     return FALSE;
1594                 }
1595             }
1596             else
1597             {
1598                 GlobalFree( hMem );
1599                 return FALSE;
1600             }
1601         } else return FALSE;
1602         *lparam = (LPARAM)hMem;
1603         break;
1604     }
1605     return TRUE;
1606 }
1607
1608 /***********************************************************************
1609  *           call_window_proc
1610  *
1611  * Call a window procedure and the corresponding hooks.
1612  */
1613 static LRESULT call_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
1614                                  BOOL unicode, BOOL same_thread, enum wm_char_mapping mapping )
1615 {
1616     struct user_thread_info *thread_info = get_user_thread_info();
1617     LRESULT result = 0;
1618     CWPSTRUCT cwp;
1619     CWPRETSTRUCT cwpret;
1620
1621     if (thread_info->recursion_count > MAX_SENDMSG_RECURSION) return 0;
1622     thread_info->recursion_count++;
1623
1624     if (msg & 0x80000000)
1625     {
1626         result = handle_internal_message( hwnd, msg, wparam, lparam );
1627         goto done;
1628     }
1629
1630     /* first the WH_CALLWNDPROC hook */
1631     hwnd = WIN_GetFullHandle( hwnd );
1632     cwp.lParam  = lparam;
1633     cwp.wParam  = wparam;
1634     cwp.message = msg;
1635     cwp.hwnd    = hwnd;
1636     HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, same_thread, (LPARAM)&cwp, unicode );
1637
1638     /* now call the window procedure */
1639     if (!WINPROC_call_window( hwnd, msg, wparam, lparam, &result, unicode, mapping )) goto done;
1640
1641     /* and finally the WH_CALLWNDPROCRET hook */
1642     cwpret.lResult = result;
1643     cwpret.lParam  = lparam;
1644     cwpret.wParam  = wparam;
1645     cwpret.message = msg;
1646     cwpret.hwnd    = hwnd;
1647     HOOK_CallHooks( WH_CALLWNDPROCRET, HC_ACTION, same_thread, (LPARAM)&cwpret, unicode );
1648  done:
1649     thread_info->recursion_count--;
1650     return result;
1651 }
1652
1653
1654 /***********************************************************************
1655  *           send_parent_notify
1656  *
1657  * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
1658  * the window has the WS_EX_NOPARENTNOTIFY style.
1659  */
1660 static void send_parent_notify( HWND hwnd, WORD event, WORD idChild, POINT pt )
1661 {
1662     /* pt has to be in the client coordinates of the parent window */
1663     MapWindowPoints( 0, hwnd, &pt, 1 );
1664     for (;;)
1665     {
1666         HWND parent;
1667
1668         if (!(GetWindowLongW( hwnd, GWL_STYLE ) & WS_CHILD)) break;
1669         if (GetWindowLongW( hwnd, GWL_EXSTYLE ) & WS_EX_NOPARENTNOTIFY) break;
1670         if (!(parent = GetParent(hwnd))) break;
1671         if (parent == GetDesktopWindow()) break;
1672         MapWindowPoints( hwnd, parent, &pt, 1 );
1673         hwnd = parent;
1674         SendMessageW( hwnd, WM_PARENTNOTIFY,
1675                       MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
1676     }
1677 }
1678
1679
1680 /***********************************************************************
1681  *          accept_hardware_message
1682  *
1683  * Tell the server we have passed the message to the app
1684  * (even though we may end up dropping it later on)
1685  */
1686 static void accept_hardware_message( UINT hw_id, BOOL remove, HWND new_hwnd )
1687 {
1688     SERVER_START_REQ( accept_hardware_message )
1689     {
1690         req->hw_id   = hw_id;
1691         req->remove  = remove;
1692         req->new_win = new_hwnd;
1693         if (wine_server_call( req ))
1694             FIXME("Failed to reply to MSG_HARDWARE message. Message may not be removed from queue.\n");
1695     }
1696     SERVER_END_REQ;
1697 }
1698
1699
1700 /***********************************************************************
1701  *          process_keyboard_message
1702  *
1703  * returns TRUE if the contents of 'msg' should be passed to the application
1704  */
1705 static BOOL process_keyboard_message( MSG *msg, UINT hw_id, HWND hwnd_filter,
1706                                       UINT first, UINT last, BOOL remove )
1707 {
1708     EVENTMSG event;
1709
1710     if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN ||
1711         msg->message == WM_KEYUP || msg->message == WM_SYSKEYUP)
1712         switch (msg->wParam)
1713         {
1714             case VK_LSHIFT: case VK_RSHIFT:
1715                 msg->wParam = VK_SHIFT;
1716                 break;
1717             case VK_LCONTROL: case VK_RCONTROL:
1718                 msg->wParam = VK_CONTROL;
1719                 break;
1720             case VK_LMENU: case VK_RMENU:
1721                 msg->wParam = VK_MENU;
1722                 break;
1723         }
1724
1725     /* FIXME: is this really the right place for this hook? */
1726     event.message = msg->message;
1727     event.hwnd    = msg->hwnd;
1728     event.time    = msg->time;
1729     event.paramL  = (msg->wParam & 0xFF) | (HIWORD(msg->lParam) << 8);
1730     event.paramH  = msg->lParam & 0x7FFF;
1731     if (HIWORD(msg->lParam) & 0x0100) event.paramH |= 0x8000; /* special_key - bit */
1732     HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event, TRUE );
1733
1734     /* check message filters */
1735     if (msg->message < first || msg->message > last) return FALSE;
1736     if (!check_hwnd_filter( msg, hwnd_filter )) return FALSE;
1737
1738     if (remove)
1739     {
1740         if((msg->message == WM_KEYDOWN) &&
1741            (msg->hwnd != GetDesktopWindow()))
1742         {
1743             /* Handle F1 key by sending out WM_HELP message */
1744             if (msg->wParam == VK_F1)
1745             {
1746                 PostMessageW( msg->hwnd, WM_KEYF1, 0, 0 );
1747             }
1748             else if(msg->wParam >= VK_BROWSER_BACK &&
1749                     msg->wParam <= VK_LAUNCH_APP2)
1750             {
1751                 /* FIXME: Process keystate */
1752                 SendMessageW(msg->hwnd, WM_APPCOMMAND, (WPARAM)msg->hwnd, MAKELPARAM(0, (FAPPCOMMAND_KEY | (msg->wParam - VK_BROWSER_BACK + 1))));
1753             }
1754         }
1755         else if (msg->message == WM_KEYUP)
1756         {
1757             /* Handle VK_APPS key by posting a WM_CONTEXTMENU message */
1758             if (msg->wParam == VK_APPS && !MENU_IsMenuActive())
1759                 PostMessageW(msg->hwnd, WM_CONTEXTMENU, (WPARAM)msg->hwnd, (LPARAM)-1);
1760         }
1761     }
1762
1763     if (HOOK_CallHooks( WH_KEYBOARD, remove ? HC_ACTION : HC_NOREMOVE,
1764                         LOWORD(msg->wParam), msg->lParam, TRUE ))
1765     {
1766         /* skip this message */
1767         HOOK_CallHooks( WH_CBT, HCBT_KEYSKIPPED, LOWORD(msg->wParam), msg->lParam, TRUE );
1768         accept_hardware_message( hw_id, TRUE, 0 );
1769         return FALSE;
1770     }
1771     accept_hardware_message( hw_id, remove, 0 );
1772
1773     if ( msg->message == WM_KEYDOWN || msg->message == WM_KEYUP )
1774         if ( ImmProcessKey(msg->hwnd, GetKeyboardLayout(0), msg->wParam, msg->lParam, 0) )
1775             msg->wParam = VK_PROCESSKEY;
1776
1777     return TRUE;
1778 }
1779
1780
1781 /***********************************************************************
1782  *          process_mouse_message
1783  *
1784  * returns TRUE if the contents of 'msg' should be passed to the application
1785  */
1786 static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, HWND hwnd_filter,
1787                                    UINT first, UINT last, BOOL remove )
1788 {
1789     static MSG clk_msg;
1790
1791     POINT pt;
1792     UINT message;
1793     INT hittest;
1794     EVENTMSG event;
1795     GUITHREADINFO info;
1796     MOUSEHOOKSTRUCT hook;
1797     BOOL eatMsg;
1798
1799     /* find the window to dispatch this mouse message to */
1800
1801     GetGUIThreadInfo( GetCurrentThreadId(), &info );
1802     if (info.hwndCapture)
1803     {
1804         hittest = HTCLIENT;
1805         msg->hwnd = info.hwndCapture;
1806     }
1807     else
1808     {
1809         msg->hwnd = WINPOS_WindowFromPoint( msg->hwnd, msg->pt, &hittest );
1810     }
1811
1812     if (!msg->hwnd || !WIN_IsCurrentThread( msg->hwnd ))
1813     {
1814         accept_hardware_message( hw_id, TRUE, msg->hwnd );
1815         return FALSE;
1816     }
1817
1818     /* FIXME: is this really the right place for this hook? */
1819     event.message = msg->message;
1820     event.time    = msg->time;
1821     event.hwnd    = msg->hwnd;
1822     event.paramL  = msg->pt.x;
1823     event.paramH  = msg->pt.y;
1824     HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event, TRUE );
1825
1826     if (!check_hwnd_filter( msg, hwnd_filter )) return FALSE;
1827
1828     pt = msg->pt;
1829     message = msg->message;
1830     /* Note: windows has no concept of a non-client wheel message */
1831     if (message != WM_MOUSEWHEEL)
1832     {
1833         if (hittest != HTCLIENT)
1834         {
1835             message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
1836             msg->wParam = hittest;
1837         }
1838         else
1839         {
1840             /* coordinates don't get translated while tracking a menu */
1841             /* FIXME: should differentiate popups and top-level menus */
1842             if (!(info.flags & GUI_INMENUMODE))
1843                 ScreenToClient( msg->hwnd, &pt );
1844         }
1845     }
1846     msg->lParam = MAKELONG( pt.x, pt.y );
1847
1848     /* translate double clicks */
1849
1850     if ((msg->message == WM_LBUTTONDOWN) ||
1851         (msg->message == WM_RBUTTONDOWN) ||
1852         (msg->message == WM_MBUTTONDOWN) ||
1853         (msg->message == WM_XBUTTONDOWN))
1854     {
1855         BOOL update = remove;
1856
1857         /* translate double clicks -
1858          * note that ...MOUSEMOVEs can slip in between
1859          * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
1860
1861         if ((info.flags & (GUI_INMENUMODE|GUI_INMOVESIZE)) ||
1862             hittest != HTCLIENT ||
1863             (GetClassLongA( msg->hwnd, GCL_STYLE ) & CS_DBLCLKS))
1864         {
1865            if ((msg->message == clk_msg.message) &&
1866                (msg->hwnd == clk_msg.hwnd) &&
1867                (msg->wParam == clk_msg.wParam) &&
1868                (msg->time - clk_msg.time < GetDoubleClickTime()) &&
1869                (abs(msg->pt.x - clk_msg.pt.x) < GetSystemMetrics(SM_CXDOUBLECLK)/2) &&
1870                (abs(msg->pt.y - clk_msg.pt.y) < GetSystemMetrics(SM_CYDOUBLECLK)/2))
1871            {
1872                message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
1873                if (update)
1874                {
1875                    clk_msg.message = 0;  /* clear the double click conditions */
1876                    update = FALSE;
1877                }
1878            }
1879         }
1880         if (message < first || message > last) return FALSE;
1881         /* update static double click conditions */
1882         if (update) clk_msg = *msg;
1883     }
1884     else
1885     {
1886         if (message < first || message > last) return FALSE;
1887     }
1888
1889     /* message is accepted now (but may still get dropped) */
1890
1891     hook.pt           = msg->pt;
1892     hook.hwnd         = msg->hwnd;
1893     hook.wHitTestCode = hittest;
1894     hook.dwExtraInfo  = extra_info;
1895     if (HOOK_CallHooks( WH_MOUSE, remove ? HC_ACTION : HC_NOREMOVE,
1896                         message, (LPARAM)&hook, TRUE ))
1897     {
1898         hook.pt           = msg->pt;
1899         hook.hwnd         = msg->hwnd;
1900         hook.wHitTestCode = hittest;
1901         hook.dwExtraInfo  = extra_info;
1902         HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook, TRUE );
1903         accept_hardware_message( hw_id, TRUE, 0 );
1904         return FALSE;
1905     }
1906
1907     if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1908     {
1909         SendMessageW( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1910                       MAKELONG( hittest, msg->message ));
1911         accept_hardware_message( hw_id, TRUE, 0 );
1912         return FALSE;
1913     }
1914
1915     accept_hardware_message( hw_id, remove, 0 );
1916
1917     if (!remove || info.hwndCapture)
1918     {
1919         msg->message = message;
1920         return TRUE;
1921     }
1922
1923     eatMsg = FALSE;
1924
1925     if ((msg->message == WM_LBUTTONDOWN) ||
1926         (msg->message == WM_RBUTTONDOWN) ||
1927         (msg->message == WM_MBUTTONDOWN) ||
1928         (msg->message == WM_XBUTTONDOWN))
1929     {
1930         /* Send the WM_PARENTNOTIFY,
1931          * note that even for double/nonclient clicks
1932          * notification message is still WM_L/M/RBUTTONDOWN.
1933          */
1934         send_parent_notify( msg->hwnd, msg->message, 0, msg->pt );
1935
1936         /* Activate the window if needed */
1937
1938         if (msg->hwnd != info.hwndActive)
1939         {
1940             HWND hwndTop = msg->hwnd;
1941             while (hwndTop)
1942             {
1943                 if ((GetWindowLongW( hwndTop, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
1944                 hwndTop = GetParent( hwndTop );
1945             }
1946
1947             if (hwndTop && hwndTop != GetDesktopWindow())
1948             {
1949                 LONG ret = SendMessageW( msg->hwnd, WM_MOUSEACTIVATE, (WPARAM)hwndTop,
1950                                          MAKELONG( hittest, msg->message ) );
1951                 switch(ret)
1952                 {
1953                 case MA_NOACTIVATEANDEAT:
1954                     eatMsg = TRUE;
1955                     /* fall through */
1956                 case MA_NOACTIVATE:
1957                     break;
1958                 case MA_ACTIVATEANDEAT:
1959                     eatMsg = TRUE;
1960                     /* fall through */
1961                 case MA_ACTIVATE:
1962                 case 0:
1963                     if (!FOCUS_MouseActivate( hwndTop )) eatMsg = TRUE;
1964                     break;
1965                 default:
1966                     WARN( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1967                     break;
1968                 }
1969             }
1970         }
1971     }
1972
1973     /* send the WM_SETCURSOR message */
1974
1975     /* Windows sends the normal mouse message as the message parameter
1976        in the WM_SETCURSOR message even if it's non-client mouse message */
1977     SendMessageW( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1978
1979     msg->message = message;
1980     return !eatMsg;
1981 }
1982
1983
1984 /***********************************************************************
1985  *           process_hardware_message
1986  *
1987  * Process a hardware message; return TRUE if message should be passed on to the app
1988  */
1989 static BOOL process_hardware_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, HWND hwnd_filter,
1990                                       UINT first, UINT last, BOOL remove )
1991 {
1992     if (is_keyboard_message( msg->message ))
1993         return process_keyboard_message( msg, hw_id, hwnd_filter, first, last, remove );
1994
1995     if (is_mouse_message( msg->message ))
1996         return process_mouse_message( msg, hw_id, extra_info, hwnd_filter, first, last, remove );
1997
1998     ERR( "unknown message type %x\n", msg->message );
1999     return FALSE;
2000 }
2001
2002
2003 /***********************************************************************
2004  *           call_sendmsg_callback
2005  *
2006  * Call the callback function of SendMessageCallback.
2007  */
2008 static inline void call_sendmsg_callback( SENDASYNCPROC callback, HWND hwnd, UINT msg,
2009                                           ULONG_PTR data, LRESULT result )
2010 {
2011     if (!callback) return;
2012
2013     if (TRACE_ON(relay))
2014         DPRINTF( "%04x:Call message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
2015                  GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
2016                  data, result );
2017     callback( hwnd, msg, data, result );
2018     if (TRACE_ON(relay))
2019         DPRINTF( "%04x:Ret  message callback %p (hwnd=%p,msg=%s,data=%08lx,result=%08lx)\n",
2020                  GetCurrentThreadId(), callback, hwnd, SPY_GetMsgName( msg, hwnd ),
2021                  data, result );
2022 }
2023
2024
2025 /***********************************************************************
2026  *           peek_message
2027  *
2028  * Peek for a message matching the given parameters. Return FALSE if none available.
2029  * All pending sent messages are processed before returning.
2030  */
2031 static BOOL peek_message( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags )
2032 {
2033     LRESULT result;
2034     ULONG_PTR extra_info = 0;
2035     struct user_thread_info *thread_info = get_user_thread_info();
2036     struct received_message_info info, *old_info;
2037     unsigned int wake_mask, changed_mask = HIWORD(flags);
2038     unsigned int hw_id = 0;  /* id of previous hardware message */
2039
2040     if (!first && !last) last = ~0;
2041     if (!changed_mask) changed_mask = QS_ALLINPUT;
2042     wake_mask = changed_mask & (QS_SENDMESSAGE | QS_SMRESULT);
2043
2044     for (;;)
2045     {
2046         NTSTATUS res;
2047         void *buffer = NULL;
2048         size_t size = 0, buffer_size = 0;
2049
2050         do  /* loop while buffer is too small */
2051         {
2052             if (buffer_size && !(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size )))
2053                 return FALSE;
2054             SERVER_START_REQ( get_message )
2055             {
2056                 req->flags     = flags;
2057                 req->get_win   = hwnd;
2058                 req->get_first = first;
2059                 req->get_last  = last;
2060                 req->hw_id     = hw_id;
2061                 req->wake_mask = wake_mask;
2062                 req->changed_mask = changed_mask;
2063                 if (buffer_size) wine_server_set_reply( req, buffer, buffer_size );
2064                 if (!(res = wine_server_call( req )))
2065                 {
2066                     size = wine_server_reply_size( reply );
2067                     info.type        = reply->type;
2068                     info.msg.hwnd    = reply->win;
2069                     info.msg.message = reply->msg;
2070                     info.msg.wParam  = reply->wparam;
2071                     info.msg.lParam  = reply->lparam;
2072                     info.msg.time    = reply->time;
2073                     info.msg.pt.x    = reply->x;
2074                     info.msg.pt.y    = reply->y;
2075                     hw_id            = reply->hw_id;
2076                     extra_info       = reply->info;
2077                     thread_info->active_hooks = reply->active_hooks;
2078                 }
2079                 else
2080                 {
2081                     HeapFree( GetProcessHeap(), 0, buffer );
2082                     buffer_size = reply->total;
2083                 }
2084             }
2085             SERVER_END_REQ;
2086         } while (res == STATUS_BUFFER_OVERFLOW);
2087
2088         if (res) return FALSE;
2089
2090         TRACE( "got type %d msg %x (%s) hwnd %p wp %lx lp %lx\n",
2091                info.type, info.msg.message,
2092                (info.type == MSG_WINEVENT) ? "MSG_WINEVENT" : SPY_GetMsgName(info.msg.message, info.msg.hwnd),
2093                info.msg.hwnd, info.msg.wParam, info.msg.lParam );
2094
2095         switch(info.type)
2096         {
2097         case MSG_ASCII:
2098         case MSG_UNICODE:
2099             info.flags = ISMEX_SEND;
2100             break;
2101         case MSG_NOTIFY:
2102             info.flags = ISMEX_NOTIFY;
2103             break;
2104         case MSG_CALLBACK:
2105             info.flags = ISMEX_CALLBACK;
2106             break;
2107         case MSG_CALLBACK_RESULT:
2108             if (size >= sizeof(struct callback_msg_data))
2109             {
2110                 const struct callback_msg_data *data = (const struct callback_msg_data *)buffer;
2111                 call_sendmsg_callback( data->callback, info.msg.hwnd,
2112                                        info.msg.message, data->data, data->result );
2113             }
2114             goto next;
2115         case MSG_WINEVENT:
2116             if (size >= sizeof(struct winevent_msg_data))
2117             {
2118                 WINEVENTPROC hook_proc;
2119                 const struct winevent_msg_data *data = (const struct winevent_msg_data *)buffer;
2120
2121                 hook_proc = data->hook_proc;
2122                 size -= sizeof(*data);
2123                 if (size)
2124                 {
2125                     WCHAR module[MAX_PATH];
2126
2127                     size = min( size, (MAX_PATH - 1) * sizeof(WCHAR) );
2128                     memcpy( module, buffer, size );
2129                     module[size / sizeof(WCHAR)] = 0;
2130                     if (!(hook_proc = get_hook_proc( hook_proc, module )))
2131                     {
2132                         ERR( "invalid winevent hook module name %s\n", debugstr_w(module) );
2133                         goto next;
2134                     }
2135                 }
2136
2137                 if (TRACE_ON(relay))
2138                     DPRINTF( "%04x:Call winevent proc %p (hook=%p,event=%x,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04x,time=%x)\n",
2139                              GetCurrentThreadId(), hook_proc,
2140                              data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2141                              info.msg.lParam, data->tid, info.msg.time);
2142
2143                 hook_proc( data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2144                                  info.msg.lParam, data->tid, info.msg.time );
2145
2146                 if (TRACE_ON(relay))
2147                     DPRINTF( "%04x:Ret  winevent proc %p (hook=%p,event=%x,hwnd=%p,object_id=%lx,child_id=%lx,tid=%04x,time=%x)\n",
2148                              GetCurrentThreadId(), hook_proc,
2149                              data->hook, info.msg.message, info.msg.hwnd, info.msg.wParam,
2150                              info.msg.lParam, data->tid, info.msg.time);
2151             }
2152             goto next;
2153         case MSG_OTHER_PROCESS:
2154             info.flags = ISMEX_SEND;
2155             if (!unpack_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
2156                                  &info.msg.lParam, &buffer, size ))
2157             {
2158                 /* ignore it */
2159                 reply_message( &info, 0, TRUE );
2160                 goto next;
2161             }
2162             break;
2163         case MSG_HARDWARE:
2164             if (!process_hardware_message( &info.msg, hw_id, extra_info,
2165                                            hwnd, first, last, flags & PM_REMOVE ))
2166             {
2167                 TRACE("dropping msg %x\n", info.msg.message );
2168                 goto next;  /* ignore it */
2169             }
2170             thread_info->GetMessagePosVal = MAKELONG( info.msg.pt.x, info.msg.pt.y );
2171             /* fall through */
2172         case MSG_POSTED:
2173             thread_info->GetMessageExtraInfoVal = extra_info;
2174             if (info.msg.message >= WM_DDE_FIRST && info.msg.message <= WM_DDE_LAST)
2175             {
2176                 if (!unpack_dde_message( info.msg.hwnd, info.msg.message, &info.msg.wParam,
2177                                          &info.msg.lParam, &buffer, size ))
2178                     goto next;  /* ignore it */
2179             }
2180             *msg = info.msg;
2181             HeapFree( GetProcessHeap(), 0, buffer );
2182             return TRUE;
2183         }
2184
2185         /* if we get here, we have a sent message; call the window procedure */
2186         old_info = thread_info->receive_info;
2187         thread_info->receive_info = &info;
2188         result = call_window_proc( info.msg.hwnd, info.msg.message, info.msg.wParam,
2189                                    info.msg.lParam, (info.type != MSG_ASCII), FALSE,
2190                                    WMCHAR_MAP_RECVMESSAGE );
2191         reply_message( &info, result, TRUE );
2192         thread_info->receive_info = old_info;
2193
2194         /* if some PM_QS* flags were specified, only handle sent messages from now on */
2195         if (HIWORD(flags)) flags = PM_QS_SENDMESSAGE | LOWORD(flags);
2196     next:
2197         HeapFree( GetProcessHeap(), 0, buffer );
2198     }
2199 }
2200
2201
2202 /***********************************************************************
2203  *           process_sent_messages
2204  *
2205  * Process all pending sent messages.
2206  */
2207 static inline void process_sent_messages(void)
2208 {
2209     MSG msg;
2210     peek_message( &msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE );
2211 }
2212
2213
2214 /***********************************************************************
2215  *           get_server_queue_handle
2216  *
2217  * Get a handle to the server message queue for the current thread.
2218  */
2219 static HANDLE get_server_queue_handle(void)
2220 {
2221     struct user_thread_info *thread_info = get_user_thread_info();
2222     HANDLE ret;
2223
2224     if (!(ret = thread_info->server_queue))
2225     {
2226         SERVER_START_REQ( get_msg_queue )
2227         {
2228             wine_server_call( req );
2229             ret = reply->handle;
2230         }
2231         SERVER_END_REQ;
2232         thread_info->server_queue = ret;
2233         if (!ret) ERR( "Cannot get server thread queue\n" );
2234     }
2235     return ret;
2236 }
2237
2238
2239 /***********************************************************************
2240  *           wait_message_reply
2241  *
2242  * Wait until a sent message gets replied to.
2243  */
2244 static void wait_message_reply( UINT flags )
2245 {
2246     HANDLE server_queue = get_server_queue_handle();
2247
2248     for (;;)
2249     {
2250         unsigned int wake_bits = 0;
2251         DWORD dwlc, res;
2252
2253         SERVER_START_REQ( set_queue_mask )
2254         {
2255             req->wake_mask    = QS_SMRESULT | ((flags & SMTO_BLOCK) ? 0 : QS_SENDMESSAGE);
2256             req->changed_mask = req->wake_mask;
2257             req->skip_wait    = 1;
2258             if (!wine_server_call( req ))
2259                 wake_bits = reply->wake_bits;
2260         }
2261         SERVER_END_REQ;
2262
2263         if (wake_bits & QS_SMRESULT) return;  /* got a result */
2264         if (wake_bits & QS_SENDMESSAGE)
2265         {
2266             /* Process the sent message immediately */
2267             process_sent_messages();
2268             continue;
2269         }
2270
2271         /* now wait for it */
2272
2273         ReleaseThunkLock( &dwlc );
2274         res = USER_Driver->pMsgWaitForMultipleObjectsEx( 1, &server_queue,
2275                                                          INFINITE, QS_SENDMESSAGE, 0 );
2276         if (dwlc) RestoreThunkLock( dwlc );
2277     }
2278 }
2279
2280 /***********************************************************************
2281  *              put_message_in_queue
2282  *
2283  * Put a sent message into the destination queue.
2284  * For inter-process message, reply_size is set to expected size of reply data.
2285  */
2286 static BOOL put_message_in_queue( const struct send_message_info *info, size_t *reply_size )
2287 {
2288     struct packed_message data;
2289     message_data_t msg_data;
2290     unsigned int res;
2291     int i;
2292     timeout_t timeout = TIMEOUT_INFINITE;
2293
2294     /* Check for INFINITE timeout for compatibility with Win9x,
2295      * although Windows >= NT does not do so
2296      */
2297     if (info->type != MSG_NOTIFY &&
2298         info->type != MSG_CALLBACK &&
2299         info->type != MSG_POSTED &&
2300         info->timeout &&
2301         info->timeout != INFINITE)
2302     {
2303         /* timeout is signed despite the prototype */
2304         timeout = (timeout_t)max( 0, (int)info->timeout ) * -10000;
2305     }
2306
2307     data.count = 0;
2308     if (info->type == MSG_OTHER_PROCESS)
2309     {
2310         *reply_size = pack_message( info->hwnd, info->msg, info->wparam, info->lparam, &data );
2311         if (data.count == -1)
2312         {
2313             WARN( "cannot pack message %x\n", info->msg );
2314             return FALSE;
2315         }
2316     }
2317     else if (info->type == MSG_CALLBACK)
2318     {
2319         msg_data.callback.callback = info->callback;
2320         msg_data.callback.data     = info->data;
2321         msg_data.callback.result   = 0;
2322         data.data[0] = &msg_data;
2323         data.size[0] = sizeof(msg_data.callback);
2324         data.count = 1;
2325     }
2326     else if (info->type == MSG_POSTED && info->msg >= WM_DDE_FIRST && info->msg <= WM_DDE_LAST)
2327     {
2328         return post_dde_message( &data, info );
2329     }
2330
2331     SERVER_START_REQ( send_message )
2332     {
2333         req->id      = info->dest_tid;
2334         req->type    = info->type;
2335         req->flags   = 0;
2336         req->win     = info->hwnd;
2337         req->msg     = info->msg;
2338         req->wparam  = info->wparam;
2339         req->lparam  = info->lparam;
2340         req->timeout = timeout;
2341
2342         if (info->flags & SMTO_ABORTIFHUNG) req->flags |= SEND_MSG_ABORT_IF_HUNG;
2343         for (i = 0; i < data.count; i++) wine_server_add_data( req, data.data[i], data.size[i] );
2344         if ((res = wine_server_call( req )))
2345         {
2346             if (res == STATUS_INVALID_PARAMETER)
2347                 /* FIXME: find a STATUS_ value for this one */
2348                 SetLastError( ERROR_INVALID_THREAD_ID );
2349             else
2350                 SetLastError( RtlNtStatusToDosError(res) );
2351         }
2352     }
2353     SERVER_END_REQ;
2354     return !res;
2355 }
2356
2357
2358 /***********************************************************************
2359  *              retrieve_reply
2360  *
2361  * Retrieve a message reply from the server.
2362  */
2363 static LRESULT retrieve_reply( const struct send_message_info *info,
2364                                size_t reply_size, LRESULT *result )
2365 {
2366     NTSTATUS status;
2367     void *reply_data = NULL;
2368
2369     if (reply_size)
2370     {
2371         if (!(reply_data = HeapAlloc( GetProcessHeap(), 0, reply_size )))
2372         {
2373             WARN( "no memory for reply, will be truncated\n" );
2374             reply_size = 0;
2375         }
2376     }
2377     SERVER_START_REQ( get_message_reply )
2378     {
2379         req->cancel = 1;
2380         if (reply_size) wine_server_set_reply( req, reply_data, reply_size );
2381         if (!(status = wine_server_call( req ))) *result = reply->result;
2382         reply_size = wine_server_reply_size( reply );
2383     }
2384     SERVER_END_REQ;
2385     if (!status && reply_size)
2386         unpack_reply( info->hwnd, info->msg, info->wparam, info->lparam, reply_data, reply_size );
2387
2388     HeapFree( GetProcessHeap(), 0, reply_data );
2389
2390     TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx got reply %lx (err=%d)\n",
2391            info->hwnd, info->msg, SPY_GetMsgName(info->msg, info->hwnd), info->wparam,
2392            info->lparam, *result, status );
2393
2394     /* MSDN states that last error is 0 on timeout, but at least NT4 returns ERROR_TIMEOUT */
2395     if (status) SetLastError( RtlNtStatusToDosError(status) );
2396     return !status;
2397 }
2398
2399
2400 /***********************************************************************
2401  *              send_inter_thread_message
2402  */
2403 static LRESULT send_inter_thread_message( const struct send_message_info *info, LRESULT *res_ptr )
2404 {
2405     size_t reply_size = 0;
2406
2407     TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx\n",
2408            info->hwnd, info->msg, SPY_GetMsgName(info->msg, info->hwnd), info->wparam, info->lparam );
2409
2410     USER_CheckNotLock();
2411
2412     if (!put_message_in_queue( info, &reply_size )) return 0;
2413
2414     /* there's no reply to wait for on notify/callback messages */
2415     if (info->type == MSG_NOTIFY || info->type == MSG_CALLBACK) return 1;
2416
2417     wait_message_reply( info->flags );
2418     return retrieve_reply( info, reply_size, res_ptr );
2419 }
2420
2421
2422 /***********************************************************************
2423  *              send_inter_thread_callback
2424  */
2425 static LRESULT send_inter_thread_callback( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
2426                                            LRESULT *result, void *arg )
2427 {
2428     struct send_message_info *info = arg;
2429     info->hwnd   = hwnd;
2430     info->msg    = msg;
2431     info->wparam = wp;
2432     info->lparam = lp;
2433     return send_inter_thread_message( info, result );
2434 }
2435
2436
2437 /***********************************************************************
2438  *              send_message
2439  *
2440  * Backend implementation of the various SendMessage functions.
2441  */
2442 static BOOL send_message( struct send_message_info *info, DWORD_PTR *res_ptr, BOOL unicode )
2443 {
2444     DWORD dest_pid;
2445     BOOL ret;
2446     LRESULT result;
2447
2448     if (is_broadcast(info->hwnd))
2449     {
2450         EnumWindows( broadcast_message_callback, (LPARAM)info );
2451         if (res_ptr) *res_ptr = 1;
2452         return TRUE;
2453     }
2454
2455     if (!(info->dest_tid = GetWindowThreadProcessId( info->hwnd, &dest_pid ))) return FALSE;
2456
2457     if (USER_IsExitingThread( info->dest_tid )) return FALSE;
2458
2459     SPY_EnterMessage( SPY_SENDMESSAGE, info->hwnd, info->msg, info->wparam, info->lparam );
2460
2461     if (info->dest_tid == GetCurrentThreadId())
2462     {
2463         result = call_window_proc( info->hwnd, info->msg, info->wparam, info->lparam,
2464                                    unicode, TRUE, info->wm_char );
2465         if (info->type == MSG_CALLBACK)
2466             call_sendmsg_callback( info->callback, info->hwnd, info->msg, info->data, result );
2467         ret = TRUE;
2468     }
2469     else
2470     {
2471         if (dest_pid != GetCurrentProcessId() && (info->type == MSG_ASCII || info->type == MSG_UNICODE))
2472             info->type = MSG_OTHER_PROCESS;
2473
2474         /* MSG_ASCII can be sent unconverted except for WM_CHAR; everything else needs to be Unicode */
2475         if (!unicode && is_unicode_message( info->msg ) &&
2476             (info->type != MSG_ASCII || info->msg == WM_CHAR))
2477             ret = WINPROC_CallProcAtoW( send_inter_thread_callback, info->hwnd, info->msg,
2478                                         info->wparam, info->lparam, &result, info, info->wm_char );
2479         else
2480             ret = send_inter_thread_message( info, &result );
2481     }
2482
2483     SPY_ExitMessage( SPY_RESULT_OK, info->hwnd, info->msg, result, info->wparam, info->lparam );
2484     if (ret && res_ptr) *res_ptr = result;
2485     return ret;
2486 }
2487
2488
2489 /***********************************************************************
2490  *              MSG_SendInternalMessageTimeout
2491  *
2492  * Same as SendMessageTimeoutW but sends the message to a specific thread
2493  * without requiring a window handle. Only works for internal Wine messages.
2494  */
2495 LRESULT MSG_SendInternalMessageTimeout( DWORD dest_pid, DWORD dest_tid,
2496                                         UINT msg, WPARAM wparam, LPARAM lparam,
2497                                         UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2498 {
2499     struct send_message_info info;
2500     LRESULT ret, result;
2501
2502     assert( msg & 0x80000000 );  /* must be an internal Wine message */
2503
2504     info.type     = MSG_UNICODE;
2505     info.dest_tid = dest_tid;
2506     info.hwnd     = 0;
2507     info.msg      = msg;
2508     info.wparam   = wparam;
2509     info.lparam   = lparam;
2510     info.flags    = flags;
2511     info.timeout  = timeout;
2512
2513     if (USER_IsExitingThread( dest_tid )) return 0;
2514
2515     if (dest_tid == GetCurrentThreadId())
2516     {
2517         result = handle_internal_message( 0, msg, wparam, lparam );
2518         ret = 1;
2519     }
2520     else
2521     {
2522         if (dest_pid != GetCurrentProcessId()) info.type = MSG_OTHER_PROCESS;
2523         ret = send_inter_thread_message( &info, &result );
2524     }
2525     if (ret && res_ptr) *res_ptr = result;
2526     return ret;
2527 }
2528
2529
2530 /***********************************************************************
2531  *              SendMessageTimeoutW  (USER32.@)
2532  */
2533 LRESULT WINAPI SendMessageTimeoutW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2534                                     UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2535 {
2536     struct send_message_info info;
2537
2538     info.type    = MSG_UNICODE;
2539     info.hwnd    = hwnd;
2540     info.msg     = msg;
2541     info.wparam  = wparam;
2542     info.lparam  = lparam;
2543     info.flags   = flags;
2544     info.timeout = timeout;
2545
2546     return send_message( &info, res_ptr, TRUE );
2547 }
2548
2549 /***********************************************************************
2550  *              SendMessageTimeoutA  (USER32.@)
2551  */
2552 LRESULT WINAPI SendMessageTimeoutA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2553                                     UINT flags, UINT timeout, PDWORD_PTR res_ptr )
2554 {
2555     struct send_message_info info;
2556
2557     info.type    = MSG_ASCII;
2558     info.hwnd    = hwnd;
2559     info.msg     = msg;
2560     info.wparam  = wparam;
2561     info.lparam  = lparam;
2562     info.flags   = flags;
2563     info.timeout = timeout;
2564     info.wm_char  = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2565
2566     return send_message( &info, res_ptr, FALSE );
2567 }
2568
2569
2570 /***********************************************************************
2571  *              SendMessageW  (USER32.@)
2572  */
2573 LRESULT WINAPI SendMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2574 {
2575     DWORD_PTR res = 0;
2576     struct send_message_info info;
2577
2578     info.type    = MSG_UNICODE;
2579     info.hwnd    = hwnd;
2580     info.msg     = msg;
2581     info.wparam  = wparam;
2582     info.lparam  = lparam;
2583     info.flags   = SMTO_NORMAL;
2584     info.timeout = 0;
2585
2586     send_message( &info, &res, TRUE );
2587     return res;
2588 }
2589
2590
2591 /***********************************************************************
2592  *              SendMessageA  (USER32.@)
2593  */
2594 LRESULT WINAPI SendMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2595 {
2596     DWORD_PTR res = 0;
2597     struct send_message_info info;
2598
2599     info.type    = MSG_ASCII;
2600     info.hwnd    = hwnd;
2601     info.msg     = msg;
2602     info.wparam  = wparam;
2603     info.lparam  = lparam;
2604     info.flags   = SMTO_NORMAL;
2605     info.timeout = 0;
2606     info.wm_char  = WMCHAR_MAP_SENDMESSAGE;
2607
2608     send_message( &info, &res, FALSE );
2609     return res;
2610 }
2611
2612
2613 /***********************************************************************
2614  *              SendNotifyMessageA  (USER32.@)
2615  */
2616 BOOL WINAPI SendNotifyMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2617 {
2618     struct send_message_info info;
2619
2620     if (is_pointer_message(msg))
2621     {
2622         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2623         return FALSE;
2624     }
2625
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;
2632     info.wm_char = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2633
2634     return send_message( &info, NULL, FALSE );
2635 }
2636
2637
2638 /***********************************************************************
2639  *              SendNotifyMessageW  (USER32.@)
2640  */
2641 BOOL WINAPI SendNotifyMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2642 {
2643     struct send_message_info info;
2644
2645     if (is_pointer_message(msg))
2646     {
2647         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2648         return FALSE;
2649     }
2650
2651     info.type    = MSG_NOTIFY;
2652     info.hwnd    = hwnd;
2653     info.msg     = msg;
2654     info.wparam  = wparam;
2655     info.lparam  = lparam;
2656     info.flags   = 0;
2657
2658     return send_message( &info, NULL, TRUE );
2659 }
2660
2661
2662 /***********************************************************************
2663  *              SendMessageCallbackA  (USER32.@)
2664  */
2665 BOOL WINAPI SendMessageCallbackA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2666                                   SENDASYNCPROC callback, ULONG_PTR data )
2667 {
2668     struct send_message_info info;
2669
2670     if (is_pointer_message(msg))
2671     {
2672         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2673         return FALSE;
2674     }
2675
2676     info.type     = MSG_CALLBACK;
2677     info.hwnd     = hwnd;
2678     info.msg      = msg;
2679     info.wparam   = wparam;
2680     info.lparam   = lparam;
2681     info.callback = callback;
2682     info.data     = data;
2683     info.flags    = 0;
2684     info.wm_char  = WMCHAR_MAP_SENDMESSAGETIMEOUT;
2685
2686     return send_message( &info, NULL, FALSE );
2687 }
2688
2689
2690 /***********************************************************************
2691  *              SendMessageCallbackW  (USER32.@)
2692  */
2693 BOOL WINAPI SendMessageCallbackW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
2694                                   SENDASYNCPROC callback, ULONG_PTR data )
2695 {
2696     struct send_message_info info;
2697
2698     if (is_pointer_message(msg))
2699     {
2700         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2701         return FALSE;
2702     }
2703
2704     info.type     = MSG_CALLBACK;
2705     info.hwnd     = hwnd;
2706     info.msg      = msg;
2707     info.wparam   = wparam;
2708     info.lparam   = lparam;
2709     info.callback = callback;
2710     info.data     = data;
2711     info.flags    = 0;
2712
2713     return send_message( &info, NULL, TRUE );
2714 }
2715
2716
2717 /***********************************************************************
2718  *              ReplyMessage  (USER32.@)
2719  */
2720 BOOL WINAPI ReplyMessage( LRESULT result )
2721 {
2722     struct received_message_info *info = get_user_thread_info()->receive_info;
2723
2724     if (!info) return FALSE;
2725     reply_message( info, result, FALSE );
2726     return TRUE;
2727 }
2728
2729
2730 /***********************************************************************
2731  *              InSendMessage  (USER32.@)
2732  */
2733 BOOL WINAPI InSendMessage(void)
2734 {
2735     return (InSendMessageEx(NULL) & (ISMEX_SEND|ISMEX_REPLIED)) == ISMEX_SEND;
2736 }
2737
2738
2739 /***********************************************************************
2740  *              InSendMessageEx  (USER32.@)
2741  */
2742 DWORD WINAPI InSendMessageEx( LPVOID reserved )
2743 {
2744     struct received_message_info *info = get_user_thread_info()->receive_info;
2745
2746     if (info) return info->flags;
2747     return ISMEX_NOSEND;
2748 }
2749
2750
2751 /***********************************************************************
2752  *              PostMessageA  (USER32.@)
2753  */
2754 BOOL WINAPI PostMessageA( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2755 {
2756     if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
2757     return PostMessageW( hwnd, msg, wparam, lparam );
2758 }
2759
2760
2761 /***********************************************************************
2762  *              PostMessageW  (USER32.@)
2763  */
2764 BOOL WINAPI PostMessageW( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2765 {
2766     struct send_message_info info;
2767
2768     if (is_pointer_message( msg ))
2769     {
2770         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2771         return FALSE;
2772     }
2773
2774     TRACE( "hwnd %p msg %x (%s) wp %lx lp %lx\n",
2775            hwnd, msg, SPY_GetMsgName(msg, hwnd), wparam, lparam );
2776
2777     info.type   = MSG_POSTED;
2778     info.hwnd   = hwnd;
2779     info.msg    = msg;
2780     info.wparam = wparam;
2781     info.lparam = lparam;
2782     info.flags  = 0;
2783
2784     if (is_broadcast(hwnd))
2785     {
2786         EnumWindows( broadcast_message_callback, (LPARAM)&info );
2787         return TRUE;
2788     }
2789
2790     if (!hwnd) return PostThreadMessageW( GetCurrentThreadId(), msg, wparam, lparam );
2791
2792     if (!(info.dest_tid = GetWindowThreadProcessId( hwnd, NULL ))) return FALSE;
2793
2794     if (USER_IsExitingThread( info.dest_tid )) return TRUE;
2795
2796     return put_message_in_queue( &info, NULL );
2797 }
2798
2799
2800 /**********************************************************************
2801  *              PostThreadMessageA  (USER32.@)
2802  */
2803 BOOL WINAPI PostThreadMessageA( DWORD thread, UINT msg, WPARAM wparam, LPARAM lparam )
2804 {
2805     if (!map_wparam_AtoW( msg, &wparam, WMCHAR_MAP_POSTMESSAGE )) return TRUE;
2806     return PostThreadMessageW( thread, msg, wparam, lparam );
2807 }
2808
2809
2810 /**********************************************************************
2811  *              PostThreadMessageW  (USER32.@)
2812  */
2813 BOOL WINAPI PostThreadMessageW( DWORD thread, UINT msg, WPARAM wparam, LPARAM lparam )
2814 {
2815     struct send_message_info info;
2816
2817     if (is_pointer_message( msg ))
2818     {
2819         SetLastError( ERROR_MESSAGE_SYNC_ONLY );
2820         return FALSE;
2821     }
2822     if (USER_IsExitingThread( thread )) return TRUE;
2823
2824     info.type     = MSG_POSTED;
2825     info.dest_tid = thread;
2826     info.hwnd     = 0;
2827     info.msg      = msg;
2828     info.wparam   = wparam;
2829     info.lparam   = lparam;
2830     info.flags    = 0;
2831     return put_message_in_queue( &info, NULL );
2832 }
2833
2834
2835 /***********************************************************************
2836  *              PostQuitMessage  (USER32.@)
2837  *
2838  * Posts a quit message to the current thread's message queue.
2839  *
2840  * PARAMS
2841  *  exit_code [I] Exit code to return from message loop.
2842  *
2843  * RETURNS
2844  *  Nothing.
2845  *
2846  * NOTES
2847  *  This function is not the same as calling:
2848  *|PostThreadMessage(GetCurrentThreadId(), WM_QUIT, exit_code, 0);
2849  *  It instead sets a flag in the message queue that signals it to generate
2850  *  a WM_QUIT message when there are no other pending sent or posted messages
2851  *  in the queue.
2852  */
2853 void WINAPI PostQuitMessage( INT exit_code )
2854 {
2855     SERVER_START_REQ( post_quit_message )
2856     {
2857         req->exit_code = exit_code;
2858         wine_server_call( req );
2859     }
2860     SERVER_END_REQ;
2861 }
2862
2863
2864 /***********************************************************************
2865  *              PeekMessageW  (USER32.@)
2866  */
2867 BOOL WINAPI PeekMessageW( MSG *msg_out, HWND hwnd, UINT first, UINT last, UINT flags )
2868 {
2869     struct user_thread_info *thread_info = get_user_thread_info();
2870     MSG msg;
2871
2872     USER_CheckNotLock();
2873
2874     /* check for graphics events */
2875     USER_Driver->pMsgWaitForMultipleObjectsEx( 0, NULL, 0, QS_ALLINPUT, 0 );
2876
2877     hwnd = WIN_GetFullHandle( hwnd );
2878
2879     for (;;)
2880     {
2881         if (!peek_message( &msg, hwnd, first, last, flags ))
2882         {
2883             if (!(flags & PM_NOYIELD))
2884             {
2885                 DWORD count;
2886                 ReleaseThunkLock(&count);
2887                 NtYieldExecution();
2888                 if (count) RestoreThunkLock(count);
2889             }
2890             return FALSE;
2891         }
2892         if (msg.message & 0x80000000)
2893         {
2894             if (!(flags & PM_REMOVE))
2895             {
2896                 /* Have to remove the message explicitly.
2897                    Do this before handling it, because the message handler may
2898                    call PeekMessage again */
2899                 peek_message( &msg, msg.hwnd, msg.message, msg.message, flags | PM_REMOVE );
2900             }
2901             handle_internal_message( msg.hwnd, msg.message, msg.wParam, msg.lParam );
2902         }
2903         else break;
2904     }
2905
2906     thread_info->GetMessageTimeVal = msg.time;
2907     msg.pt.x = (short)LOWORD( thread_info->GetMessagePosVal );
2908     msg.pt.y = (short)HIWORD( thread_info->GetMessagePosVal );
2909
2910     HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, flags & PM_REMOVE, (LPARAM)&msg, TRUE );
2911
2912     /* copy back our internal safe copy of message data to msg_out.
2913      * msg_out is a variable from the *program*, so it can't be used
2914      * internally as it can get "corrupted" by our use of SendMessage()
2915      * (back to the program) inside the message handling itself. */
2916     if (!msg_out)
2917     {
2918         SetLastError( ERROR_NOACCESS );
2919         return FALSE;
2920     }
2921     *msg_out = msg;
2922     return TRUE;
2923 }
2924
2925
2926 /***********************************************************************
2927  *              PeekMessageA  (USER32.@)
2928  */
2929 BOOL WINAPI PeekMessageA( MSG *msg, HWND hwnd, UINT first, UINT last, UINT flags )
2930 {
2931     if (get_pending_wmchar( msg, first, last, (flags & PM_REMOVE) )) return TRUE;
2932     if (!PeekMessageW( msg, hwnd, first, last, flags )) return FALSE;
2933     map_wparam_WtoA( msg, (flags & PM_REMOVE) );
2934     return TRUE;
2935 }
2936
2937
2938 /***********************************************************************
2939  *              GetMessageW  (USER32.@)
2940  */
2941 BOOL WINAPI GetMessageW( MSG *msg, HWND hwnd, UINT first, UINT last )
2942 {
2943     HANDLE server_queue = get_server_queue_handle();
2944     int mask = QS_POSTMESSAGE | QS_SENDMESSAGE;  /* Always selected */
2945
2946     if (first || last)
2947     {
2948         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
2949         if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
2950              ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
2951         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
2952         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
2953         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
2954     }
2955     else mask = QS_ALLINPUT;
2956
2957     while (!PeekMessageW( msg, hwnd, first, last, PM_REMOVE | PM_NOYIELD | (mask << 16) ))
2958     {
2959         DWORD dwlc;
2960
2961         ReleaseThunkLock( &dwlc );
2962         USER_Driver->pMsgWaitForMultipleObjectsEx( 1, &server_queue, INFINITE, mask, 0 );
2963         if (dwlc) RestoreThunkLock( dwlc );
2964     }
2965
2966     return (msg->message != WM_QUIT);
2967 }
2968
2969
2970 /***********************************************************************
2971  *              GetMessageA  (USER32.@)
2972  */
2973 BOOL WINAPI GetMessageA( MSG *msg, HWND hwnd, UINT first, UINT last )
2974 {
2975     if (get_pending_wmchar( msg, first, last, TRUE )) return TRUE;
2976     GetMessageW( msg, hwnd, first, last );
2977     map_wparam_WtoA( msg, TRUE );
2978     return (msg->message != WM_QUIT);
2979 }
2980
2981
2982 /***********************************************************************
2983  *              IsDialogMessageA (USER32.@)
2984  *              IsDialogMessage  (USER32.@)
2985  */
2986 BOOL WINAPI IsDialogMessageA( HWND hwndDlg, LPMSG pmsg )
2987 {
2988     MSG msg = *pmsg;
2989     map_wparam_AtoW( msg.message, &msg.wParam, WMCHAR_MAP_NOMAPPING );
2990     return IsDialogMessageW( hwndDlg, &msg );
2991 }
2992
2993
2994 /***********************************************************************
2995  *              TranslateMessage (USER32.@)
2996  *
2997  * Implementation of TranslateMessage.
2998  *
2999  * TranslateMessage translates virtual-key messages into character-messages,
3000  * as follows :
3001  * WM_KEYDOWN/WM_KEYUP combinations produce a WM_CHAR or WM_DEADCHAR message.
3002  * ditto replacing WM_* with WM_SYS*
3003  * This produces WM_CHAR messages only for keys mapped to ASCII characters
3004  * by the keyboard driver.
3005  *
3006  * If the message is WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, or WM_SYSKEYUP, the
3007  * return value is nonzero, regardless of the translation.
3008  *
3009  */
3010 BOOL WINAPI TranslateMessage( const MSG *msg )
3011 {
3012     UINT message;
3013     WCHAR wp[2];
3014     BYTE state[256];
3015
3016     if (msg->message < WM_KEYFIRST || msg->message > WM_KEYLAST) return FALSE;
3017     if (msg->message != WM_KEYDOWN && msg->message != WM_SYSKEYDOWN) return TRUE;
3018
3019     TRACE_(key)("Translating key %s (%04lx), scancode %02x\n",
3020                  SPY_GetVKeyName(msg->wParam), msg->wParam, LOBYTE(HIWORD(msg->lParam)));
3021
3022     if ( msg->wParam == VK_PROCESSKEY )
3023         return ImmTranslateMessage(msg->hwnd, msg->message, msg->wParam, msg->lParam);
3024
3025     GetKeyboardState( state );
3026     /* FIXME : should handle ToUnicode yielding 2 */
3027     switch (ToUnicode(msg->wParam, HIWORD(msg->lParam), state, wp, 2, 0))
3028     {
3029     case 1:
3030         message = (msg->message == WM_KEYDOWN) ? WM_CHAR : WM_SYSCHAR;
3031         TRACE_(key)("1 -> PostMessageW(%p,%s,%04x,%08lx)\n",
3032             msg->hwnd, SPY_GetMsgName(message, msg->hwnd), wp[0], msg->lParam);
3033         PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
3034         break;
3035
3036     case -1:
3037         message = (msg->message == WM_KEYDOWN) ? WM_DEADCHAR : WM_SYSDEADCHAR;
3038         TRACE_(key)("-1 -> PostMessageW(%p,%s,%04x,%08lx)\n",
3039             msg->hwnd, SPY_GetMsgName(message, msg->hwnd), wp[0], msg->lParam);
3040         PostMessageW( msg->hwnd, message, wp[0], msg->lParam );
3041         break;
3042     }
3043     return TRUE;
3044 }
3045
3046
3047 /***********************************************************************
3048  *              DispatchMessageA (USER32.@)
3049  *
3050  * See DispatchMessageW.
3051  */
3052 LRESULT WINAPI DispatchMessageA( const MSG* msg )
3053 {
3054     LRESULT retval;
3055
3056       /* Process timer messages */
3057     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
3058     {
3059         if (msg->lParam) return CallWindowProcA( (WNDPROC)msg->lParam, msg->hwnd,
3060                                                  msg->message, msg->wParam, GetTickCount() );
3061     }
3062     if (!msg->hwnd) return 0;
3063
3064     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
3065                       msg->wParam, msg->lParam );
3066
3067     if (!WINPROC_call_window( msg->hwnd, msg->message, msg->wParam, msg->lParam,
3068                               &retval, FALSE, WMCHAR_MAP_DISPATCHMESSAGE ))
3069     {
3070         if (!IsWindow( msg->hwnd )) SetLastError( ERROR_INVALID_WINDOW_HANDLE );
3071         else SetLastError( ERROR_MESSAGE_SYNC_ONLY );
3072         retval = 0;
3073     }
3074
3075     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
3076                      msg->wParam, msg->lParam );
3077
3078     if (msg->message == WM_PAINT)
3079     {
3080         /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */
3081         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
3082         GetUpdateRgn( msg->hwnd, hrgn, TRUE );
3083         DeleteObject( hrgn );
3084     }
3085     return retval;
3086 }
3087
3088
3089 /***********************************************************************
3090  *              DispatchMessageW (USER32.@) Process a message
3091  *
3092  * Process the message specified in the structure *_msg_.
3093  *
3094  * If the lpMsg parameter points to a WM_TIMER message and the
3095  * parameter of the WM_TIMER message is not NULL, the lParam parameter
3096  * points to the function that is called instead of the window
3097  * procedure.
3098  *
3099  * The message must be valid.
3100  *
3101  * RETURNS
3102  *
3103  *   DispatchMessage() returns the result of the window procedure invoked.
3104  *
3105  * CONFORMANCE
3106  *
3107  *   ECMA-234, Win32
3108  *
3109  */
3110 LRESULT WINAPI DispatchMessageW( const MSG* msg )
3111 {
3112     LRESULT retval;
3113
3114       /* Process timer messages */
3115     if ((msg->message == WM_TIMER) || (msg->message == WM_SYSTIMER))
3116     {
3117         if (msg->lParam) return CallWindowProcW( (WNDPROC)msg->lParam, msg->hwnd,
3118                                                  msg->message, msg->wParam, GetTickCount() );
3119     }
3120     if (!msg->hwnd) return 0;
3121
3122     SPY_EnterMessage( SPY_DISPATCHMESSAGE, msg->hwnd, msg->message,
3123                       msg->wParam, msg->lParam );
3124
3125     if (!WINPROC_call_window( msg->hwnd, msg->message, msg->wParam, msg->lParam,
3126                               &retval, TRUE, WMCHAR_MAP_DISPATCHMESSAGE ))
3127     {
3128         if (!IsWindow( msg->hwnd )) SetLastError( ERROR_INVALID_WINDOW_HANDLE );
3129         else SetLastError( ERROR_MESSAGE_SYNC_ONLY );
3130         retval = 0;
3131     }
3132
3133     SPY_ExitMessage( SPY_RESULT_OK, msg->hwnd, msg->message, retval,
3134                      msg->wParam, msg->lParam );
3135
3136     if (msg->message == WM_PAINT)
3137     {
3138         /* send a WM_NCPAINT and WM_ERASEBKGND if the non-client area is still invalid */
3139         HRGN hrgn = CreateRectRgn( 0, 0, 0, 0 );
3140         GetUpdateRgn( msg->hwnd, hrgn, TRUE );
3141         DeleteObject( hrgn );
3142     }
3143     return retval;
3144 }
3145
3146
3147 /***********************************************************************
3148  *              GetMessagePos (USER.119)
3149  *              GetMessagePos (USER32.@)
3150  *
3151  * The GetMessagePos() function returns a long value representing a
3152  * cursor position, in screen coordinates, when the last message
3153  * retrieved by the GetMessage() function occurs. The x-coordinate is
3154  * in the low-order word of the return value, the y-coordinate is in
3155  * the high-order word. The application can use the MAKEPOINT()
3156  * macro to obtain a POINT structure from the return value.
3157  *
3158  * For the current cursor position, use GetCursorPos().
3159  *
3160  * RETURNS
3161  *
3162  * Cursor position of last message on success, zero on failure.
3163  *
3164  * CONFORMANCE
3165  *
3166  * ECMA-234, Win32
3167  *
3168  */
3169 DWORD WINAPI GetMessagePos(void)
3170 {
3171     return get_user_thread_info()->GetMessagePosVal;
3172 }
3173
3174
3175 /***********************************************************************
3176  *              GetMessageTime (USER.120)
3177  *              GetMessageTime (USER32.@)
3178  *
3179  * GetMessageTime() returns the message time for the last message
3180  * retrieved by the function. The time is measured in milliseconds with
3181  * the same offset as GetTickCount().
3182  *
3183  * Since the tick count wraps, this is only useful for moderately short
3184  * relative time comparisons.
3185  *
3186  * RETURNS
3187  *
3188  * Time of last message on success, zero on failure.
3189  */
3190 LONG WINAPI GetMessageTime(void)
3191 {
3192     return get_user_thread_info()->GetMessageTimeVal;
3193 }
3194
3195
3196 /***********************************************************************
3197  *              GetMessageExtraInfo (USER.288)
3198  *              GetMessageExtraInfo (USER32.@)
3199  */
3200 LPARAM WINAPI GetMessageExtraInfo(void)
3201 {
3202     return get_user_thread_info()->GetMessageExtraInfoVal;
3203 }
3204
3205
3206 /***********************************************************************
3207  *              SetMessageExtraInfo (USER32.@)
3208  */
3209 LPARAM WINAPI SetMessageExtraInfo(LPARAM lParam)
3210 {
3211     struct user_thread_info *thread_info = get_user_thread_info();
3212     LONG old_value = thread_info->GetMessageExtraInfoVal;
3213     thread_info->GetMessageExtraInfoVal = lParam;
3214     return old_value;
3215 }
3216
3217
3218 /***********************************************************************
3219  *              WaitMessage (USER.112) Suspend thread pending messages
3220  *              WaitMessage (USER32.@) Suspend thread pending messages
3221  *
3222  * WaitMessage() suspends a thread until events appear in the thread's
3223  * queue.
3224  */
3225 BOOL WINAPI WaitMessage(void)
3226 {
3227     return (MsgWaitForMultipleObjectsEx( 0, NULL, INFINITE, QS_ALLINPUT, 0 ) != WAIT_FAILED);
3228 }
3229
3230
3231 /***********************************************************************
3232  *              MsgWaitForMultipleObjectsEx   (USER32.@)
3233  */
3234 DWORD WINAPI MsgWaitForMultipleObjectsEx( DWORD count, CONST HANDLE *pHandles,
3235                                           DWORD timeout, DWORD mask, DWORD flags )
3236 {
3237     HANDLE handles[MAXIMUM_WAIT_OBJECTS];
3238     DWORD i, ret, lock;
3239
3240     if (count > MAXIMUM_WAIT_OBJECTS-1)
3241     {
3242         SetLastError( ERROR_INVALID_PARAMETER );
3243         return WAIT_FAILED;
3244     }
3245
3246     /* set the queue mask */
3247     SERVER_START_REQ( set_queue_mask )
3248     {
3249         req->wake_mask    = (flags & MWMO_INPUTAVAILABLE) ? mask : 0;
3250         req->changed_mask = mask;
3251         req->skip_wait    = 0;
3252         wine_server_call( req );
3253     }
3254     SERVER_END_REQ;
3255
3256     /* add the queue to the handle list */
3257     for (i = 0; i < count; i++) handles[i] = pHandles[i];
3258     handles[count] = get_server_queue_handle();
3259
3260     ReleaseThunkLock( &lock );
3261     ret = USER_Driver->pMsgWaitForMultipleObjectsEx( count+1, handles, timeout, mask, flags );
3262     if (lock) RestoreThunkLock( lock );
3263     return ret;
3264 }
3265
3266
3267 /***********************************************************************
3268  *              MsgWaitForMultipleObjects (USER32.@)
3269  */
3270 DWORD WINAPI MsgWaitForMultipleObjects( DWORD count, CONST HANDLE *handles,
3271                                         BOOL wait_all, DWORD timeout, DWORD mask )
3272 {
3273     return MsgWaitForMultipleObjectsEx( count, handles, timeout, mask,
3274                                         wait_all ? MWMO_WAITALL : 0 );
3275 }
3276
3277
3278 /***********************************************************************
3279  *              WaitForInputIdle (USER32.@)
3280  */
3281 DWORD WINAPI WaitForInputIdle( HANDLE hProcess, DWORD dwTimeOut )
3282 {
3283     DWORD start_time, elapsed, ret;
3284     HANDLE handles[2];
3285
3286     handles[0] = hProcess;
3287     SERVER_START_REQ( get_process_idle_event )
3288     {
3289         req->handle = hProcess;
3290         if (!(ret = wine_server_call_err( req ))) handles[1] = reply->event;
3291     }
3292     SERVER_END_REQ;
3293     if (ret) return WAIT_FAILED;  /* error */
3294     if (!handles[1]) return 0;  /* no event to wait on */
3295
3296     start_time = GetTickCount();
3297     elapsed = 0;
3298
3299     TRACE("waiting for %p\n", handles[1] );
3300     do
3301     {
3302         ret = MsgWaitForMultipleObjects ( 2, handles, FALSE, dwTimeOut - elapsed, QS_SENDMESSAGE );
3303         switch (ret)
3304         {
3305         case WAIT_OBJECT_0:
3306             return WAIT_FAILED;
3307         case WAIT_OBJECT_0+2:
3308             process_sent_messages();
3309             break;
3310         case WAIT_TIMEOUT:
3311         case WAIT_FAILED:
3312             TRACE("timeout or error\n");
3313             return ret;
3314         default:
3315             TRACE("finished\n");
3316             return 0;
3317         }
3318         if (dwTimeOut != INFINITE)
3319         {
3320             elapsed = GetTickCount() - start_time;
3321             if (elapsed > dwTimeOut)
3322                 break;
3323         }
3324     }
3325     while (1);
3326
3327     return WAIT_TIMEOUT;
3328 }
3329
3330
3331 /***********************************************************************
3332  *              UserYield (USER.332)
3333  */
3334 void WINAPI UserYield16(void)
3335 {
3336    DWORD count;
3337
3338     /* Handle sent messages */
3339     process_sent_messages();
3340
3341     /* Yield */
3342     ReleaseThunkLock(&count);
3343
3344     if (count)
3345     {
3346         RestoreThunkLock(count);
3347         /* Handle sent messages again */
3348         process_sent_messages();
3349     }
3350 }
3351
3352
3353 /***********************************************************************
3354  *              RegisterWindowMessageA (USER32.@)
3355  *              RegisterWindowMessage (USER.118)
3356  */
3357 UINT WINAPI RegisterWindowMessageA( LPCSTR str )
3358 {
3359     UINT ret = GlobalAddAtomA(str);
3360     TRACE("%s, ret=%x\n", str, ret);
3361     return ret;
3362 }
3363
3364
3365 /***********************************************************************
3366  *              RegisterWindowMessageW (USER32.@)
3367  */
3368 UINT WINAPI RegisterWindowMessageW( LPCWSTR str )
3369 {
3370     UINT ret = GlobalAddAtomW(str);
3371     TRACE("%s ret=%x\n", debugstr_w(str), ret);
3372     return ret;
3373 }
3374
3375 typedef struct BroadcastParm
3376 {
3377     DWORD flags;
3378     LPDWORD recipients;
3379     UINT msg;
3380     WPARAM wp;
3381     LPARAM lp;
3382     DWORD success;
3383     HWINSTA winsta;
3384 } BroadcastParm;
3385
3386 static BOOL CALLBACK bcast_childwindow( HWND hw, LPARAM lp )
3387 {
3388     BroadcastParm *parm = (BroadcastParm*)lp;
3389     DWORD_PTR retval = 0;
3390     LRESULT lresult;
3391
3392     if (parm->flags & BSF_IGNORECURRENTTASK && WIN_IsCurrentProcess(hw))
3393     {
3394         TRACE("Not telling myself %p\n", hw);
3395         return TRUE;
3396     }
3397
3398     /* I don't know 100% for sure if this is what Windows does, but it fits the tests */
3399     if (parm->flags & BSF_QUERY)
3400     {
3401         TRACE("Telling window %p using SendMessageTimeout\n", hw);
3402
3403         /* Not tested for conflicting flags */
3404         if (parm->flags & BSF_FORCEIFHUNG || parm->flags & BSF_NOHANG)
3405             lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_ABORTIFHUNG, 2000, &retval );
3406         else if (parm->flags & BSF_NOTIMEOUTIFNOTHUNG)
3407             lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NOTIMEOUTIFNOTHUNG, 2000, &retval );
3408         else
3409             lresult = SendMessageTimeoutW( hw, parm->msg, parm->wp, parm->lp, SMTO_NORMAL, 2000, &retval );
3410
3411         if (!lresult && GetLastError() == ERROR_TIMEOUT)
3412         {
3413             WARN("Timed out!\n");
3414             if (!(parm->flags & BSF_FORCEIFHUNG))
3415                 goto fail;
3416         }
3417         if (retval == BROADCAST_QUERY_DENY)
3418             goto fail;
3419
3420         return TRUE;
3421
3422 fail:
3423         parm->success = 0;
3424         return FALSE;
3425     }
3426     else if (parm->flags & BSF_POSTMESSAGE)
3427     {
3428         TRACE("Telling window %p using PostMessage\n", hw);
3429         PostMessageW( hw, parm->msg, parm->wp, parm->lp );
3430     }
3431     else
3432     {
3433         TRACE("Telling window %p using SendNotifyMessage\n", hw);
3434         SendNotifyMessageW( hw, parm->msg, parm->wp, parm->lp );
3435     }
3436
3437     return TRUE;
3438 }
3439
3440 static BOOL CALLBACK bcast_desktop( LPWSTR desktop, LPARAM lp )
3441 {
3442     BOOL ret;
3443     HDESK hdesktop;
3444     BroadcastParm *parm = (BroadcastParm*)lp;
3445
3446     TRACE("desktop: %s\n", debugstr_w( desktop ));
3447
3448     hdesktop = open_winstation_desktop( parm->winsta, desktop, 0, FALSE, DESKTOP_ENUMERATE|DESKTOP_WRITEOBJECTS|STANDARD_RIGHTS_WRITE );
3449     if (!hdesktop)
3450     {
3451         FIXME("Could not open desktop %s\n", debugstr_w(desktop));
3452         return TRUE;
3453     }
3454
3455     ret = EnumDesktopWindows( hdesktop, bcast_childwindow, lp );
3456     CloseDesktop(hdesktop);
3457     TRACE("-->%d\n", ret);
3458     return parm->success;
3459 }
3460
3461 static BOOL CALLBACK bcast_winsta( LPWSTR winsta, LPARAM lp )
3462 {
3463     BOOL ret;
3464     HWINSTA hwinsta = OpenWindowStationW( winsta, FALSE, WINSTA_ENUMDESKTOPS );
3465     TRACE("hwinsta: %p/%s/%08x\n", hwinsta, debugstr_w( winsta ), GetLastError());
3466     if (!hwinsta)
3467         return TRUE;
3468     ((BroadcastParm *)lp)->winsta = hwinsta;
3469     ret = EnumDesktopsW( hwinsta, bcast_desktop, lp );
3470     CloseWindowStation( hwinsta );
3471     TRACE("-->%d\n", ret);
3472     return ret;
3473 }
3474
3475 /***********************************************************************
3476  *              BroadcastSystemMessageA (USER32.@)
3477  *              BroadcastSystemMessage  (USER32.@)
3478  */
3479 LONG WINAPI BroadcastSystemMessageA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
3480 {
3481     return BroadcastSystemMessageExA( flags, recipients, msg, wp, lp, NULL );
3482 }
3483
3484
3485 /***********************************************************************
3486  *              BroadcastSystemMessageW (USER32.@)
3487  */
3488 LONG WINAPI BroadcastSystemMessageW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp )
3489 {
3490     return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
3491 }
3492
3493 /***********************************************************************
3494  *              BroadcastSystemMessageExA (USER32.@)
3495  */
3496 LONG WINAPI BroadcastSystemMessageExA( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
3497 {
3498     map_wparam_AtoW( msg, &wp, WMCHAR_MAP_NOMAPPING );
3499     return BroadcastSystemMessageExW( flags, recipients, msg, wp, lp, NULL );
3500 }
3501
3502
3503 /***********************************************************************
3504  *              BroadcastSystemMessageExW (USER32.@)
3505  */
3506 LONG WINAPI BroadcastSystemMessageExW( DWORD flags, LPDWORD recipients, UINT msg, WPARAM wp, LPARAM lp, PBSMINFO pinfo )
3507 {
3508     BroadcastParm parm;
3509     DWORD recips = BSM_ALLCOMPONENTS;
3510     BOOL ret = TRUE;
3511     static const DWORD all_flags = ( BSF_QUERY | BSF_IGNORECURRENTTASK | BSF_FLUSHDISK | BSF_NOHANG
3512                                    | BSF_POSTMESSAGE | BSF_FORCEIFHUNG | BSF_NOTIMEOUTIFNOTHUNG
3513                                    | BSF_ALLOWSFW | BSF_SENDNOTIFYMESSAGE | BSF_RETURNHDESK | BSF_LUID );
3514
3515     TRACE("Flags: %08x, recipients: %p(0x%x), msg: %04x, wparam: %08lx, lparam: %08lx\n", flags, recipients,
3516          (recipients ? *recipients : recips), msg, wp, lp);
3517
3518     if (flags & ~all_flags)
3519     {
3520         SetLastError(ERROR_INVALID_PARAMETER);
3521         return 0;
3522     }
3523
3524     if (!recipients)
3525         recipients = &recips;
3526
3527     if ( pinfo && flags & BSF_QUERY )
3528         FIXME("Not returning PBSMINFO information yet\n");
3529
3530     parm.flags = flags;
3531     parm.recipients = recipients;
3532     parm.msg = msg;
3533     parm.wp = wp;
3534     parm.lp = lp;
3535     parm.success = TRUE;
3536
3537     if (*recipients & BSM_ALLDESKTOPS || *recipients == BSM_ALLCOMPONENTS)
3538         ret = EnumWindowStationsW(bcast_winsta, (LONG_PTR)&parm);
3539     else if (*recipients & BSM_APPLICATIONS)
3540     {
3541         EnumWindows(bcast_childwindow, (LONG_PTR)&parm);
3542         ret = parm.success;
3543     }
3544     else
3545         FIXME("Recipients %08x not supported!\n", *recipients);
3546
3547     return ret;
3548 }
3549
3550 /***********************************************************************
3551  *              SetMessageQueue (USER32.@)
3552  */
3553 BOOL WINAPI SetMessageQueue( INT size )
3554 {
3555     /* now obsolete the message queue will be expanded dynamically as necessary */
3556     return TRUE;
3557 }
3558
3559
3560 /***********************************************************************
3561  *              MessageBeep (USER32.@)
3562  */
3563 BOOL WINAPI MessageBeep( UINT i )
3564 {
3565     BOOL active = TRUE;
3566     SystemParametersInfoA( SPI_GETBEEP, 0, &active, FALSE );
3567     if (active) USER_Driver->pBeep();
3568     return TRUE;
3569 }
3570
3571
3572 /***********************************************************************
3573  *              SetTimer (USER32.@)
3574  */
3575 UINT_PTR WINAPI SetTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
3576 {
3577     UINT_PTR ret;
3578     WNDPROC winproc = 0;
3579
3580     if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, NULL );
3581
3582     SERVER_START_REQ( set_win_timer )
3583     {
3584         req->win    = hwnd;
3585         req->msg    = WM_TIMER;
3586         req->id     = id;
3587         req->rate   = max( timeout, SYS_TIMER_RATE );
3588         req->lparam = (unsigned long)winproc;
3589         if (!wine_server_call_err( req ))
3590         {
3591             ret = reply->id;
3592             if (!ret) ret = TRUE;
3593         }
3594         else ret = 0;
3595     }
3596     SERVER_END_REQ;
3597
3598     TRACE("Added %p %lx %p timeout %d\n", hwnd, id, winproc, timeout );
3599     return ret;
3600 }
3601
3602
3603 /***********************************************************************
3604  *              SetSystemTimer (USER32.@)
3605  */
3606 UINT_PTR WINAPI SetSystemTimer( HWND hwnd, UINT_PTR id, UINT timeout, TIMERPROC proc )
3607 {
3608     UINT_PTR ret;
3609     WNDPROC winproc = 0;
3610
3611     if (proc) winproc = WINPROC_AllocProc( (WNDPROC)proc, NULL );
3612
3613     SERVER_START_REQ( set_win_timer )
3614     {
3615         req->win    = hwnd;
3616         req->msg    = WM_SYSTIMER;
3617         req->id     = id;
3618         req->rate   = max( timeout, SYS_TIMER_RATE );
3619         req->lparam = (unsigned long)winproc;
3620         if (!wine_server_call_err( req ))
3621         {
3622             ret = reply->id;
3623             if (!ret) ret = TRUE;
3624         }
3625         else ret = 0;
3626     }
3627     SERVER_END_REQ;
3628
3629     TRACE("Added %p %lx %p timeout %d\n", hwnd, id, winproc, timeout );
3630     return ret;
3631 }
3632
3633
3634 /***********************************************************************
3635  *              KillTimer (USER32.@)
3636  */
3637 BOOL WINAPI KillTimer( HWND hwnd, UINT_PTR id )
3638 {
3639     BOOL ret;
3640
3641     SERVER_START_REQ( kill_win_timer )
3642     {
3643         req->win = hwnd;
3644         req->msg = WM_TIMER;
3645         req->id  = id;
3646         ret = !wine_server_call_err( req );
3647     }
3648     SERVER_END_REQ;
3649     return ret;
3650 }
3651
3652
3653 /***********************************************************************
3654  *              KillSystemTimer (USER32.@)
3655  */
3656 BOOL WINAPI KillSystemTimer( HWND hwnd, UINT_PTR id )
3657 {
3658     BOOL ret;
3659
3660     SERVER_START_REQ( kill_win_timer )
3661     {
3662         req->win = hwnd;
3663         req->msg = WM_SYSTIMER;
3664         req->id  = id;
3665         ret = !wine_server_call_err( req );
3666     }
3667     SERVER_END_REQ;
3668     return ret;
3669 }
3670
3671
3672 /**********************************************************************
3673  *              GetGUIThreadInfo  (USER32.@)
3674  */
3675 BOOL WINAPI GetGUIThreadInfo( DWORD id, GUITHREADINFO *info )
3676 {
3677     BOOL ret;
3678
3679     SERVER_START_REQ( get_thread_input )
3680     {
3681         req->tid = id;
3682         if ((ret = !wine_server_call_err( req )))
3683         {
3684             info->flags          = 0;
3685             info->hwndActive     = reply->active;
3686             info->hwndFocus      = reply->focus;
3687             info->hwndCapture    = reply->capture;
3688             info->hwndMenuOwner  = reply->menu_owner;
3689             info->hwndMoveSize   = reply->move_size;
3690             info->hwndCaret      = reply->caret;
3691             info->rcCaret.left   = reply->rect.left;
3692             info->rcCaret.top    = reply->rect.top;
3693             info->rcCaret.right  = reply->rect.right;
3694             info->rcCaret.bottom = reply->rect.bottom;
3695             if (reply->menu_owner) info->flags |= GUI_INMENUMODE;
3696             if (reply->move_size) info->flags |= GUI_INMOVESIZE;
3697             if (reply->caret) info->flags |= GUI_CARETBLINKING;
3698         }
3699     }
3700     SERVER_END_REQ;
3701     return ret;
3702 }
3703
3704
3705 /******************************************************************
3706  *              IsHungAppWindow (USER32.@)
3707  *
3708  */
3709 BOOL WINAPI IsHungAppWindow( HWND hWnd )
3710 {
3711     BOOL ret;
3712
3713     SERVER_START_REQ( is_window_hung )
3714     {
3715         req->win = hWnd;
3716         ret = !wine_server_call_err( req ) && reply->is_hung;
3717     }
3718     SERVER_END_REQ;
3719     return ret;
3720 }