a3179e10fa4344718a061cf69827cf07eaf0f8ac
[reactos.git] / reactos / subsys / win32k / ntuser / msgqueue.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Message queues
24 * FILE: subsys/win32k/ntuser/msgqueue.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <w32k.h>
33
34 #define NDEBUG
35 #include <win32k/debug1.h>
36 #include <debug.h>
37
38 /* GLOBALS *******************************************************************/
39
40 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
41
42 static MSG SystemMessageQueue[SYSTEM_MESSAGE_QUEUE_SIZE];
43 static ULONG SystemMessageQueueHead = 0;
44 static ULONG SystemMessageQueueTail = 0;
45 static ULONG SystemMessageQueueCount = 0;
46 static ULONG SystemMessageQueueMouseMove = -1;
47 static KSPIN_LOCK SystemMessageQueueLock;
48
49 static ULONG volatile HardwareMessageQueueStamp = 0;
50 static LIST_ENTRY HardwareMessageQueueHead;
51 static KMUTEX HardwareMessageQueueLock;
52
53 static KEVENT HardwareMessageEvent;
54
55 static PAGED_LOOKASIDE_LIST MessageLookasideList;
56 static PAGED_LOOKASIDE_LIST TimerLookasideList;
57
58 #define IntLockSystemMessageQueue(OldIrql) \
59 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
60
61 #define IntUnLockSystemMessageQueue(OldIrql) \
62 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
63
64 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
65 KeReleaseMutex(&HardwareMessageQueueLock, Wait)
66
67 /* FUNCTIONS *****************************************************************/
68
69 HANDLE FASTCALL
70 IntMsqSetWakeMask(DWORD WakeMask)
71 {
72 PW32THREAD Win32Thread;
73 PUSER_MESSAGE_QUEUE MessageQueue;
74 HANDLE MessageEventHandle;
75
76 Win32Thread = PsGetWin32Thread();
77 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
78 return 0;
79
80 MessageQueue = Win32Thread->MessageQueue;
81 IntLockMessageQueue(MessageQueue);
82 MessageQueue->WakeMask = WakeMask;
83 MessageEventHandle = MessageQueue->NewMessagesHandle;
84 IntUnLockMessageQueue(MessageQueue);
85
86 return MessageEventHandle;
87 }
88
89 BOOL FASTCALL
90 IntMsqClearWakeMask(VOID)
91 {
92 PW32THREAD Win32Thread;
93 PUSER_MESSAGE_QUEUE MessageQueue;
94
95 Win32Thread = PsGetWin32Thread();
96 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
97 return FALSE;
98
99 MessageQueue = Win32Thread->MessageQueue;
100 IntLockMessageQueue(MessageQueue);
101 MessageQueue->WakeMask = ~0;
102 IntUnLockMessageQueue(MessageQueue);
103
104 return TRUE;
105 }
106
107 VOID FASTCALL
108 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
109 {
110 IntLockMessageQueue(Queue);
111 Queue->PaintCount++;
112 Queue->PaintPosted = TRUE;
113 Queue->QueueBits |= QS_PAINT;
114 Queue->ChangedBits |= QS_PAINT;
115 if (Queue->WakeMask & QS_PAINT)
116 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
117 IntUnLockMessageQueue(Queue);
118 }
119
120 VOID FASTCALL
121 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
122 {
123 IntLockMessageQueue(Queue);
124 Queue->PaintCount--;
125 if (Queue->PaintCount == 0)
126 {
127 Queue->PaintPosted = FALSE;
128 }
129 IntUnLockMessageQueue(Queue);
130 }
131
132
133 NTSTATUS FASTCALL
134 MsqInitializeImpl(VOID)
135 {
136 /*CurrentFocusMessageQueue = NULL;*/
137 InitializeListHead(&HardwareMessageQueueHead);
138 KeInitializeEvent(&HardwareMessageEvent, NotificationEvent, 0);
139 KeInitializeSpinLock(&SystemMessageQueueLock);
140 KeInitializeMutex(&HardwareMessageQueueLock, 0);
141
142 ExInitializePagedLookasideList(&MessageLookasideList,
143 NULL,
144 NULL,
145 0,
146 sizeof(USER_MESSAGE),
147 0,
148 256);
149 ExInitializePagedLookasideList(&TimerLookasideList,
150 NULL,
151 NULL,
152 0,
153 sizeof(TIMER_ENTRY),
154 0,
155 64);
156
157 return(STATUS_SUCCESS);
158 }
159
160 VOID FASTCALL
161 MsqInsertSystemMessage(MSG* Msg)
162 {
163 LARGE_INTEGER LargeTickCount;
164 KIRQL OldIrql;
165 ULONG mmov = (ULONG)-1;
166
167 KeQueryTickCount(&LargeTickCount);
168 Msg->time = LargeTickCount.u.LowPart;
169
170 IntLockSystemMessageQueue(OldIrql);
171
172 /* only insert WM_MOUSEMOVE messages if not already in system message queue */
173 if(Msg->message == WM_MOUSEMOVE)
174 mmov = SystemMessageQueueMouseMove;
175
176 if(mmov != (ULONG)-1)
177 {
178 /* insert message at the queue head */
179 while (mmov != SystemMessageQueueHead )
180 {
181 ULONG prev = mmov ? mmov - 1 : SYSTEM_MESSAGE_QUEUE_SIZE - 1;
182 ASSERT((LONG) mmov >= 0);
183 ASSERT(mmov < SYSTEM_MESSAGE_QUEUE_SIZE);
184 SystemMessageQueue[mmov] = SystemMessageQueue[prev];
185 mmov = prev;
186 }
187 SystemMessageQueue[SystemMessageQueueHead] = *Msg;
188 }
189 else
190 {
191 if (SystemMessageQueueCount == SYSTEM_MESSAGE_QUEUE_SIZE)
192 {
193 IntUnLockSystemMessageQueue(OldIrql);
194 return;
195 }
196 SystemMessageQueue[SystemMessageQueueTail] = *Msg;
197 if(Msg->message == WM_MOUSEMOVE)
198 SystemMessageQueueMouseMove = SystemMessageQueueTail;
199 SystemMessageQueueTail =
200 (SystemMessageQueueTail + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
201 SystemMessageQueueCount++;
202 }
203 IntUnLockSystemMessageQueue(OldIrql);
204 KeSetEvent(&HardwareMessageEvent, IO_NO_INCREMENT, FALSE);
205 }
206
207 BOOL FASTCALL
208 MsqIsDblClk(LPMSG Msg, BOOL Remove)
209 {
210 PWINSTATION_OBJECT WinStaObject;
211 PSYSTEM_CURSORINFO CurInfo;
212 LONG dX, dY;
213 BOOL Res;
214
215 if (PsGetWin32Thread()->Desktop == NULL)
216 {
217 return FALSE;
218 }
219 WinStaObject = PsGetWin32Thread()->Desktop->WindowStation;
220
221 CurInfo = IntGetSysCursorInfo(WinStaObject);
222 Res = (Msg->hwnd == (HWND)CurInfo->LastClkWnd) &&
223 ((Msg->time - CurInfo->LastBtnDown) < CurInfo->DblClickSpeed);
224 if(Res)
225 {
226
227 dX = CurInfo->LastBtnDownX - Msg->pt.x;
228 dY = CurInfo->LastBtnDownY - Msg->pt.y;
229 if(dX < 0) dX = -dX;
230 if(dY < 0) dY = -dY;
231
232 Res = (dX <= CurInfo->DblClickWidth) &&
233 (dY <= CurInfo->DblClickHeight);
234 }
235
236 if(Remove)
237 {
238 if (Res)
239 {
240 CurInfo->LastBtnDown = 0;
241 CurInfo->LastBtnDownX = Msg->pt.x;
242 CurInfo->LastBtnDownY = Msg->pt.y;
243 CurInfo->LastClkWnd = NULL;
244 }
245 else
246 {
247 CurInfo->LastBtnDownX = Msg->pt.x;
248 CurInfo->LastBtnDownY = Msg->pt.y;
249 CurInfo->LastClkWnd = (HANDLE)Msg->hwnd;
250 CurInfo->LastBtnDown = Msg->time;
251 }
252 }
253
254 return Res;
255 }
256
257 BOOL STATIC STDCALL
258 MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd, UINT FilterLow, UINT FilterHigh,
259 PUSER_MESSAGE Message, BOOL Remove, PBOOL Freed,
260 PWINDOW_OBJECT ScopeWin, PPOINT ScreenPoint, BOOL FromGlobalQueue)
261 {
262 USHORT Msg = Message->Msg.message;
263 PWINDOW_OBJECT Window = NULL;
264 HWND CaptureWin;
265
266 CaptureWin = IntGetCaptureWindow();
267 if (CaptureWin == NULL)
268 {
269 if(Msg == WM_MOUSEWHEEL)
270 {
271 Window = IntGetWindowObject(IntGetFocusWindow());
272 }
273 else
274 {
275 WinPosWindowFromPoint(ScopeWin, NULL, &Message->Msg.pt, &Window);
276 if(Window == NULL)
277 {
278 Window = ScopeWin;
279 IntReferenceWindowObject(Window);
280 }
281 }
282 }
283 else
284 {
285 /* FIXME - window messages should go to the right window if no buttons are
286 pressed */
287 Window = IntGetWindowObject(CaptureWin);
288 }
289
290 if (Window == NULL)
291 {
292 if(!FromGlobalQueue)
293 {
294 RemoveEntryList(&Message->ListEntry);
295 if(MessageQueue->MouseMoveMsg == Message)
296 {
297 MessageQueue->MouseMoveMsg = NULL;
298 }
299 }
300 ExFreePool(Message);
301 *Freed = TRUE;
302 return(FALSE);
303 }
304
305 if (Window->MessageQueue != MessageQueue)
306 {
307 if (! FromGlobalQueue)
308 {
309 DPRINT("Moving msg between private queues\n");
310 /* This message is already queued in a private queue, but we need
311 * to move it to a different queue, perhaps because a new window
312 * was created which now covers the screen area previously taken
313 * by another window. To move it, we need to take it out of the
314 * old queue. Note that we're already holding the lock mutexes of the
315 * old queue */
316 RemoveEntryList(&Message->ListEntry);
317
318 /* remove the pointer for the current WM_MOUSEMOVE message in case we
319 just removed it */
320 if(MessageQueue->MouseMoveMsg == Message)
321 {
322 MessageQueue->MouseMoveMsg = NULL;
323 }
324 }
325
326 /* lock the destination message queue, so we don't get in trouble with other
327 threads, messing with it at the same time */
328 IntLockHardwareMessageQueue(Window->MessageQueue);
329 InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
330 &Message->ListEntry);
331 if(Message->Msg.message == WM_MOUSEMOVE)
332 {
333 if(Window->MessageQueue->MouseMoveMsg)
334 {
335 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
336 one */
337 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
338 ExFreePool(Window->MessageQueue->MouseMoveMsg);
339 }
340 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
341 Window->MessageQueue->MouseMoveMsg = Message;
342
343 Window->MessageQueue->QueueBits |= QS_MOUSEMOVE;
344 Window->MessageQueue->ChangedBits |= QS_MOUSEMOVE;
345 if (Window->MessageQueue->WakeMask & QS_MOUSEMOVE)
346 KeSetEvent(Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
347 }
348 else
349 {
350 Window->MessageQueue->QueueBits |= QS_MOUSEBUTTON;
351 Window->MessageQueue->ChangedBits |= QS_MOUSEBUTTON;
352 if (Window->MessageQueue->WakeMask & QS_MOUSEBUTTON)
353 KeSetEvent(Window->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
354 }
355 IntUnLockHardwareMessageQueue(Window->MessageQueue);
356
357 *Freed = FALSE;
358 IntReleaseWindowObject(Window);
359 return(FALSE);
360 }
361
362 /* From here on, we're in the same message queue as the caller! */
363
364 *ScreenPoint = Message->Msg.pt;
365
366 if((hWnd != NULL && Window->Self != hWnd) ||
367 ((FilterLow != 0 || FilterLow != 0) && (Msg < FilterLow || Msg > FilterHigh)))
368 {
369 /* Reject the message because it doesn't match the filter */
370
371 if(FromGlobalQueue)
372 {
373 /* Lock the message queue so no other thread can mess with it.
374 Our own message queue is not locked while fetching from the global
375 queue, so we have to make sure nothing interferes! */
376 IntLockHardwareMessageQueue(Window->MessageQueue);
377 /* if we're from the global queue, we need to add our message to our
378 private queue so we don't loose it! */
379 InsertTailList(&Window->MessageQueue->HardwareMessagesListHead,
380 &Message->ListEntry);
381 }
382
383 if (Message->Msg.message == WM_MOUSEMOVE)
384 {
385 if(Window->MessageQueue->MouseMoveMsg &&
386 (Window->MessageQueue->MouseMoveMsg != Message))
387 {
388 /* delete the old message */
389 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
390 ExFreePool(Window->MessageQueue->MouseMoveMsg);
391 }
392 /* always save a pointer to this WM_MOUSEMOVE message here because we're
393 sure that the message is in the private queue */
394 Window->MessageQueue->MouseMoveMsg = Message;
395 }
396 if(FromGlobalQueue)
397 {
398 IntUnLockHardwareMessageQueue(Window->MessageQueue);
399 }
400
401 IntReleaseWindowObject(Window);
402 *Freed = FALSE;
403 return(FALSE);
404 }
405
406 /* FIXME - only assign if removing? */
407 Message->Msg.hwnd = Window->Self;
408 Message->Msg.message = Msg;
409 Message->Msg.lParam = MAKELONG(Message->Msg.pt.x, Message->Msg.pt.y);
410
411 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
412 is it */
413 if (Message->Msg.message == WM_MOUSEMOVE ||
414 Message->Msg.message == WM_NCMOUSEMOVE)
415 {
416 if(FromGlobalQueue)
417 {
418 /* Lock the message queue so no other thread can mess with it.
419 Our own message queue is not locked while fetching from the global
420 queue, so we have to make sure nothing interferes! */
421 IntLockHardwareMessageQueue(Window->MessageQueue);
422 if(Window->MessageQueue->MouseMoveMsg)
423 {
424 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
425 with one that's been sent later */
426 RemoveEntryList(&Window->MessageQueue->MouseMoveMsg->ListEntry);
427 ExFreePool(Window->MessageQueue->MouseMoveMsg);
428 /* our message is not in the private queue so we can remove the pointer
429 instead of setting it to the current message we're processing */
430 Window->MessageQueue->MouseMoveMsg = NULL;
431 }
432 IntUnLockHardwareMessageQueue(Window->MessageQueue);
433 }
434 else if(Window->MessageQueue->MouseMoveMsg == Message)
435 {
436 Window->MessageQueue->MouseMoveMsg = NULL;
437 }
438 }
439
440 IntReleaseWindowObject(Window);
441 *Freed = FALSE;
442 return(TRUE);
443 }
444
445 BOOL STDCALL
446 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue, HWND hWnd,
447 UINT FilterLow, UINT FilterHigh, BOOL Remove,
448 PUSER_MESSAGE* Message)
449 {
450 KIRQL OldIrql;
451 POINT ScreenPoint;
452 BOOL Accept, Freed;
453 PLIST_ENTRY CurrentEntry;
454 PWINDOW_OBJECT DesktopWindow;
455 PVOID WaitObjects[2];
456 NTSTATUS WaitStatus;
457
458 if( !IntGetScreenDC() ||
459 PsGetWin32Thread()->MessageQueue == W32kGetPrimitiveMessageQueue() )
460 {
461 return FALSE;
462 }
463
464 WaitObjects[1] = MessageQueue->NewMessages;
465 WaitObjects[0] = &HardwareMessageQueueLock;
466 do
467 {
468 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
469 UserMode, FALSE, NULL, NULL);
470 while (MsqDispatchOneSentMessage(MessageQueue))
471 {
472 ;
473 }
474 }
475 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
476
477 DesktopWindow = IntGetWindowObject(IntGetDesktopWindow());
478
479 /* Process messages in the message queue itself. */
480 IntLockHardwareMessageQueue(MessageQueue);
481 CurrentEntry = MessageQueue->HardwareMessagesListHead.Flink;
482 while (CurrentEntry != &MessageQueue->HardwareMessagesListHead)
483 {
484 PUSER_MESSAGE Current =
485 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
486 CurrentEntry = CurrentEntry->Flink;
487 if (Current->Msg.message >= WM_MOUSEFIRST &&
488 Current->Msg.message <= WM_MOUSELAST)
489 {
490 Accept = MsqTranslateMouseMessage(MessageQueue, hWnd, FilterLow, FilterHigh,
491 Current, Remove, &Freed,
492 DesktopWindow, &ScreenPoint, FALSE);
493 if (Accept)
494 {
495 if (Remove)
496 {
497 RemoveEntryList(&Current->ListEntry);
498 }
499 IntUnLockHardwareMessageQueue(MessageQueue);
500 IntUnLockSystemHardwareMessageQueueLock(FALSE);
501 *Message = Current;
502 IntReleaseWindowObject(DesktopWindow);
503 return(TRUE);
504 }
505
506 }
507 }
508 IntUnLockHardwareMessageQueue(MessageQueue);
509
510 /* Now try the global queue. */
511
512 /* Transfer all messages from the DPC accessible queue to the main queue. */
513 IntLockSystemMessageQueue(OldIrql);
514 while (SystemMessageQueueCount > 0)
515 {
516 PUSER_MESSAGE UserMsg;
517 MSG Msg;
518
519 ASSERT(SystemMessageQueueHead < SYSTEM_MESSAGE_QUEUE_SIZE);
520 Msg = SystemMessageQueue[SystemMessageQueueHead];
521 SystemMessageQueueHead =
522 (SystemMessageQueueHead + 1) % SYSTEM_MESSAGE_QUEUE_SIZE;
523 SystemMessageQueueCount--;
524 IntUnLockSystemMessageQueue(OldIrql);
525 UserMsg = ExAllocateFromPagedLookasideList(&MessageLookasideList);
526 /* What to do if out of memory? For now we just panic a bit in debug */
527 ASSERT(UserMsg);
528 UserMsg->FreeLParam = FALSE;
529 UserMsg->Msg = Msg;
530 InsertTailList(&HardwareMessageQueueHead, &UserMsg->ListEntry);
531 IntLockSystemMessageQueue(OldIrql);
532 }
533 /*
534 * we could set this to -1 conditionally if we find one, but
535 * this is more efficient and just as effective.
536 */
537 SystemMessageQueueMouseMove = -1;
538 HardwareMessageQueueStamp++;
539 IntUnLockSystemMessageQueue(OldIrql);
540
541 /* Process messages in the queue until we find one to return. */
542 CurrentEntry = HardwareMessageQueueHead.Flink;
543 while (CurrentEntry != &HardwareMessageQueueHead)
544 {
545 PUSER_MESSAGE Current =
546 CONTAINING_RECORD(CurrentEntry, USER_MESSAGE, ListEntry);
547 CurrentEntry = CurrentEntry->Flink;
548 RemoveEntryList(&Current->ListEntry);
549 HardwareMessageQueueStamp++;
550 if (Current->Msg.message >= WM_MOUSEFIRST &&
551 Current->Msg.message <= WM_MOUSELAST)
552 {
553 const ULONG ActiveStamp = HardwareMessageQueueStamp;
554 /* Translate the message. */
555 Accept = MsqTranslateMouseMessage(MessageQueue, hWnd, FilterLow, FilterHigh,
556 Current, Remove, &Freed,
557 DesktopWindow, &ScreenPoint, TRUE);
558 if (Accept)
559 {
560 /* Check for no more messages in the system queue. */
561 IntLockSystemMessageQueue(OldIrql);
562 if (SystemMessageQueueCount == 0 &&
563 IsListEmpty(&HardwareMessageQueueHead))
564 {
565 KeClearEvent(&HardwareMessageEvent);
566 }
567 IntUnLockSystemMessageQueue(OldIrql);
568
569 /*
570 If we aren't removing the message then add it to the private
571 queue.
572 */
573 if (!Remove)
574 {
575 IntLockHardwareMessageQueue(MessageQueue);
576 if(Current->Msg.message == WM_MOUSEMOVE)
577 {
578 if(MessageQueue->MouseMoveMsg)
579 {
580 RemoveEntryList(&MessageQueue->MouseMoveMsg->ListEntry);
581 ExFreePool(MessageQueue->MouseMoveMsg);
582 }
583 MessageQueue->MouseMoveMsg = Current;
584 }
585 InsertTailList(&MessageQueue->HardwareMessagesListHead,
586 &Current->ListEntry);
587 IntUnLockHardwareMessageQueue(MessageQueue);
588 }
589 IntUnLockSystemHardwareMessageQueueLock(FALSE);
590 *Message = Current;
591 IntReleaseWindowObject(DesktopWindow);
592 return(TRUE);
593 }
594 /* If the contents of the queue changed then restart processing. */
595 if (HardwareMessageQueueStamp != ActiveStamp)
596 {
597 CurrentEntry = HardwareMessageQueueHead.Flink;
598 continue;
599 }
600 }
601 }
602 IntReleaseWindowObject(DesktopWindow);
603 /* Check if the system message queue is now empty. */
604 IntLockSystemMessageQueue(OldIrql);
605 if (SystemMessageQueueCount == 0 && IsListEmpty(&HardwareMessageQueueHead))
606 {
607 KeClearEvent(&HardwareMessageEvent);
608 }
609 IntUnLockSystemMessageQueue(OldIrql);
610 IntUnLockSystemHardwareMessageQueueLock(FALSE);
611
612 return(FALSE);
613 }
614
615 VOID FASTCALL
616 MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
617 {
618 PUSER_MESSAGE_QUEUE FocusMessageQueue;
619 MSG Msg;
620 LARGE_INTEGER LargeTickCount;
621 KBDLLHOOKSTRUCT KbdHookData;
622
623 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
624 uMsg, wParam, lParam);
625
626 Msg.hwnd = 0;
627 Msg.message = uMsg;
628 Msg.wParam = wParam;
629 Msg.lParam = lParam;
630
631 KeQueryTickCount(&LargeTickCount);
632 Msg.time = LargeTickCount.u.LowPart;
633 /* We can't get the Msg.pt point here since we don't know thread
634 (and thus the window station) the message will end up in yet. */
635
636 KbdHookData.vkCode = Msg.wParam;
637 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
638 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
639 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
640 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
641 KbdHookData.time = Msg.time;
642 KbdHookData.dwExtraInfo = 0;
643 if (HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
644 {
645 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
646 Msg.message, Msg.wParam, Msg.lParam);
647 return;
648 }
649
650 FocusMessageQueue = IntGetFocusMessageQueue();
651 if( !IntGetScreenDC() ) {
652 /* FIXME: What to do about Msg.pt here? */
653 if( W32kGetPrimitiveMessageQueue() ) {
654 MsqPostMessage(W32kGetPrimitiveMessageQueue(), &Msg, FALSE, QS_KEY);
655 }
656 } else {
657 if (FocusMessageQueue == NULL)
658 {
659 DPRINT("No focus message queue\n");
660 return;
661 }
662
663 if (FocusMessageQueue->FocusWindow != (HWND)0)
664 {
665 Msg.hwnd = FocusMessageQueue->FocusWindow;
666 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
667 IntGetCursorLocation(FocusMessageQueue->Desktop->WindowStation,
668 &Msg.pt);
669 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
670 }
671 else
672 {
673 DPRINT("Invalid focus window handle\n");
674 }
675 }
676 }
677
678 VOID FASTCALL
679 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
680 {
681 PWINDOW_OBJECT Window;
682 PW32THREAD Win32Thread;
683 PWINSTATION_OBJECT WinSta;
684 MSG Mesg;
685 LARGE_INTEGER LargeTickCount;
686 NTSTATUS Status;
687
688 Status = ObReferenceObjectByPointer (Thread,
689 THREAD_ALL_ACCESS,
690 PsThreadType,
691 KernelMode);
692 if (!NT_SUCCESS(Status))
693 return;
694
695 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
696 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
697 {
698 ObDereferenceObject ((PETHREAD)Thread);
699 return;
700 }
701
702 WinSta = Win32Thread->Desktop->WindowStation;
703 Status = ObmReferenceObjectByHandle(WinSta->HandleTable,
704 hWnd, otWindow, (PVOID*)&Window);
705 if (!NT_SUCCESS(Status))
706 {
707 ObDereferenceObject ((PETHREAD)Thread);
708 return;
709 }
710
711 Mesg.hwnd = hWnd;
712 Mesg.message = WM_HOTKEY;
713 Mesg.wParam = wParam;
714 Mesg.lParam = lParam;
715 KeQueryTickCount(&LargeTickCount);
716 Mesg.time = LargeTickCount.u.LowPart;
717 IntGetCursorLocation(WinSta, &Mesg.pt);
718 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
719 ObmDereferenceObject(Window);
720 ObDereferenceObject (Thread);
721
722 // IntLockMessageQueue(pThread->MessageQueue);
723 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
724 // &Message->ListEntry);
725 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
726 // IntUnLockMessageQueue(pThread->MessageQueue);
727
728 }
729
730 PUSER_MESSAGE FASTCALL
731 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam)
732 {
733 PUSER_MESSAGE Message;
734
735 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
736 if (!Message)
737 {
738 return NULL;
739 }
740
741 Message->FreeLParam = FreeLParam;
742 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
743
744 return Message;
745 }
746
747 VOID FASTCALL
748 MsqDestroyMessage(PUSER_MESSAGE Message)
749 {
750 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
751 }
752
753 VOID FASTCALL
754 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
755 {
756 PLIST_ENTRY ListEntry;
757 PUSER_SENT_MESSAGE_NOTIFY Message;
758
759 IntLockMessageQueue(MessageQueue);
760 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
761 {
762 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
763 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
764 ListEntry);
765 IntUnLockMessageQueue(MessageQueue);
766
767 IntCallSentMessageCallback(Message->CompletionCallback,
768 Message->hWnd,
769 Message->Msg,
770 Message->CompletionCallbackContext,
771 Message->Result);
772
773 IntLockMessageQueue(MessageQueue);
774 }
775 IntUnLockMessageQueue(MessageQueue);
776 }
777
778 BOOLEAN FASTCALL
779 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
780 {
781 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
782 }
783
784 BOOLEAN FASTCALL
785 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
786 {
787 PUSER_SENT_MESSAGE Message;
788 PLIST_ENTRY Entry;
789 LRESULT Result;
790 BOOL SenderReturned;
791 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
792
793 IntLockMessageQueue(MessageQueue);
794 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
795 {
796 IntUnLockMessageQueue(MessageQueue);
797 return(FALSE);
798 }
799
800 /* remove it from the list of pending messages */
801 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
802 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
803
804 /* insert it to the list of messages that are currently dispatched by this
805 message queue */
806 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
807 &Message->ListEntry);
808
809 IntUnLockMessageQueue(MessageQueue);
810
811 if (Message->HookMessage)
812 {
813 Result = HOOK_CallHooks(Message->Msg.message,
814 (INT) Message->Msg.hwnd,
815 Message->Msg.wParam,
816 Message->Msg.lParam);
817 }
818 else
819 {
820 /* Call the window procedure. */
821 Result = IntSendMessage(Message->Msg.hwnd,
822 Message->Msg.message,
823 Message->Msg.wParam,
824 Message->Msg.lParam);
825 }
826
827 /* remove the message from the local dispatching list, because it doesn't need
828 to be cleaned up on thread termination anymore */
829 IntLockMessageQueue(MessageQueue);
830 RemoveEntryList(&Message->ListEntry);
831 IntUnLockMessageQueue(MessageQueue);
832
833 /* remove the message from the dispatching list, so lock the sender's message queue */
834 IntLockMessageQueue(Message->SenderQueue);
835
836 SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
837 if(!SenderReturned)
838 {
839 /* only remove it from the dispatching list if not already removed by a timeout */
840 RemoveEntryList(&Message->DispatchingListEntry);
841 }
842 /* still keep the sender's message queue locked, so the sender can't exit the
843 MsqSendMessage() function (if timed out) */
844
845 /* Let the sender know the result. */
846 if (Message->Result != NULL)
847 {
848 *Message->Result = Result;
849 }
850
851 /* Notify the sender. */
852 if (Message->CompletionEvent != NULL)
853 {
854 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
855 }
856
857 /* unlock the sender's message queue, the safe operation is done */
858 IntUnLockMessageQueue(Message->SenderQueue);
859
860 /* Notify the sender if they specified a callback. */
861 if (!SenderReturned && Message->CompletionCallback != NULL)
862 {
863 if(!(NotifyMessage = ExAllocatePoolWithTag(NonPagedPool,
864 sizeof(USER_SENT_MESSAGE_NOTIFY), TAG_USRMSG)))
865 {
866 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
867 goto Notified;
868 }
869 NotifyMessage->CompletionCallback =
870 Message->CompletionCallback;
871 NotifyMessage->CompletionCallbackContext =
872 Message->CompletionCallbackContext;
873 NotifyMessage->Result = Result;
874 NotifyMessage->hWnd = Message->Msg.hwnd;
875 NotifyMessage->Msg = Message->Msg.message;
876 MsqSendNotifyMessage(Message->SenderQueue, NotifyMessage);
877 }
878
879 Notified:
880
881 /* dereference both sender and our queue */
882 IntDereferenceMessageQueue(MessageQueue);
883 IntDereferenceMessageQueue(Message->SenderQueue);
884
885 /* free the message */
886 ExFreePool(Message);
887 return(TRUE);
888 }
889
890 VOID STDCALL
891 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
892 {
893 PUSER_SENT_MESSAGE SentMessage;
894 PUSER_MESSAGE PostedMessage;
895 PUSER_MESSAGE_QUEUE MessageQueue;
896 PLIST_ENTRY CurrentEntry, ListHead;
897 PWINDOW_OBJECT Window = pWindow;
898
899 ASSERT(Window);
900
901 MessageQueue = Window->MessageQueue;
902 ASSERT(MessageQueue);
903
904 IntLockMessageQueue(MessageQueue);
905
906 /* remove the posted messages for this window */
907 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
908 ListHead = &MessageQueue->PostedMessagesListHead;
909 while (CurrentEntry != ListHead)
910 {
911 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
912 ListEntry);
913 if (PostedMessage->Msg.hwnd == Window->Self)
914 {
915 RemoveEntryList(&PostedMessage->ListEntry);
916 MsqDestroyMessage(PostedMessage);
917 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
918 }
919 else
920 {
921 CurrentEntry = CurrentEntry->Flink;
922 }
923 }
924
925 /* remove the sent messages for this window */
926 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
927 ListHead = &MessageQueue->SentMessagesListHead;
928 while (CurrentEntry != ListHead)
929 {
930 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
931 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
932 ListEntry);
933 if(SentMessage->Msg.hwnd == Window->Self)
934 {
935 IntLockMessageQueue(SentMessage->SenderQueue);
936 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
937
938 /* remove the message from the dispatching list */
939 if(SentMessage->DispatchingListEntry.Flink != NULL)
940 {
941 RemoveEntryList(&SentMessage->DispatchingListEntry);
942 }
943
944 /* wake the sender's thread */
945 if (SentMessage->CompletionEvent != NULL)
946 {
947 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
948 }
949 IntUnLockMessageQueue(SentMessage->SenderQueue);
950
951 /* dereference our and the sender's message queue */
952 IntDereferenceMessageQueue(MessageQueue);
953 IntDereferenceMessageQueue(SentMessage->SenderQueue);
954
955 /* free the message */
956 ExFreePool(SentMessage);
957
958 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
959 }
960 else
961 {
962 CurrentEntry = CurrentEntry->Flink;
963 }
964 }
965 IntUnLockMessageQueue(MessageQueue);
966 }
967
968 VOID FASTCALL
969 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
970 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
971 {
972 IntLockMessageQueue(MessageQueue);
973 InsertTailList(&MessageQueue->NotifyMessagesListHead,
974 &NotifyMessage->ListEntry);
975 MessageQueue->QueueBits |= QS_SENDMESSAGE;
976 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
977 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
978 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
979 IntUnLockMessageQueue(MessageQueue);
980 }
981
982 NTSTATUS FASTCALL
983 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
984 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
985 UINT uTimeout, BOOL Block, BOOL HookMessage,
986 ULONG_PTR *uResult)
987 {
988 PUSER_SENT_MESSAGE Message;
989 KEVENT CompletionEvent;
990 NTSTATUS WaitStatus;
991 LRESULT Result;
992 PUSER_MESSAGE_QUEUE ThreadQueue;
993 LARGE_INTEGER Timeout;
994 PLIST_ENTRY Entry;
995
996 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
997 {
998 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
999 return STATUS_INSUFFICIENT_RESOURCES;
1000 }
1001
1002 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
1003
1004 ThreadQueue = PsGetWin32Thread()->MessageQueue;
1005 ASSERT(ThreadQueue != MessageQueue);
1006
1007 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
1008
1009 /* FIXME - increase reference counter of sender's message queue here */
1010
1011 Result = 0;
1012 Message->Msg.hwnd = Wnd;
1013 Message->Msg.message = Msg;
1014 Message->Msg.wParam = wParam;
1015 Message->Msg.lParam = lParam;
1016 Message->CompletionEvent = &CompletionEvent;
1017 Message->Result = &Result;
1018 Message->SenderQueue = ThreadQueue;
1019 IntReferenceMessageQueue(ThreadQueue);
1020 Message->CompletionCallback = NULL;
1021 Message->HookMessage = HookMessage;
1022
1023 IntReferenceMessageQueue(MessageQueue);
1024
1025 /* add it to the list of pending messages */
1026 IntLockMessageQueue(ThreadQueue);
1027 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
1028 IntUnLockMessageQueue(ThreadQueue);
1029
1030 /* queue it in the destination's message queue */
1031 IntLockMessageQueue(MessageQueue);
1032 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
1033 IntUnLockMessageQueue(MessageQueue);
1034
1035 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1036 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1037 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1038 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1039
1040 /* we can't access the Message anymore since it could have already been deleted! */
1041
1042 if(Block)
1043 {
1044 /* don't process messages sent to the thread */
1045 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1046 FALSE, (uTimeout ? &Timeout : NULL));
1047 if(WaitStatus == STATUS_TIMEOUT)
1048 {
1049 /* look up if the message has not yet dispatched, if so
1050 make sure it can't pass a result and it must not set the completion event anymore */
1051 IntLockMessageQueue(MessageQueue);
1052 Entry = MessageQueue->SentMessagesListHead.Flink;
1053 while (Entry != &MessageQueue->SentMessagesListHead)
1054 {
1055 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1056 == Message)
1057 {
1058 /* we can access Message here, it's secure because the message queue is locked
1059 and the message is still hasn't been dispatched */
1060 Message->CompletionEvent = NULL;
1061 Message->Result = NULL;
1062 break;
1063 }
1064 Entry = Entry->Flink;
1065 }
1066 IntUnLockMessageQueue(MessageQueue);
1067
1068 /* remove from the local dispatching list so the other thread knows,
1069 it can't pass a result and it must not set the completion event anymore */
1070 IntLockMessageQueue(ThreadQueue);
1071 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1072 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1073 {
1074 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1075 == Message)
1076 {
1077 /* we can access Message here, it's secure because the sender's message is locked
1078 and the message has definitely not yet been destroyed, otherwise it would
1079 have been removed from this list by the dispatching routine right after
1080 dispatching the message */
1081 Message->CompletionEvent = NULL;
1082 Message->Result = NULL;
1083 RemoveEntryList(&Message->DispatchingListEntry);
1084 break;
1085 }
1086 Entry = Entry->Flink;
1087 }
1088 IntUnLockMessageQueue(ThreadQueue);
1089
1090 DPRINT("MsqSendMessage (blocked) timed out\n");
1091 }
1092 while (MsqDispatchOneSentMessage(ThreadQueue));
1093 }
1094 else
1095 {
1096 PVOID WaitObjects[2];
1097
1098 WaitObjects[0] = &CompletionEvent;
1099 WaitObjects[1] = ThreadQueue->NewMessages;
1100 do
1101 {
1102 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1103 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1104 if(WaitStatus == STATUS_TIMEOUT)
1105 {
1106 /* look up if the message has not yet been dispatched, if so
1107 make sure it can't pass a result and it must not set the completion event anymore */
1108 IntLockMessageQueue(MessageQueue);
1109 Entry = MessageQueue->SentMessagesListHead.Flink;
1110 while (Entry != &MessageQueue->SentMessagesListHead)
1111 {
1112 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1113 == Message)
1114 {
1115 /* we can access Message here, it's secure because the message queue is locked
1116 and the message is still hasn't been dispatched */
1117 Message->CompletionEvent = NULL;
1118 Message->Result = NULL;
1119 break;
1120 }
1121 Entry = Entry->Flink;
1122 }
1123 IntUnLockMessageQueue(MessageQueue);
1124
1125 /* remove from the local dispatching list so the other thread knows,
1126 it can't pass a result and it must not set the completion event anymore */
1127 IntLockMessageQueue(ThreadQueue);
1128 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1129 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1130 {
1131 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1132 == Message)
1133 {
1134 /* we can access Message here, it's secure because the sender's message is locked
1135 and the message has definitely not yet been destroyed, otherwise it would
1136 have been removed from this list by the dispatching routine right after
1137 dispatching the message */
1138 Message->CompletionEvent = NULL;
1139 Message->Result = NULL;
1140 RemoveEntryList(&Message->DispatchingListEntry);
1141 break;
1142 }
1143 Entry = Entry->Flink;
1144 }
1145 IntUnLockMessageQueue(ThreadQueue);
1146
1147 DPRINT("MsqSendMessage timed out\n");
1148 break;
1149 }
1150 while (MsqDispatchOneSentMessage(ThreadQueue));
1151 }
1152 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1153 }
1154
1155 if(WaitStatus != STATUS_TIMEOUT)
1156 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1157
1158 return WaitStatus;
1159 }
1160
1161 VOID FASTCALL
1162 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1163 DWORD MessageBits)
1164 {
1165 PUSER_MESSAGE Message;
1166
1167 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1168 {
1169 return;
1170 }
1171 IntLockMessageQueue(MessageQueue);
1172 InsertTailList(&MessageQueue->PostedMessagesListHead,
1173 &Message->ListEntry);
1174 MessageQueue->QueueBits |= MessageBits;
1175 MessageQueue->ChangedBits |= MessageBits;
1176 if (MessageQueue->WakeMask & MessageBits)
1177 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1178 IntUnLockMessageQueue(MessageQueue);
1179 }
1180
1181 VOID FASTCALL
1182 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1183 {
1184 IntLockMessageQueue(MessageQueue);
1185 MessageQueue->QuitPosted = TRUE;
1186 MessageQueue->QuitExitCode = ExitCode;
1187 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1188 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1189 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1190 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1191 IntUnLockMessageQueue(MessageQueue);
1192 }
1193
1194 BOOLEAN STDCALL
1195 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1196 IN BOOLEAN Hardware,
1197 IN BOOLEAN Remove,
1198 IN HWND Wnd,
1199 IN UINT MsgFilterLow,
1200 IN UINT MsgFilterHigh,
1201 OUT PUSER_MESSAGE* Message)
1202 {
1203 PLIST_ENTRY CurrentEntry;
1204 PUSER_MESSAGE CurrentMessage;
1205 PLIST_ENTRY ListHead;
1206
1207 if (Hardware)
1208 {
1209 return(MsqPeekHardwareMessage(MessageQueue, Wnd,
1210 MsgFilterLow, MsgFilterHigh,
1211 Remove, Message));
1212 }
1213
1214 IntLockMessageQueue(MessageQueue);
1215 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1216 ListHead = &MessageQueue->PostedMessagesListHead;
1217 while (CurrentEntry != ListHead)
1218 {
1219 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1220 ListEntry);
1221 if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
1222 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1223 (MsgFilterLow <= CurrentMessage->Msg.message &&
1224 MsgFilterHigh >= CurrentMessage->Msg.message)))
1225 {
1226 if (Remove)
1227 {
1228 RemoveEntryList(&CurrentMessage->ListEntry);
1229 }
1230 IntUnLockMessageQueue(MessageQueue);
1231 *Message = CurrentMessage;
1232 return(TRUE);
1233 }
1234 CurrentEntry = CurrentEntry->Flink;
1235 }
1236 IntUnLockMessageQueue(MessageQueue);
1237 return(FALSE);
1238 }
1239
1240 NTSTATUS FASTCALL
1241 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, HWND WndFilter,
1242 UINT MsgFilterMin, UINT MsgFilterMax)
1243 {
1244 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1245 LARGE_INTEGER TimerExpiry;
1246 PLARGE_INTEGER Timeout;
1247
1248 if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
1249 {
1250 Timeout = &TimerExpiry;
1251 }
1252 else
1253 {
1254 Timeout = NULL;
1255 }
1256
1257 return(KeWaitForMultipleObjects(2,
1258 WaitObjects,
1259 WaitAny,
1260 Executive,
1261 UserMode,
1262 FALSE,
1263 Timeout,
1264 NULL));
1265 }
1266
1267 BOOL FASTCALL
1268 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1269 {
1270 LARGE_INTEGER LargeTickCount;
1271
1272 KeQueryTickCount(&LargeTickCount);
1273 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1274 }
1275
1276 BOOLEAN FASTCALL
1277 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1278 {
1279 LARGE_INTEGER LargeTickCount;
1280 NTSTATUS Status;
1281
1282 MessageQueue->Thread = Thread;
1283 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1284 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1285 InitializeListHead(&MessageQueue->SentMessagesListHead);
1286 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1287 InitializeListHead(&MessageQueue->TimerListHead);
1288 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1289 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1290 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1291 ExInitializeFastMutex(&MessageQueue->Lock);
1292 MessageQueue->QuitPosted = FALSE;
1293 MessageQueue->QuitExitCode = 0;
1294 KeQueryTickCount(&LargeTickCount);
1295 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1296 MessageQueue->FocusWindow = NULL;
1297 MessageQueue->PaintPosted = FALSE;
1298 MessageQueue->PaintCount = 0;
1299 MessageQueue->WakeMask = ~0;
1300 MessageQueue->NewMessagesHandle = NULL;
1301
1302 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1303 NULL, SynchronizationEvent, FALSE);
1304 if (!NT_SUCCESS(Status))
1305 {
1306 return FALSE;
1307 }
1308
1309 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1310 ExEventObjectType, KernelMode,
1311 (PVOID*)&MessageQueue->NewMessages, NULL);
1312 if (!NT_SUCCESS(Status))
1313 {
1314 ZwClose(MessageQueue->NewMessagesHandle);
1315 MessageQueue->NewMessagesHandle = NULL;
1316 return FALSE;
1317 }
1318
1319 return TRUE;
1320 }
1321
1322 VOID FASTCALL
1323 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1324 {
1325 PLIST_ENTRY CurrentEntry;
1326 PUSER_MESSAGE CurrentMessage;
1327 PTIMER_ENTRY CurrentTimer;
1328 PUSER_SENT_MESSAGE CurrentSentMessage;
1329
1330 IntLockMessageQueue(MessageQueue);
1331
1332 /* cleanup posted messages */
1333 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1334 {
1335 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1336 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1337 ListEntry);
1338 MsqDestroyMessage(CurrentMessage);
1339 }
1340
1341 /* remove the messages that have not yet been dispatched */
1342 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1343 {
1344 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1345 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1346 ListEntry);
1347
1348 IntLockMessageQueue(CurrentSentMessage->SenderQueue);
1349 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1350
1351 /* remove the message from the dispatching list */
1352 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1353 {
1354 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1355 }
1356
1357 /* wake the sender's thread */
1358 if (CurrentSentMessage->CompletionEvent != NULL)
1359 {
1360 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1361 }
1362 IntUnLockMessageQueue(CurrentSentMessage->SenderQueue);
1363
1364 /* dereference our and the sender's message queue */
1365 IntDereferenceMessageQueue(MessageQueue);
1366 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1367
1368 /* free the message */
1369 ExFreePool(CurrentSentMessage);
1370 }
1371
1372 /* cleanup timers */
1373 while (! IsListEmpty(&MessageQueue->TimerListHead))
1374 {
1375 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1376 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1377 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1378 }
1379
1380 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1381 ExitThread() was called in a SendMessage() umode callback */
1382 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1383 {
1384 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1385 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1386 ListEntry);
1387
1388 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1389
1390 /* wake the sender's thread */
1391 if (CurrentSentMessage->CompletionEvent != NULL)
1392 {
1393 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1394 }
1395
1396 /* dereference our and the sender's message queue */
1397 IntDereferenceMessageQueue(MessageQueue);
1398 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1399
1400 /* free the message */
1401 ExFreePool(CurrentSentMessage);
1402 }
1403
1404 /* tell other threads not to bother returning any info to us */
1405 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1406 {
1407 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1408 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1409 DispatchingListEntry);
1410 CurrentSentMessage->CompletionEvent = NULL;
1411 CurrentSentMessage->Result = NULL;
1412
1413 /* do NOT dereference our message queue as it might get attempted to be
1414 locked later */
1415 }
1416
1417 IntUnLockMessageQueue(MessageQueue);
1418 }
1419
1420 PUSER_MESSAGE_QUEUE FASTCALL
1421 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1422 {
1423 PUSER_MESSAGE_QUEUE MessageQueue;
1424
1425 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(PagedPool,
1426 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1427 TAG_MSGQ);
1428
1429 if (!MessageQueue)
1430 {
1431 return NULL;
1432 }
1433
1434 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1435 /* hold at least one reference until it'll be destroyed */
1436 IntReferenceMessageQueue(MessageQueue);
1437 /* initialize the queue */
1438 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1439 {
1440 IntDereferenceMessageQueue(MessageQueue);
1441 return NULL;
1442 }
1443
1444 return MessageQueue;
1445 }
1446
1447 VOID FASTCALL
1448 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1449 {
1450 PDESKTOP_OBJECT desk;
1451
1452 /* remove the message queue from any desktops */
1453 if ((desk = (PDESKTOP_OBJECT)InterlockedExchange((LONG*)&MessageQueue->Desktop, 0)))
1454 {
1455 InterlockedExchange((LONG*)&desk->ActiveMessageQueue, 0);
1456 IntDereferenceMessageQueue(MessageQueue);
1457 }
1458
1459 /* if this is the primitive message queue, deregister it */
1460 if (MessageQueue == W32kGetPrimitiveMessageQueue())
1461 W32kUnregisterPrimitiveMessageQueue();
1462
1463 /* clean it up */
1464 MsqCleanupMessageQueue(MessageQueue);
1465
1466 /* decrease the reference counter, if it hits zero, the queue will be freed */
1467 IntDereferenceMessageQueue(MessageQueue);
1468 }
1469
1470 PHOOKTABLE FASTCALL
1471 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1472 {
1473 return Queue->Hooks;
1474 }
1475
1476 VOID FASTCALL
1477 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1478 {
1479 Queue->Hooks = Hooks;
1480 }
1481
1482 LPARAM FASTCALL
1483 MsqSetMessageExtraInfo(LPARAM lParam)
1484 {
1485 LPARAM Ret;
1486 PUSER_MESSAGE_QUEUE MessageQueue;
1487
1488 MessageQueue = PsGetWin32Thread()->MessageQueue;
1489 if(!MessageQueue)
1490 {
1491 return 0;
1492 }
1493
1494 Ret = MessageQueue->ExtraInfo;
1495 MessageQueue->ExtraInfo = lParam;
1496
1497 return Ret;
1498 }
1499
1500 LPARAM FASTCALL
1501 MsqGetMessageExtraInfo(VOID)
1502 {
1503 PUSER_MESSAGE_QUEUE MessageQueue;
1504
1505 MessageQueue = PsGetWin32Thread()->MessageQueue;
1506 if(!MessageQueue)
1507 {
1508 return 0;
1509 }
1510
1511 return MessageQueue->ExtraInfo;
1512 }
1513
1514 HWND FASTCALL
1515 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1516 {
1517 HWND Prev;
1518
1519 switch(Type)
1520 {
1521 case MSQ_STATE_CAPTURE:
1522 Prev = MessageQueue->CaptureWindow;
1523 MessageQueue->CaptureWindow = hWnd;
1524 return Prev;
1525 case MSQ_STATE_ACTIVE:
1526 Prev = MessageQueue->ActiveWindow;
1527 MessageQueue->ActiveWindow = hWnd;
1528 return Prev;
1529 case MSQ_STATE_FOCUS:
1530 Prev = MessageQueue->FocusWindow;
1531 MessageQueue->FocusWindow = hWnd;
1532 return Prev;
1533 case MSQ_STATE_MENUOWNER:
1534 Prev = MessageQueue->MenuOwner;
1535 MessageQueue->MenuOwner = hWnd;
1536 return Prev;
1537 case MSQ_STATE_MOVESIZE:
1538 Prev = MessageQueue->MoveSize;
1539 MessageQueue->MoveSize = hWnd;
1540 return Prev;
1541 case MSQ_STATE_CARET:
1542 ASSERT(MessageQueue->CaretInfo);
1543 Prev = MessageQueue->CaretInfo->hWnd;
1544 MessageQueue->CaretInfo->hWnd = hWnd;
1545 return Prev;
1546 }
1547
1548 return NULL;
1549 }
1550
1551 #ifndef NDEBUG
1552 static VOID FASTCALL
1553 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
1554 {
1555 PLIST_ENTRY Current;
1556 PTIMER_ENTRY Timer;
1557
1558 Current = MessageQueue->TimerListHead.Flink;
1559 if (Current == &MessageQueue->TimerListHead)
1560 {
1561 DPRINT("timer list is empty for queue %p\n", MessageQueue);
1562 }
1563 while (Current != &MessageQueue->TimerListHead)
1564 {
1565 Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
1566 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1567 MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
1568 Timer->Period, Timer->TimerFunc, Timer->Msg);
1569 Current = Current->Flink;
1570 }
1571 }
1572 #endif /* ! defined(NDEBUG) */
1573
1574 /* Must have the message queue locked while calling this */
1575 static VOID FASTCALL
1576 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
1577 {
1578 PLIST_ENTRY Current;
1579
1580 Current = MessageQueue->TimerListHead.Flink;
1581 while (Current != &MessageQueue->TimerListHead)
1582 {
1583 if (NewTimer->ExpiryTime.QuadPart <
1584 CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
1585 {
1586 break;
1587 }
1588 Current = Current->Flink;
1589 }
1590
1591 InsertTailList(Current, &NewTimer->ListEntry);
1592 }
1593
1594 /* Must have the message queue locked while calling this */
1595 static PTIMER_ENTRY FASTCALL
1596 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
1597 {
1598 PTIMER_ENTRY Timer;
1599 PLIST_ENTRY EnumEntry;
1600
1601 /* Remove timer if already in the queue */
1602 EnumEntry = MessageQueue->TimerListHead.Flink;
1603 while (EnumEntry != &MessageQueue->TimerListHead)
1604 {
1605 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1606 EnumEntry = EnumEntry->Flink;
1607
1608 if (Timer->Wnd == Wnd &&
1609 Timer->IDEvent == IDEvent &&
1610 Timer->Msg == Msg)
1611 {
1612 RemoveEntryList(&Timer->ListEntry);
1613 return Timer;
1614 }
1615 }
1616
1617 return NULL;
1618 }
1619
1620 BOOLEAN FASTCALL
1621 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1622 UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
1623 UINT Msg)
1624 {
1625 PTIMER_ENTRY Timer;
1626 LARGE_INTEGER CurrentTime;
1627
1628 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1629 MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
1630
1631 IntLockMessageQueue(MessageQueue);
1632 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1633 if (NULL == Timer)
1634 {
1635 Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
1636 if (NULL == Timer)
1637 {
1638 IntUnLockMessageQueue(MessageQueue);
1639 DPRINT1("Failed to allocate timer entry\n");
1640 return FALSE;
1641 }
1642 DPRINT("Allocated new timer entry %p\n", Timer);
1643 Timer->Wnd = Wnd;
1644 Timer->IDEvent = IDEvent;
1645 Timer->Msg = Msg;
1646 }
1647 else
1648 {
1649 DPRINT("Updating existing timer entry %p\n", Timer);
1650 }
1651
1652 KeQuerySystemTime(&CurrentTime);
1653 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1654 (ULONGLONG) Period * (ULONGLONG) 10000;
1655 Timer->Period = Period;
1656 Timer->TimerFunc = TimerFunc;
1657 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
1658 Timer->ExpiryTime.QuadPart);
1659
1660 InsertTimer(MessageQueue, Timer);
1661
1662 #ifndef NDEBUG
1663 DumpTimerList(MessageQueue);
1664 #endif /* ! defined(NDEBUG) */
1665
1666 IntUnLockMessageQueue(MessageQueue);
1667
1668 return TRUE;
1669 }
1670
1671 BOOLEAN FASTCALL
1672 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1673 UINT_PTR IDEvent, UINT Msg)
1674 {
1675 PTIMER_ENTRY Timer;
1676
1677 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1678 MessageQueue, Wnd, IDEvent, Msg);
1679
1680 IntLockMessageQueue(MessageQueue);
1681 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1682
1683 if (NULL == Timer)
1684 {
1685 DPRINT("Failed to remove timer from list, not found\n");
1686 }
1687 else
1688 {
1689 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1690 }
1691
1692 #ifndef NDEBUG
1693 DumpTimerList(MessageQueue);
1694 #endif /* ! defined(NDEBUG) */
1695
1696 IntUnLockMessageQueue(MessageQueue);
1697
1698 return NULL != Timer;
1699 }
1700
1701 BOOLEAN FASTCALL
1702 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1703 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1704 MSG *Msg, BOOLEAN Restart)
1705 {
1706 PTIMER_ENTRY Timer;
1707 LARGE_INTEGER CurrentTime;
1708 PLIST_ENTRY EnumEntry;
1709 BOOLEAN GotMessage;
1710
1711 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1712 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1713
1714 IntLockMessageQueue(MessageQueue);
1715 KeQuerySystemTime(&CurrentTime);
1716 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1717 EnumEntry = MessageQueue->TimerListHead.Flink;
1718 GotMessage = FALSE;
1719 while (EnumEntry != &MessageQueue->TimerListHead)
1720 {
1721 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1722 TIMER_ENTRY, ListEntry);
1723 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1724 Timer->ExpiryTime.QuadPart);
1725 EnumEntry = EnumEntry->Flink;
1726 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1727 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1728 (MsgFilterMin <= Timer->Msg &&
1729 Timer->Msg <= MsgFilterMax)))
1730 {
1731 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1732 {
1733 DPRINT("Timer is expired\n");
1734 GotMessage = TRUE;
1735 break;
1736 }
1737 else
1738 {
1739 DPRINT("No need to check later timers\n");
1740 break;
1741 }
1742 }
1743 else
1744 {
1745 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1746 Timer, Timer->Wnd, Timer->Msg, WndFilter, MsgFilterMin, MsgFilterMax);
1747 }
1748 }
1749
1750 if (! GotMessage)
1751 {
1752 DPRINT("No timer pending\n");
1753 IntUnLockMessageQueue(MessageQueue);
1754 return FALSE;
1755 }
1756
1757 Msg->hwnd = Timer->Wnd;
1758 Msg->message = Timer->Msg;
1759 Msg->wParam = (WPARAM) Timer->IDEvent;
1760 Msg->lParam = (LPARAM) Timer->TimerFunc;
1761
1762 if (Restart)
1763 {
1764 RemoveEntryList(&Timer->ListEntry);
1765 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1766 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1767 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1768 InsertTimer(MessageQueue, Timer);
1769
1770 #ifndef NDEBUG
1771 DumpTimerList(MessageQueue);
1772 #endif /* ! defined(NDEBUG) */
1773 }
1774
1775 IntUnLockMessageQueue(MessageQueue);
1776
1777 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1778 Msg->wParam, Msg->lParam);
1779
1780 return TRUE;
1781 }
1782
1783 VOID FASTCALL
1784 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1785 {
1786 PTIMER_ENTRY Timer;
1787 PLIST_ENTRY EnumEntry;
1788
1789 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1790
1791 IntLockMessageQueue(MessageQueue);
1792 EnumEntry = MessageQueue->TimerListHead.Flink;
1793 while (EnumEntry != &MessageQueue->TimerListHead)
1794 {
1795 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1796 EnumEntry = EnumEntry->Flink;
1797 if (Timer->Wnd == Wnd)
1798 {
1799 DPRINT("Removing timer %p because its window is going away\n", Timer);
1800 RemoveEntryList(&Timer->ListEntry);
1801 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1802 }
1803 }
1804
1805 #ifndef NDEBUG
1806 DumpTimerList(MessageQueue);
1807 #endif /* ! defined(NDEBUG) */
1808
1809 IntUnLockMessageQueue(MessageQueue);
1810 }
1811
1812 BOOLEAN FASTCALL
1813 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1814 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1815 PLARGE_INTEGER FirstTimerExpiry)
1816 {
1817 PTIMER_ENTRY Timer;
1818 PLIST_ENTRY EnumEntry;
1819
1820 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1821 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1822
1823 IntLockMessageQueue(MessageQueue);
1824 EnumEntry = MessageQueue->TimerListHead.Flink;
1825 while (EnumEntry != &MessageQueue->TimerListHead)
1826 {
1827 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1828 TIMER_ENTRY, ListEntry);
1829 EnumEntry = EnumEntry->Flink;
1830 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1831 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1832 (MsgFilterMin <= Timer->Msg &&
1833 Timer->Msg <= MsgFilterMax)))
1834 {
1835 *FirstTimerExpiry = Timer->ExpiryTime;
1836 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1837 IntUnLockMessageQueue(MessageQueue);
1838 return TRUE;
1839 }
1840 }
1841
1842 IntUnLockMessageQueue(MessageQueue);
1843
1844 return FALSE;
1845 }
1846
1847 /* EOF */