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