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