Copy wininet to branch
[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
621 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
622 uMsg, wParam, lParam);
623
624 Msg.hwnd = 0;
625 Msg.message = uMsg;
626 Msg.wParam = wParam;
627 Msg.lParam = lParam;
628 /* FIXME: Initialize time and point. */
629
630 FocusMessageQueue = IntGetFocusMessageQueue();
631 if( !IntGetScreenDC() ) {
632 if( W32kGetPrimitiveMessageQueue() ) {
633 MsqPostMessage(W32kGetPrimitiveMessageQueue(), &Msg, FALSE, QS_KEY);
634 }
635 } else {
636 if (FocusMessageQueue == NULL)
637 {
638 DPRINT("No focus message queue\n");
639 return;
640 }
641
642 if (FocusMessageQueue->FocusWindow != (HWND)0)
643 {
644 Msg.hwnd = FocusMessageQueue->FocusWindow;
645 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
646 MsqPostMessage(FocusMessageQueue, &Msg, FALSE, QS_KEY);
647 }
648 else
649 {
650 DPRINT("Invalid focus window handle\n");
651 }
652 }
653 }
654
655 VOID FASTCALL
656 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
657 {
658 PWINDOW_OBJECT Window;
659 PW32THREAD Win32Thread;
660 PWINSTATION_OBJECT WinSta;
661 MSG Mesg;
662 NTSTATUS Status;
663
664 Status = ObReferenceObjectByPointer (Thread,
665 THREAD_ALL_ACCESS,
666 PsThreadType,
667 KernelMode);
668 if (!NT_SUCCESS(Status))
669 return;
670
671 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
672 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
673 {
674 ObDereferenceObject ((PETHREAD)Thread);
675 return;
676 }
677
678 WinSta = Win32Thread->Desktop->WindowStation;
679 Status = ObmReferenceObjectByHandle(WinSta->HandleTable,
680 hWnd, otWindow, (PVOID*)&Window);
681 if (!NT_SUCCESS(Status))
682 {
683 ObDereferenceObject ((PETHREAD)Thread);
684 return;
685 }
686
687 Mesg.hwnd = hWnd;
688 Mesg.message = WM_HOTKEY;
689 Mesg.wParam = wParam;
690 Mesg.lParam = lParam;
691 // Mesg.pt.x = PsGetWin32Process()->WindowStation->SystemCursor.x;
692 // Mesg.pt.y = PsGetWin32Process()->WindowStation->SystemCursor.y;
693 // KeQueryTickCount(&LargeTickCount);
694 // Mesg.time = LargeTickCount.u.LowPart;
695 MsqPostMessage(Window->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
696 ObmDereferenceObject(Window);
697 ObDereferenceObject (Thread);
698
699 // IntLockMessageQueue(pThread->MessageQueue);
700 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
701 // &Message->ListEntry);
702 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
703 // IntUnLockMessageQueue(pThread->MessageQueue);
704
705 }
706
707 PUSER_MESSAGE FASTCALL
708 MsqCreateMessage(LPMSG Msg, BOOLEAN FreeLParam)
709 {
710 PUSER_MESSAGE Message;
711
712 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
713 if (!Message)
714 {
715 return NULL;
716 }
717
718 Message->FreeLParam = FreeLParam;
719 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
720
721 return Message;
722 }
723
724 VOID FASTCALL
725 MsqDestroyMessage(PUSER_MESSAGE Message)
726 {
727 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
728 }
729
730 VOID FASTCALL
731 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
732 {
733 PLIST_ENTRY ListEntry;
734 PUSER_SENT_MESSAGE_NOTIFY Message;
735
736 IntLockMessageQueue(MessageQueue);
737 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
738 {
739 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
740 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
741 ListEntry);
742 IntUnLockMessageQueue(MessageQueue);
743
744 IntCallSentMessageCallback(Message->CompletionCallback,
745 Message->hWnd,
746 Message->Msg,
747 Message->CompletionCallbackContext,
748 Message->Result);
749
750 IntLockMessageQueue(MessageQueue);
751 }
752 IntUnLockMessageQueue(MessageQueue);
753 }
754
755 BOOLEAN FASTCALL
756 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
757 {
758 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
759 }
760
761 BOOLEAN FASTCALL
762 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
763 {
764 PUSER_SENT_MESSAGE Message;
765 PLIST_ENTRY Entry;
766 LRESULT Result;
767 BOOL SenderReturned;
768 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage;
769
770 IntLockMessageQueue(MessageQueue);
771 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
772 {
773 IntUnLockMessageQueue(MessageQueue);
774 return(FALSE);
775 }
776
777 /* remove it from the list of pending messages */
778 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
779 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
780
781 /* insert it to the list of messages that are currently dispatched by this
782 message queue */
783 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
784 &Message->ListEntry);
785
786 IntUnLockMessageQueue(MessageQueue);
787
788 /* Call the window procedure. */
789 Result = IntSendMessage(Message->Msg.hwnd,
790 Message->Msg.message,
791 Message->Msg.wParam,
792 Message->Msg.lParam);
793
794 /* remove the message from the local dispatching list, because it doesn't need
795 to be cleaned up on thread termination anymore */
796 IntLockMessageQueue(MessageQueue);
797 RemoveEntryList(&Message->ListEntry);
798 IntUnLockMessageQueue(MessageQueue);
799
800 /* remove the message from the dispatching list, so lock the sender's message queue */
801 IntLockMessageQueue(Message->SenderQueue);
802
803 SenderReturned = (Message->DispatchingListEntry.Flink == NULL);
804 if(!SenderReturned)
805 {
806 /* only remove it from the dispatching list if not already removed by a timeout */
807 RemoveEntryList(&Message->DispatchingListEntry);
808 }
809 /* still keep the sender's message queue locked, so the sender can't exit the
810 MsqSendMessage() function (if timed out) */
811
812 /* Let the sender know the result. */
813 if (Message->Result != NULL)
814 {
815 *Message->Result = Result;
816 }
817
818 /* Notify the sender. */
819 if (Message->CompletionEvent != NULL)
820 {
821 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
822 }
823
824 /* unlock the sender's message queue, the safe operation is done */
825 IntUnLockMessageQueue(Message->SenderQueue);
826
827 /* Notify the sender if they specified a callback. */
828 if (!SenderReturned && Message->CompletionCallback != NULL)
829 {
830 if(!(NotifyMessage = ExAllocatePoolWithTag(NonPagedPool,
831 sizeof(USER_SENT_MESSAGE_NOTIFY), TAG_USRMSG)))
832 {
833 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
834 goto Notified;
835 }
836 NotifyMessage->CompletionCallback =
837 Message->CompletionCallback;
838 NotifyMessage->CompletionCallbackContext =
839 Message->CompletionCallbackContext;
840 NotifyMessage->Result = Result;
841 NotifyMessage->hWnd = Message->Msg.hwnd;
842 NotifyMessage->Msg = Message->Msg.message;
843 MsqSendNotifyMessage(Message->SenderQueue, NotifyMessage);
844 }
845
846 Notified:
847
848 /* dereference both sender and our queue */
849 IntDereferenceMessageQueue(MessageQueue);
850 IntDereferenceMessageQueue(Message->SenderQueue);
851
852 /* free the message */
853 ExFreePool(Message);
854 return(TRUE);
855 }
856
857 VOID STDCALL
858 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
859 {
860 PUSER_SENT_MESSAGE SentMessage;
861 PUSER_MESSAGE PostedMessage;
862 PUSER_MESSAGE_QUEUE MessageQueue;
863 PLIST_ENTRY CurrentEntry, ListHead;
864 PWINDOW_OBJECT Window = pWindow;
865
866 ASSERT(Window);
867
868 MessageQueue = Window->MessageQueue;
869 ASSERT(MessageQueue);
870
871 IntLockMessageQueue(MessageQueue);
872
873 /* remove the posted messages for this window */
874 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
875 ListHead = &MessageQueue->PostedMessagesListHead;
876 while (CurrentEntry != ListHead)
877 {
878 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
879 ListEntry);
880 if (PostedMessage->Msg.hwnd == Window->Self)
881 {
882 RemoveEntryList(&PostedMessage->ListEntry);
883 MsqDestroyMessage(PostedMessage);
884 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
885 }
886 else
887 {
888 CurrentEntry = CurrentEntry->Flink;
889 }
890 }
891
892 /* remove the sent messages for this window */
893 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
894 ListHead = &MessageQueue->SentMessagesListHead;
895 while (CurrentEntry != ListHead)
896 {
897 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
898 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
899 ListEntry);
900 if(SentMessage->Msg.hwnd == Window->Self)
901 {
902 IntLockMessageQueue(SentMessage->SenderQueue);
903 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
904
905 /* remove the message from the dispatching list */
906 if(SentMessage->DispatchingListEntry.Flink != NULL)
907 {
908 RemoveEntryList(&SentMessage->DispatchingListEntry);
909 }
910
911 /* wake the sender's thread */
912 if (SentMessage->CompletionEvent != NULL)
913 {
914 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
915 }
916 IntUnLockMessageQueue(SentMessage->SenderQueue);
917
918 /* dereference our and the sender's message queue */
919 IntDereferenceMessageQueue(MessageQueue);
920 IntDereferenceMessageQueue(SentMessage->SenderQueue);
921
922 /* free the message */
923 ExFreePool(SentMessage);
924
925 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
926 }
927 else
928 {
929 CurrentEntry = CurrentEntry->Flink;
930 }
931 }
932 IntUnLockMessageQueue(MessageQueue);
933 }
934
935 VOID FASTCALL
936 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
937 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
938 {
939 IntLockMessageQueue(MessageQueue);
940 InsertTailList(&MessageQueue->NotifyMessagesListHead,
941 &NotifyMessage->ListEntry);
942 MessageQueue->QueueBits |= QS_SENDMESSAGE;
943 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
944 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
945 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
946 IntUnLockMessageQueue(MessageQueue);
947 }
948
949 NTSTATUS FASTCALL
950 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
951 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
952 UINT uTimeout, BOOL Block, ULONG_PTR *uResult)
953 {
954 PUSER_SENT_MESSAGE Message;
955 KEVENT CompletionEvent;
956 NTSTATUS WaitStatus;
957 LRESULT Result;
958 PUSER_MESSAGE_QUEUE ThreadQueue;
959 LARGE_INTEGER Timeout;
960 PLIST_ENTRY Entry;
961
962 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
963 {
964 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
965 return STATUS_INSUFFICIENT_RESOURCES;
966 }
967
968 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
969
970 ThreadQueue = PsGetWin32Thread()->MessageQueue;
971 ASSERT(ThreadQueue != MessageQueue);
972
973 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
974
975 /* FIXME - increase reference counter of sender's message queue here */
976
977 Result = 0;
978 Message->Msg.hwnd = Wnd;
979 Message->Msg.message = Msg;
980 Message->Msg.wParam = wParam;
981 Message->Msg.lParam = lParam;
982 Message->CompletionEvent = &CompletionEvent;
983 Message->Result = &Result;
984 Message->SenderQueue = ThreadQueue;
985 IntReferenceMessageQueue(ThreadQueue);
986 Message->CompletionCallback = NULL;
987
988 IntReferenceMessageQueue(MessageQueue);
989
990 /* add it to the list of pending messages */
991 IntLockMessageQueue(ThreadQueue);
992 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
993 IntUnLockMessageQueue(ThreadQueue);
994
995 /* queue it in the destination's message queue */
996 IntLockMessageQueue(MessageQueue);
997 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
998 IntUnLockMessageQueue(MessageQueue);
999
1000 MessageQueue->QueueBits |= QS_SENDMESSAGE;
1001 MessageQueue->ChangedBits |= QS_SENDMESSAGE;
1002 if (MessageQueue->WakeMask & QS_SENDMESSAGE)
1003 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1004
1005 /* we can't access the Message anymore since it could have already been deleted! */
1006
1007 if(Block)
1008 {
1009 /* don't process messages sent to the thread */
1010 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
1011 FALSE, (uTimeout ? &Timeout : NULL));
1012 if(WaitStatus == STATUS_TIMEOUT)
1013 {
1014 /* look up if the message has not yet dispatched, if so
1015 make sure it can't pass a result and it must not set the completion event anymore */
1016 IntLockMessageQueue(MessageQueue);
1017 Entry = MessageQueue->SentMessagesListHead.Flink;
1018 while (Entry != &MessageQueue->SentMessagesListHead)
1019 {
1020 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1021 == Message)
1022 {
1023 /* we can access Message here, it's secure because the message queue is locked
1024 and the message is still hasn't been dispatched */
1025 Message->CompletionEvent = NULL;
1026 Message->Result = NULL;
1027 break;
1028 }
1029 Entry = Entry->Flink;
1030 }
1031 IntUnLockMessageQueue(MessageQueue);
1032
1033 /* remove from the local dispatching list so the other thread knows,
1034 it can't pass a result and it must not set the completion event anymore */
1035 IntLockMessageQueue(ThreadQueue);
1036 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1037 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1038 {
1039 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1040 == Message)
1041 {
1042 /* we can access Message here, it's secure because the sender's message is locked
1043 and the message has definitely not yet been destroyed, otherwise it would
1044 have been removed from this list by the dispatching routine right after
1045 dispatching the message */
1046 Message->CompletionEvent = NULL;
1047 Message->Result = NULL;
1048 RemoveEntryList(&Message->DispatchingListEntry);
1049 break;
1050 }
1051 Entry = Entry->Flink;
1052 }
1053 IntUnLockMessageQueue(ThreadQueue);
1054
1055 DPRINT("MsqSendMessage (blocked) timed out\n");
1056 }
1057 while (MsqDispatchOneSentMessage(ThreadQueue));
1058 }
1059 else
1060 {
1061 PVOID WaitObjects[2];
1062
1063 WaitObjects[0] = &CompletionEvent;
1064 WaitObjects[1] = ThreadQueue->NewMessages;
1065 do
1066 {
1067 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
1068 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
1069 if(WaitStatus == STATUS_TIMEOUT)
1070 {
1071 /* look up if the message has not yet been dispatched, if so
1072 make sure it can't pass a result and it must not set the completion event anymore */
1073 IntLockMessageQueue(MessageQueue);
1074 Entry = MessageQueue->SentMessagesListHead.Flink;
1075 while (Entry != &MessageQueue->SentMessagesListHead)
1076 {
1077 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
1078 == Message)
1079 {
1080 /* we can access Message here, it's secure because the message queue is locked
1081 and the message is still hasn't been dispatched */
1082 Message->CompletionEvent = NULL;
1083 Message->Result = NULL;
1084 break;
1085 }
1086 Entry = Entry->Flink;
1087 }
1088 IntUnLockMessageQueue(MessageQueue);
1089
1090 /* remove from the local dispatching list so the other thread knows,
1091 it can't pass a result and it must not set the completion event anymore */
1092 IntLockMessageQueue(ThreadQueue);
1093 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
1094 while (Entry != &ThreadQueue->DispatchingMessagesHead)
1095 {
1096 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
1097 == Message)
1098 {
1099 /* we can access Message here, it's secure because the sender's message is locked
1100 and the message has definitely not yet been destroyed, otherwise it would
1101 have been removed from this list by the dispatching routine right after
1102 dispatching the message */
1103 Message->CompletionEvent = NULL;
1104 Message->Result = NULL;
1105 RemoveEntryList(&Message->DispatchingListEntry);
1106 break;
1107 }
1108 Entry = Entry->Flink;
1109 }
1110 IntUnLockMessageQueue(ThreadQueue);
1111
1112 DPRINT("MsqSendMessage timed out\n");
1113 break;
1114 }
1115 while (MsqDispatchOneSentMessage(ThreadQueue));
1116 }
1117 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
1118 }
1119
1120 if(WaitStatus != STATUS_TIMEOUT)
1121 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
1122
1123 return WaitStatus;
1124 }
1125
1126 VOID FASTCALL
1127 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN FreeLParam,
1128 DWORD MessageBits)
1129 {
1130 PUSER_MESSAGE Message;
1131
1132 if(!(Message = MsqCreateMessage(Msg, FreeLParam)))
1133 {
1134 return;
1135 }
1136 IntLockMessageQueue(MessageQueue);
1137 InsertTailList(&MessageQueue->PostedMessagesListHead,
1138 &Message->ListEntry);
1139 MessageQueue->QueueBits |= MessageBits;
1140 MessageQueue->ChangedBits |= MessageBits;
1141 if (MessageQueue->WakeMask & MessageBits)
1142 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1143 IntUnLockMessageQueue(MessageQueue);
1144 }
1145
1146 VOID FASTCALL
1147 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
1148 {
1149 IntLockMessageQueue(MessageQueue);
1150 MessageQueue->QuitPosted = TRUE;
1151 MessageQueue->QuitExitCode = ExitCode;
1152 MessageQueue->QueueBits |= QS_POSTMESSAGE;
1153 MessageQueue->ChangedBits |= QS_POSTMESSAGE;
1154 if (MessageQueue->WakeMask & QS_POSTMESSAGE)
1155 KeSetEvent(MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
1156 IntUnLockMessageQueue(MessageQueue);
1157 }
1158
1159 BOOLEAN STDCALL
1160 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1161 IN BOOLEAN Hardware,
1162 IN BOOLEAN Remove,
1163 IN HWND Wnd,
1164 IN UINT MsgFilterLow,
1165 IN UINT MsgFilterHigh,
1166 OUT PUSER_MESSAGE* Message)
1167 {
1168 PLIST_ENTRY CurrentEntry;
1169 PUSER_MESSAGE CurrentMessage;
1170 PLIST_ENTRY ListHead;
1171
1172 if (Hardware)
1173 {
1174 return(MsqPeekHardwareMessage(MessageQueue, Wnd,
1175 MsgFilterLow, MsgFilterHigh,
1176 Remove, Message));
1177 }
1178
1179 IntLockMessageQueue(MessageQueue);
1180 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1181 ListHead = &MessageQueue->PostedMessagesListHead;
1182 while (CurrentEntry != ListHead)
1183 {
1184 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1185 ListEntry);
1186 if ((Wnd == 0 || Wnd == CurrentMessage->Msg.hwnd) &&
1187 ((MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1188 (MsgFilterLow <= CurrentMessage->Msg.message &&
1189 MsgFilterHigh >= CurrentMessage->Msg.message)))
1190 {
1191 if (Remove)
1192 {
1193 RemoveEntryList(&CurrentMessage->ListEntry);
1194 }
1195 IntUnLockMessageQueue(MessageQueue);
1196 *Message = CurrentMessage;
1197 return(TRUE);
1198 }
1199 CurrentEntry = CurrentEntry->Flink;
1200 }
1201 IntUnLockMessageQueue(MessageQueue);
1202 return(FALSE);
1203 }
1204
1205 NTSTATUS FASTCALL
1206 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, HWND WndFilter,
1207 UINT MsgFilterMin, UINT MsgFilterMax)
1208 {
1209 PVOID WaitObjects[2] = {MessageQueue->NewMessages, &HardwareMessageEvent};
1210 LARGE_INTEGER TimerExpiry;
1211 PLARGE_INTEGER Timeout;
1212
1213 if (MsqGetFirstTimerExpiry(MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, &TimerExpiry))
1214 {
1215 Timeout = &TimerExpiry;
1216 }
1217 else
1218 {
1219 Timeout = NULL;
1220 }
1221
1222 return(KeWaitForMultipleObjects(2,
1223 WaitObjects,
1224 WaitAny,
1225 Executive,
1226 UserMode,
1227 FALSE,
1228 Timeout,
1229 NULL));
1230 }
1231
1232 BOOL FASTCALL
1233 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1234 {
1235 LARGE_INTEGER LargeTickCount;
1236
1237 KeQueryTickCount(&LargeTickCount);
1238 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1239 }
1240
1241 BOOLEAN FASTCALL
1242 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1243 {
1244 LARGE_INTEGER LargeTickCount;
1245 NTSTATUS Status;
1246
1247 MessageQueue->Thread = Thread;
1248 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1249 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1250 InitializeListHead(&MessageQueue->SentMessagesListHead);
1251 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1252 InitializeListHead(&MessageQueue->TimerListHead);
1253 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1254 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1255 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1256 ExInitializeFastMutex(&MessageQueue->Lock);
1257 MessageQueue->QuitPosted = FALSE;
1258 MessageQueue->QuitExitCode = 0;
1259 KeQueryTickCount(&LargeTickCount);
1260 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1261 MessageQueue->FocusWindow = NULL;
1262 MessageQueue->PaintPosted = FALSE;
1263 MessageQueue->PaintCount = 0;
1264 MessageQueue->WakeMask = ~0;
1265 MessageQueue->NewMessagesHandle = NULL;
1266
1267 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1268 NULL, SynchronizationEvent, FALSE);
1269 if (!NT_SUCCESS(Status))
1270 {
1271 return FALSE;
1272 }
1273
1274 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1275 ExEventObjectType, KernelMode,
1276 (PVOID*)&MessageQueue->NewMessages, NULL);
1277 if (!NT_SUCCESS(Status))
1278 {
1279 ZwClose(MessageQueue->NewMessagesHandle);
1280 MessageQueue->NewMessagesHandle = NULL;
1281 return FALSE;
1282 }
1283
1284 return TRUE;
1285 }
1286
1287 VOID FASTCALL
1288 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1289 {
1290 PLIST_ENTRY CurrentEntry;
1291 PUSER_MESSAGE CurrentMessage;
1292 PTIMER_ENTRY CurrentTimer;
1293 PUSER_SENT_MESSAGE CurrentSentMessage;
1294
1295 IntLockMessageQueue(MessageQueue);
1296
1297 /* cleanup posted messages */
1298 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1299 {
1300 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1301 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1302 ListEntry);
1303 MsqDestroyMessage(CurrentMessage);
1304 }
1305
1306 /* remove the messages that have not yet been dispatched */
1307 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1308 {
1309 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1310 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1311 ListEntry);
1312
1313 IntLockMessageQueue(CurrentSentMessage->SenderQueue);
1314 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1315
1316 /* remove the message from the dispatching list */
1317 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1318 {
1319 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1320 }
1321
1322 /* wake the sender's thread */
1323 if (CurrentSentMessage->CompletionEvent != NULL)
1324 {
1325 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1326 }
1327 IntUnLockMessageQueue(CurrentSentMessage->SenderQueue);
1328
1329 /* dereference our and the sender's message queue */
1330 IntDereferenceMessageQueue(MessageQueue);
1331 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1332
1333 /* free the message */
1334 ExFreePool(CurrentSentMessage);
1335 }
1336
1337 /* cleanup timers */
1338 while (! IsListEmpty(&MessageQueue->TimerListHead))
1339 {
1340 CurrentEntry = RemoveHeadList(&MessageQueue->TimerListHead);
1341 CurrentTimer = CONTAINING_RECORD(CurrentEntry, TIMER_ENTRY, ListEntry);
1342 ExFreeToPagedLookasideList(&TimerLookasideList, CurrentTimer);
1343 }
1344
1345 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1346 ExitThread() was called in a SendMessage() umode callback */
1347 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1348 {
1349 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1350 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1351 ListEntry);
1352
1353 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1354
1355 /* wake the sender's thread */
1356 if (CurrentSentMessage->CompletionEvent != NULL)
1357 {
1358 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1359 }
1360
1361 /* dereference our and the sender's message queue */
1362 IntDereferenceMessageQueue(MessageQueue);
1363 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1364
1365 /* free the message */
1366 ExFreePool(CurrentSentMessage);
1367 }
1368
1369 /* tell other threads not to bother returning any info to us */
1370 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1371 {
1372 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1373 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1374 DispatchingListEntry);
1375 CurrentSentMessage->CompletionEvent = NULL;
1376 CurrentSentMessage->Result = NULL;
1377
1378 /* do NOT dereference our message queue as it might get attempted to be
1379 locked later */
1380 }
1381
1382 IntUnLockMessageQueue(MessageQueue);
1383 }
1384
1385 PUSER_MESSAGE_QUEUE FASTCALL
1386 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1387 {
1388 PUSER_MESSAGE_QUEUE MessageQueue;
1389
1390 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(PagedPool,
1391 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1392 TAG_MSGQ);
1393
1394 if (!MessageQueue)
1395 {
1396 return NULL;
1397 }
1398
1399 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1400 /* hold at least one reference until it'll be destroyed */
1401 IntReferenceMessageQueue(MessageQueue);
1402 /* initialize the queue */
1403 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1404 {
1405 IntDereferenceMessageQueue(MessageQueue);
1406 return NULL;
1407 }
1408
1409 return MessageQueue;
1410 }
1411
1412 VOID FASTCALL
1413 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1414 {
1415 PDESKTOP_OBJECT desk;
1416
1417 /* remove the message queue from any desktops */
1418 if ((desk = (PDESKTOP_OBJECT)InterlockedExchange((LONG*)&MessageQueue->Desktop, 0)))
1419 {
1420 InterlockedExchange((LONG*)&desk->ActiveMessageQueue, 0);
1421 IntDereferenceMessageQueue(MessageQueue);
1422 }
1423
1424 /* if this is the primitive message queue, deregister it */
1425 if (MessageQueue == W32kGetPrimitiveMessageQueue())
1426 W32kUnregisterPrimitiveMessageQueue();
1427
1428 /* clean it up */
1429 MsqCleanupMessageQueue(MessageQueue);
1430
1431 /* decrease the reference counter, if it hits zero, the queue will be freed */
1432 IntDereferenceMessageQueue(MessageQueue);
1433 }
1434
1435 PHOOKTABLE FASTCALL
1436 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue)
1437 {
1438 return Queue->Hooks;
1439 }
1440
1441 VOID FASTCALL
1442 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue, PHOOKTABLE Hooks)
1443 {
1444 Queue->Hooks = Hooks;
1445 }
1446
1447 LPARAM FASTCALL
1448 MsqSetMessageExtraInfo(LPARAM lParam)
1449 {
1450 LPARAM Ret;
1451 PUSER_MESSAGE_QUEUE MessageQueue;
1452
1453 MessageQueue = PsGetWin32Thread()->MessageQueue;
1454 if(!MessageQueue)
1455 {
1456 return 0;
1457 }
1458
1459 Ret = MessageQueue->ExtraInfo;
1460 MessageQueue->ExtraInfo = lParam;
1461
1462 return Ret;
1463 }
1464
1465 LPARAM FASTCALL
1466 MsqGetMessageExtraInfo(VOID)
1467 {
1468 PUSER_MESSAGE_QUEUE MessageQueue;
1469
1470 MessageQueue = PsGetWin32Thread()->MessageQueue;
1471 if(!MessageQueue)
1472 {
1473 return 0;
1474 }
1475
1476 return MessageQueue->ExtraInfo;
1477 }
1478
1479 HWND FASTCALL
1480 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1481 {
1482 HWND Prev;
1483
1484 switch(Type)
1485 {
1486 case MSQ_STATE_CAPTURE:
1487 Prev = MessageQueue->CaptureWindow;
1488 MessageQueue->CaptureWindow = hWnd;
1489 return Prev;
1490 case MSQ_STATE_ACTIVE:
1491 Prev = MessageQueue->ActiveWindow;
1492 MessageQueue->ActiveWindow = hWnd;
1493 return Prev;
1494 case MSQ_STATE_FOCUS:
1495 Prev = MessageQueue->FocusWindow;
1496 MessageQueue->FocusWindow = hWnd;
1497 return Prev;
1498 case MSQ_STATE_MENUOWNER:
1499 Prev = MessageQueue->MenuOwner;
1500 MessageQueue->MenuOwner = hWnd;
1501 return Prev;
1502 case MSQ_STATE_MOVESIZE:
1503 Prev = MessageQueue->MoveSize;
1504 MessageQueue->MoveSize = hWnd;
1505 return Prev;
1506 case MSQ_STATE_CARET:
1507 ASSERT(MessageQueue->CaretInfo);
1508 Prev = MessageQueue->CaretInfo->hWnd;
1509 MessageQueue->CaretInfo->hWnd = hWnd;
1510 return Prev;
1511 }
1512
1513 return NULL;
1514 }
1515
1516 #ifndef NDEBUG
1517 static VOID FASTCALL
1518 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue)
1519 {
1520 PLIST_ENTRY Current;
1521 PTIMER_ENTRY Timer;
1522
1523 Current = MessageQueue->TimerListHead.Flink;
1524 if (Current == &MessageQueue->TimerListHead)
1525 {
1526 DPRINT("timer list is empty for queue %p\n", MessageQueue);
1527 }
1528 while (Current != &MessageQueue->TimerListHead)
1529 {
1530 Timer = CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry);
1531 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1532 MessageQueue, Timer, Timer->ExpiryTime.QuadPart, Timer->Wnd, Timer->IDEvent,
1533 Timer->Period, Timer->TimerFunc, Timer->Msg);
1534 Current = Current->Flink;
1535 }
1536 }
1537 #endif /* ! defined(NDEBUG) */
1538
1539 /* Must have the message queue locked while calling this */
1540 static VOID FASTCALL
1541 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue, PTIMER_ENTRY NewTimer)
1542 {
1543 PLIST_ENTRY Current;
1544
1545 Current = MessageQueue->TimerListHead.Flink;
1546 while (Current != &MessageQueue->TimerListHead)
1547 {
1548 if (NewTimer->ExpiryTime.QuadPart <
1549 CONTAINING_RECORD(Current, TIMER_ENTRY, ListEntry)->ExpiryTime.QuadPart)
1550 {
1551 break;
1552 }
1553 Current = Current->Flink;
1554 }
1555
1556 InsertTailList(Current, &NewTimer->ListEntry);
1557 }
1558
1559 /* Must have the message queue locked while calling this */
1560 static PTIMER_ENTRY FASTCALL
1561 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd, UINT_PTR IDEvent, UINT Msg)
1562 {
1563 PTIMER_ENTRY Timer;
1564 PLIST_ENTRY EnumEntry;
1565
1566 /* Remove timer if already in the queue */
1567 EnumEntry = MessageQueue->TimerListHead.Flink;
1568 while (EnumEntry != &MessageQueue->TimerListHead)
1569 {
1570 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1571 EnumEntry = EnumEntry->Flink;
1572
1573 if (Timer->Wnd == Wnd &&
1574 Timer->IDEvent == IDEvent &&
1575 Timer->Msg == Msg)
1576 {
1577 RemoveEntryList(&Timer->ListEntry);
1578 return Timer;
1579 }
1580 }
1581
1582 return NULL;
1583 }
1584
1585 BOOLEAN FASTCALL
1586 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1587 UINT_PTR IDEvent, UINT Period, TIMERPROC TimerFunc,
1588 UINT Msg)
1589 {
1590 PTIMER_ENTRY Timer;
1591 LARGE_INTEGER CurrentTime;
1592
1593 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1594 MessageQueue, Wnd, IDEvent, Period, TimerFunc, Msg);
1595
1596 IntLockMessageQueue(MessageQueue);
1597 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1598 if (NULL == Timer)
1599 {
1600 Timer = ExAllocateFromPagedLookasideList(&TimerLookasideList);
1601 if (NULL == Timer)
1602 {
1603 IntUnLockMessageQueue(MessageQueue);
1604 DPRINT1("Failed to allocate timer entry\n");
1605 return FALSE;
1606 }
1607 DPRINT("Allocated new timer entry %p\n", Timer);
1608 Timer->Wnd = Wnd;
1609 Timer->IDEvent = IDEvent;
1610 Timer->Msg = Msg;
1611 }
1612 else
1613 {
1614 DPRINT("Updating existing timer entry %p\n", Timer);
1615 }
1616
1617 KeQuerySystemTime(&CurrentTime);
1618 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1619 (ULONGLONG) Period * (ULONGLONG) 10000;
1620 Timer->Period = Period;
1621 Timer->TimerFunc = TimerFunc;
1622 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime.QuadPart,
1623 Timer->ExpiryTime.QuadPart);
1624
1625 InsertTimer(MessageQueue, Timer);
1626
1627 #ifndef NDEBUG
1628 DumpTimerList(MessageQueue);
1629 #endif /* ! defined(NDEBUG) */
1630
1631 IntUnLockMessageQueue(MessageQueue);
1632
1633 return TRUE;
1634 }
1635
1636 BOOLEAN FASTCALL
1637 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd,
1638 UINT_PTR IDEvent, UINT Msg)
1639 {
1640 PTIMER_ENTRY Timer;
1641
1642 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1643 MessageQueue, Wnd, IDEvent, Msg);
1644
1645 IntLockMessageQueue(MessageQueue);
1646 Timer = RemoveTimer(MessageQueue, Wnd, IDEvent, Msg);
1647
1648 if (NULL == Timer)
1649 {
1650 DPRINT("Failed to remove timer from list, not found\n");
1651 }
1652 else
1653 {
1654 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1655 }
1656
1657 #ifndef NDEBUG
1658 DumpTimerList(MessageQueue);
1659 #endif /* ! defined(NDEBUG) */
1660
1661 IntUnLockMessageQueue(MessageQueue);
1662
1663 return NULL != Timer;
1664 }
1665
1666 BOOLEAN FASTCALL
1667 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue,
1668 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1669 MSG *Msg, BOOLEAN Restart)
1670 {
1671 PTIMER_ENTRY Timer;
1672 LARGE_INTEGER CurrentTime;
1673 PLIST_ENTRY EnumEntry;
1674 BOOLEAN GotMessage;
1675
1676 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1677 MessageQueue, Msg, Restart ? "TRUE" : "FALSE");
1678
1679 IntLockMessageQueue(MessageQueue);
1680 KeQuerySystemTime(&CurrentTime);
1681 DPRINT("Current time %I64d\n", CurrentTime.QuadPart);
1682 EnumEntry = MessageQueue->TimerListHead.Flink;
1683 GotMessage = FALSE;
1684 while (EnumEntry != &MessageQueue->TimerListHead)
1685 {
1686 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1687 TIMER_ENTRY, ListEntry);
1688 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer, Timer->Wnd,
1689 Timer->ExpiryTime.QuadPart);
1690 EnumEntry = EnumEntry->Flink;
1691 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1692 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1693 (MsgFilterMin <= Timer->Msg &&
1694 Timer->Msg <= MsgFilterMax)))
1695 {
1696 if (Timer->ExpiryTime.QuadPart <= CurrentTime.QuadPart)
1697 {
1698 DPRINT("Timer is expired\n");
1699 GotMessage = TRUE;
1700 break;
1701 }
1702 else
1703 {
1704 DPRINT("No need to check later timers\n");
1705 break;
1706 }
1707 }
1708 else
1709 {
1710 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1711 Timer, Timer->Wnd, Timer->Msg, WndFilter, MsgFilterMin, MsgFilterMax);
1712 }
1713 }
1714
1715 if (! GotMessage)
1716 {
1717 DPRINT("No timer pending\n");
1718 IntUnLockMessageQueue(MessageQueue);
1719 return FALSE;
1720 }
1721
1722 Msg->hwnd = Timer->Wnd;
1723 Msg->message = Timer->Msg;
1724 Msg->wParam = (WPARAM) Timer->IDEvent;
1725 Msg->lParam = (LPARAM) Timer->TimerFunc;
1726
1727 if (Restart)
1728 {
1729 RemoveEntryList(&Timer->ListEntry);
1730 Timer->ExpiryTime.QuadPart = CurrentTime.QuadPart +
1731 (ULONGLONG) Timer->Period * (ULONGLONG) 10000;
1732 DPRINT("Restarting timer %p expires %I64d\n", Timer, Timer->ExpiryTime.QuadPart);
1733 InsertTimer(MessageQueue, Timer);
1734
1735 #ifndef NDEBUG
1736 DumpTimerList(MessageQueue);
1737 #endif /* ! defined(NDEBUG) */
1738 }
1739
1740 IntUnLockMessageQueue(MessageQueue);
1741
1742 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg->hwnd, Msg->message,
1743 Msg->wParam, Msg->lParam);
1744
1745 return TRUE;
1746 }
1747
1748 VOID FASTCALL
1749 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue, HWND Wnd)
1750 {
1751 PTIMER_ENTRY Timer;
1752 PLIST_ENTRY EnumEntry;
1753
1754 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue, Wnd);
1755
1756 IntLockMessageQueue(MessageQueue);
1757 EnumEntry = MessageQueue->TimerListHead.Flink;
1758 while (EnumEntry != &MessageQueue->TimerListHead)
1759 {
1760 Timer = CONTAINING_RECORD(EnumEntry, TIMER_ENTRY, ListEntry);
1761 EnumEntry = EnumEntry->Flink;
1762 if (Timer->Wnd == Wnd)
1763 {
1764 DPRINT("Removing timer %p because its window is going away\n", Timer);
1765 RemoveEntryList(&Timer->ListEntry);
1766 ExFreeToPagedLookasideList(&TimerLookasideList, Timer);
1767 }
1768 }
1769
1770 #ifndef NDEBUG
1771 DumpTimerList(MessageQueue);
1772 #endif /* ! defined(NDEBUG) */
1773
1774 IntUnLockMessageQueue(MessageQueue);
1775 }
1776
1777 BOOLEAN FASTCALL
1778 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue,
1779 HWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax,
1780 PLARGE_INTEGER FirstTimerExpiry)
1781 {
1782 PTIMER_ENTRY Timer;
1783 PLIST_ENTRY EnumEntry;
1784
1785 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1786 MessageQueue, WndFilter, MsgFilterMin, MsgFilterMax, FirstTimerExpiry);
1787
1788 IntLockMessageQueue(MessageQueue);
1789 EnumEntry = MessageQueue->TimerListHead.Flink;
1790 while (EnumEntry != &MessageQueue->TimerListHead)
1791 {
1792 Timer = CONTAINING_RECORD(MessageQueue->TimerListHead.Flink,
1793 TIMER_ENTRY, ListEntry);
1794 EnumEntry = EnumEntry->Flink;
1795 if ((NULL == WndFilter || Timer->Wnd == WndFilter) &&
1796 ((MsgFilterMin == 0 && MsgFilterMax == 0) ||
1797 (MsgFilterMin <= Timer->Msg &&
1798 Timer->Msg <= MsgFilterMax)))
1799 {
1800 *FirstTimerExpiry = Timer->ExpiryTime;
1801 DPRINT("First timer expires %I64d\n", Timer->ExpiryTime);
1802 IntUnLockMessageQueue(MessageQueue);
1803 return TRUE;
1804 }
1805 }
1806
1807 IntUnLockMessageQueue(MessageQueue);
1808
1809 return FALSE;
1810 }
1811
1812 /* EOF */