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