xref: /reactos/win32ss/user/ntuser/message.c (revision 7e069ccd)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS Win32k subsystem
4  * PURPOSE:          Messages
5  * FILE:             win32ss/user/ntuser/message.c
6  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
7  */
8 
9 #include <win32k.h>
10 
11 #include <dde.h>
12 
13 DBG_DEFAULT_CHANNEL(UserMsg);
14 
15 #define PM_BADMSGFLAGS ~((QS_RAWINPUT << 16)|PM_QS_SENDMESSAGE|PM_QS_PAINT|PM_QS_POSTMESSAGE|PM_QS_INPUT|PM_NOYIELD|PM_REMOVE)
16 
17 /* FUNCTIONS *****************************************************************/
18 
19 NTSTATUS FASTCALL
20 IntInitMessageImpl(VOID)
21 {
22     return STATUS_SUCCESS;
23 }
24 
25 NTSTATUS FASTCALL
26 IntCleanupMessageImpl(VOID)
27 {
28     return STATUS_SUCCESS;
29 }
30 
31 /* From wine: */
32 /* flag for messages that contain pointers */
33 /* 32 messages per entry, messages 0..31 map to bits 0..31 */
34 
35 #define SET(msg) (1 << ((msg) & 31))
36 
37 static const unsigned int message_pointer_flags[] =
38 {
39     /* 0x00 - 0x1f */
40     SET(WM_CREATE) | SET(WM_SETTEXT) | SET(WM_GETTEXT) |
41     SET(WM_WININICHANGE) | SET(WM_DEVMODECHANGE),
42     /* 0x20 - 0x3f */
43     SET(WM_GETMINMAXINFO) | SET(WM_DRAWITEM) | SET(WM_MEASUREITEM) | SET(WM_DELETEITEM) |
44     SET(WM_COMPAREITEM),
45     /* 0x40 - 0x5f */
46     SET(WM_WINDOWPOSCHANGING) | SET(WM_WINDOWPOSCHANGED) | SET(WM_COPYDATA) | SET(WM_COPYGLOBALDATA) | SET(WM_HELP),
47     /* 0x60 - 0x7f */
48     SET(WM_STYLECHANGING) | SET(WM_STYLECHANGED),
49     /* 0x80 - 0x9f */
50     SET(WM_NCCREATE) | SET(WM_NCCALCSIZE) | SET(WM_GETDLGCODE),
51     /* 0xa0 - 0xbf */
52     SET(EM_GETSEL) | SET(EM_GETRECT) | SET(EM_SETRECT) | SET(EM_SETRECTNP),
53     /* 0xc0 - 0xdf */
54     SET(EM_REPLACESEL) | SET(EM_GETLINE) | SET(EM_SETTABSTOPS),
55     /* 0xe0 - 0xff */
56     SET(SBM_GETRANGE) | SET(SBM_SETSCROLLINFO) | SET(SBM_GETSCROLLINFO) | SET(SBM_GETSCROLLBARINFO),
57     /* 0x100 - 0x11f */
58     0,
59     /* 0x120 - 0x13f */
60     0,
61     /* 0x140 - 0x15f */
62     SET(CB_GETEDITSEL) | SET(CB_ADDSTRING) | SET(CB_DIR) | SET(CB_GETLBTEXT) |
63     SET(CB_INSERTSTRING) | SET(CB_FINDSTRING) | SET(CB_SELECTSTRING) |
64     SET(CB_GETDROPPEDCONTROLRECT) | SET(CB_FINDSTRINGEXACT),
65     /* 0x160 - 0x17f */
66     0,
67     /* 0x180 - 0x19f */
68     SET(LB_ADDSTRING) | SET(LB_INSERTSTRING) | SET(LB_GETTEXT) | SET(LB_SELECTSTRING) |
69     SET(LB_DIR) | SET(LB_FINDSTRING) |
70     SET(LB_GETSELITEMS) | SET(LB_SETTABSTOPS) | SET(LB_ADDFILE) | SET(LB_GETITEMRECT),
71     /* 0x1a0 - 0x1bf */
72     SET(LB_FINDSTRINGEXACT),
73     /* 0x1c0 - 0x1df */
74     0,
75     /* 0x1e0 - 0x1ff */
76     0,
77     /* 0x200 - 0x21f */
78     SET(WM_NEXTMENU) | SET(WM_SIZING) | SET(WM_MOVING) | SET(WM_DEVICECHANGE),
79     /* 0x220 - 0x23f */
80     SET(WM_MDICREATE) | SET(WM_MDIGETACTIVE) | SET(WM_DROPOBJECT) |
81     SET(WM_QUERYDROPOBJECT) | SET(WM_DRAGLOOP) | SET(WM_DRAGSELECT) | SET(WM_DRAGMOVE),
82     /* 0x240 - 0x25f */
83     0,
84     /* 0x260 - 0x27f */
85     0,
86     /* 0x280 - 0x29f */
87     0,
88     /* 0x2a0 - 0x2bf */
89     0,
90     /* 0x2c0 - 0x2df */
91     0,
92     /* 0x2e0 - 0x2ff */
93     0,
94     /* 0x300 - 0x31f */
95     SET(WM_ASKCBFORMATNAME)
96 };
97 
98 /* check whether a given message type includes pointers */
99 static inline int is_pointer_message( UINT message )
100 {
101     if (message >= 8*sizeof(message_pointer_flags)) return FALSE;
102         return (message_pointer_flags[message / 32] & SET(message)) != 0;
103 }
104 #undef SET
105 
106 #define MMS_SIZE_WPARAM      -1
107 #define MMS_SIZE_WPARAMWCHAR -2
108 #define MMS_SIZE_LPARAMSZ    -3
109 #define MMS_SIZE_SPECIAL     -4
110 #define MMS_FLAG_READ        0x01
111 #define MMS_FLAG_WRITE       0x02
112 #define MMS_FLAG_READWRITE   (MMS_FLAG_READ | MMS_FLAG_WRITE)
113 typedef struct tagMSGMEMORY
114 {
115     UINT Message;
116     UINT Size;
117     INT Flags;
118 }
119 MSGMEMORY, *PMSGMEMORY;
120 
121 static MSGMEMORY g_MsgMemory[] =
122 {
123     { WM_CREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
124     { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
125     { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
126     { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
127     { WM_NCCREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
128     { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
129     { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
130     { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
131     { WM_SETTINGCHANGE, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
132     { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ },
133     { WM_COPYGLOBALDATA, MMS_SIZE_WPARAM, MMS_FLAG_READ },
134     { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
135     { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
136     { WM_SIZING, sizeof(RECT), MMS_FLAG_READWRITE },
137     { WM_MOVING, sizeof(RECT), MMS_FLAG_READWRITE },
138     { WM_MEASUREITEM, sizeof(MEASUREITEMSTRUCT), MMS_FLAG_READWRITE },
139     { WM_DRAWITEM, sizeof(DRAWITEMSTRUCT), MMS_FLAG_READWRITE },
140     { WM_HELP, sizeof(HELPINFO), MMS_FLAG_READWRITE },
141     { WM_NEXTMENU, sizeof(MDINEXTMENU), MMS_FLAG_READWRITE },
142 };
143 
144 static PMSGMEMORY FASTCALL
145 FindMsgMemory(UINT Msg)
146 {
147     PMSGMEMORY MsgMemoryEntry;
148 
149     /* See if this message type is present in the table */
150     for (MsgMemoryEntry = g_MsgMemory;
151     MsgMemoryEntry < g_MsgMemory + sizeof(g_MsgMemory) / sizeof(MSGMEMORY);
152     MsgMemoryEntry++)
153     {
154         if (Msg == MsgMemoryEntry->Message)
155         {
156             return MsgMemoryEntry;
157         }
158     }
159 
160     return NULL;
161 }
162 
163 static UINT FASTCALL
164 MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
165 {
166     CREATESTRUCTW *Cs;
167     PLARGE_STRING WindowName;
168     PUNICODE_STRING ClassName;
169     UINT Size = 0;
170 
171     _SEH2_TRY
172     {
173         if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
174         {
175             Size = (UINT)wParam;
176         }
177         else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
178         {
179             Size = (UINT) (wParam * sizeof(WCHAR));
180         }
181         else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
182         {
183             // WM_SETTEXT and WM_SETTINGCHANGE can be null!
184             if (!lParam)
185             {
186                TRACE("lParam is NULL!\n");
187                Size = 0;
188             }
189             else
190                Size = (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
191         }
192         else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
193         {
194             switch(MsgMemoryEntry->Message)
195             {
196             case WM_CREATE:
197             case WM_NCCREATE:
198                 Cs = (CREATESTRUCTW *) lParam;
199                 WindowName = (PLARGE_STRING) Cs->lpszName;
200                 ClassName = (PUNICODE_STRING) Cs->lpszClass;
201                 Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
202                 if (IS_ATOM(ClassName->Buffer))
203                 {
204                     Size += sizeof(WCHAR) + sizeof(ATOM);
205                 }
206                 else
207                 {
208                     Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
209                 }
210                 break;
211 
212             case WM_NCCALCSIZE:
213                 Size = wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
214                 break;
215 
216             case WM_COPYDATA:
217                 {
218                 COPYDATASTRUCT *cds = (COPYDATASTRUCT *)lParam;
219                 Size = sizeof(COPYDATASTRUCT) + cds->cbData;
220                 }
221                 break;
222 
223             default:
224                 ASSERT(FALSE);
225                 Size = 0;
226                 break;
227             }
228         }
229         else
230         {
231             Size = MsgMemoryEntry->Size;
232         }
233     }
234     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
235     {
236         ERR("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH2_GetExceptionCode());
237         Size = 0;
238     }
239     _SEH2_END;
240     return Size;
241 }
242 
243 UINT lParamMemorySize(UINT Msg, WPARAM wParam, LPARAM lParam)
244 {
245     PMSGMEMORY MsgMemoryEntry = FindMsgMemory(Msg);
246     if(MsgMemoryEntry == NULL) return 0;
247     return MsgMemorySize(MsgMemoryEntry, wParam, lParam);
248 }
249 
250 static NTSTATUS
251 PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL NonPagedPoolNeeded)
252 {
253     NCCALCSIZE_PARAMS *UnpackedNcCalcsize;
254     NCCALCSIZE_PARAMS *PackedNcCalcsize;
255     CREATESTRUCTW *UnpackedCs;
256     CREATESTRUCTW *PackedCs;
257     PLARGE_STRING WindowName;
258     PUNICODE_STRING ClassName;
259     POOL_TYPE PoolType;
260     UINT Size;
261     PCHAR CsData;
262 
263     *lParamPacked = lParam;
264 
265     if (NonPagedPoolNeeded)
266         PoolType = NonPagedPool;
267     else
268         PoolType = PagedPool;
269 
270     if (WM_NCCALCSIZE == Msg && wParam)
271     {
272 
273         UnpackedNcCalcsize = (NCCALCSIZE_PARAMS *) lParam;
274         PackedNcCalcsize = ExAllocatePoolWithTag(PoolType,
275         sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
276         TAG_MSG);
277 
278         if (NULL == PackedNcCalcsize)
279         {
280             ERR("Not enough memory to pack lParam\n");
281             return STATUS_NO_MEMORY;
282         }
283         RtlCopyMemory(PackedNcCalcsize, UnpackedNcCalcsize, sizeof(NCCALCSIZE_PARAMS));
284         PackedNcCalcsize->lppos = (PWINDOWPOS) (PackedNcCalcsize + 1);
285         RtlCopyMemory(PackedNcCalcsize->lppos, UnpackedNcCalcsize->lppos, sizeof(WINDOWPOS));
286         *lParamPacked = (LPARAM) PackedNcCalcsize;
287     }
288     else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
289     {
290         UnpackedCs = (CREATESTRUCTW *) lParam;
291         WindowName = (PLARGE_STRING) UnpackedCs->lpszName;
292         ClassName = (PUNICODE_STRING) UnpackedCs->lpszClass;
293         Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
294         if (IS_ATOM(ClassName->Buffer))
295         {
296             Size += sizeof(WCHAR) + sizeof(ATOM);
297         }
298         else
299         {
300             Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
301         }
302         PackedCs = ExAllocatePoolWithTag(PoolType, Size, TAG_MSG);
303         if (NULL == PackedCs)
304         {
305             ERR("Not enough memory to pack lParam\n");
306             return STATUS_NO_MEMORY;
307         }
308         RtlCopyMemory(PackedCs, UnpackedCs, sizeof(CREATESTRUCTW));
309         CsData = (PCHAR) (PackedCs + 1);
310         PackedCs->lpszName = (LPCWSTR) (CsData - (PCHAR) PackedCs);
311         RtlCopyMemory(CsData, WindowName->Buffer, WindowName->Length);
312         CsData += WindowName->Length;
313         *((WCHAR *) CsData) = L'\0';
314         CsData += sizeof(WCHAR);
315         PackedCs->lpszClass = (LPCWSTR) (CsData - (PCHAR) PackedCs);
316         if (IS_ATOM(ClassName->Buffer))
317         {
318             *((WCHAR *) CsData) = L'A';
319             CsData += sizeof(WCHAR);
320             *((ATOM *) CsData) = (ATOM)(DWORD_PTR) ClassName->Buffer;
321             CsData += sizeof(ATOM);
322         }
323         else
324         {
325             NT_ASSERT(ClassName->Buffer != NULL);
326             *((WCHAR *) CsData) = L'S';
327             CsData += sizeof(WCHAR);
328             RtlCopyMemory(CsData, ClassName->Buffer, ClassName->Length);
329             CsData += ClassName->Length;
330             *((WCHAR *) CsData) = L'\0';
331             CsData += sizeof(WCHAR);
332         }
333         ASSERT(CsData == (PCHAR) PackedCs + Size);
334         *lParamPacked = (LPARAM) PackedCs;
335     }
336     else if (PoolType == NonPagedPool)
337     {
338         PMSGMEMORY MsgMemoryEntry;
339         PVOID PackedData;
340         SIZE_T size;
341 
342         MsgMemoryEntry = FindMsgMemory(Msg);
343 
344         if (!MsgMemoryEntry)
345         {
346             /* Keep previous behavior */
347             return STATUS_SUCCESS;
348         }
349         size = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
350         if (!size)
351         {
352            ERR("No size for lParamPacked\n");
353            return STATUS_SUCCESS;
354         }
355         PackedData = ExAllocatePoolWithTag(NonPagedPool, size, TAG_MSG);
356         if (PackedData == NULL)
357         {
358             ERR("Not enough memory to pack lParam\n");
359             return STATUS_NO_MEMORY;
360         }
361         RtlCopyMemory(PackedData, (PVOID)lParam, MsgMemorySize(MsgMemoryEntry, wParam, lParam));
362         *lParamPacked = (LPARAM)PackedData;
363     }
364 
365     return STATUS_SUCCESS;
366 }
367 
368 static NTSTATUS
369 UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam, BOOL NonPagedPoolUsed)
370 {
371     NCCALCSIZE_PARAMS *UnpackedParams;
372     NCCALCSIZE_PARAMS *PackedParams;
373     PWINDOWPOS UnpackedWindowPos;
374 
375     if (lParamPacked == lParam)
376     {
377         return STATUS_SUCCESS;
378     }
379 
380     if (WM_NCCALCSIZE == Msg && wParam)
381     {
382         PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
383         UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
384         UnpackedWindowPos = UnpackedParams->lppos;
385         RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
386         UnpackedParams->lppos = UnpackedWindowPos;
387         RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
388         ExFreePool((PVOID) lParamPacked);
389 
390         return STATUS_SUCCESS;
391     }
392     else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
393     {
394         ExFreePool((PVOID) lParamPacked);
395 
396         return STATUS_SUCCESS;
397     }
398     else if (NonPagedPoolUsed)
399     {
400         PMSGMEMORY MsgMemoryEntry;
401         MsgMemoryEntry = FindMsgMemory(Msg);
402         ASSERT(MsgMemoryEntry);
403 
404         if (MsgMemoryEntry->Flags == MMS_FLAG_READWRITE)
405         {
406             //RtlCopyMemory((PVOID)lParam, (PVOID)lParamPacked, MsgMemoryEntry->Size);
407         }
408         ExFreePool((PVOID) lParamPacked);
409         return STATUS_SUCCESS;
410     }
411 
412     ASSERT(FALSE);
413 
414     return STATUS_INVALID_PARAMETER;
415 }
416 
417 static NTSTATUS FASTCALL
418 CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg, PMSGMEMORY MsgMemoryEntry)
419 {
420     NTSTATUS Status;
421 
422     PVOID KernelMem;
423     UINT Size;
424 
425     *KernelModeMsg = *UserModeMsg;
426 
427     /* See if this message type is present in the table */
428     if (NULL == MsgMemoryEntry)
429     {
430         /* Not present, no copying needed */
431         return STATUS_SUCCESS;
432     }
433 
434     /* Determine required size */
435     Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
436 
437     if (0 != Size)
438     {
439         /* Allocate kernel mem */
440         KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
441         if (NULL == KernelMem)
442         {
443             ERR("Not enough memory to copy message to kernel mem\n");
444             return STATUS_NO_MEMORY;
445         }
446         KernelModeMsg->lParam = (LPARAM) KernelMem;
447 
448         /* Copy data if required */
449         if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
450         {
451             TRACE("Copy Message %u from usermode buffer\n", KernelModeMsg->message);
452             Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
453             if (! NT_SUCCESS(Status))
454             {
455                 ERR("Failed to copy message to kernel: invalid usermode lParam buffer\n");
456                 ExFreePoolWithTag(KernelMem, TAG_MSG);
457                 return Status;
458             }
459         }
460         else
461         {
462             /* Make sure we don't pass any secrets to usermode */
463             RtlZeroMemory(KernelMem, Size);
464         }
465     }
466     else
467     {
468         KernelModeMsg->lParam = 0;
469     }
470 
471     return STATUS_SUCCESS;
472 }
473 
474 static NTSTATUS FASTCALL
475 CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
476 {
477     NTSTATUS Status;
478     PMSGMEMORY MsgMemoryEntry;
479     UINT Size;
480 
481     /* See if this message type is present in the table */
482     MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
483     if (NULL == MsgMemoryEntry)
484     {
485         /* Not present, no copying needed */
486         return STATUS_SUCCESS;
487     }
488 
489     /* Determine required size */
490     Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
491 
492     if (0 != Size)
493     {
494         /* Copy data if required */
495         if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
496         {
497             Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
498             if (! NT_SUCCESS(Status))
499             {
500                 ERR("Failed to copy message from kernel: invalid usermode lParam buffer\n");
501                 ExFreePool((PVOID) KernelModeMsg->lParam);
502                 return Status;
503             }
504         }
505         ExFreePool((PVOID) KernelModeMsg->lParam);
506     }
507 
508     return STATUS_SUCCESS;
509 }
510 
511 //
512 // Wakeup any thread/process waiting on idle input.
513 //
514 VOID FASTCALL
515 IdlePing(VOID)
516 {
517    PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
518    PTHREADINFO pti;
519 
520    pti = PsGetCurrentThreadWin32Thread();
521 
522    if ( pti )
523    {
524       pti->pClientInfo->cSpins = 0; // Reset spins.
525 
526       if ( pti->pDeskInfo && pti == gptiForeground )
527       {
528          if ( pti->fsHooks & HOOKID_TO_FLAG(WH_FOREGROUNDIDLE) ||
529               pti->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_FOREGROUNDIDLE) )
530          {
531             co_HOOK_CallHooks(WH_FOREGROUNDIDLE,HC_ACTION,0,0);
532          }
533       }
534    }
535 
536    TRACE("IdlePing ppi %p\n", ppi);
537    if ( ppi && ppi->InputIdleEvent )
538    {
539       TRACE("InputIdleEvent\n");
540       KeSetEvent( ppi->InputIdleEvent, IO_NO_INCREMENT, FALSE);
541    }
542 }
543 
544 VOID FASTCALL
545 IdlePong(VOID)
546 {
547    PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
548 
549    TRACE("IdlePong ppi %p\n", ppi);
550    if ( ppi && ppi->InputIdleEvent )
551    {
552       KeClearEvent(ppi->InputIdleEvent);
553    }
554 }
555 
556 static BOOL is_message_broadcastable(UINT msg)
557 {
558     return msg < WM_USER || msg >= 0xc000;
559 }
560 
561 UINT FASTCALL
562 GetWakeMask(UINT first, UINT last )
563 {
564     UINT mask = QS_POSTMESSAGE | QS_SENDMESSAGE;  /* Always selected */
565 
566     if (first || last)
567     {
568         if ((first <= WM_KEYLAST) && (last >= WM_KEYFIRST)) mask |= QS_KEY;
569         if ( ((first <= WM_MOUSELAST) && (last >= WM_MOUSEFIRST)) ||
570              ((first <= WM_NCMOUSELAST) && (last >= WM_NCMOUSEFIRST)) ) mask |= QS_MOUSE;
571         if ((first <= WM_TIMER) && (last >= WM_TIMER)) mask |= QS_TIMER;
572         if ((first <= WM_SYSTIMER) && (last >= WM_SYSTIMER)) mask |= QS_TIMER;
573         if ((first <= WM_PAINT) && (last >= WM_PAINT)) mask |= QS_PAINT;
574     }
575     else mask = QS_ALLINPUT;
576 
577     return mask;
578 }
579 
580 //
581 // Pass Strings to User Heap Space for Message Hook Callbacks.
582 //
583 BOOL
584 FASTCALL
585 IntMsgCreateStructW(
586     PWND Window,
587     CREATESTRUCTW *pCsw,
588     CREATESTRUCTW *Cs,
589     PVOID *ppszClass,
590     PVOID *ppszName )
591 {
592     PLARGE_STRING WindowName;
593     PUNICODE_STRING ClassName;
594     PVOID pszClass = NULL, pszName = NULL;
595 
596     /* Fill the new CREATESTRUCTW */
597     RtlCopyMemory(pCsw, Cs, sizeof(CREATESTRUCTW));
598     pCsw->style = Window->style; /* HCBT_CREATEWND needs the real window style */
599 
600     WindowName = (PLARGE_STRING)   Cs->lpszName;
601     ClassName  = (PUNICODE_STRING) Cs->lpszClass;
602 
603     // Based on the assumption this is from "unicode source" user32, ReactOS, answer is yes.
604     if (!IS_ATOM(ClassName->Buffer))
605     {
606         if (ClassName->Length)
607         {
608             if (Window->state & WNDS_ANSICREATOR)
609             {
610                 ANSI_STRING AnsiString;
611                 AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(ClassName)+sizeof(CHAR);
612                 pszClass = UserHeapAlloc(AnsiString.MaximumLength);
613                 if (!pszClass)
614                 {
615                     ERR("UserHeapAlloc() failed!\n");
616                     return FALSE;
617                 }
618                 RtlZeroMemory(pszClass, AnsiString.MaximumLength);
619                 AnsiString.Buffer = (PCHAR)pszClass;
620                 RtlUnicodeStringToAnsiString(&AnsiString, ClassName, FALSE);
621             }
622             else
623             {
624                 UNICODE_STRING UnicodeString;
625                 UnicodeString.MaximumLength = ClassName->Length + sizeof(UNICODE_NULL);
626                 pszClass = UserHeapAlloc(UnicodeString.MaximumLength);
627                 if (!pszClass)
628                 {
629                     ERR("UserHeapAlloc() failed!\n");
630                     return FALSE;
631                 }
632                 RtlZeroMemory(pszClass, UnicodeString.MaximumLength);
633                 UnicodeString.Buffer = (PWSTR)pszClass;
634                 RtlCopyUnicodeString(&UnicodeString, ClassName);
635             }
636             *ppszClass = pszClass;
637             pCsw->lpszClass = UserHeapAddressToUser(pszClass);
638         }
639         else
640         {
641             pCsw->lpszClass = NULL;
642         }
643     }
644     else
645     {
646         pCsw->lpszClass = ClassName->Buffer;
647     }
648     if (WindowName->Length)
649     {
650         UNICODE_STRING Name;
651         Name.Buffer = WindowName->Buffer;
652         Name.Length = (USHORT)min(WindowName->Length, MAXUSHORT); // FIXME: LARGE_STRING truncated
653         Name.MaximumLength = (USHORT)min(WindowName->MaximumLength, MAXUSHORT);
654 
655         if (Window->state & WNDS_ANSICREATOR)
656         {
657             ANSI_STRING AnsiString;
658             AnsiString.MaximumLength = (USHORT)RtlUnicodeStringToAnsiSize(&Name) + sizeof(CHAR);
659             pszName = UserHeapAlloc(AnsiString.MaximumLength);
660             if (!pszName)
661             {
662                ERR("UserHeapAlloc() failed!\n");
663                return FALSE;
664             }
665             RtlZeroMemory(pszName, AnsiString.MaximumLength);
666             AnsiString.Buffer = (PCHAR)pszName;
667             RtlUnicodeStringToAnsiString(&AnsiString, &Name, FALSE);
668         }
669         else
670         {
671             UNICODE_STRING UnicodeString;
672             UnicodeString.MaximumLength = Name.Length + sizeof(UNICODE_NULL);
673             pszName = UserHeapAlloc(UnicodeString.MaximumLength);
674             if (!pszName)
675             {
676                ERR("UserHeapAlloc() failed!\n");
677                return FALSE;
678             }
679             RtlZeroMemory(pszName, UnicodeString.MaximumLength);
680             UnicodeString.Buffer = (PWSTR)pszName;
681             RtlCopyUnicodeString(&UnicodeString, &Name);
682         }
683         *ppszName = pszName;
684         pCsw->lpszName = UserHeapAddressToUser(pszName);
685     }
686     else
687     {
688         pCsw->lpszName = NULL;
689     }
690 
691     return TRUE;
692 }
693 
694 static VOID FASTCALL
695 IntCallWndProc( PWND Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
696 {
697     BOOL SameThread = FALSE;
698     CWPSTRUCT CWP;
699     PVOID pszClass = NULL, pszName = NULL;
700     CREATESTRUCTW Csw;
701 
702     //// Check for a hook to eliminate overhead. ////
703     if ( !ISITHOOKED(WH_CALLWNDPROC) && !(Window->head.rpdesk->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_CALLWNDPROC)) )
704         return;
705 
706     if (Window->head.pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread()))
707         SameThread = TRUE;
708 
709     if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
710     {   //
711         // String pointers are in user heap space, like WH_CBT HCBT_CREATEWND.
712         //
713         if (!IntMsgCreateStructW( Window, &Csw, (CREATESTRUCTW *)lParam, &pszClass, &pszName ))
714             return;
715         lParam = (LPARAM)&Csw;
716     }
717 
718     CWP.hwnd    = hWnd;
719     CWP.message = Msg;
720     CWP.wParam  = wParam;
721     CWP.lParam  = lParam;
722     co_HOOK_CallHooks( WH_CALLWNDPROC, HC_ACTION, SameThread, (LPARAM)&CWP );
723 
724     if (pszName)  UserHeapFree(pszName);
725     if (pszClass) UserHeapFree(pszClass);
726 }
727 
728 static VOID FASTCALL
729 IntCallWndProcRet( PWND Window, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *uResult )
730 {
731     BOOL SameThread = FALSE;
732     CWPRETSTRUCT CWPR;
733     PVOID pszClass = NULL, pszName = NULL;
734     CREATESTRUCTW Csw;
735 
736     if ( !ISITHOOKED(WH_CALLWNDPROCRET) && !(Window->head.rpdesk->pDeskInfo->fsHooks & HOOKID_TO_FLAG(WH_CALLWNDPROCRET)) )
737         return;
738 
739     if (Window->head.pti == ((PTHREADINFO)PsGetCurrentThreadWin32Thread()))
740         SameThread = TRUE;
741 
742     if ( Msg == WM_CREATE || Msg == WM_NCCREATE )
743     {
744         if (!IntMsgCreateStructW( Window, &Csw, (CREATESTRUCTW *)lParam, &pszClass, &pszName ))
745             return;
746         lParam = (LPARAM)&Csw;
747     }
748 
749     CWPR.hwnd    = hWnd;
750     CWPR.message = Msg;
751     CWPR.wParam  = wParam;
752     CWPR.lParam  = lParam;
753     CWPR.lResult = uResult ? (*uResult) : 0;
754     co_HOOK_CallHooks( WH_CALLWNDPROCRET, HC_ACTION, SameThread, (LPARAM)&CWPR );
755 
756     if (pszName)  UserHeapFree(pszName);
757     if (pszClass) UserHeapFree(pszClass);
758 }
759 
760 static LRESULT handle_internal_message( PWND pWnd, UINT msg, WPARAM wparam, LPARAM lparam )
761 {
762     LRESULT lRes;
763 //    USER_REFERENCE_ENTRY Ref;
764 //    PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
765 
766     if (!pWnd || UserIsDesktopWindow(pWnd) || UserIsMessageWindow(pWnd))
767        return 0;
768 
769     TRACE("Internal Event Msg 0x%x hWnd 0x%p\n", msg, pWnd->head.h);
770 
771     switch(msg)
772     {
773        case WM_ASYNC_SHOWWINDOW:
774           return co_WinPosShowWindow( pWnd, wparam );
775        case WM_ASYNC_SETWINDOWPOS:
776        {
777           PWINDOWPOS winpos = (PWINDOWPOS)lparam;
778           if (!winpos) return 0;
779           lRes = co_WinPosSetWindowPos( pWnd,
780                                         winpos->hwndInsertAfter,
781                                         winpos->x,
782                                         winpos->y,
783                                         winpos->cx,
784                                         winpos->cy,
785                                         winpos->flags);
786           ExFreePoolWithTag(winpos, USERTAG_SWP);
787           return lRes;
788        }
789        case WM_ASYNC_DESTROYWINDOW:
790        {
791           ERR("WM_ASYNC_DESTROYWINDOW\n");
792           if (pWnd->style & WS_CHILD)
793              return co_UserFreeWindow(pWnd, PsGetCurrentProcessWin32Process(), PsGetCurrentThreadWin32Thread(), TRUE);
794           else
795              co_UserDestroyWindow(pWnd);
796        }
797     }
798     return 0;
799 }
800 
801 static LRESULT handle_internal_events( PTHREADINFO pti, PWND pWnd, DWORD dwQEvent, LONG_PTR ExtraInfo, PMSG pMsg)
802 {
803     LRESULT Result = 0;
804 
805     switch(dwQEvent)
806     {
807        case POSTEVENT_NWE:
808        {
809           co_EVENT_CallEvents( pMsg->message, pMsg->hwnd, pMsg->wParam, ExtraInfo);
810        }
811        break;
812        case POSTEVENT_SAW:
813        {
814           //ERR("HIE : SAW : pti 0x%p hWnd 0x%p\n",pti,pMsg->hwnd);
815           IntActivateWindow((PWND)pMsg->wParam, pti, (HANDLE)pMsg->lParam, (DWORD)ExtraInfo);
816        }
817        break;
818        case POSTEVENT_DAW:
819        {
820           //ERR("HIE : DAW : pti 0x%p tid 0x%p hWndPrev 0x%p\n",pti,ExtraInfo,pMsg->hwnd);
821           IntDeactivateWindow(pti, (HANDLE)ExtraInfo);
822        }
823        break;
824     }
825     return Result;
826 }
827 
828 LRESULT FASTCALL
829 IntDispatchMessage(PMSG pMsg)
830 {
831     LONG Time;
832     LRESULT retval = 0;
833     PTHREADINFO pti;
834     PWND Window = NULL;
835     BOOL DoCallBack = TRUE;
836 
837     if (pMsg->hwnd)
838     {
839         Window = UserGetWindowObject(pMsg->hwnd);
840         if (!Window) return 0;
841     }
842 
843     pti = PsGetCurrentThreadWin32Thread();
844 
845     if ( Window && Window->head.pti != pti)
846     {
847        EngSetLastError( ERROR_MESSAGE_SYNC_ONLY );
848        return 0;
849     }
850 
851     if (((pMsg->message == WM_SYSTIMER) ||
852          (pMsg->message == WM_TIMER)) &&
853          (pMsg->lParam) )
854     {
855         if (pMsg->message == WM_TIMER)
856         {
857             if (ValidateTimerCallback(pti,pMsg->lParam))
858             {
859                 Time = EngGetTickCount32();
860                 retval = co_IntCallWindowProc((WNDPROC)pMsg->lParam,
861                                               TRUE,
862                                               pMsg->hwnd,
863                                               WM_TIMER,
864                                               pMsg->wParam,
865                                               (LPARAM)Time,
866                                               -1);
867             }
868             return retval;
869         }
870         else
871         {
872             PTIMER pTimer = FindSystemTimer(pMsg);
873             if (pTimer && pTimer->pfn)
874             {
875                 Time = EngGetTickCount32();
876                 pTimer->pfn(pMsg->hwnd, WM_SYSTIMER, (UINT)pMsg->wParam, Time);
877             }
878             return 0;
879         }
880     }
881     // Need a window!
882     if ( !Window ) return 0;
883 
884     if (pMsg->message == WM_PAINT) Window->state |= WNDS_PAINTNOTPROCESSED;
885 
886     if ( Window->state & WNDS_SERVERSIDEWINDOWPROC )
887     {
888        TRACE("Dispatch: Server Side Window Procedure\n");
889        switch(Window->fnid)
890        {
891           case FNID_DESKTOP:
892             DoCallBack = !DesktopWindowProc( Window,
893                                              pMsg->message,
894                                              pMsg->wParam,
895                                              pMsg->lParam,
896                                             &retval);
897             break;
898           case FNID_MESSAGEWND:
899             DoCallBack = !UserMessageWindowProc( Window,
900                                                  pMsg->message,
901                                                  pMsg->wParam,
902                                                  pMsg->lParam,
903                                                 &retval);
904             break;
905           case FNID_MENU:
906             DoCallBack = !PopupMenuWndProc( Window,
907                                             pMsg->message,
908                                             pMsg->wParam,
909                                             pMsg->lParam,
910                                            &retval);
911             break;
912        }
913     }
914 
915     /* Since we are doing a callback on the same thread right away, there is
916        no need to copy the lparam to kernel mode and then back to usermode.
917        We just pretend it isn't a pointer */
918 
919     if (DoCallBack)
920     retval = co_IntCallWindowProc( Window->lpfnWndProc,
921                                    !Window->Unicode,
922                                    pMsg->hwnd,
923                                    pMsg->message,
924                                    pMsg->wParam,
925                                    pMsg->lParam,
926                                    -1);
927 
928     if ( pMsg->message == WM_PAINT &&
929          VerifyWnd(Window) &&
930          Window->state & WNDS_PAINTNOTPROCESSED ) // <--- Cleared, paint was already processed!
931     {
932         Window->state2 &= ~WNDS2_WMPAINTSENT;
933         /* send a WM_ERASEBKGND if the non-client area is still invalid */
934         ERR("Message WM_PAINT count %d Internal Paint Set? %s\n",Window->head.pti->cPaintsReady, Window->state & WNDS_INTERNALPAINT ? "TRUE" : "FALSE");
935         IntPaintWindow( Window );
936     }
937 
938     return retval;
939 }
940 
941 /*
942  * Internal version of PeekMessage() doing all the work
943  *
944  * MSDN:
945  *   Sent messages
946  *   Posted messages
947  *   Input (hardware) messages and system internal events
948  *   Sent messages (again)
949  *   WM_PAINT messages
950  *   WM_TIMER messages
951  */
952 BOOL APIENTRY
953 co_IntPeekMessage( PMSG Msg,
954                    PWND Window,
955                    UINT MsgFilterMin,
956                    UINT MsgFilterMax,
957                    UINT RemoveMsg,
958                    LONG_PTR *ExtraInfo,
959                    BOOL bGMSG )
960 {
961     PTHREADINFO pti;
962     BOOL RemoveMessages;
963     UINT ProcessMask;
964     BOOL Hit = FALSE;
965 
966     pti = PsGetCurrentThreadWin32Thread();
967 
968     RemoveMessages = RemoveMsg & PM_REMOVE;
969     ProcessMask = HIWORD(RemoveMsg);
970 
971  /* Hint, "If wMsgFilterMin and wMsgFilterMax are both zero, PeekMessage returns
972     all available messages (that is, no range filtering is performed)".        */
973     if (!ProcessMask) ProcessMask = (QS_ALLPOSTMESSAGE|QS_ALLINPUT);
974 
975     IdlePong();
976 
977     do
978     {
979         /* Update the last message-queue access time */
980         pti->pcti->timeLastRead = EngGetTickCount32();
981 
982         // Post mouse moves while looping through peek messages.
983         if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED)
984         {
985            IntCoalesceMouseMove(pti);
986         }
987 
988         /* Dispatch sent messages here. */
989         while ( co_MsqDispatchOneSentMessage(pti) )
990         {
991            /* if some PM_QS* flags were specified, only handle sent messages from now on */
992            if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE; // wine does this; ProcessMask = QS_SENDMESSAGE;
993         }
994         if (Hit) return FALSE;
995 
996         /* Clear changed bits so we can wait on them if we don't find a message */
997         if (ProcessMask & QS_POSTMESSAGE)
998         {
999            pti->pcti->fsChangeBits &= ~(QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER);
1000            if (MsgFilterMin == 0 && MsgFilterMax == 0) // Wine hack does this; ~0U)
1001            {
1002               pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE;
1003            }
1004         }
1005 
1006         if (ProcessMask & QS_INPUT)
1007         {
1008            pti->pcti->fsChangeBits &= ~QS_INPUT;
1009         }
1010 
1011         /* Now check for normal messages. */
1012         if (( (ProcessMask & QS_POSTMESSAGE) ||
1013               (ProcessMask & QS_HOTKEY) ) &&
1014             MsqPeekMessage( pti,
1015                             RemoveMessages,
1016                             Window,
1017                             MsgFilterMin,
1018                             MsgFilterMax,
1019                             ProcessMask,
1020                             ExtraInfo,
1021                             0,
1022                             Msg ))
1023         {
1024             goto GotMessage;
1025         }
1026 
1027         /* Only check for quit messages if not posted messages pending. */
1028         if (ProcessMask & QS_POSTMESSAGE && pti->QuitPosted)
1029         {
1030             /* According to the PSDK, WM_QUIT messages are always returned, regardless
1031                of the filter specified */
1032             Msg->hwnd = NULL;
1033             Msg->message = WM_QUIT;
1034             Msg->wParam = pti->exitCode;
1035             Msg->lParam = 0;
1036             if (RemoveMessages)
1037             {
1038                 pti->QuitPosted = FALSE;
1039                 ClearMsgBitsMask(pti, QS_POSTMESSAGE);
1040                 pti->pcti->fsWakeBits &= ~QS_ALLPOSTMESSAGE;
1041                 pti->pcti->fsChangeBits &= ~QS_ALLPOSTMESSAGE;
1042             }
1043             goto GotMessage;
1044         }
1045 
1046         /* Check for hardware events. */
1047         if ((ProcessMask & QS_INPUT) &&
1048             co_MsqPeekHardwareMessage( pti,
1049                                        RemoveMessages,
1050                                        Window,
1051                                        MsgFilterMin,
1052                                        MsgFilterMax,
1053                                        ProcessMask,
1054                                        Msg))
1055         {
1056             goto GotMessage;
1057         }
1058 
1059         /* Now check for System Event messages. */
1060         {
1061            LONG_PTR eExtraInfo;
1062            MSG eMsg;
1063            DWORD dwQEvent;
1064            if (MsqPeekMessage( pti,
1065                                TRUE,
1066                                Window,
1067                                0,
1068                                0,
1069                                QS_EVENT,
1070                               &eExtraInfo,
1071                               &dwQEvent,
1072                               &eMsg ))
1073            {
1074               handle_internal_events( pti, Window, dwQEvent, eExtraInfo, &eMsg);
1075               continue;
1076            }
1077         }
1078 
1079         /* Check for sent messages again. */
1080         while ( co_MsqDispatchOneSentMessage(pti) )
1081         {
1082            if (HIWORD(RemoveMsg) && !bGMSG) Hit = TRUE;
1083         }
1084         if (Hit) return FALSE;
1085 
1086         /* Check for paint messages. */
1087         if ((ProcessMask & QS_PAINT) &&
1088             pti->cPaintsReady &&
1089             IntGetPaintMessage( Window,
1090                                 MsgFilterMin,
1091                                 MsgFilterMax,
1092                                 pti,
1093                                 Msg,
1094                                 RemoveMessages))
1095         {
1096             goto GotMessage;
1097         }
1098 
1099        /* This is correct, check for the current threads timers waiting to be
1100           posted to this threads message queue. If any we loop again.
1101         */
1102         if ((ProcessMask & QS_TIMER) &&
1103             PostTimerMessages(Window))
1104         {
1105             continue;
1106         }
1107 
1108         return FALSE;
1109     }
1110     while (TRUE);
1111 
1112 GotMessage:
1113     /* Update the last message-queue access time */
1114     pti->pcti->timeLastRead = EngGetTickCount32();
1115     return TRUE;
1116 }
1117 
1118 BOOL FASTCALL
1119 co_IntWaitMessage( PWND Window,
1120                    UINT MsgFilterMin,
1121                    UINT MsgFilterMax )
1122 {
1123     PTHREADINFO pti;
1124     NTSTATUS Status = STATUS_SUCCESS;
1125     MSG Msg;
1126     LONG_PTR ExtraInfo = 0;
1127 
1128     pti = PsGetCurrentThreadWin32Thread();
1129 
1130     do
1131     {
1132         if ( co_IntPeekMessage( &Msg,       // Dont reenter!
1133                                  Window,
1134                                  MsgFilterMin,
1135                                  MsgFilterMax,
1136                                  MAKELONG( PM_NOREMOVE, GetWakeMask( MsgFilterMin, MsgFilterMax)),
1137                                  &ExtraInfo,
1138                                  TRUE ) )   // act like GetMessage.
1139         {
1140             return TRUE;
1141         }
1142 
1143         /* Nothing found. Wait for new messages. */
1144         Status = co_MsqWaitForNewMessages( pti,
1145                                            Window,
1146                                            MsgFilterMin,
1147                                            MsgFilterMax);
1148         if (!NT_SUCCESS(Status))
1149         {
1150             SetLastNtError(Status);
1151             ERR("Exit co_IntWaitMessage on error!\n");
1152             return FALSE;
1153         }
1154         if (Status == STATUS_USER_APC || Status == STATUS_TIMEOUT)
1155         {
1156            return FALSE;
1157         }
1158     }
1159     while ( TRUE );
1160 
1161     return FALSE;
1162 }
1163 
1164 BOOL APIENTRY
1165 co_IntGetPeekMessage( PMSG pMsg,
1166                       HWND hWnd,
1167                       UINT MsgFilterMin,
1168                       UINT MsgFilterMax,
1169                       UINT RemoveMsg,
1170                       BOOL bGMSG )
1171 {
1172     PWND Window;
1173     PTHREADINFO pti;
1174     BOOL Present = FALSE;
1175     NTSTATUS Status;
1176     LONG_PTR ExtraInfo = 0;
1177 
1178     if ( hWnd == HWND_TOPMOST || hWnd == HWND_BROADCAST )
1179         hWnd = HWND_BOTTOM;
1180 
1181     /* Validate input */
1182     if (hWnd && hWnd != HWND_BOTTOM)
1183     {
1184         if (!(Window = UserGetWindowObject(hWnd)))
1185         {
1186             if (bGMSG)
1187                 return -1;
1188             else
1189                 return FALSE;
1190         }
1191     }
1192     else
1193     {
1194         Window = (PWND)hWnd;
1195     }
1196 
1197     if (MsgFilterMax < MsgFilterMin)
1198     {
1199         MsgFilterMin = 0;
1200         MsgFilterMax = 0;
1201     }
1202 
1203     if (bGMSG)
1204     {
1205        RemoveMsg |= ((GetWakeMask( MsgFilterMin, MsgFilterMax ))<< 16);
1206     }
1207 
1208     pti = PsGetCurrentThreadWin32Thread();
1209     pti->pClientInfo->cSpins++; // Bump up the spin count.
1210 
1211     do
1212     {
1213         Present = co_IntPeekMessage( pMsg,
1214                                      Window,
1215                                      MsgFilterMin,
1216                                      MsgFilterMax,
1217                                      RemoveMsg,
1218                                      &ExtraInfo,
1219                                      bGMSG );
1220         if (Present)
1221         {
1222            /* GetMessage or PostMessage must never get messages that contain pointers */
1223            ASSERT(FindMsgMemory(pMsg->message) == NULL);
1224 
1225            if ( pMsg->message >= WM_DDE_FIRST && pMsg->message <= WM_DDE_LAST )
1226            {
1227               if (!IntDdeGetMessageHook(pMsg, ExtraInfo))
1228               {
1229                  TRACE("DDE Get return ERROR\n");
1230                  continue;
1231               }
1232            }
1233 
1234            if (pMsg->message != WM_PAINT && pMsg->message != WM_QUIT)
1235            {
1236               if (!RtlEqualMemory(&pti->ptLast, &pMsg->pt, sizeof(POINT)))
1237               {
1238                  pti->TIF_flags |= TIF_MSGPOSCHANGED;
1239               }
1240               pti->timeLast = pMsg->time;
1241               pti->ptLast   = pMsg->pt;
1242            }
1243 
1244            // The WH_GETMESSAGE hook enables an application to monitor messages about to
1245            // be returned by the GetMessage or PeekMessage function.
1246 
1247            co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)pMsg);
1248 
1249            if ( bGMSG || pMsg->message == WM_PAINT) break;
1250         }
1251 
1252         if ( bGMSG )
1253         {
1254             Status = co_MsqWaitForNewMessages( pti,
1255                                                Window,
1256                                                MsgFilterMin,
1257                                                MsgFilterMax);
1258            if ( !NT_SUCCESS(Status) ||
1259                 Status == STATUS_USER_APC ||
1260                 Status == STATUS_TIMEOUT )
1261            {
1262               Present = -1;
1263               break;
1264            }
1265         }
1266         else
1267         {
1268            if (!(RemoveMsg & PM_NOYIELD))
1269            {
1270               IdlePing();
1271               // Yield this thread!
1272               UserLeave();
1273               ZwYieldExecution();
1274               UserEnterExclusive();
1275               // Fall through to exit.
1276               IdlePong();
1277            }
1278            break;
1279         }
1280     }
1281     while( bGMSG && !Present );
1282 
1283     // Been spinning, time to swap vinyl...
1284     if (pti->pClientInfo->cSpins >= 100)
1285     {
1286        // Clear the spin cycle to fix the mix.
1287        pti->pClientInfo->cSpins = 0;
1288        //if (!(pti->TIF_flags & TIF_SPINNING)) // FIXME: Need to swap vinyl...
1289     }
1290     return Present;
1291 }
1292 
1293 BOOL FASTCALL
1294 UserPostThreadMessage( PTHREADINFO pti,
1295                        UINT Msg,
1296                        WPARAM wParam,
1297                        LPARAM lParam )
1298 {
1299     MSG Message;
1300 
1301     if (is_pointer_message(Msg))
1302     {
1303         EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
1304         return FALSE;
1305     }
1306     Message.hwnd = NULL;
1307     Message.message = Msg;
1308     Message.wParam = wParam;
1309     Message.lParam = lParam;
1310     Message.pt = gpsi->ptCursor;
1311     Message.time = EngGetTickCount32();
1312     MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0, 0);
1313     return TRUE;
1314 }
1315 
1316 PTHREADINFO FASTCALL
1317 IntSendTo(PWND Window, PTHREADINFO ptiCur, UINT Msg)
1318 {
1319    if ( ptiCur )
1320    {
1321       if (!Window ||
1322            Window->head.pti == ptiCur )
1323       {
1324          return NULL;
1325       }
1326    }
1327    return Window ? Window->head.pti : NULL;
1328 }
1329 
1330 BOOL FASTCALL
1331 UserPostMessage( HWND Wnd,
1332                  UINT Msg,
1333                  WPARAM wParam,
1334                  LPARAM lParam )
1335 {
1336     PTHREADINFO pti;
1337     MSG Message;
1338     LONG_PTR ExtraInfo = 0;
1339 
1340     Message.hwnd = Wnd;
1341     Message.message = Msg;
1342     Message.wParam = wParam;
1343     Message.lParam = lParam;
1344     Message.pt = gpsi->ptCursor;
1345     Message.time = EngGetTickCount32();
1346 
1347     if (is_pointer_message(Message.message))
1348     {
1349         EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
1350         return FALSE;
1351     }
1352 
1353     if (Wnd == HWND_BROADCAST || Wnd == HWND_TOPMOST)
1354     {
1355         HWND *List;
1356         PWND DesktopWindow;
1357         ULONG i;
1358 
1359         if (!is_message_broadcastable(Msg)) return TRUE;
1360 
1361         DesktopWindow = UserGetDesktopWindow();
1362         List = IntWinListChildren(DesktopWindow);
1363 
1364         if (List != NULL)
1365         {
1366             UserPostMessage(DesktopWindow->head.h, Msg, wParam, lParam);
1367             for (i = 0; List[i]; i++)
1368             {
1369                 PWND pwnd = UserGetWindowObject(List[i]);
1370                 if (!pwnd) continue;
1371 
1372                 if ( pwnd->fnid == FNID_MENU || // Also need pwnd->pcls->atomClassName == gaOleMainThreadWndClass
1373                      pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
1374                    continue;
1375 
1376                 UserPostMessage(List[i], Msg, wParam, lParam);
1377             }
1378             ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
1379         }
1380     }
1381     else
1382     {
1383         PWND Window;
1384 
1385         if (!Wnd)
1386         {
1387            return UserPostThreadMessage( gptiCurrent,
1388                                          Msg,
1389                                          wParam,
1390                                          lParam);
1391         }
1392 
1393         Window = UserGetWindowObject(Wnd);
1394         if ( !Window )
1395         {
1396             ERR("UserPostMessage: Invalid handle 0x%p Msg 0x%x!\n", Wnd, Msg);
1397             return FALSE;
1398         }
1399 
1400         pti = Window->head.pti;
1401 
1402         if ( pti->TIF_flags & TIF_INCLEANUP )
1403         {
1404             ERR("Attempted to post message to window %p when the thread is in cleanup!\n", Wnd);
1405             return FALSE;
1406         }
1407 
1408         if ( Window->state & WNDS_DESTROYED )
1409         {
1410             ERR("Attempted to post message to window %p that is being destroyed!\n", Wnd);
1411             /* FIXME: Last error code? */
1412             return FALSE;
1413         }
1414 
1415         if ( Msg >= WM_DDE_FIRST && Msg <= WM_DDE_LAST )
1416         {
1417            if (!IntDdePostMessageHook(Window, Msg, wParam, &lParam, &ExtraInfo))
1418            {
1419               TRACE("Posting Exit DDE 0x%x\n",Msg);
1420               return FALSE;
1421            }
1422            Message.lParam = lParam;
1423         }
1424 
1425         MsqPostMessage(pti, &Message, FALSE, QS_POSTMESSAGE, 0, ExtraInfo);
1426     }
1427     return TRUE;
1428 }
1429 
1430 LRESULT FASTCALL
1431 co_IntSendMessage( HWND hWnd,
1432                    UINT Msg,
1433                    WPARAM wParam,
1434                    LPARAM lParam )
1435 {
1436     ULONG_PTR Result = 0;
1437 
1438     if (co_IntSendMessageTimeout(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
1439     {
1440         return (LRESULT)Result;
1441     }
1442     return 0;
1443 }
1444 
1445 static LRESULT FASTCALL
1446 co_IntSendMessageTimeoutSingle( HWND hWnd,
1447                                 UINT Msg,
1448                                 WPARAM wParam,
1449                                 LPARAM lParam,
1450                                 UINT uFlags,
1451                                 UINT uTimeout,
1452                                 ULONG_PTR *uResult )
1453 {
1454     NTSTATUS Status = STATUS_SUCCESS;
1455     PWND Window = NULL;
1456     PMSGMEMORY MsgMemoryEntry;
1457     INT lParamBufferSize;
1458     LPARAM lParamPacked;
1459     PTHREADINFO Win32Thread, ptiSendTo = NULL;
1460     ULONG_PTR Result = 0;
1461     DECLARE_RETURN(LRESULT);
1462     USER_REFERENCE_ENTRY Ref;
1463     BOOL DoCallBack = TRUE;
1464 
1465     if (!(Window = UserGetWindowObject(hWnd)))
1466     {
1467         TRACE("SendMessageTimeoutSingle: Invalid handle 0x%p!\n",hWnd);
1468         RETURN( FALSE);
1469     }
1470 
1471     UserRefObjectCo(Window, &Ref);
1472 
1473     Win32Thread = PsGetCurrentThreadWin32Thread();
1474 
1475     ptiSendTo = IntSendTo(Window, Win32Thread, Msg);
1476 
1477     if ( Msg >= WM_DDE_FIRST && Msg <= WM_DDE_LAST )
1478     {
1479        if (!IntDdeSendMessageHook(Window, Msg, wParam, lParam))
1480        {
1481           ERR("Sending Exit DDE 0x%x\n",Msg);
1482           RETURN( FALSE);
1483        }
1484     }
1485 
1486     if ( !ptiSendTo )
1487     {
1488         if (Win32Thread->TIF_flags & TIF_INCLEANUP)
1489         {
1490             /* Never send messages to exiting threads */
1491             RETURN( FALSE);
1492         }
1493 
1494         if (Msg & 0x80000000)
1495         {
1496            TRACE("SMTS: Internal Message!\n");
1497            Result = (ULONG_PTR)handle_internal_message( Window, Msg, wParam, lParam );
1498            if (uResult) *uResult = Result;
1499            RETURN( TRUE);
1500         }
1501 
1502         // Only happens when calling the client!
1503         IntCallWndProc( Window, hWnd, Msg, wParam, lParam);
1504 
1505         if ( Window->state & WNDS_SERVERSIDEWINDOWPROC )
1506         {
1507            TRACE("SMT: Server Side Window Procedure\n");
1508            // Handle it here. Safeguard against excessive recursions.
1509            if (IoGetRemainingStackSize() < PAGE_SIZE)
1510            {
1511               ERR("Server Callback Exceeded Stack!\n");
1512               RETURN( FALSE);
1513            }
1514            /* Return after server side call, IntCallWndProcRet will not be called. */
1515            switch(Window->fnid)
1516            {
1517               case FNID_DESKTOP:
1518                 DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
1519                 break;
1520               case FNID_MESSAGEWND:
1521                 DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
1522                 break;
1523               case FNID_MENU:
1524                 DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result);
1525                 break;
1526            }
1527            if (!DoCallBack)
1528            {
1529               if (uResult) *uResult = Result;
1530               RETURN( TRUE);
1531            }
1532         }
1533         /* See if this message type is present in the table */
1534         MsgMemoryEntry = FindMsgMemory(Msg);
1535         if (NULL == MsgMemoryEntry)
1536         {
1537            lParamBufferSize = -1;
1538         }
1539         else
1540         {
1541            lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1542            // If zero, do not allow callback on client side to allocate a buffer!!!!! See CORE-7695.
1543            if (!lParamBufferSize) lParamBufferSize = -1;
1544         }
1545 
1546         if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, FALSE)))
1547         {
1548            ERR("Failed to pack message parameters\n");
1549            RETURN( FALSE);
1550         }
1551 
1552         Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc,
1553                                                   !Window->Unicode,
1554                                                   hWnd,
1555                                                   Msg,
1556                                                   wParam,
1557                                                   lParamPacked,
1558                                                   lParamBufferSize );
1559         if (uResult)
1560         {
1561             *uResult = Result;
1562         }
1563 
1564         if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
1565         {
1566             ERR("Failed to unpack message parameters\n");
1567             RETURN( TRUE);
1568         }
1569 
1570         // Only happens when calling the client!
1571         IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
1572 
1573         RETURN( TRUE);
1574     }
1575 
1576     if (Window->state & WNDS_DESTROYED)
1577     {
1578         /* FIXME: Last error? */
1579         ERR("Attempted to send message to window %p that is being destroyed!\n", hWnd);
1580         RETURN( FALSE);
1581     }
1582 
1583     if ((uFlags & SMTO_ABORTIFHUNG) && MsqIsHung(ptiSendTo, 4 * MSQ_HUNG))
1584     {
1585         // FIXME: Set window hung and add to a list.
1586         /* FIXME: Set a LastError? */
1587         ERR("Window %p (%p) (pti %p) is hung!\n", hWnd, Window, ptiSendTo);
1588         RETURN( FALSE);
1589     }
1590 
1591     do
1592     {
1593         Status = co_MsqSendMessage( ptiSendTo,
1594                                     hWnd,
1595                                     Msg,
1596                                     wParam,
1597                                     lParam,
1598                                     uTimeout,
1599                                     (uFlags & SMTO_BLOCK),
1600                                     MSQ_NORMAL,
1601                                     uResult );
1602     }
1603     while ((Status == STATUS_TIMEOUT) &&
1604            (uFlags & SMTO_NOTIMEOUTIFNOTHUNG) &&
1605            !MsqIsHung(ptiSendTo, MSQ_HUNG)); // FIXME: Set window hung and add to a list.
1606 
1607     if (Status == STATUS_TIMEOUT)
1608     {
1609         if (0 && MsqIsHung(ptiSendTo, MSQ_HUNG))
1610         {
1611             TRACE("Let's go Ghost!\n");
1612             IntMakeHungWindowGhosted(hWnd);
1613         }
1614 /*
1615  *  MSDN says:
1616  *  Microsoft Windows 2000: If GetLastError returns zero, then the function
1617  *  timed out.
1618  *  XP+ : If the function fails or times out, the return value is zero.
1619  *  To get extended error information, call GetLastError. If GetLastError
1620  *  returns ERROR_TIMEOUT, then the function timed out.
1621  */
1622         EngSetLastError(ERROR_TIMEOUT);
1623         RETURN( FALSE);
1624     }
1625     else if (!NT_SUCCESS(Status))
1626     {
1627         SetLastNtError(Status);
1628         RETURN( FALSE);
1629     }
1630 
1631     RETURN( TRUE);
1632 
1633 CLEANUP:
1634     if (Window) UserDerefObjectCo(Window);
1635     END_CLEANUP;
1636 }
1637 
1638 LRESULT FASTCALL
1639 co_IntSendMessageTimeout( HWND hWnd,
1640                           UINT Msg,
1641                           WPARAM wParam,
1642                           LPARAM lParam,
1643                           UINT uFlags,
1644                           UINT uTimeout,
1645                           ULONG_PTR *uResult )
1646 {
1647     PWND DesktopWindow;
1648     HWND *Children;
1649     HWND *Child;
1650 
1651     if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST)
1652     {
1653         return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1654     }
1655 
1656     if (!is_message_broadcastable(Msg)) return TRUE;
1657 
1658     DesktopWindow = UserGetDesktopWindow();
1659     if (NULL == DesktopWindow)
1660     {
1661        EngSetLastError(ERROR_INTERNAL_ERROR);
1662        return 0;
1663     }
1664 
1665     if (hWnd != HWND_TOPMOST)
1666     {
1667        /* Send message to the desktop window too! */
1668        co_IntSendMessageTimeoutSingle(DesktopWindow->head.h, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1669     }
1670 
1671     Children = IntWinListChildren(DesktopWindow);
1672     if (NULL == Children)
1673     {
1674         return 0;
1675     }
1676 
1677     for (Child = Children; NULL != *Child; Child++)
1678     {
1679         PWND pwnd = UserGetWindowObject(*Child);
1680         if (!pwnd) continue;
1681 
1682         if ( pwnd->fnid == FNID_MENU ||
1683              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
1684            continue;
1685 
1686         co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1687     }
1688 
1689     ExFreePoolWithTag(Children, USERTAG_WINDOWLIST);
1690 
1691     return (LRESULT) TRUE;
1692 }
1693 
1694 LRESULT FASTCALL
1695 co_IntSendMessageNoWait(HWND hWnd,
1696                         UINT Msg,
1697                         WPARAM wParam,
1698                         LPARAM lParam)
1699 {
1700     ULONG_PTR Result = 0;
1701     return co_IntSendMessageWithCallBack( hWnd,
1702                                           Msg,
1703                                           wParam,
1704                                           lParam,
1705                                           NULL,
1706                                           0,
1707                                          &Result);
1708 }
1709 /* MSDN:
1710    If you send a message in the range below WM_USER to the asynchronous message
1711    functions (PostMessage, SendNotifyMessage, and SendMessageCallback), its
1712    message parameters cannot include pointers. Otherwise, the operation will fail.
1713    The functions will return before the receiving thread has had a chance to
1714    process the message and the sender will free the memory before it is used.
1715 */
1716 LRESULT FASTCALL
1717 co_IntSendMessageWithCallBack( HWND hWnd,
1718                               UINT Msg,
1719                               WPARAM wParam,
1720                               LPARAM lParam,
1721                               SENDASYNCPROC CompletionCallback,
1722                               ULONG_PTR CompletionCallbackContext,
1723                               ULONG_PTR *uResult)
1724 {
1725     ULONG_PTR Result;
1726     PWND Window = NULL;
1727     PMSGMEMORY MsgMemoryEntry;
1728     INT lParamBufferSize;
1729     LPARAM lParamPacked;
1730     PTHREADINFO Win32Thread, ptiSendTo = NULL;
1731     DECLARE_RETURN(LRESULT);
1732     USER_REFERENCE_ENTRY Ref;
1733     PUSER_SENT_MESSAGE Message;
1734     BOOL DoCallBack = TRUE;
1735 
1736     if (!(Window = UserGetWindowObject(hWnd)))
1737     {
1738         TRACE("SendMessageWithCallBack: Invalid handle 0x%p!\n",hWnd);
1739         RETURN(FALSE);
1740     }
1741 
1742     UserRefObjectCo(Window, &Ref);
1743 
1744     if (Window->state & WNDS_DESTROYED)
1745     {
1746         /* FIXME: last error? */
1747         ERR("Attempted to send message to window %p that is being destroyed!\n", hWnd);
1748         RETURN(FALSE);
1749     }
1750 
1751     Win32Thread = PsGetCurrentThreadWin32Thread();
1752 
1753     if (Win32Thread == NULL ||
1754         Win32Thread->TIF_flags & TIF_INCLEANUP)
1755     {
1756         RETURN(FALSE);
1757     }
1758 
1759     ptiSendTo = IntSendTo(Window, Win32Thread, Msg);
1760 
1761     if (Msg & 0x80000000 &&
1762         !ptiSendTo)
1763     {
1764        if (Win32Thread->TIF_flags & TIF_INCLEANUP) RETURN( FALSE);
1765 
1766        TRACE("SMWCB: Internal Message!\n");
1767        Result = (ULONG_PTR)handle_internal_message( Window, Msg, wParam, lParam );
1768        if (uResult) *uResult = Result;
1769        RETURN( TRUE);
1770     }
1771 
1772     /* See if this message type is present in the table */
1773     MsgMemoryEntry = FindMsgMemory(Msg);
1774     if (NULL == MsgMemoryEntry)
1775     {
1776         lParamBufferSize = -1;
1777     }
1778     else
1779     {
1780         lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1781         if (!lParamBufferSize) lParamBufferSize = -1;
1782     }
1783 
1784     if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, !!ptiSendTo)))
1785     {
1786         ERR("Failed to pack message parameters\n");
1787         RETURN( FALSE);
1788     }
1789 
1790     /* If it can be sent now, then send it. */
1791     if ( !ptiSendTo )
1792     {
1793         if (Win32Thread->TIF_flags & TIF_INCLEANUP)
1794         {
1795             UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE);
1796             /* Never send messages to exiting threads */
1797             RETURN(FALSE);
1798         }
1799 
1800         IntCallWndProc( Window, hWnd, Msg, wParam, lParam);
1801 
1802         if ( Window->state & WNDS_SERVERSIDEWINDOWPROC )
1803         {
1804            TRACE("SMWCB: Server Side Window Procedure\n");
1805            switch(Window->fnid)
1806            {
1807               case FNID_DESKTOP:
1808                 DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParamPacked, (LRESULT*)&Result);
1809                 break;
1810               case FNID_MESSAGEWND:
1811                 DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
1812                 break;
1813               case FNID_MENU:
1814                 DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result);
1815                 break;
1816            }
1817         }
1818 
1819         if (DoCallBack)
1820         Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc,
1821                                                   !Window->Unicode,
1822                                                   hWnd,
1823                                                   Msg,
1824                                                   wParam,
1825                                                   lParamPacked,
1826                                                   lParamBufferSize );
1827         if(uResult)
1828         {
1829             *uResult = Result;
1830         }
1831 
1832         IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
1833 
1834         if (CompletionCallback)
1835         {
1836             co_IntCallSentMessageCallback(CompletionCallback,
1837                                           hWnd,
1838                                           Msg,
1839                                           CompletionCallbackContext,
1840                                           Result);
1841         }
1842     }
1843 
1844     if ( !ptiSendTo)
1845     {
1846         if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
1847         {
1848             ERR("Failed to unpack message parameters\n");
1849         }
1850         RETURN(TRUE);
1851     }
1852 
1853     if(!(Message = AllocateUserMessage(FALSE)))
1854     {
1855         ERR("MsqSendMessage(): Not enough memory to allocate a message");
1856         RETURN( FALSE);
1857     }
1858 
1859     Message->Msg.hwnd = hWnd;
1860     Message->Msg.message = Msg;
1861     Message->Msg.wParam = wParam;
1862     Message->Msg.lParam = lParamPacked;
1863     Message->pkCompletionEvent = NULL; // No event needed.
1864     Message->lResult = 0;
1865     Message->QS_Flags = 0;
1866     Message->ptiReceiver = ptiSendTo;
1867     Message->ptiSender = NULL; // mjmartin, you are right! This is null.
1868     Message->ptiCallBackSender = Win32Thread;
1869     Message->CompletionCallback = CompletionCallback;
1870     Message->CompletionCallbackContext = CompletionCallbackContext;
1871     Message->HookMessage = MSQ_NORMAL;
1872     Message->HasPackedLParam = (lParamBufferSize > 0);
1873     Message->QS_Flags = QS_SENDMESSAGE;
1874     Message->flags = SMF_RECEIVERFREE;
1875 
1876     if (Msg & 0x80000000) // Higher priority event message!
1877        InsertHeadList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry);
1878     else
1879        InsertTailList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry);
1880     MsqWakeQueue(ptiSendTo, QS_SENDMESSAGE, TRUE);
1881 
1882     RETURN(TRUE);
1883 
1884 CLEANUP:
1885     if (Window) UserDerefObjectCo(Window);
1886     END_CLEANUP;
1887 }
1888 
1889 #if 0
1890 /*
1891   This HACK function posts a message if the destination's message queue belongs to
1892   another thread, otherwise it sends the message. It does not support broadcast
1893   messages!
1894 */
1895 LRESULT FASTCALL
1896 co_IntPostOrSendMessage( HWND hWnd,
1897                          UINT Msg,
1898                          WPARAM wParam,
1899                          LPARAM lParam )
1900 {
1901     ULONG_PTR Result;
1902     PTHREADINFO pti;
1903     PWND Window;
1904 
1905     if ( hWnd == HWND_BROADCAST )
1906     {
1907         return 0;
1908     }
1909 
1910     if(!(Window = UserGetWindowObject(hWnd)))
1911     {
1912         TRACE("PostOrSendMessage: Invalid handle 0x%p!\n",hWnd);
1913         return 0;
1914     }
1915 
1916     pti = PsGetCurrentThreadWin32Thread();
1917 
1918     if ( IntSendTo(Window, pti, Msg) &&
1919          FindMsgMemory(Msg) == 0 )
1920     {
1921         Result = UserPostMessage(hWnd, Msg, wParam, lParam);
1922     }
1923     else
1924     {
1925         if ( !co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result) )
1926         {
1927             Result = 0;
1928         }
1929     }
1930 
1931     return (LRESULT)Result;
1932 }
1933 #endif
1934 
1935 static LRESULT FASTCALL
1936 co_IntDoSendMessage( HWND hWnd,
1937                      UINT Msg,
1938                      WPARAM wParam,
1939                      LPARAM lParam,
1940                      PDOSENDMESSAGE dsm)
1941 {
1942     LRESULT Result = TRUE;
1943     NTSTATUS Status;
1944     PWND Window = NULL;
1945     MSG UserModeMsg, KernelModeMsg;
1946     PMSGMEMORY MsgMemoryEntry;
1947     PTHREADINFO ptiSendTo;
1948 
1949     if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST)
1950     {
1951         Window = UserGetWindowObject(hWnd);
1952         if ( !Window )
1953         {
1954             return 0;
1955         }
1956     }
1957 
1958     /* Check for an exiting window. */
1959     if (Window && Window->state & WNDS_DESTROYED)
1960     {
1961         ERR("co_IntDoSendMessage Window Exiting!\n");
1962     }
1963 
1964     /* See if the current thread can handle this message */
1965     ptiSendTo = IntSendTo(Window, gptiCurrent, Msg);
1966 
1967     // If broadcasting or sending to another thread, save the users data.
1968     if (!Window || ptiSendTo )
1969     {
1970        UserModeMsg.hwnd    = hWnd;
1971        UserModeMsg.message = Msg;
1972        UserModeMsg.wParam  = wParam;
1973        UserModeMsg.lParam  = lParam;
1974        MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1975        Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1976        if (!NT_SUCCESS(Status))
1977        {
1978           EngSetLastError(ERROR_INVALID_PARAMETER);
1979           return (dsm ? 0 : -1);
1980        }
1981     }
1982     else
1983     {
1984        KernelModeMsg.hwnd    = hWnd;
1985        KernelModeMsg.message = Msg;
1986        KernelModeMsg.wParam  = wParam;
1987        KernelModeMsg.lParam  = lParam;
1988     }
1989 
1990     if (!dsm)
1991     {
1992        Result = co_IntSendMessage( KernelModeMsg.hwnd,
1993                                    KernelModeMsg.message,
1994                                    KernelModeMsg.wParam,
1995                                    KernelModeMsg.lParam );
1996     }
1997     else
1998     {
1999        Result = co_IntSendMessageTimeout( KernelModeMsg.hwnd,
2000                                           KernelModeMsg.message,
2001                                           KernelModeMsg.wParam,
2002                                           KernelModeMsg.lParam,
2003                                           dsm->uFlags,
2004                                           dsm->uTimeout,
2005                                          &dsm->Result );
2006     }
2007 
2008     if (!Window || ptiSendTo )
2009     {
2010        Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
2011        if (!NT_SUCCESS(Status))
2012        {
2013           EngSetLastError(ERROR_INVALID_PARAMETER);
2014           return(dsm ? 0 : -1);
2015        }
2016     }
2017 
2018     return (LRESULT)Result;
2019 }
2020 
2021 BOOL FASTCALL
2022 UserSendNotifyMessage( HWND hWnd,
2023                        UINT Msg,
2024                        WPARAM wParam,
2025                        LPARAM lParam )
2026 {
2027     BOOL Ret = TRUE;
2028 
2029     if (is_pointer_message(Msg))
2030     {
2031         EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
2032         return FALSE;
2033     }
2034 
2035     // Basicly the same as IntPostOrSendMessage
2036     if (hWnd == HWND_BROADCAST) // Handle Broadcast
2037     {
2038         HWND *List;
2039         PWND DesktopWindow;
2040         ULONG i;
2041 
2042         DesktopWindow = UserGetDesktopWindow();
2043         List = IntWinListChildren(DesktopWindow);
2044 
2045         if (List != NULL)
2046         {
2047             UserSendNotifyMessage(DesktopWindow->head.h, Msg, wParam, lParam);
2048             for (i = 0; List[i]; i++)
2049             {
2050                PWND pwnd = UserGetWindowObject(List[i]);
2051                if (!pwnd) continue;
2052 
2053                if ( pwnd->fnid == FNID_MENU ||
2054                     pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2055                   continue;
2056 
2057                Ret = UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2058             }
2059             ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2060         }
2061     }
2062     else
2063     {
2064         Ret = co_IntSendMessageNoWait( hWnd, Msg, wParam, lParam);
2065     }
2066     return Ret;
2067 }
2068 
2069 
2070 DWORD APIENTRY
2071 IntGetQueueStatus(DWORD Changes)
2072 {
2073     PTHREADINFO pti;
2074     DWORD Result;
2075 
2076     pti = PsGetCurrentThreadWin32Thread();
2077 // wine:
2078     Changes &= (QS_ALLINPUT|QS_ALLPOSTMESSAGE|QS_SMRESULT);
2079 
2080     /* High word, types of messages currently in the queue.
2081        Low  word, types of messages that have been added to the queue and that
2082                   are still in the queue
2083      */
2084     Result = MAKELONG(pti->pcti->fsChangeBits & Changes, pti->pcti->fsWakeBits & Changes);
2085 
2086     pti->pcti->fsChangeBits &= ~Changes;
2087 
2088     return Result;
2089 }
2090 
2091 BOOL APIENTRY
2092 IntInitMessagePumpHook(VOID)
2093 {
2094     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2095 
2096     if (pti->pcti)
2097     {
2098         pti->pcti->dwcPumpHook++;
2099         return TRUE;
2100     }
2101     return FALSE;
2102 }
2103 
2104 BOOL APIENTRY
2105 IntUninitMessagePumpHook(VOID)
2106 {
2107     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2108 
2109     if (pti->pcti)
2110     {
2111         if (pti->pcti->dwcPumpHook <= 0)
2112         {
2113             return FALSE;
2114         }
2115         pti->pcti->dwcPumpHook--;
2116         return TRUE;
2117     }
2118     return FALSE;
2119 }
2120 
2121 BOOL FASTCALL
2122 IntCallMsgFilter( LPMSG lpmsg, INT code)
2123 {
2124     BOOL Ret = FALSE;
2125 
2126     if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)lpmsg))
2127     {
2128         Ret = TRUE;
2129     }
2130     else
2131     {
2132         Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)lpmsg);
2133     }
2134     return Ret;
2135 }
2136 
2137 /** Functions ******************************************************************/
2138 
2139 BOOL
2140 APIENTRY
2141 NtUserDragDetect(
2142    HWND hWnd,
2143    POINT pt) // Just like the User call.
2144 {
2145     MSG msg;
2146     RECT rect;
2147     ULONG wDragWidth, wDragHeight;
2148     DECLARE_RETURN(BOOL);
2149 
2150     TRACE("Enter NtUserDragDetect(%p)\n", hWnd);
2151     UserEnterExclusive();
2152 
2153     wDragWidth = UserGetSystemMetrics(SM_CXDRAG);
2154     wDragHeight= UserGetSystemMetrics(SM_CYDRAG);
2155 
2156     rect.left = pt.x - wDragWidth;
2157     rect.right = pt.x + wDragWidth;
2158 
2159     rect.top = pt.y - wDragHeight;
2160     rect.bottom = pt.y + wDragHeight;
2161 
2162     co_UserSetCapture(hWnd);
2163 
2164     for (;;)
2165     {
2166         while (co_IntGetPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, FALSE ) ||
2167                co_IntGetPeekMessage( &msg, 0, WM_QUEUESYNC,  WM_QUEUESYNC, PM_REMOVE, FALSE ) ||
2168                co_IntGetPeekMessage( &msg, 0, WM_KEYFIRST,   WM_KEYLAST,   PM_REMOVE, FALSE ) )
2169         {
2170             if ( msg.message == WM_LBUTTONUP )
2171             {
2172                 co_UserSetCapture(NULL);
2173                 RETURN( FALSE);
2174             }
2175             if ( msg.message == WM_MOUSEMOVE )
2176             {
2177                 POINT tmp;
2178                 tmp.x = (short)LOWORD(msg.lParam);
2179                 tmp.y = (short)HIWORD(msg.lParam);
2180                 if( !RECTL_bPointInRect( &rect, tmp.x, tmp.y ) )
2181                 {
2182                     co_UserSetCapture(NULL);
2183                     RETURN( TRUE);
2184                 }
2185             }
2186             if ( msg.message == WM_KEYDOWN )
2187             {
2188                 if ( msg.wParam == VK_ESCAPE )
2189                 {
2190                    co_UserSetCapture(NULL);
2191                    RETURN( TRUE);
2192                 }
2193             }
2194             if ( msg.message == WM_QUEUESYNC )
2195             {
2196                 co_HOOK_CallHooks( WH_CBT, HCBT_QS, 0, 0 );
2197             }
2198         }
2199         co_IntWaitMessage(NULL, 0, 0);
2200     }
2201     RETURN( FALSE);
2202 
2203 CLEANUP:
2204    TRACE("Leave NtUserDragDetect, ret=%i\n",_ret_);
2205    UserLeave();
2206    END_CLEANUP;
2207 }
2208 
2209 BOOL APIENTRY
2210 NtUserPostMessage(HWND hWnd,
2211                   UINT Msg,
2212                   WPARAM wParam,
2213                   LPARAM lParam)
2214 {
2215     BOOL ret;
2216 
2217     UserEnterExclusive();
2218 
2219     ret = UserPostMessage(hWnd, Msg, wParam, lParam);
2220 
2221     UserLeave();
2222 
2223     return ret;
2224 }
2225 
2226 BOOL APIENTRY
2227 NtUserPostThreadMessage(DWORD idThread,
2228                         UINT Msg,
2229                         WPARAM wParam,
2230                         LPARAM lParam)
2231 {
2232     BOOL ret = FALSE;
2233     PETHREAD peThread;
2234     PTHREADINFO pThread;
2235     NTSTATUS Status;
2236 
2237     UserEnterExclusive();
2238 
2239     Status = PsLookupThreadByThreadId(UlongToHandle(idThread), &peThread);
2240 
2241     if ( Status == STATUS_SUCCESS )
2242     {
2243         pThread = (PTHREADINFO)peThread->Tcb.Win32Thread;
2244         if( !pThread ||
2245             !pThread->MessageQueue ||
2246             (pThread->TIF_flags & TIF_INCLEANUP))
2247         {
2248             ObDereferenceObject( peThread );
2249             goto exit;
2250         }
2251         ret = UserPostThreadMessage( pThread, Msg, wParam, lParam);
2252         ObDereferenceObject( peThread );
2253     }
2254     else
2255     {
2256         SetLastNtError( Status );
2257     }
2258 exit:
2259     UserLeave();
2260     return ret;
2261 }
2262 
2263 BOOL APIENTRY
2264 NtUserWaitMessage(VOID)
2265 {
2266     BOOL ret;
2267 
2268     UserEnterExclusive();
2269     TRACE("NtUserWaitMessage Enter\n");
2270     ret = co_IntWaitMessage(NULL, 0, 0);
2271     TRACE("NtUserWaitMessage Leave\n");
2272     UserLeave();
2273 
2274     return ret;
2275 }
2276 
2277 BOOL APIENTRY
2278 NtUserGetMessage(PMSG pMsg,
2279                   HWND hWnd,
2280                   UINT MsgFilterMin,
2281                   UINT MsgFilterMax )
2282 {
2283     MSG Msg;
2284     BOOL Ret;
2285 
2286     if ( (MsgFilterMin|MsgFilterMax) & ~WM_MAXIMUM )
2287     {
2288         EngSetLastError(ERROR_INVALID_PARAMETER);
2289         return FALSE;
2290     }
2291 
2292     UserEnterExclusive();
2293 
2294     RtlZeroMemory(&Msg, sizeof(MSG));
2295 
2296     Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE, TRUE);
2297 
2298     UserLeave();
2299 
2300     if (Ret)
2301     {
2302         _SEH2_TRY
2303         {
2304             ProbeForWrite(pMsg, sizeof(MSG), 1);
2305             RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
2306         }
2307         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2308         {
2309             SetLastNtError(_SEH2_GetExceptionCode());
2310             Ret = FALSE;
2311         }
2312         _SEH2_END;
2313     }
2314 
2315     if ((INT)Ret != -1)
2316        Ret = Ret ? (WM_QUIT != pMsg->message) : FALSE;
2317 
2318     return Ret;
2319 }
2320 
2321 BOOL APIENTRY
2322 NtUserPeekMessage( PMSG pMsg,
2323                   HWND hWnd,
2324                   UINT MsgFilterMin,
2325                   UINT MsgFilterMax,
2326                   UINT RemoveMsg)
2327 {
2328     MSG Msg;
2329     BOOL Ret;
2330 
2331     if ( RemoveMsg & PM_BADMSGFLAGS )
2332     {
2333         EngSetLastError(ERROR_INVALID_FLAGS);
2334         return FALSE;
2335     }
2336 
2337     UserEnterExclusive();
2338 
2339     RtlZeroMemory(&Msg, sizeof(MSG));
2340 
2341     Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg, FALSE);
2342 
2343     UserLeave();
2344 
2345     if (Ret)
2346     {
2347         _SEH2_TRY
2348         {
2349             ProbeForWrite(pMsg, sizeof(MSG), 1);
2350             RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
2351         }
2352         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2353         {
2354             SetLastNtError(_SEH2_GetExceptionCode());
2355             Ret = FALSE;
2356         }
2357         _SEH2_END;
2358     }
2359 
2360     return Ret;
2361 }
2362 
2363 BOOL APIENTRY
2364 NtUserCallMsgFilter( LPMSG lpmsg, INT code)
2365 {
2366     BOOL Ret = FALSE;
2367     MSG Msg;
2368 
2369     _SEH2_TRY
2370     {
2371         ProbeForRead(lpmsg, sizeof(MSG), 1);
2372         RtlCopyMemory( &Msg, lpmsg, sizeof(MSG));
2373     }
2374     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2375     {
2376         _SEH2_YIELD(return FALSE);
2377     }
2378     _SEH2_END;
2379 
2380     UserEnterExclusive();
2381 
2382     if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)&Msg))
2383     {
2384         Ret = TRUE;
2385     }
2386     else
2387     {
2388         Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)&Msg);
2389     }
2390 
2391     UserLeave();
2392 
2393     _SEH2_TRY
2394     {
2395         ProbeForWrite(lpmsg, sizeof(MSG), 1);
2396         RtlCopyMemory(lpmsg, &Msg, sizeof(MSG));
2397     }
2398     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2399     {
2400         Ret = FALSE;
2401     }
2402     _SEH2_END;
2403 
2404     return Ret;
2405 }
2406 
2407 LRESULT APIENTRY
2408 NtUserDispatchMessage(PMSG UnsafeMsgInfo)
2409 {
2410     LRESULT Res = 0;
2411     MSG SafeMsg;
2412 
2413     _SEH2_TRY
2414     {
2415         ProbeForRead(UnsafeMsgInfo, sizeof(MSG), 1);
2416         RtlCopyMemory(&SafeMsg, UnsafeMsgInfo, sizeof(MSG));
2417     }
2418     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2419     {
2420         SetLastNtError(_SEH2_GetExceptionCode());
2421         _SEH2_YIELD(return FALSE);
2422     }
2423     _SEH2_END;
2424 
2425     UserEnterExclusive();
2426 
2427     Res = IntDispatchMessage(&SafeMsg);
2428 
2429     UserLeave();
2430     return Res;
2431 }
2432 
2433 BOOL APIENTRY
2434 NtUserTranslateMessage(LPMSG lpMsg, UINT flags)
2435 {
2436     MSG SafeMsg;
2437     BOOL Ret;
2438     PWND pWnd;
2439 
2440     _SEH2_TRY
2441     {
2442         ProbeForRead(lpMsg, sizeof(MSG), 1);
2443         RtlCopyMemory(&SafeMsg, lpMsg, sizeof(MSG));
2444     }
2445     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2446     {
2447         SetLastNtError(_SEH2_GetExceptionCode());
2448         _SEH2_YIELD(return FALSE);
2449     }
2450     _SEH2_END;
2451 
2452     UserEnterExclusive();
2453     pWnd = UserGetWindowObject(SafeMsg.hwnd);
2454     if (pWnd) // Must have a window!
2455     {
2456        Ret = IntTranslateKbdMessage(&SafeMsg, flags);
2457     }
2458     else
2459     {
2460         TRACE("No Window for Translate. hwnd 0x%p Msg %u\n", SafeMsg.hwnd, SafeMsg.message);
2461         Ret = FALSE;
2462     }
2463     UserLeave();
2464 
2465     return Ret;
2466 }
2467 
2468 LRESULT APIENTRY ScrollBarWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam);
2469 
2470 BOOL APIENTRY
2471 NtUserMessageCall( HWND hWnd,
2472                    UINT Msg,
2473                    WPARAM wParam,
2474                    LPARAM lParam,
2475                    ULONG_PTR ResultInfo,
2476                    DWORD dwType, // fnID?
2477                    BOOL Ansi)
2478 {
2479     LRESULT lResult = 0;
2480     BOOL Ret = FALSE;
2481     PWND Window = NULL;
2482     USER_REFERENCE_ENTRY Ref;
2483 
2484     UserEnterExclusive();
2485 
2486     switch(dwType)
2487     {
2488     case FNID_SCROLLBAR:
2489         {
2490            lResult = ScrollBarWndProc(hWnd, Msg, wParam, lParam);
2491            break;
2492         }
2493     case FNID_DESKTOP:
2494         {
2495            Window = UserGetWindowObject(hWnd);
2496            if (Window)
2497            {
2498               //ERR("FNID_DESKTOP IN\n");
2499               Ret = DesktopWindowProc(Window, Msg, wParam, lParam, &lResult);
2500               //ERR("FNID_DESKTOP OUT\n");
2501            }
2502            break;
2503         }
2504    case FNID_MENU:
2505        {
2506           Window = UserGetWindowObject(hWnd);
2507           if (Window)
2508           {
2509               Ret = PopupMenuWndProc( Window, Msg, wParam, lParam, &lResult);
2510           }
2511           break;
2512        }
2513    case FNID_MESSAGEWND:
2514        {
2515            Window = UserGetWindowObject(hWnd);
2516            if (Window)
2517            {
2518                 Ret = !UserMessageWindowProc(Window, Msg, wParam, lParam, &lResult);
2519            }
2520            break;
2521        }
2522     case FNID_DEFWINDOWPROC:
2523         /* Validate input */
2524         if (hWnd)
2525         {
2526            Window = UserGetWindowObject(hWnd);
2527            if (!Window)
2528            {
2529                UserLeave();
2530                return FALSE;
2531            }
2532            UserRefObjectCo(Window, &Ref);
2533         }
2534         lResult = IntDefWindowProc(Window, Msg, wParam, lParam, Ansi);
2535         Ret = TRUE;
2536         if (hWnd)
2537             UserDerefObjectCo(Window);
2538         break;
2539     case FNID_SENDNOTIFYMESSAGE:
2540         Ret = UserSendNotifyMessage(hWnd, Msg, wParam, lParam);
2541         break;
2542     case FNID_BROADCASTSYSTEMMESSAGE:
2543         {
2544             BROADCASTPARM parm, *retparam;
2545             DWORD_PTR RetVal = 0;
2546 
2547             if (ResultInfo)
2548             {
2549                 _SEH2_TRY
2550                 {
2551                     ProbeForWrite((PVOID)ResultInfo, sizeof(BROADCASTPARM), 1);
2552                     RtlCopyMemory(&parm, (PVOID)ResultInfo, sizeof(BROADCASTPARM));
2553                 }
2554                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2555                 {
2556                     _SEH2_YIELD(break);
2557                 }
2558                 _SEH2_END;
2559             }
2560             else
2561                 break;
2562 
2563             if ( parm.recipients & BSM_ALLDESKTOPS ||
2564                  parm.recipients == BSM_ALLCOMPONENTS )
2565             {
2566                PLIST_ENTRY DesktopEntry;
2567                PDESKTOP rpdesk;
2568                HWND *List, hwndDenied = NULL;
2569                HDESK hDesk = NULL;
2570                PWND pwnd, pwndDesk;
2571                ULONG i;
2572                UINT fuFlags;
2573 
2574                for (DesktopEntry = InputWindowStation->DesktopListHead.Flink;
2575                     DesktopEntry != &InputWindowStation->DesktopListHead;
2576                     DesktopEntry = DesktopEntry->Flink)
2577                {
2578                   rpdesk = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
2579                   pwndDesk = rpdesk->pDeskInfo->spwnd;
2580                   List = IntWinListChildren(pwndDesk);
2581 
2582                   if (parm.flags & BSF_QUERY)
2583                   {
2584                      if (List != NULL)
2585                      {
2586                         if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG)
2587                         {
2588                            fuFlags = SMTO_ABORTIFHUNG;
2589                         }
2590                         else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG)
2591                         {
2592                            fuFlags = SMTO_NOTIMEOUTIFNOTHUNG;
2593                         }
2594                         else
2595                         {
2596                            fuFlags = SMTO_NORMAL;
2597                         }
2598                         co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk),
2599                                                   Msg,
2600                                                   wParam,
2601                                                   lParam,
2602                                                   fuFlags,
2603                                                   2000,
2604                                                  &RetVal);
2605                         Ret = TRUE;
2606                         for (i = 0; List[i]; i++)
2607                         {
2608                            pwnd = UserGetWindowObject(List[i]);
2609                            if (!pwnd) continue;
2610 
2611                            if ( pwnd->fnid == FNID_MENU ||
2612                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2613                               continue;
2614 
2615                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2616                            {
2617                               if ( pwnd->head.pti == gptiCurrent )
2618                                  continue;
2619                            }
2620                            co_IntSendMessageTimeout( List[i],
2621                                                      Msg,
2622                                                      wParam,
2623                                                      lParam,
2624                                                      fuFlags,
2625                                                      2000,
2626                                                     &RetVal);
2627 
2628                            if (!RetVal && EngGetLastError() == ERROR_TIMEOUT)
2629                            {
2630                               if (!(parm.flags & BSF_FORCEIFHUNG))
2631                                  Ret = FALSE;
2632                            }
2633                            if (RetVal == BROADCAST_QUERY_DENY)
2634                            {
2635                               hwndDenied = List[i];
2636                               hDesk = UserHMGetHandle(pwndDesk);
2637                               Ret = FALSE;
2638                            }
2639                         }
2640                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2641                         _SEH2_TRY
2642                         {
2643                            retparam = (PBROADCASTPARM) ResultInfo;
2644                            retparam->hDesk = hDesk;
2645                            retparam->hWnd = hwndDenied;
2646                         }
2647                         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2648                         {
2649                            _SEH2_YIELD(break);
2650                         }
2651                         _SEH2_END;
2652                         if (!Ret) break; // Have a hit! Let everyone know!
2653                      }
2654                   }
2655                   else if (parm.flags & BSF_POSTMESSAGE)
2656                   {
2657                      if (List != NULL)
2658                      {
2659                         UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2660 
2661                         for (i = 0; List[i]; i++)
2662                         {
2663                            pwnd = UserGetWindowObject(List[i]);
2664                            if (!pwnd) continue;
2665 
2666                            if ( pwnd->fnid == FNID_MENU ||
2667                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2668                               continue;
2669 
2670                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2671                            {
2672                               if ( pwnd->head.pti == gptiCurrent )
2673                                  continue;
2674                            }
2675                            UserPostMessage(List[i], Msg, wParam, lParam);
2676                         }
2677                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2678                      }
2679                      Ret = TRUE;
2680                   }
2681                   else
2682                   {
2683                      if (List != NULL)
2684                      {
2685                         UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2686 
2687                         for (i = 0; List[i]; i++)
2688                         {
2689                            pwnd = UserGetWindowObject(List[i]);
2690                            if (!pwnd) continue;
2691 
2692                            if ( pwnd->fnid == FNID_MENU ||
2693                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2694                               continue;
2695 
2696                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2697                            {
2698                               if ( pwnd->head.pti == gptiCurrent )
2699                                  continue;
2700                            }
2701                            UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2702                         }
2703                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2704                      }
2705                      Ret = TRUE;
2706                   }
2707                }
2708             }
2709             else if (parm.recipients & BSM_APPLICATIONS)
2710             {
2711                HWND *List, hwndDenied = NULL;
2712                HDESK hDesk = NULL;
2713                PWND pwnd, pwndDesk;
2714                ULONG i;
2715                UINT fuFlags;
2716 
2717                pwndDesk = UserGetDesktopWindow();
2718                List = IntWinListChildren(pwndDesk);
2719 
2720                if (parm.flags & BSF_QUERY)
2721                {
2722                   if (List != NULL)
2723                   {
2724                      if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG)
2725                      {
2726                         fuFlags = SMTO_ABORTIFHUNG;
2727                      }
2728                      else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG)
2729                      {
2730                         fuFlags = SMTO_NOTIMEOUTIFNOTHUNG;
2731                      }
2732                      else
2733                      {
2734                         fuFlags = SMTO_NORMAL;
2735                      }
2736                      co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk),
2737                                                Msg,
2738                                                wParam,
2739                                                lParam,
2740                                                fuFlags,
2741                                                2000,
2742                                               &RetVal);
2743                      Ret = TRUE;
2744                      for (i = 0; List[i]; i++)
2745                      {
2746                         pwnd = UserGetWindowObject(List[i]);
2747                         if (!pwnd) continue;
2748 
2749                         if ( pwnd->fnid == FNID_MENU ||
2750                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2751                            continue;
2752 
2753                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2754                         {
2755                            if ( pwnd->head.pti == gptiCurrent )
2756                               continue;
2757                         }
2758                         co_IntSendMessageTimeout( List[i],
2759                                                   Msg,
2760                                                   wParam,
2761                                                   lParam,
2762                                                   fuFlags,
2763                                                   2000,
2764                                                  &RetVal);
2765 
2766                         if (!RetVal && EngGetLastError() == ERROR_TIMEOUT)
2767                         {
2768                            if (!(parm.flags & BSF_FORCEIFHUNG))
2769                               Ret = FALSE;
2770                         }
2771                         if (RetVal == BROADCAST_QUERY_DENY)
2772                         {
2773                            hwndDenied = List[i];
2774                            hDesk = UserHMGetHandle(pwndDesk);
2775                            Ret = FALSE;
2776                         }
2777                      }
2778                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2779                      _SEH2_TRY
2780                      {
2781                         retparam = (PBROADCASTPARM) ResultInfo;
2782                         retparam->hDesk = hDesk;
2783                         retparam->hWnd = hwndDenied;
2784                      }
2785                      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2786                      {
2787                         _SEH2_YIELD(break);
2788                      }
2789                      _SEH2_END;
2790                   }
2791                }
2792                else if (parm.flags & BSF_POSTMESSAGE)
2793                {
2794                   if (List != NULL)
2795                   {
2796                      UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2797 
2798                      for (i = 0; List[i]; i++)
2799                      {
2800                         pwnd = UserGetWindowObject(List[i]);
2801                         if (!pwnd) continue;
2802 
2803                         if ( pwnd->fnid == FNID_MENU ||
2804                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2805                            continue;
2806 
2807                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2808                         {
2809                            if ( pwnd->head.pti == gptiCurrent )
2810                               continue;
2811                         }
2812                         UserPostMessage(List[i], Msg, wParam, lParam);
2813                      }
2814                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2815                   }
2816                   Ret = TRUE;
2817                }
2818                else
2819                {
2820                   if (List != NULL)
2821                   {
2822                      UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2823 
2824                      for (i = 0; List[i]; i++)
2825                      {
2826                         pwnd = UserGetWindowObject(List[i]);
2827                         if (!pwnd) continue;
2828 
2829                         if ( pwnd->fnid == FNID_MENU ||
2830                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2831                            continue;
2832 
2833                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2834                         {
2835                            if ( pwnd->head.pti == gptiCurrent )
2836                               continue;
2837                         }
2838                         UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2839                      }
2840                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2841                   }
2842                   Ret = TRUE;
2843                }
2844             }
2845         }
2846         break;
2847     case FNID_SENDMESSAGECALLBACK:
2848         {
2849             CALL_BACK_INFO CallBackInfo;
2850             ULONG_PTR uResult;
2851 
2852             _SEH2_TRY
2853             {
2854                 ProbeForRead((PVOID)ResultInfo, sizeof(CALL_BACK_INFO), 1);
2855                 RtlCopyMemory(&CallBackInfo, (PVOID)ResultInfo, sizeof(CALL_BACK_INFO));
2856             }
2857             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2858             {
2859                 _SEH2_YIELD(break);
2860             }
2861             _SEH2_END;
2862 
2863             if (is_pointer_message(Msg))
2864             {
2865                EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
2866                break;
2867             }
2868 
2869             if (!(Ret = co_IntSendMessageWithCallBack(hWnd, Msg, wParam, lParam,
2870                         CallBackInfo.CallBack, CallBackInfo.Context, &uResult)))
2871             {
2872                 ERR("Callback failure!\n");
2873             }
2874         }
2875         break;
2876     case FNID_SENDMESSAGE:
2877         {
2878             lResult = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, 0);
2879             Ret = TRUE;
2880 
2881             if (ResultInfo)
2882             {
2883                 _SEH2_TRY
2884                 {
2885                     ProbeForWrite((PVOID)ResultInfo, sizeof(ULONG_PTR), 1);
2886                     RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(ULONG_PTR));
2887                 }
2888                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2889                 {
2890                     Ret = FALSE;
2891                     _SEH2_YIELD(break);
2892                 }
2893                 _SEH2_END;
2894             }
2895             break;
2896         }
2897     case FNID_SENDMESSAGEFF:
2898     case FNID_SENDMESSAGEWTOOPTION:
2899         {
2900             DOSENDMESSAGE dsm, *pdsm = (PDOSENDMESSAGE)ResultInfo;
2901             if (ResultInfo)
2902             {
2903                 _SEH2_TRY
2904                 {
2905                     ProbeForRead(pdsm, sizeof(DOSENDMESSAGE), 1);
2906                     RtlCopyMemory(&dsm, pdsm, sizeof(DOSENDMESSAGE));
2907                 }
2908                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2909                 {
2910                     _SEH2_YIELD(break);
2911                 }
2912                 _SEH2_END;
2913             }
2914 
2915             Ret = co_IntDoSendMessage( hWnd, Msg, wParam, lParam, pdsm ? &dsm : NULL );
2916 
2917             if (pdsm)
2918             {
2919                 _SEH2_TRY
2920                 {
2921                     ProbeForWrite(pdsm, sizeof(DOSENDMESSAGE), 1);
2922                     RtlCopyMemory(pdsm, &dsm, sizeof(DOSENDMESSAGE));
2923                 }
2924                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2925                 {
2926                     Ret = FALSE;
2927                     _SEH2_YIELD(break);
2928                 }
2929                 _SEH2_END;
2930             }
2931             break;
2932         }
2933         // CallNextHook bypass.
2934     case FNID_CALLWNDPROC:
2935     case FNID_CALLWNDPROCRET:
2936         {
2937             PTHREADINFO pti;
2938             PCLIENTINFO ClientInfo;
2939             PHOOK NextObj, Hook;
2940 
2941             pti = GetW32ThreadInfo();
2942 
2943             Hook = pti->sphkCurrent;
2944 
2945             if (!Hook) break;
2946 
2947             NextObj = Hook->phkNext;
2948             ClientInfo = pti->pClientInfo;
2949             _SEH2_TRY
2950             {
2951                 ClientInfo->phkCurrent = NextObj;
2952             }
2953             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2954             {
2955                 ClientInfo = NULL;
2956             }
2957             _SEH2_END;
2958 
2959             if (!ClientInfo || !NextObj) break;
2960 
2961             NextObj->phkNext = IntGetNextHook(NextObj);
2962 
2963             if ( Hook->HookId == WH_CALLWNDPROC)
2964             {
2965                 CWPSTRUCT CWP;
2966                 CWP.hwnd    = hWnd;
2967                 CWP.message = Msg;
2968                 CWP.wParam  = wParam;
2969                 CWP.lParam  = lParam;
2970                 TRACE("WH_CALLWNDPROC: Hook %p NextHook %p\n", Hook, NextObj);
2971 
2972                 lResult = co_IntCallHookProc( Hook->HookId,
2973                                               HC_ACTION,
2974                                               ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
2975                                               (LPARAM)&CWP,
2976                                               Hook->Proc,
2977                                               Hook->ihmod,
2978                                               Hook->offPfn,
2979                                               Hook->Ansi,
2980                                               &Hook->ModuleName);
2981             }
2982             else
2983             {
2984                 CWPRETSTRUCT CWPR;
2985                 CWPR.hwnd    = hWnd;
2986                 CWPR.message = Msg;
2987                 CWPR.wParam  = wParam;
2988                 CWPR.lParam  = lParam;
2989                 CWPR.lResult = ClientInfo->dwHookData;
2990 
2991                 lResult = co_IntCallHookProc( Hook->HookId,
2992                                               HC_ACTION,
2993                                               ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
2994                                               (LPARAM)&CWPR,
2995                                               Hook->Proc,
2996                                               Hook->ihmod,
2997                                               Hook->offPfn,
2998                                               Hook->Ansi,
2999                                               &Hook->ModuleName);
3000             }
3001         }
3002         break;
3003     }
3004 
3005     switch(dwType)
3006     {
3007     case FNID_DEFWINDOWPROC:
3008     case FNID_CALLWNDPROC:
3009     case FNID_CALLWNDPROCRET:
3010     case FNID_SCROLLBAR:
3011     case FNID_DESKTOP:
3012     case FNID_MENU:
3013         if (ResultInfo)
3014         {
3015             _SEH2_TRY
3016             {
3017                 ProbeForWrite((PVOID)ResultInfo, sizeof(LRESULT), 1);
3018                 RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(LRESULT));
3019             }
3020             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3021             {
3022                 Ret = FALSE;
3023             }
3024             _SEH2_END;
3025         }
3026         break;
3027     default:
3028         break;
3029     }
3030 
3031     UserLeave();
3032 
3033     return Ret;
3034 }
3035 
3036 #define INFINITE 0xFFFFFFFF
3037 #define WAIT_FAILED ((DWORD)0xFFFFFFFF)
3038 
3039 DWORD
3040 APIENTRY
3041 NtUserWaitForInputIdle( IN HANDLE hProcess,
3042                         IN DWORD dwMilliseconds,
3043                         IN BOOL bSharedWow)
3044 {
3045     PEPROCESS Process;
3046     PPROCESSINFO W32Process;
3047     PTHREADINFO pti;
3048     NTSTATUS Status;
3049     HANDLE Handles[3];
3050     LARGE_INTEGER Timeout;
3051     KAPC_STATE ApcState;
3052 
3053     UserEnterExclusive();
3054 
3055     Status = ObReferenceObjectByHandle(hProcess,
3056                                        PROCESS_QUERY_INFORMATION,
3057                                        *PsProcessType,
3058                                        UserMode,
3059                                        (PVOID*)&Process,
3060                                        NULL);
3061 
3062     if (!NT_SUCCESS(Status))
3063     {
3064         UserLeave();
3065         SetLastNtError(Status);
3066         return WAIT_FAILED;
3067     }
3068 
3069     pti = PsGetCurrentThreadWin32Thread();
3070 
3071     W32Process = (PPROCESSINFO)Process->Win32Process;
3072 
3073     if ( PsGetProcessExitProcessCalled(Process) ||
3074          !W32Process ||
3075          pti->ppi == W32Process)
3076     {
3077         ObDereferenceObject(Process);
3078         UserLeave();
3079         EngSetLastError(ERROR_INVALID_PARAMETER);
3080         return WAIT_FAILED;
3081     }
3082 
3083     Handles[0] = Process;
3084     Handles[1] = W32Process->InputIdleEvent;
3085     Handles[2] = pti->pEventQueueServer; // IntMsqSetWakeMask returns hEventQueueClient
3086 
3087     if (!Handles[1])
3088     {
3089         ObDereferenceObject(Process);
3090         UserLeave();
3091         return STATUS_SUCCESS;  /* no event to wait on */
3092     }
3093 
3094     if (dwMilliseconds != INFINITE)
3095        Timeout.QuadPart = (LONGLONG) dwMilliseconds * (LONGLONG) -10000;
3096 
3097     KeStackAttachProcess(&Process->Pcb, &ApcState);
3098 
3099     W32Process->W32PF_flags |= W32PF_WAITFORINPUTIDLE;
3100     for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling)
3101     {
3102        pti->TIF_flags |= TIF_WAITFORINPUTIDLE;
3103        pti->pClientInfo->dwTIFlags = pti->TIF_flags;
3104     }
3105 
3106     KeUnstackDetachProcess(&ApcState);
3107 
3108     TRACE("WFII: ppi %p\n", W32Process);
3109     TRACE("WFII: waiting for %p\n", Handles[1] );
3110 
3111     /*
3112      * We must add a refcount to our current PROCESSINFO,
3113      * because anything could happen (including process death) we're leaving win32k
3114      */
3115     IntReferenceProcessInfo(W32Process);
3116 
3117     do
3118     {
3119         UserLeave();
3120         Status = KeWaitForMultipleObjects( 3,
3121                                            Handles,
3122                                            WaitAny,
3123                                            UserRequest,
3124                                            UserMode,
3125                                            FALSE,
3126                                            dwMilliseconds == INFINITE ? NULL : &Timeout,
3127                                            NULL);
3128         UserEnterExclusive();
3129 
3130         if (!NT_SUCCESS(Status))
3131         {
3132             SetLastNtError(Status);
3133             Status = WAIT_FAILED;
3134             goto WaitExit;
3135         }
3136 
3137         switch (Status)
3138         {
3139         case STATUS_WAIT_0:
3140             goto WaitExit;
3141 
3142         case STATUS_WAIT_2:
3143             {
3144                MSG Msg;
3145                co_IntGetPeekMessage( &Msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE, FALSE);
3146                ERR("WFII: WAIT 2\n");
3147             }
3148             break;
3149 
3150         case STATUS_TIMEOUT:
3151             ERR("WFII: timeout\n");
3152         case WAIT_FAILED:
3153             goto WaitExit;
3154 
3155         default:
3156             ERR("WFII: finished\n");
3157             Status = STATUS_SUCCESS;
3158             goto WaitExit;
3159         }
3160     }
3161     while (TRUE);
3162 
3163 WaitExit:
3164     for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling)
3165     {
3166        pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE;
3167        pti->pClientInfo->dwTIFlags = pti->TIF_flags;
3168     }
3169     W32Process->W32PF_flags &= ~W32PF_WAITFORINPUTIDLE;
3170     IntDereferenceProcessInfo(W32Process);
3171     ObDereferenceObject(Process);
3172     UserLeave();
3173     return Status;
3174 }
3175 
3176 /* EOF */
3177