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