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