xref: /reactos/win32ss/user/ntuser/message.c (revision 6a6b5ec2)
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, pWnd->head.h);
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(DesktopWindow->head.h, 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 = NULL;
1470     PMSGMEMORY MsgMemoryEntry;
1471     INT lParamBufferSize;
1472     LPARAM lParamPacked;
1473     PTHREADINFO Win32Thread, ptiSendTo = NULL;
1474     ULONG_PTR Result = 0;
1475     DECLARE_RETURN(LRESULT);
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           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             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            RETURN( TRUE);
1514         }
1515 
1516         // Only happens when calling the client!
1517         IntCallWndProc( Window, hWnd, Msg, wParam, lParam);
1518 
1519         if ( Window->state & WNDS_SERVERSIDEWINDOWPROC )
1520         {
1521            TRACE("SMT: Server Side Window Procedure\n");
1522            // Handle it here. Safeguard against excessive recursions.
1523            if (IoGetRemainingStackSize() < PAGE_SIZE)
1524            {
1525               ERR("Server Callback Exceeded Stack!\n");
1526               RETURN( FALSE);
1527            }
1528            /* Return after server side call, IntCallWndProcRet will not be called. */
1529            switch(Window->fnid)
1530            {
1531               case FNID_DESKTOP:
1532                 DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
1533                 break;
1534               case FNID_MESSAGEWND:
1535                 DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
1536                 break;
1537               case FNID_MENU:
1538                 DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result);
1539                 break;
1540            }
1541            if (!DoCallBack)
1542            {
1543               if (uResult) *uResult = Result;
1544               RETURN( TRUE);
1545            }
1546         }
1547         /* See if this message type is present in the table */
1548         MsgMemoryEntry = FindMsgMemory(Msg);
1549         if (NULL == MsgMemoryEntry)
1550         {
1551            lParamBufferSize = -1;
1552         }
1553         else
1554         {
1555            lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1556            // If zero, do not allow callback on client side to allocate a buffer!!!!! See CORE-7695.
1557            if (!lParamBufferSize) lParamBufferSize = -1;
1558         }
1559 
1560         if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, FALSE)))
1561         {
1562            ERR("Failed to pack message parameters\n");
1563            RETURN( FALSE);
1564         }
1565 
1566         Result = (ULONG_PTR)co_IntCallWindowProc( Window->lpfnWndProc,
1567                                                   !Window->Unicode,
1568                                                   hWnd,
1569                                                   Msg,
1570                                                   wParam,
1571                                                   lParamPacked,
1572                                                   lParamBufferSize );
1573         if (uResult)
1574         {
1575             *uResult = Result;
1576         }
1577 
1578         if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
1579         {
1580             ERR("Failed to unpack message parameters\n");
1581             RETURN( TRUE);
1582         }
1583 
1584         // Only happens when calling the client!
1585         IntCallWndProcRet( Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
1586 
1587         RETURN( TRUE);
1588     }
1589 
1590     if (Window->state & WNDS_DESTROYED)
1591     {
1592         /* FIXME: Last error? */
1593         ERR("Attempted to send message to window %p that is being destroyed!\n", hWnd);
1594         RETURN( FALSE);
1595     }
1596 
1597     if ((uFlags & SMTO_ABORTIFHUNG) && MsqIsHung(ptiSendTo, 4 * MSQ_HUNG))
1598     {
1599         // FIXME: Set window hung and add to a list.
1600         /* FIXME: Set a LastError? */
1601         ERR("Window %p (%p) (pti %p) is hung!\n", hWnd, Window, ptiSendTo);
1602         RETURN( FALSE);
1603     }
1604 
1605     do
1606     {
1607         Status = co_MsqSendMessage( ptiSendTo,
1608                                     hWnd,
1609                                     Msg,
1610                                     wParam,
1611                                     lParam,
1612                                     uTimeout,
1613                                     (uFlags & SMTO_BLOCK),
1614                                     MSQ_NORMAL,
1615                                     uResult );
1616     }
1617     while ((Status == STATUS_TIMEOUT) &&
1618            (uFlags & SMTO_NOTIMEOUTIFNOTHUNG) &&
1619            !MsqIsHung(ptiSendTo, MSQ_HUNG)); // FIXME: Set window hung and add to a list.
1620 
1621     if (Status == STATUS_TIMEOUT)
1622     {
1623         if (0 && MsqIsHung(ptiSendTo, MSQ_HUNG))
1624         {
1625             TRACE("Let's go Ghost!\n");
1626             IntMakeHungWindowGhosted(hWnd);
1627         }
1628 /*
1629  *  MSDN says:
1630  *  Microsoft Windows 2000: If GetLastError returns zero, then the function
1631  *  timed out.
1632  *  XP+ : If the function fails or times out, the return value is zero.
1633  *  To get extended error information, call GetLastError. If GetLastError
1634  *  returns ERROR_TIMEOUT, then the function timed out.
1635  */
1636         EngSetLastError(ERROR_TIMEOUT);
1637         RETURN( FALSE);
1638     }
1639     else if (!NT_SUCCESS(Status))
1640     {
1641         SetLastNtError(Status);
1642         RETURN( FALSE);
1643     }
1644 
1645     RETURN( TRUE);
1646 
1647 CLEANUP:
1648     if (Window) UserDerefObjectCo(Window);
1649     END_CLEANUP;
1650 }
1651 
1652 LRESULT FASTCALL
1653 co_IntSendMessageTimeout( HWND hWnd,
1654                           UINT Msg,
1655                           WPARAM wParam,
1656                           LPARAM lParam,
1657                           UINT uFlags,
1658                           UINT uTimeout,
1659                           ULONG_PTR *uResult )
1660 {
1661     PWND DesktopWindow;
1662     HWND *Children;
1663     HWND *Child;
1664 
1665     if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST)
1666     {
1667         return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1668     }
1669 
1670     if (!is_message_broadcastable(Msg)) return TRUE;
1671 
1672     DesktopWindow = UserGetDesktopWindow();
1673     if (NULL == DesktopWindow)
1674     {
1675        EngSetLastError(ERROR_INTERNAL_ERROR);
1676        return 0;
1677     }
1678 
1679     if (hWnd != HWND_TOPMOST)
1680     {
1681        /* Send message to the desktop window too! */
1682        co_IntSendMessageTimeoutSingle(DesktopWindow->head.h, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1683     }
1684 
1685     Children = IntWinListChildren(DesktopWindow);
1686     if (NULL == Children)
1687     {
1688         return 0;
1689     }
1690 
1691     for (Child = Children; NULL != *Child; Child++)
1692     {
1693         PWND pwnd = UserGetWindowObject(*Child);
1694         if (!pwnd) continue;
1695 
1696         if ( pwnd->fnid == FNID_MENU ||
1697              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
1698            continue;
1699 
1700         co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1701     }
1702 
1703     ExFreePoolWithTag(Children, USERTAG_WINDOWLIST);
1704 
1705     return (LRESULT) TRUE;
1706 }
1707 
1708 LRESULT FASTCALL
1709 co_IntSendMessageNoWait(HWND hWnd,
1710                         UINT Msg,
1711                         WPARAM wParam,
1712                         LPARAM lParam)
1713 {
1714     ULONG_PTR Result = 0;
1715     return co_IntSendMessageWithCallBack(hWnd,
1716                                          Msg,
1717                                          wParam,
1718                                          lParam,
1719                                          NULL,
1720                                          0,
1721                                          &Result);
1722 }
1723 /* MSDN:
1724    If you send a message in the range below WM_USER to the asynchronous message
1725    functions (PostMessage, SendNotifyMessage, and SendMessageCallback), its
1726    message parameters cannot include pointers. Otherwise, the operation will fail.
1727    The functions will return before the receiving thread has had a chance to
1728    process the message and the sender will free the memory before it is used.
1729 */
1730 LRESULT FASTCALL
1731 co_IntSendMessageWithCallBack(HWND hWnd,
1732                               UINT Msg,
1733                               WPARAM wParam,
1734                               LPARAM lParam,
1735                               SENDASYNCPROC CompletionCallback,
1736                               ULONG_PTR CompletionCallbackContext,
1737                               ULONG_PTR *uResult)
1738 {
1739     ULONG_PTR Result;
1740     PWND Window = NULL;
1741     PMSGMEMORY MsgMemoryEntry;
1742     INT lParamBufferSize;
1743     LPARAM lParamPacked;
1744     PTHREADINFO Win32Thread, ptiSendTo = NULL;
1745     DECLARE_RETURN(LRESULT);
1746     USER_REFERENCE_ENTRY Ref;
1747     PUSER_SENT_MESSAGE Message;
1748     BOOL DoCallBack = TRUE;
1749 
1750     if (!(Window = UserGetWindowObject(hWnd)))
1751     {
1752         TRACE("SendMessageWithCallBack: Invalid handle 0x%p\n",hWnd);
1753         RETURN(FALSE);
1754     }
1755 
1756     UserRefObjectCo(Window, &Ref);
1757 
1758     if (Window->state & WNDS_DESTROYED)
1759     {
1760         /* FIXME: last error? */
1761         ERR("Attempted to send message to window %p that is being destroyed\n", hWnd);
1762         RETURN(FALSE);
1763     }
1764 
1765     Win32Thread = PsGetCurrentThreadWin32Thread();
1766 
1767     if (Win32Thread == NULL || Win32Thread->TIF_flags & TIF_INCLEANUP)
1768         RETURN(FALSE);
1769 
1770     ptiSendTo = IntSendTo(Window, Win32Thread, Msg);
1771 
1772     if (Msg & 0x80000000 &&
1773         !ptiSendTo)
1774     {
1775         if (Win32Thread->TIF_flags & TIF_INCLEANUP) RETURN(FALSE);
1776 
1777         TRACE("SMWCB: Internal Message\n");
1778         Result = (ULONG_PTR)handle_internal_message(Window, Msg, wParam, lParam);
1779         if (uResult) *uResult = Result;
1780         RETURN(TRUE);
1781     }
1782 
1783     /* See if this message type is present in the table */
1784     MsgMemoryEntry = FindMsgMemory(Msg);
1785     if (NULL == MsgMemoryEntry)
1786     {
1787         lParamBufferSize = -1;
1788     }
1789     else
1790     {
1791         lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1792         if (!lParamBufferSize) lParamBufferSize = -1;
1793     }
1794 
1795     if (!NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam, !!ptiSendTo)))
1796     {
1797         ERR("Failed to pack message parameters\n");
1798         RETURN(FALSE);
1799     }
1800 
1801     /* If it can be sent now, then send it. */
1802     if (!ptiSendTo)
1803     {
1804         if (Win32Thread->TIF_flags & TIF_INCLEANUP)
1805         {
1806             UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE);
1807             /* Never send messages to exiting threads */
1808             RETURN(FALSE);
1809         }
1810 
1811         IntCallWndProc(Window, hWnd, Msg, wParam, lParam);
1812 
1813         if (Window->state & WNDS_SERVERSIDEWINDOWPROC)
1814         {
1815            TRACE("SMWCB: Server Side Window Procedure\n");
1816            switch(Window->fnid)
1817            {
1818               case FNID_DESKTOP:
1819                 DoCallBack = !DesktopWindowProc(Window, Msg, wParam, lParamPacked, (LRESULT*)&Result);
1820                 break;
1821               case FNID_MESSAGEWND:
1822                 DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam, (LRESULT*)&Result);
1823                 break;
1824               case FNID_MENU:
1825                 DoCallBack = !PopupMenuWndProc(Window, Msg, wParam, lParam, (LRESULT*)&Result);
1826                 break;
1827            }
1828         }
1829 
1830         if (DoCallBack)
1831         Result = (ULONG_PTR)co_IntCallWindowProc(Window->lpfnWndProc,
1832                                                  !Window->Unicode,
1833                                                  hWnd,
1834                                                  Msg,
1835                                                  wParam,
1836                                                  lParamPacked,
1837                                                  lParamBufferSize);
1838         if(uResult)
1839         {
1840             *uResult = Result;
1841         }
1842 
1843         IntCallWndProcRet(Window, hWnd, Msg, wParam, lParam, (LRESULT *)uResult);
1844 
1845         if (CompletionCallback)
1846         {
1847             co_IntCallSentMessageCallback(CompletionCallback,
1848                                           hWnd,
1849                                           Msg,
1850                                           CompletionCallbackContext,
1851                                           Result);
1852         }
1853     }
1854 
1855     if (!ptiSendTo)
1856     {
1857         if (!NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam, FALSE)))
1858         {
1859             ERR("Failed to unpack message parameters\n");
1860         }
1861         RETURN(TRUE);
1862     }
1863 
1864     if(!(Message = AllocateUserMessage(FALSE)))
1865     {
1866         ERR("Failed to allocate message\n");
1867         RETURN(FALSE);
1868     }
1869 
1870     Message->Msg.hwnd = hWnd;
1871     Message->Msg.message = Msg;
1872     Message->Msg.wParam = wParam;
1873     Message->Msg.lParam = lParamPacked;
1874     Message->pkCompletionEvent = NULL; // No event needed.
1875     Message->lResult = 0;
1876     Message->QS_Flags = 0;
1877     Message->ptiReceiver = ptiSendTo;
1878     Message->ptiSender = NULL;
1879     Message->ptiCallBackSender = Win32Thread;
1880     Message->CompletionCallback = CompletionCallback;
1881     Message->CompletionCallbackContext = CompletionCallbackContext;
1882     Message->HookMessage = MSQ_NORMAL;
1883     Message->HasPackedLParam = (lParamBufferSize > 0);
1884     Message->QS_Flags = QS_SENDMESSAGE;
1885     Message->flags = SMF_RECEIVERFREE;
1886 
1887     if (Msg & 0x80000000) // Higher priority event message!
1888         InsertHeadList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry);
1889     else
1890         InsertTailList(&ptiSendTo->SentMessagesListHead, &Message->ListEntry);
1891     MsqWakeQueue(ptiSendTo, QS_SENDMESSAGE, TRUE);
1892 
1893     RETURN(TRUE);
1894 
1895 CLEANUP:
1896     if (Window) UserDerefObjectCo(Window);
1897     END_CLEANUP;
1898 }
1899 
1900 #if 0
1901 /*
1902   This HACK function posts a message if the destination's message queue belongs to
1903   another thread, otherwise it sends the message. It does not support broadcast
1904   messages!
1905 */
1906 LRESULT FASTCALL
1907 co_IntPostOrSendMessage( HWND hWnd,
1908                          UINT Msg,
1909                          WPARAM wParam,
1910                          LPARAM lParam )
1911 {
1912     ULONG_PTR Result;
1913     PTHREADINFO pti;
1914     PWND Window;
1915 
1916     if ( hWnd == HWND_BROADCAST )
1917     {
1918         return 0;
1919     }
1920 
1921     if(!(Window = UserGetWindowObject(hWnd)))
1922     {
1923         TRACE("PostOrSendMessage: Invalid handle 0x%p!\n",hWnd);
1924         return 0;
1925     }
1926 
1927     pti = PsGetCurrentThreadWin32Thread();
1928 
1929     if ( IntSendTo(Window, pti, Msg) &&
1930          FindMsgMemory(Msg) == 0 )
1931     {
1932         Result = UserPostMessage(hWnd, Msg, wParam, lParam);
1933     }
1934     else
1935     {
1936         if ( !co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result) )
1937         {
1938             Result = 0;
1939         }
1940     }
1941 
1942     return (LRESULT)Result;
1943 }
1944 #endif
1945 
1946 static LRESULT FASTCALL
1947 co_IntDoSendMessage( HWND hWnd,
1948                      UINT Msg,
1949                      WPARAM wParam,
1950                      LPARAM lParam,
1951                      PDOSENDMESSAGE dsm)
1952 {
1953     LRESULT Result = TRUE;
1954     NTSTATUS Status;
1955     PWND Window = NULL;
1956     MSG UserModeMsg, KernelModeMsg;
1957     PMSGMEMORY MsgMemoryEntry;
1958     PTHREADINFO ptiSendTo;
1959 
1960     if (hWnd != HWND_BROADCAST && hWnd != HWND_TOPMOST)
1961     {
1962         Window = UserGetWindowObject(hWnd);
1963         if ( !Window )
1964         {
1965             return 0;
1966         }
1967     }
1968 
1969     /* Check for an exiting window. */
1970     if (Window && Window->state & WNDS_DESTROYED)
1971     {
1972         ERR("co_IntDoSendMessage Window Exiting!\n");
1973     }
1974 
1975     /* See if the current thread can handle this message */
1976     ptiSendTo = IntSendTo(Window, gptiCurrent, Msg);
1977 
1978     // If broadcasting or sending to another thread, save the users data.
1979     if (!Window || ptiSendTo )
1980     {
1981        UserModeMsg.hwnd    = hWnd;
1982        UserModeMsg.message = Msg;
1983        UserModeMsg.wParam  = wParam;
1984        UserModeMsg.lParam  = lParam;
1985        MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1986        Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1987        if (!NT_SUCCESS(Status))
1988        {
1989           EngSetLastError(ERROR_INVALID_PARAMETER);
1990           return (dsm ? 0 : -1);
1991        }
1992     }
1993     else
1994     {
1995        KernelModeMsg.hwnd    = hWnd;
1996        KernelModeMsg.message = Msg;
1997        KernelModeMsg.wParam  = wParam;
1998        KernelModeMsg.lParam  = lParam;
1999     }
2000 
2001     if (!dsm)
2002     {
2003        Result = co_IntSendMessage( KernelModeMsg.hwnd,
2004                                    KernelModeMsg.message,
2005                                    KernelModeMsg.wParam,
2006                                    KernelModeMsg.lParam );
2007     }
2008     else
2009     {
2010        Result = co_IntSendMessageTimeout( KernelModeMsg.hwnd,
2011                                           KernelModeMsg.message,
2012                                           KernelModeMsg.wParam,
2013                                           KernelModeMsg.lParam,
2014                                           dsm->uFlags,
2015                                           dsm->uTimeout,
2016                                          &dsm->Result );
2017     }
2018 
2019     if (!Window || ptiSendTo )
2020     {
2021        Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
2022        if (!NT_SUCCESS(Status))
2023        {
2024           EngSetLastError(ERROR_INVALID_PARAMETER);
2025           return(dsm ? 0 : -1);
2026        }
2027     }
2028 
2029     return (LRESULT)Result;
2030 }
2031 
2032 BOOL FASTCALL
2033 UserSendNotifyMessage( HWND hWnd,
2034                        UINT Msg,
2035                        WPARAM wParam,
2036                        LPARAM lParam )
2037 {
2038     BOOL Ret = TRUE;
2039 
2040     if (is_pointer_message(Msg, wParam))
2041     {
2042         EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
2043         return FALSE;
2044     }
2045 
2046     // Basicly the same as IntPostOrSendMessage
2047     if (hWnd == HWND_BROADCAST) // Handle Broadcast
2048     {
2049         HWND *List;
2050         PWND DesktopWindow;
2051         ULONG i;
2052 
2053         DesktopWindow = UserGetDesktopWindow();
2054         List = IntWinListChildren(DesktopWindow);
2055 
2056         if (List != NULL)
2057         {
2058             UserSendNotifyMessage(DesktopWindow->head.h, Msg, wParam, lParam);
2059             for (i = 0; List[i]; i++)
2060             {
2061                PWND pwnd = UserGetWindowObject(List[i]);
2062                if (!pwnd) continue;
2063 
2064                if ( pwnd->fnid == FNID_MENU ||
2065                     pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2066                   continue;
2067 
2068                Ret = UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2069             }
2070             ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2071         }
2072     }
2073     else
2074     {
2075         Ret = co_IntSendMessageNoWait( hWnd, Msg, wParam, lParam);
2076     }
2077     return Ret;
2078 }
2079 
2080 
2081 DWORD APIENTRY
2082 IntGetQueueStatus(DWORD Changes)
2083 {
2084     PTHREADINFO pti;
2085     DWORD Result;
2086 
2087     pti = PsGetCurrentThreadWin32Thread();
2088 // wine:
2089     Changes &= (QS_ALLINPUT|QS_ALLPOSTMESSAGE|QS_SMRESULT);
2090 
2091     /* High word, types of messages currently in the queue.
2092        Low  word, types of messages that have been added to the queue and that
2093                   are still in the queue
2094      */
2095     Result = MAKELONG(pti->pcti->fsChangeBits & Changes, pti->pcti->fsWakeBits & Changes);
2096 
2097     pti->pcti->fsChangeBits &= ~Changes;
2098 
2099     return Result;
2100 }
2101 
2102 BOOL APIENTRY
2103 IntInitMessagePumpHook(VOID)
2104 {
2105     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2106 
2107     if (pti->pcti)
2108     {
2109         pti->pcti->dwcPumpHook++;
2110         return TRUE;
2111     }
2112     return FALSE;
2113 }
2114 
2115 BOOL APIENTRY
2116 IntUninitMessagePumpHook(VOID)
2117 {
2118     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2119 
2120     if (pti->pcti)
2121     {
2122         if (pti->pcti->dwcPumpHook <= 0)
2123         {
2124             return FALSE;
2125         }
2126         pti->pcti->dwcPumpHook--;
2127         return TRUE;
2128     }
2129     return FALSE;
2130 }
2131 
2132 BOOL FASTCALL
2133 IntCallMsgFilter( LPMSG lpmsg, INT code)
2134 {
2135     BOOL Ret = FALSE;
2136 
2137     if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)lpmsg))
2138     {
2139         Ret = TRUE;
2140     }
2141     else
2142     {
2143         Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)lpmsg);
2144     }
2145     return Ret;
2146 }
2147 
2148 /** Functions ******************************************************************/
2149 
2150 BOOL
2151 APIENTRY
2152 NtUserDragDetect(
2153    HWND hWnd,
2154    POINT pt) // Just like the User call.
2155 {
2156     MSG msg;
2157     RECT rect;
2158     ULONG wDragWidth, wDragHeight;
2159     DECLARE_RETURN(BOOL);
2160 
2161     TRACE("Enter NtUserDragDetect(%p)\n", hWnd);
2162     UserEnterExclusive();
2163 
2164     wDragWidth = UserGetSystemMetrics(SM_CXDRAG);
2165     wDragHeight= UserGetSystemMetrics(SM_CYDRAG);
2166 
2167     rect.left = pt.x - wDragWidth;
2168     rect.right = pt.x + wDragWidth;
2169 
2170     rect.top = pt.y - wDragHeight;
2171     rect.bottom = pt.y + wDragHeight;
2172 
2173     co_UserSetCapture(hWnd);
2174 
2175     for (;;)
2176     {
2177         while (co_IntGetPeekMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, FALSE ) ||
2178                co_IntGetPeekMessage( &msg, 0, WM_QUEUESYNC,  WM_QUEUESYNC, PM_REMOVE, FALSE ) ||
2179                co_IntGetPeekMessage( &msg, 0, WM_KEYFIRST,   WM_KEYLAST,   PM_REMOVE, FALSE ) )
2180         {
2181             if ( msg.message == WM_LBUTTONUP )
2182             {
2183                 co_UserSetCapture(NULL);
2184                 RETURN( FALSE);
2185             }
2186             if ( msg.message == WM_MOUSEMOVE )
2187             {
2188                 POINT tmp;
2189                 tmp.x = (short)LOWORD(msg.lParam);
2190                 tmp.y = (short)HIWORD(msg.lParam);
2191                 if( !RECTL_bPointInRect( &rect, tmp.x, tmp.y ) )
2192                 {
2193                     co_UserSetCapture(NULL);
2194                     RETURN( TRUE);
2195                 }
2196             }
2197             if ( msg.message == WM_KEYDOWN )
2198             {
2199                 if ( msg.wParam == VK_ESCAPE )
2200                 {
2201                    co_UserSetCapture(NULL);
2202                    RETURN( TRUE);
2203                 }
2204             }
2205             if ( msg.message == WM_QUEUESYNC )
2206             {
2207                 co_HOOK_CallHooks( WH_CBT, HCBT_QS, 0, 0 );
2208             }
2209         }
2210         co_IntWaitMessage(NULL, 0, 0);
2211     }
2212     RETURN( FALSE);
2213 
2214 CLEANUP:
2215    TRACE("Leave NtUserDragDetect, ret=%i\n",_ret_);
2216    UserLeave();
2217    END_CLEANUP;
2218 }
2219 
2220 BOOL APIENTRY
2221 NtUserPostMessage(HWND hWnd,
2222                   UINT Msg,
2223                   WPARAM wParam,
2224                   LPARAM lParam)
2225 {
2226     BOOL ret;
2227 
2228     UserEnterExclusive();
2229 
2230     ret = UserPostMessage(hWnd, Msg, wParam, lParam);
2231 
2232     UserLeave();
2233 
2234     return ret;
2235 }
2236 
2237 BOOL APIENTRY
2238 NtUserPostThreadMessage(DWORD idThread,
2239                         UINT Msg,
2240                         WPARAM wParam,
2241                         LPARAM lParam)
2242 {
2243     BOOL ret = FALSE;
2244     PETHREAD peThread;
2245     PTHREADINFO pThread;
2246     NTSTATUS Status;
2247 
2248     UserEnterExclusive();
2249 
2250     Status = PsLookupThreadByThreadId(UlongToHandle(idThread), &peThread);
2251 
2252     if ( Status == STATUS_SUCCESS )
2253     {
2254         pThread = (PTHREADINFO)peThread->Tcb.Win32Thread;
2255         if( !pThread ||
2256             !pThread->MessageQueue ||
2257             (pThread->TIF_flags & TIF_INCLEANUP))
2258         {
2259             ObDereferenceObject( peThread );
2260             goto exit;
2261         }
2262         ret = UserPostThreadMessage( pThread, Msg, wParam, lParam);
2263         ObDereferenceObject( peThread );
2264     }
2265     else
2266     {
2267         SetLastNtError( Status );
2268     }
2269 exit:
2270     UserLeave();
2271     return ret;
2272 }
2273 
2274 BOOL APIENTRY
2275 NtUserWaitMessage(VOID)
2276 {
2277     BOOL ret;
2278 
2279     UserEnterExclusive();
2280     TRACE("NtUserWaitMessage Enter\n");
2281     ret = co_IntWaitMessage(NULL, 0, 0);
2282     TRACE("NtUserWaitMessage Leave\n");
2283     UserLeave();
2284 
2285     return ret;
2286 }
2287 
2288 BOOL APIENTRY
2289 NtUserGetMessage(PMSG pMsg,
2290                   HWND hWnd,
2291                   UINT MsgFilterMin,
2292                   UINT MsgFilterMax )
2293 {
2294     MSG Msg;
2295     BOOL Ret;
2296 
2297     if ( (MsgFilterMin|MsgFilterMax) & ~WM_MAXIMUM )
2298     {
2299         EngSetLastError(ERROR_INVALID_PARAMETER);
2300         return FALSE;
2301     }
2302 
2303     UserEnterExclusive();
2304 
2305     RtlZeroMemory(&Msg, sizeof(MSG));
2306 
2307     Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE, TRUE);
2308 
2309     UserLeave();
2310 
2311     if (Ret)
2312     {
2313         _SEH2_TRY
2314         {
2315             ProbeForWrite(pMsg, sizeof(MSG), 1);
2316             RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
2317         }
2318         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2319         {
2320             SetLastNtError(_SEH2_GetExceptionCode());
2321             Ret = FALSE;
2322         }
2323         _SEH2_END;
2324     }
2325 
2326     if ((INT)Ret != -1)
2327        Ret = Ret ? (WM_QUIT != pMsg->message) : FALSE;
2328 
2329     return Ret;
2330 }
2331 
2332 BOOL APIENTRY
2333 NtUserPeekMessage( PMSG pMsg,
2334                   HWND hWnd,
2335                   UINT MsgFilterMin,
2336                   UINT MsgFilterMax,
2337                   UINT RemoveMsg)
2338 {
2339     MSG Msg;
2340     BOOL Ret;
2341 
2342     if ( RemoveMsg & PM_BADMSGFLAGS )
2343     {
2344         EngSetLastError(ERROR_INVALID_FLAGS);
2345         return FALSE;
2346     }
2347 
2348     UserEnterExclusive();
2349 
2350     RtlZeroMemory(&Msg, sizeof(MSG));
2351 
2352     Ret = co_IntGetPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg, FALSE);
2353 
2354     UserLeave();
2355 
2356     if (Ret)
2357     {
2358         _SEH2_TRY
2359         {
2360             ProbeForWrite(pMsg, sizeof(MSG), 1);
2361             RtlCopyMemory(pMsg, &Msg, sizeof(MSG));
2362         }
2363         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2364         {
2365             SetLastNtError(_SEH2_GetExceptionCode());
2366             Ret = FALSE;
2367         }
2368         _SEH2_END;
2369     }
2370 
2371     return Ret;
2372 }
2373 
2374 BOOL APIENTRY
2375 NtUserCallMsgFilter( LPMSG lpmsg, INT code)
2376 {
2377     BOOL Ret = FALSE;
2378     MSG Msg;
2379 
2380     _SEH2_TRY
2381     {
2382         ProbeForRead(lpmsg, sizeof(MSG), 1);
2383         RtlCopyMemory( &Msg, lpmsg, sizeof(MSG));
2384     }
2385     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2386     {
2387         _SEH2_YIELD(return FALSE);
2388     }
2389     _SEH2_END;
2390 
2391     UserEnterExclusive();
2392 
2393     if ( co_HOOK_CallHooks( WH_SYSMSGFILTER, code, 0, (LPARAM)&Msg))
2394     {
2395         Ret = TRUE;
2396     }
2397     else
2398     {
2399         Ret = co_HOOK_CallHooks( WH_MSGFILTER, code, 0, (LPARAM)&Msg);
2400     }
2401 
2402     UserLeave();
2403 
2404     _SEH2_TRY
2405     {
2406         ProbeForWrite(lpmsg, sizeof(MSG), 1);
2407         RtlCopyMemory(lpmsg, &Msg, sizeof(MSG));
2408     }
2409     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2410     {
2411         Ret = FALSE;
2412     }
2413     _SEH2_END;
2414 
2415     return Ret;
2416 }
2417 
2418 LRESULT APIENTRY
2419 NtUserDispatchMessage(PMSG UnsafeMsgInfo)
2420 {
2421     LRESULT Res = 0;
2422     MSG SafeMsg;
2423 
2424     _SEH2_TRY
2425     {
2426         ProbeForRead(UnsafeMsgInfo, sizeof(MSG), 1);
2427         RtlCopyMemory(&SafeMsg, UnsafeMsgInfo, sizeof(MSG));
2428     }
2429     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2430     {
2431         SetLastNtError(_SEH2_GetExceptionCode());
2432         _SEH2_YIELD(return FALSE);
2433     }
2434     _SEH2_END;
2435 
2436     UserEnterExclusive();
2437 
2438     Res = IntDispatchMessage(&SafeMsg);
2439 
2440     UserLeave();
2441     return Res;
2442 }
2443 
2444 BOOL APIENTRY
2445 NtUserTranslateMessage(LPMSG lpMsg, UINT flags)
2446 {
2447     MSG SafeMsg;
2448     BOOL Ret;
2449     PWND pWnd;
2450 
2451     _SEH2_TRY
2452     {
2453         ProbeForRead(lpMsg, sizeof(MSG), 1);
2454         RtlCopyMemory(&SafeMsg, lpMsg, sizeof(MSG));
2455     }
2456     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2457     {
2458         SetLastNtError(_SEH2_GetExceptionCode());
2459         _SEH2_YIELD(return FALSE);
2460     }
2461     _SEH2_END;
2462 
2463     UserEnterExclusive();
2464     pWnd = UserGetWindowObject(SafeMsg.hwnd);
2465     if (pWnd) // Must have a window!
2466     {
2467        Ret = IntTranslateKbdMessage(&SafeMsg, flags);
2468     }
2469     else
2470     {
2471         TRACE("No Window for Translate. hwnd 0x%p Msg %u\n", SafeMsg.hwnd, SafeMsg.message);
2472         Ret = FALSE;
2473     }
2474     UserLeave();
2475 
2476     return Ret;
2477 }
2478 
2479 LRESULT APIENTRY ScrollBarWndProc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam);
2480 
2481 BOOL APIENTRY
2482 NtUserMessageCall( HWND hWnd,
2483                    UINT Msg,
2484                    WPARAM wParam,
2485                    LPARAM lParam,
2486                    ULONG_PTR ResultInfo,
2487                    DWORD dwType, // fnID?
2488                    BOOL Ansi)
2489 {
2490     LRESULT lResult = 0;
2491     BOOL Ret = FALSE;
2492     PWND Window = NULL;
2493     USER_REFERENCE_ENTRY Ref;
2494 
2495     UserEnterExclusive();
2496 
2497     switch(dwType)
2498     {
2499     case FNID_SCROLLBAR:
2500         {
2501            lResult = ScrollBarWndProc(hWnd, Msg, wParam, lParam);
2502            break;
2503         }
2504     case FNID_DESKTOP:
2505         {
2506            Window = UserGetWindowObject(hWnd);
2507            if (Window)
2508            {
2509               //ERR("FNID_DESKTOP IN\n");
2510               Ret = DesktopWindowProc(Window, Msg, wParam, lParam, &lResult);
2511               //ERR("FNID_DESKTOP OUT\n");
2512            }
2513            break;
2514         }
2515    case FNID_MENU:
2516        {
2517           Window = UserGetWindowObject(hWnd);
2518           if (Window)
2519           {
2520               Ret = PopupMenuWndProc( Window, Msg, wParam, lParam, &lResult);
2521           }
2522           break;
2523        }
2524    case FNID_MESSAGEWND:
2525        {
2526            Window = UserGetWindowObject(hWnd);
2527            if (Window)
2528            {
2529                 Ret = !UserMessageWindowProc(Window, Msg, wParam, lParam, &lResult);
2530            }
2531            break;
2532        }
2533     case FNID_DEFWINDOWPROC:
2534         /* Validate input */
2535         if (hWnd)
2536         {
2537            Window = UserGetWindowObject(hWnd);
2538            if (!Window)
2539            {
2540                UserLeave();
2541                return FALSE;
2542            }
2543            UserRefObjectCo(Window, &Ref);
2544         }
2545         lResult = IntDefWindowProc(Window, Msg, wParam, lParam, Ansi);
2546         Ret = TRUE;
2547         if (hWnd)
2548             UserDerefObjectCo(Window);
2549         break;
2550     case FNID_SENDNOTIFYMESSAGE:
2551         Ret = UserSendNotifyMessage(hWnd, Msg, wParam, lParam);
2552         break;
2553     case FNID_BROADCASTSYSTEMMESSAGE:
2554         {
2555             BROADCASTPARM parm, *retparam;
2556             DWORD_PTR RetVal = 0;
2557 
2558             if (ResultInfo)
2559             {
2560                 _SEH2_TRY
2561                 {
2562                     ProbeForWrite((PVOID)ResultInfo, sizeof(BROADCASTPARM), 1);
2563                     RtlCopyMemory(&parm, (PVOID)ResultInfo, sizeof(BROADCASTPARM));
2564                 }
2565                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2566                 {
2567                     _SEH2_YIELD(break);
2568                 }
2569                 _SEH2_END;
2570             }
2571             else
2572                 break;
2573 
2574             if ( parm.recipients & BSM_ALLDESKTOPS ||
2575                  parm.recipients == BSM_ALLCOMPONENTS )
2576             {
2577                PLIST_ENTRY DesktopEntry;
2578                PDESKTOP rpdesk;
2579                HWND *List, hwndDenied = NULL;
2580                HDESK hDesk = NULL;
2581                PWND pwnd, pwndDesk;
2582                ULONG i;
2583                UINT fuFlags;
2584 
2585                for (DesktopEntry = InputWindowStation->DesktopListHead.Flink;
2586                     DesktopEntry != &InputWindowStation->DesktopListHead;
2587                     DesktopEntry = DesktopEntry->Flink)
2588                {
2589                   rpdesk = CONTAINING_RECORD(DesktopEntry, DESKTOP, ListEntry);
2590                   pwndDesk = rpdesk->pDeskInfo->spwnd;
2591                   List = IntWinListChildren(pwndDesk);
2592 
2593                   if (parm.flags & BSF_QUERY)
2594                   {
2595                      if (List != NULL)
2596                      {
2597                         if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG)
2598                         {
2599                            fuFlags = SMTO_ABORTIFHUNG;
2600                         }
2601                         else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG)
2602                         {
2603                            fuFlags = SMTO_NOTIMEOUTIFNOTHUNG;
2604                         }
2605                         else
2606                         {
2607                            fuFlags = SMTO_NORMAL;
2608                         }
2609                         co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk),
2610                                                   Msg,
2611                                                   wParam,
2612                                                   lParam,
2613                                                   fuFlags,
2614                                                   2000,
2615                                                  &RetVal);
2616                         Ret = TRUE;
2617                         for (i = 0; List[i]; i++)
2618                         {
2619                            pwnd = UserGetWindowObject(List[i]);
2620                            if (!pwnd) continue;
2621 
2622                            if ( pwnd->fnid == FNID_MENU ||
2623                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2624                               continue;
2625 
2626                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2627                            {
2628                               if ( pwnd->head.pti == gptiCurrent )
2629                                  continue;
2630                            }
2631                            co_IntSendMessageTimeout( List[i],
2632                                                      Msg,
2633                                                      wParam,
2634                                                      lParam,
2635                                                      fuFlags,
2636                                                      2000,
2637                                                     &RetVal);
2638 
2639                            if (!RetVal && EngGetLastError() == ERROR_TIMEOUT)
2640                            {
2641                               if (!(parm.flags & BSF_FORCEIFHUNG))
2642                                  Ret = FALSE;
2643                            }
2644                            if (RetVal == BROADCAST_QUERY_DENY)
2645                            {
2646                               hwndDenied = List[i];
2647                               hDesk = UserHMGetHandle(pwndDesk);
2648                               Ret = FALSE;
2649                            }
2650                         }
2651                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2652                         _SEH2_TRY
2653                         {
2654                            retparam = (PBROADCASTPARM) ResultInfo;
2655                            retparam->hDesk = hDesk;
2656                            retparam->hWnd = hwndDenied;
2657                         }
2658                         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2659                         {
2660                            _SEH2_YIELD(break);
2661                         }
2662                         _SEH2_END;
2663                         if (!Ret) break; // Have a hit! Let everyone know!
2664                      }
2665                   }
2666                   else if (parm.flags & BSF_POSTMESSAGE)
2667                   {
2668                      if (List != NULL)
2669                      {
2670                         UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2671 
2672                         for (i = 0; List[i]; i++)
2673                         {
2674                            pwnd = UserGetWindowObject(List[i]);
2675                            if (!pwnd) continue;
2676 
2677                            if ( pwnd->fnid == FNID_MENU ||
2678                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2679                               continue;
2680 
2681                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2682                            {
2683                               if ( pwnd->head.pti == gptiCurrent )
2684                                  continue;
2685                            }
2686                            UserPostMessage(List[i], Msg, wParam, lParam);
2687                         }
2688                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2689                      }
2690                      Ret = TRUE;
2691                   }
2692                   else
2693                   {
2694                      if (List != NULL)
2695                      {
2696                         UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2697 
2698                         for (i = 0; List[i]; i++)
2699                         {
2700                            pwnd = UserGetWindowObject(List[i]);
2701                            if (!pwnd) continue;
2702 
2703                            if ( pwnd->fnid == FNID_MENU ||
2704                                 pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2705                               continue;
2706 
2707                            if ( parm.flags & BSF_IGNORECURRENTTASK )
2708                            {
2709                               if ( pwnd->head.pti == gptiCurrent )
2710                                  continue;
2711                            }
2712                            UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2713                         }
2714                         ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2715                      }
2716                      Ret = TRUE;
2717                   }
2718                }
2719             }
2720             else if (parm.recipients & BSM_APPLICATIONS)
2721             {
2722                HWND *List, hwndDenied = NULL;
2723                HDESK hDesk = NULL;
2724                PWND pwnd, pwndDesk;
2725                ULONG i;
2726                UINT fuFlags;
2727 
2728                pwndDesk = UserGetDesktopWindow();
2729                List = IntWinListChildren(pwndDesk);
2730 
2731                if (parm.flags & BSF_QUERY)
2732                {
2733                   if (List != NULL)
2734                   {
2735                      if (parm.flags & BSF_FORCEIFHUNG || parm.flags & BSF_NOHANG)
2736                      {
2737                         fuFlags = SMTO_ABORTIFHUNG;
2738                      }
2739                      else if (parm.flags & BSF_NOTIMEOUTIFNOTHUNG)
2740                      {
2741                         fuFlags = SMTO_NOTIMEOUTIFNOTHUNG;
2742                      }
2743                      else
2744                      {
2745                         fuFlags = SMTO_NORMAL;
2746                      }
2747                      co_IntSendMessageTimeout( UserHMGetHandle(pwndDesk),
2748                                                Msg,
2749                                                wParam,
2750                                                lParam,
2751                                                fuFlags,
2752                                                2000,
2753                                               &RetVal);
2754                      Ret = TRUE;
2755                      for (i = 0; List[i]; i++)
2756                      {
2757                         pwnd = UserGetWindowObject(List[i]);
2758                         if (!pwnd) continue;
2759 
2760                         if ( pwnd->fnid == FNID_MENU ||
2761                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2762                            continue;
2763 
2764                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2765                         {
2766                            if ( pwnd->head.pti == gptiCurrent )
2767                               continue;
2768                         }
2769                         co_IntSendMessageTimeout( List[i],
2770                                                   Msg,
2771                                                   wParam,
2772                                                   lParam,
2773                                                   fuFlags,
2774                                                   2000,
2775                                                  &RetVal);
2776 
2777                         if (!RetVal && EngGetLastError() == ERROR_TIMEOUT)
2778                         {
2779                            if (!(parm.flags & BSF_FORCEIFHUNG))
2780                               Ret = FALSE;
2781                         }
2782                         if (RetVal == BROADCAST_QUERY_DENY)
2783                         {
2784                            hwndDenied = List[i];
2785                            hDesk = UserHMGetHandle(pwndDesk);
2786                            Ret = FALSE;
2787                         }
2788                      }
2789                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2790                      _SEH2_TRY
2791                      {
2792                         retparam = (PBROADCASTPARM) ResultInfo;
2793                         retparam->hDesk = hDesk;
2794                         retparam->hWnd = hwndDenied;
2795                      }
2796                      _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2797                      {
2798                         _SEH2_YIELD(break);
2799                      }
2800                      _SEH2_END;
2801                   }
2802                }
2803                else if (parm.flags & BSF_POSTMESSAGE)
2804                {
2805                   if (List != NULL)
2806                   {
2807                      UserPostMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2808 
2809                      for (i = 0; List[i]; i++)
2810                      {
2811                         pwnd = UserGetWindowObject(List[i]);
2812                         if (!pwnd) continue;
2813 
2814                         if ( pwnd->fnid == FNID_MENU ||
2815                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2816                            continue;
2817 
2818                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2819                         {
2820                            if ( pwnd->head.pti == gptiCurrent )
2821                               continue;
2822                         }
2823                         UserPostMessage(List[i], Msg, wParam, lParam);
2824                      }
2825                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2826                   }
2827                   Ret = TRUE;
2828                }
2829                else
2830                {
2831                   if (List != NULL)
2832                   {
2833                      UserSendNotifyMessage(UserHMGetHandle(pwndDesk), Msg, wParam, lParam);
2834 
2835                      for (i = 0; List[i]; i++)
2836                      {
2837                         pwnd = UserGetWindowObject(List[i]);
2838                         if (!pwnd) continue;
2839 
2840                         if ( pwnd->fnid == FNID_MENU ||
2841                              pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_SWITCH] )
2842                            continue;
2843 
2844                         if ( parm.flags & BSF_IGNORECURRENTTASK )
2845                         {
2846                            if ( pwnd->head.pti == gptiCurrent )
2847                               continue;
2848                         }
2849                         UserSendNotifyMessage(List[i], Msg, wParam, lParam);
2850                      }
2851                      ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
2852                   }
2853                   Ret = TRUE;
2854                }
2855             }
2856         }
2857         break;
2858     case FNID_SENDMESSAGECALLBACK:
2859         {
2860             CALL_BACK_INFO CallBackInfo;
2861             ULONG_PTR uResult;
2862 
2863             _SEH2_TRY
2864             {
2865                 ProbeForRead((PVOID)ResultInfo, sizeof(CALL_BACK_INFO), 1);
2866                 RtlCopyMemory(&CallBackInfo, (PVOID)ResultInfo, sizeof(CALL_BACK_INFO));
2867             }
2868             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2869             {
2870                 _SEH2_YIELD(break);
2871             }
2872             _SEH2_END;
2873 
2874             if (is_pointer_message(Msg, wParam))
2875             {
2876                EngSetLastError(ERROR_MESSAGE_SYNC_ONLY );
2877                break;
2878             }
2879 
2880             if (!(Ret = co_IntSendMessageWithCallBack(hWnd, Msg, wParam, lParam,
2881                         CallBackInfo.CallBack, CallBackInfo.Context, &uResult)))
2882             {
2883                 ERR("Callback failure!\n");
2884             }
2885         }
2886         break;
2887     case FNID_SENDMESSAGE:
2888         {
2889             lResult = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, 0);
2890             Ret = TRUE;
2891 
2892             if (ResultInfo)
2893             {
2894                 _SEH2_TRY
2895                 {
2896                     ProbeForWrite((PVOID)ResultInfo, sizeof(ULONG_PTR), 1);
2897                     RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(ULONG_PTR));
2898                 }
2899                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2900                 {
2901                     Ret = FALSE;
2902                     _SEH2_YIELD(break);
2903                 }
2904                 _SEH2_END;
2905             }
2906             break;
2907         }
2908     case FNID_SENDMESSAGEFF:
2909     case FNID_SENDMESSAGEWTOOPTION:
2910         {
2911             DOSENDMESSAGE dsm, *pdsm = (PDOSENDMESSAGE)ResultInfo;
2912             if (ResultInfo)
2913             {
2914                 _SEH2_TRY
2915                 {
2916                     ProbeForRead(pdsm, sizeof(DOSENDMESSAGE), 1);
2917                     RtlCopyMemory(&dsm, pdsm, sizeof(DOSENDMESSAGE));
2918                 }
2919                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2920                 {
2921                     _SEH2_YIELD(break);
2922                 }
2923                 _SEH2_END;
2924             }
2925 
2926             Ret = co_IntDoSendMessage( hWnd, Msg, wParam, lParam, pdsm ? &dsm : NULL );
2927 
2928             if (pdsm)
2929             {
2930                 _SEH2_TRY
2931                 {
2932                     ProbeForWrite(pdsm, sizeof(DOSENDMESSAGE), 1);
2933                     RtlCopyMemory(pdsm, &dsm, sizeof(DOSENDMESSAGE));
2934                 }
2935                 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2936                 {
2937                     Ret = FALSE;
2938                     _SEH2_YIELD(break);
2939                 }
2940                 _SEH2_END;
2941             }
2942             break;
2943         }
2944         // CallNextHook bypass.
2945     case FNID_CALLWNDPROC:
2946     case FNID_CALLWNDPROCRET:
2947         {
2948             PTHREADINFO pti;
2949             PCLIENTINFO ClientInfo;
2950             PHOOK NextObj, Hook;
2951 
2952             pti = GetW32ThreadInfo();
2953 
2954             Hook = pti->sphkCurrent;
2955 
2956             if (!Hook) break;
2957 
2958             NextObj = Hook->phkNext;
2959             ClientInfo = pti->pClientInfo;
2960             _SEH2_TRY
2961             {
2962                 ClientInfo->phkCurrent = NextObj;
2963             }
2964             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2965             {
2966                 ClientInfo = NULL;
2967             }
2968             _SEH2_END;
2969 
2970             if (!ClientInfo || !NextObj) break;
2971 
2972             NextObj->phkNext = IntGetNextHook(NextObj);
2973 
2974             if ( Hook->HookId == WH_CALLWNDPROC)
2975             {
2976                 CWPSTRUCT CWP;
2977                 CWP.hwnd    = hWnd;
2978                 CWP.message = Msg;
2979                 CWP.wParam  = wParam;
2980                 CWP.lParam  = lParam;
2981                 TRACE("WH_CALLWNDPROC: Hook %p NextHook %p\n", Hook, NextObj);
2982 
2983                 lResult = co_IntCallHookProc( Hook->HookId,
2984                                               HC_ACTION,
2985                                               ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
2986                                               (LPARAM)&CWP,
2987                                               Hook->Proc,
2988                                               Hook->ihmod,
2989                                               Hook->offPfn,
2990                                               Hook->Ansi,
2991                                               &Hook->ModuleName);
2992             }
2993             else
2994             {
2995                 CWPRETSTRUCT CWPR;
2996                 CWPR.hwnd    = hWnd;
2997                 CWPR.message = Msg;
2998                 CWPR.wParam  = wParam;
2999                 CWPR.lParam  = lParam;
3000                 CWPR.lResult = ClientInfo->dwHookData;
3001 
3002                 lResult = co_IntCallHookProc( Hook->HookId,
3003                                               HC_ACTION,
3004                                               ((ClientInfo->CI_flags & CI_CURTHPRHOOK) ? 1 : 0),
3005                                               (LPARAM)&CWPR,
3006                                               Hook->Proc,
3007                                               Hook->ihmod,
3008                                               Hook->offPfn,
3009                                               Hook->Ansi,
3010                                               &Hook->ModuleName);
3011             }
3012         }
3013         break;
3014     }
3015 
3016     switch(dwType)
3017     {
3018     case FNID_DEFWINDOWPROC:
3019     case FNID_CALLWNDPROC:
3020     case FNID_CALLWNDPROCRET:
3021     case FNID_SCROLLBAR:
3022     case FNID_DESKTOP:
3023     case FNID_MENU:
3024         if (ResultInfo)
3025         {
3026             _SEH2_TRY
3027             {
3028                 ProbeForWrite((PVOID)ResultInfo, sizeof(LRESULT), 1);
3029                 RtlCopyMemory((PVOID)ResultInfo, &lResult, sizeof(LRESULT));
3030             }
3031             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
3032             {
3033                 Ret = FALSE;
3034             }
3035             _SEH2_END;
3036         }
3037         break;
3038     default:
3039         break;
3040     }
3041 
3042     UserLeave();
3043 
3044     return Ret;
3045 }
3046 
3047 #define INFINITE 0xFFFFFFFF
3048 #define WAIT_FAILED ((DWORD)0xFFFFFFFF)
3049 
3050 DWORD
3051 APIENTRY
3052 NtUserWaitForInputIdle( IN HANDLE hProcess,
3053                         IN DWORD dwMilliseconds,
3054                         IN BOOL bSharedWow)
3055 {
3056     PEPROCESS Process;
3057     PPROCESSINFO W32Process;
3058     PTHREADINFO pti;
3059     NTSTATUS Status;
3060     HANDLE Handles[3];
3061     LARGE_INTEGER Timeout;
3062     KAPC_STATE ApcState;
3063 
3064     UserEnterExclusive();
3065 
3066     Status = ObReferenceObjectByHandle(hProcess,
3067                                        PROCESS_QUERY_INFORMATION,
3068                                        *PsProcessType,
3069                                        UserMode,
3070                                        (PVOID*)&Process,
3071                                        NULL);
3072 
3073     if (!NT_SUCCESS(Status))
3074     {
3075         UserLeave();
3076         SetLastNtError(Status);
3077         return WAIT_FAILED;
3078     }
3079 
3080     pti = PsGetCurrentThreadWin32Thread();
3081 
3082     W32Process = (PPROCESSINFO)Process->Win32Process;
3083 
3084     if ( PsGetProcessExitProcessCalled(Process) ||
3085          !W32Process ||
3086          pti->ppi == W32Process)
3087     {
3088         ObDereferenceObject(Process);
3089         UserLeave();
3090         EngSetLastError(ERROR_INVALID_PARAMETER);
3091         return WAIT_FAILED;
3092     }
3093 
3094     Handles[0] = Process;
3095     Handles[1] = W32Process->InputIdleEvent;
3096     Handles[2] = pti->pEventQueueServer; // IntMsqSetWakeMask returns hEventQueueClient
3097 
3098     if (!Handles[1])
3099     {
3100         ObDereferenceObject(Process);
3101         UserLeave();
3102         return STATUS_SUCCESS;  /* no event to wait on */
3103     }
3104 
3105     if (dwMilliseconds != INFINITE)
3106        Timeout.QuadPart = (LONGLONG) dwMilliseconds * (LONGLONG) -10000;
3107 
3108     KeStackAttachProcess(&Process->Pcb, &ApcState);
3109     W32Process->W32PF_flags |= W32PF_WAITFORINPUTIDLE;
3110     for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling)
3111     {
3112        pti->TIF_flags |= TIF_WAITFORINPUTIDLE;
3113        pti->pClientInfo->dwTIFlags = pti->TIF_flags;
3114     }
3115     KeUnstackDetachProcess(&ApcState);
3116 
3117     TRACE("WFII: ppi %p\n", W32Process);
3118     TRACE("WFII: waiting for %p\n", Handles[1] );
3119 
3120     /*
3121      * We must add a refcount to our current PROCESSINFO,
3122      * because anything could happen (including process death) we're leaving win32k
3123      */
3124     IntReferenceProcessInfo(W32Process);
3125 
3126     do
3127     {
3128         UserLeave();
3129         Status = KeWaitForMultipleObjects( 3,
3130                                            Handles,
3131                                            WaitAny,
3132                                            UserRequest,
3133                                            UserMode,
3134                                            FALSE,
3135                                            dwMilliseconds == INFINITE ? NULL : &Timeout,
3136                                            NULL);
3137         UserEnterExclusive();
3138 
3139         if (!NT_SUCCESS(Status))
3140         {
3141             SetLastNtError(Status);
3142             Status = WAIT_FAILED;
3143             goto WaitExit;
3144         }
3145 
3146         switch (Status)
3147         {
3148         case STATUS_WAIT_0:
3149             goto WaitExit;
3150 
3151         case STATUS_WAIT_2:
3152             {
3153                MSG Msg;
3154                co_IntGetPeekMessage( &Msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE, FALSE);
3155                ERR("WFII: WAIT 2\n");
3156             }
3157             break;
3158 
3159         case STATUS_TIMEOUT:
3160             ERR("WFII: timeout\n");
3161         case WAIT_FAILED:
3162             goto WaitExit;
3163 
3164         default:
3165             ERR("WFII: finished\n");
3166             Status = STATUS_SUCCESS;
3167             goto WaitExit;
3168         }
3169     }
3170     while (TRUE);
3171 
3172 WaitExit:
3173     KeStackAttachProcess(&Process->Pcb, &ApcState);
3174     for (pti = W32Process->ptiList; pti; pti = pti->ptiSibling)
3175     {
3176        pti->TIF_flags &= ~TIF_WAITFORINPUTIDLE;
3177        pti->pClientInfo->dwTIFlags = pti->TIF_flags;
3178     }
3179     W32Process->W32PF_flags &= ~W32PF_WAITFORINPUTIDLE;
3180     KeUnstackDetachProcess(&ApcState);
3181 
3182     IntDereferenceProcessInfo(W32Process);
3183     ObDereferenceObject(Process);
3184     UserLeave();
3185     return Status;
3186 }
3187 
3188 /* EOF */
3189