2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
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.
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.
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.
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)
27 * 06-06-2001 CSH Created
30 /* INCLUDES ******************************************************************/
35 #include <win32k/debug1.h>
38 /* GLOBALS *******************************************************************/
40 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
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
;
49 static ULONG
volatile HardwareMessageQueueStamp
= 0;
50 static LIST_ENTRY HardwareMessageQueueHead
;
51 static KMUTEX HardwareMessageQueueLock
;
53 static KEVENT HardwareMessageEvent
;
55 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
56 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
58 #define IntLockSystemMessageQueue(OldIrql) \
59 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
61 #define IntUnLockSystemMessageQueue(OldIrql) \
62 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
64 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
65 KeReleaseMutex(&HardwareMessageQueueLock, Wait)
67 /* FUNCTIONS *****************************************************************/
70 IntMsqSetWakeMask(DWORD WakeMask
)
72 PW32THREAD Win32Thread
;
73 PUSER_MESSAGE_QUEUE MessageQueue
;
74 HANDLE MessageEventHandle
;
76 Win32Thread
= PsGetWin32Thread();
77 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
80 MessageQueue
= Win32Thread
->MessageQueue
;
81 IntLockMessageQueue(MessageQueue
);
82 MessageQueue
->WakeMask
= WakeMask
;
83 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
84 IntUnLockMessageQueue(MessageQueue
);
86 return MessageEventHandle
;
90 IntMsqClearWakeMask(VOID
)
92 PW32THREAD Win32Thread
;
93 PUSER_MESSAGE_QUEUE MessageQueue
;
95 Win32Thread
= PsGetWin32Thread();
96 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
99 MessageQueue
= Win32Thread
->MessageQueue
;
100 IntLockMessageQueue(MessageQueue
);
101 MessageQueue
->WakeMask
= ~0;
102 IntUnLockMessageQueue(MessageQueue
);
108 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
110 IntLockMessageQueue(Queue
);
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
);
121 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
123 IntLockMessageQueue(Queue
);
125 if (Queue
->PaintCount
== 0)
127 Queue
->PaintPosted
= FALSE
;
129 IntUnLockMessageQueue(Queue
);
134 MsqInitializeImpl(VOID
)
136 /*CurrentFocusMessageQueue = NULL;*/
137 InitializeListHead(&HardwareMessageQueueHead
);
138 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
139 KeInitializeSpinLock(&SystemMessageQueueLock
);
140 KeInitializeMutex(&HardwareMessageQueueLock
, 0);
142 ExInitializePagedLookasideList(&MessageLookasideList
,
146 sizeof(USER_MESSAGE
),
149 ExInitializePagedLookasideList(&TimerLookasideList
,
157 return(STATUS_SUCCESS
);
161 MsqInsertSystemMessage(MSG
* Msg
)
163 LARGE_INTEGER LargeTickCount
;
165 ULONG mmov
= (ULONG
)-1;
167 KeQueryTickCount(&LargeTickCount
);
168 Msg
->time
= LargeTickCount
.u
.LowPart
;
170 IntLockSystemMessageQueue(OldIrql
);
172 /* only insert WM_MOUSEMOVE messages if not already in system message queue */
173 if(Msg
->message
== WM_MOUSEMOVE
)
174 mmov
= SystemMessageQueueMouseMove
;
176 if(mmov
!= (ULONG
)-1)
178 /* insert message at the queue head */
179 while (mmov
!= SystemMessageQueueHead
)
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
];
187 SystemMessageQueue
[SystemMessageQueueHead
] = *Msg
;
191 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
193 IntUnLockSystemMessageQueue(OldIrql
);
196 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
197 if(Msg
->message
== WM_MOUSEMOVE
)
198 SystemMessageQueueMouseMove
= SystemMessageQueueTail
;
199 SystemMessageQueueTail
=
200 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
201 SystemMessageQueueCount
++;
203 IntUnLockSystemMessageQueue(OldIrql
);
204 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
208 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
210 PWINSTATION_OBJECT WinStaObject
;
211 PSYSTEM_CURSORINFO CurInfo
;
215 if (PsGetWin32Thread()->Desktop
== NULL
)
219 WinStaObject
= PsGetWin32Thread()->Desktop
->WindowStation
;
221 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
222 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
223 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
227 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
228 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
232 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
233 (dY
<= CurInfo
->DblClickHeight
);
240 CurInfo
->LastBtnDown
= 0;
241 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
242 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
243 CurInfo
->LastClkWnd
= NULL
;
247 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
248 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
249 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
250 CurInfo
->LastBtnDown
= Msg
->time
;
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
)
262 USHORT Msg
= Message
->Msg
.message
;
263 PWINDOW_OBJECT Window
= NULL
;
266 CaptureWin
= IntGetCaptureWindow();
267 if (CaptureWin
== NULL
)
269 if(Msg
== WM_MOUSEWHEEL
)
271 Window
= IntGetWindowObject(IntGetFocusWindow());
275 WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &Window
);
279 IntReferenceWindowObject(Window
);
285 /* FIXME - window messages should go to the right window if no buttons are
287 Window
= IntGetWindowObject(CaptureWin
);
294 RemoveEntryList(&Message
->ListEntry
);
295 if(MessageQueue
->MouseMoveMsg
== Message
)
297 MessageQueue
->MouseMoveMsg
= NULL
;
305 if (Window
->MessageQueue
!= MessageQueue
)
307 if (! FromGlobalQueue
)
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
316 RemoveEntryList(&Message
->ListEntry
);
318 /* remove the pointer for the current WM_MOUSEMOVE message in case we
320 if(MessageQueue
->MouseMoveMsg
== Message
)
322 MessageQueue
->MouseMoveMsg
= NULL
;
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
)
333 if(Window
->MessageQueue
->MouseMoveMsg
)
335 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
337 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
338 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
340 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
341 Window
->MessageQueue
->MouseMoveMsg
= Message
;
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
);
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
);
355 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
358 IntReleaseWindowObject(Window
);
362 /* From here on, we're in the same message queue as the caller! */
364 *ScreenPoint
= Message
->Msg
.pt
;
366 if((hWnd
!= NULL
&& Window
->Self
!= hWnd
) ||
367 ((FilterLow
!= 0 || FilterLow
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
369 /* Reject the message because it doesn't match the filter */
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
);
383 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
385 if(Window
->MessageQueue
->MouseMoveMsg
&&
386 (Window
->MessageQueue
->MouseMoveMsg
!= Message
))
388 /* delete the old message */
389 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
390 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
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
;
398 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
401 IntReleaseWindowObject(Window
);
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
);
411 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
413 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
414 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
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
)
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
;
432 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
434 else if(Window
->MessageQueue
->MouseMoveMsg
== Message
)
436 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
440 IntReleaseWindowObject(Window
);
446 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
,
447 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
448 PUSER_MESSAGE
* Message
)
453 PLIST_ENTRY CurrentEntry
;
454 PWINDOW_OBJECT DesktopWindow
;
455 PVOID WaitObjects
[2];
458 if( !IntGetScreenDC() ||
459 PsGetWin32Thread()->MessageQueue
== W32kGetPrimitiveMessageQueue() )
464 WaitObjects
[1] = MessageQueue
->NewMessages
;
465 WaitObjects
[0] = &HardwareMessageQueueLock
;
468 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
469 UserMode
, FALSE
, NULL
, NULL
);
470 while (MsqDispatchOneSentMessage(MessageQueue
))
475 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
477 DesktopWindow
= IntGetWindowObject(IntGetDesktopWindow());
479 /* Process messages in the message queue itself. */
480 IntLockHardwareMessageQueue(MessageQueue
);
481 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
482 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
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
)
490 Accept
= MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
491 Current
, Remove
, &Freed
,
492 DesktopWindow
, &ScreenPoint
, FALSE
);
497 RemoveEntryList(&Current
->ListEntry
);
499 IntUnLockHardwareMessageQueue(MessageQueue
);
500 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
502 IntReleaseWindowObject(DesktopWindow
);
508 IntUnLockHardwareMessageQueue(MessageQueue
);
510 /* Now try the global queue. */
512 /* Transfer all messages from the DPC accessible queue to the main queue. */
513 IntLockSystemMessageQueue(OldIrql
);
514 while (SystemMessageQueueCount
> 0)
516 PUSER_MESSAGE UserMsg
;
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 */
528 UserMsg
->FreeLParam
= FALSE
;
530 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
531 IntLockSystemMessageQueue(OldIrql
);
534 * we could set this to -1 conditionally if we find one, but
535 * this is more efficient and just as effective.
537 SystemMessageQueueMouseMove
= -1;
538 HardwareMessageQueueStamp
++;
539 IntUnLockSystemMessageQueue(OldIrql
);
541 /* Process messages in the queue until we find one to return. */
542 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
543 while (CurrentEntry
!= &HardwareMessageQueueHead
)
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
)
553 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
554 /* Translate the message. */
555 Accept
= MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
556 Current
, Remove
, &Freed
,
557 DesktopWindow
, &ScreenPoint
, TRUE
);
560 /* Check for no more messages in the system queue. */
561 IntLockSystemMessageQueue(OldIrql
);
562 if (SystemMessageQueueCount
== 0 &&
563 IsListEmpty(&HardwareMessageQueueHead
))
565 KeClearEvent(&HardwareMessageEvent
);
567 IntUnLockSystemMessageQueue(OldIrql
);
570 If we aren't removing the message then add it to the private
575 IntLockHardwareMessageQueue(MessageQueue
);
576 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
578 if(MessageQueue
->MouseMoveMsg
)
580 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
581 ExFreePool(MessageQueue
->MouseMoveMsg
);
583 MessageQueue
->MouseMoveMsg
= Current
;
585 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
586 &Current
->ListEntry
);
587 IntUnLockHardwareMessageQueue(MessageQueue
);
589 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
591 IntReleaseWindowObject(DesktopWindow
);
594 /* If the contents of the queue changed then restart processing. */
595 if (HardwareMessageQueueStamp
!= ActiveStamp
)
597 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
602 IntReleaseWindowObject(DesktopWindow
);
603 /* Check if the system message queue is now empty. */
604 IntLockSystemMessageQueue(OldIrql
);
605 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
607 KeClearEvent(&HardwareMessageEvent
);
609 IntUnLockSystemMessageQueue(OldIrql
);
610 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
616 MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
618 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
620 LARGE_INTEGER LargeTickCount
;
621 KBDLLHOOKSTRUCT KbdHookData
;
623 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
624 uMsg
, wParam
, lParam
);
631 KeQueryTickCount(&LargeTickCount
);
632 Msg
.time
= LargeTickCount
.u
.LowPart
;
633 /* We can't get the Msg.pt point here since we don't know thread
634 (and thus the window station) the message will end up in yet. */
636 KbdHookData
.vkCode
= Msg
.wParam
;
637 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
638 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
639 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
640 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
641 KbdHookData
.time
= Msg
.time
;
642 KbdHookData
.dwExtraInfo
= 0;
643 if (HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
645 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
646 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
650 FocusMessageQueue
= IntGetFocusMessageQueue();
651 if( !IntGetScreenDC() ) {
652 /* FIXME: What to do about Msg.pt here? */
653 if( W32kGetPrimitiveMessageQueue() ) {
654 MsqPostMessage(W32kGetPrimitiveMessageQueue(), &Msg
, FALSE
, QS_KEY
);
657 if (FocusMessageQueue
== NULL
)
659 DPRINT("No focus message queue\n");
663 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
665 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
666 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
667 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
669 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
673 DPRINT("Invalid focus window handle\n");
679 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
681 PWINDOW_OBJECT Window
;
682 PW32THREAD Win32Thread
;
683 PWINSTATION_OBJECT WinSta
;
685 LARGE_INTEGER LargeTickCount
;
688 Status
= ObReferenceObjectByPointer (Thread
,
692 if (!NT_SUCCESS(Status
))
695 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
696 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
698 ObDereferenceObject ((PETHREAD
)Thread
);
702 WinSta
= Win32Thread
->Desktop
->WindowStation
;
703 Status
= ObmReferenceObjectByHandle(WinSta
->HandleTable
,
704 hWnd
, otWindow
, (PVOID
*)&Window
);
705 if (!NT_SUCCESS(Status
))
707 ObDereferenceObject ((PETHREAD
)Thread
);
712 Mesg
.message
= WM_HOTKEY
;
713 Mesg
.wParam
= wParam
;
714 Mesg
.lParam
= lParam
;
715 KeQueryTickCount(&LargeTickCount
);
716 Mesg
.time
= LargeTickCount
.u
.LowPart
;
717 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
718 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
719 ObmDereferenceObject(Window
);
720 ObDereferenceObject (Thread
);
722 // IntLockMessageQueue(pThread->MessageQueue);
723 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
724 // &Message->ListEntry);
725 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
726 // IntUnLockMessageQueue(pThread->MessageQueue);
730 PUSER_MESSAGE FASTCALL
731 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
733 PUSER_MESSAGE Message
;
735 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
741 Message
->FreeLParam
= FreeLParam
;
742 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
748 MsqDestroyMessage(PUSER_MESSAGE Message
)
750 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
754 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
756 PLIST_ENTRY ListEntry
;
757 PUSER_SENT_MESSAGE_NOTIFY Message
;
759 IntLockMessageQueue(MessageQueue
);
760 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
762 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
763 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
765 IntUnLockMessageQueue(MessageQueue
);
767 IntCallSentMessageCallback(Message
->CompletionCallback
,
770 Message
->CompletionCallbackContext
,
773 IntLockMessageQueue(MessageQueue
);
775 IntUnLockMessageQueue(MessageQueue
);
779 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
781 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
785 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
787 PUSER_SENT_MESSAGE Message
;
791 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
793 IntLockMessageQueue(MessageQueue
);
794 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
796 IntUnLockMessageQueue(MessageQueue
);
800 /* remove it from the list of pending messages */
801 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
802 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
804 /* insert it to the list of messages that are currently dispatched by this
806 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
807 &Message
->ListEntry
);
809 IntUnLockMessageQueue(MessageQueue
);
811 if (Message
->HookMessage
)
813 Result
= HOOK_CallHooks(Message
->Msg
.message
,
814 (INT
) Message
->Msg
.hwnd
,
816 Message
->Msg
.lParam
);
820 /* Call the window procedure. */
821 Result
= IntSendMessage(Message
->Msg
.hwnd
,
822 Message
->Msg
.message
,
824 Message
->Msg
.lParam
);
827 /* remove the message from the local dispatching list, because it doesn't need
828 to be cleaned up on thread termination anymore */
829 IntLockMessageQueue(MessageQueue
);
830 RemoveEntryList(&Message
->ListEntry
);
831 IntUnLockMessageQueue(MessageQueue
);
833 /* remove the message from the dispatching list, so lock the sender's message queue */
834 IntLockMessageQueue(Message
->SenderQueue
);
836 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
839 /* only remove it from the dispatching list if not already removed by a timeout */
840 RemoveEntryList(&Message
->DispatchingListEntry
);
842 /* still keep the sender's message queue locked, so the sender can't exit the
843 MsqSendMessage() function (if timed out) */
845 /* Let the sender know the result. */
846 if (Message
->Result
!= NULL
)
848 *Message
->Result
= Result
;
851 /* Notify the sender. */
852 if (Message
->CompletionEvent
!= NULL
)
854 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
857 /* unlock the sender's message queue, the safe operation is done */
858 IntUnLockMessageQueue(Message
->SenderQueue
);
860 /* Notify the sender if they specified a callback. */
861 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
863 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
864 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
866 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
869 NotifyMessage
->CompletionCallback
=
870 Message
->CompletionCallback
;
871 NotifyMessage
->CompletionCallbackContext
=
872 Message
->CompletionCallbackContext
;
873 NotifyMessage
->Result
= Result
;
874 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
875 NotifyMessage
->Msg
= Message
->Msg
.message
;
876 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
881 /* dereference both sender and our queue */
882 IntDereferenceMessageQueue(MessageQueue
);
883 IntDereferenceMessageQueue(Message
->SenderQueue
);
885 /* free the message */
891 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
893 PUSER_SENT_MESSAGE SentMessage
;
894 PUSER_MESSAGE PostedMessage
;
895 PUSER_MESSAGE_QUEUE MessageQueue
;
896 PLIST_ENTRY CurrentEntry
, ListHead
;
897 PWINDOW_OBJECT Window
= pWindow
;
901 MessageQueue
= Window
->MessageQueue
;
902 ASSERT(MessageQueue
);
904 IntLockMessageQueue(MessageQueue
);
906 /* remove the posted messages for this window */
907 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
908 ListHead
= &MessageQueue
->PostedMessagesListHead
;
909 while (CurrentEntry
!= ListHead
)
911 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
913 if (PostedMessage
->Msg
.hwnd
== Window
->Self
)
915 RemoveEntryList(&PostedMessage
->ListEntry
);
916 MsqDestroyMessage(PostedMessage
);
917 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
921 CurrentEntry
= CurrentEntry
->Flink
;
925 /* remove the sent messages for this window */
926 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
927 ListHead
= &MessageQueue
->SentMessagesListHead
;
928 while (CurrentEntry
!= ListHead
)
930 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
931 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
933 if(SentMessage
->Msg
.hwnd
== Window
->Self
)
935 IntLockMessageQueue(SentMessage
->SenderQueue
);
936 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
938 /* remove the message from the dispatching list */
939 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
941 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
944 /* wake the sender's thread */
945 if (SentMessage
->CompletionEvent
!= NULL
)
947 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
949 IntUnLockMessageQueue(SentMessage
->SenderQueue
);
951 /* dereference our and the sender's message queue */
952 IntDereferenceMessageQueue(MessageQueue
);
953 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
955 /* free the message */
956 ExFreePool(SentMessage
);
958 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
962 CurrentEntry
= CurrentEntry
->Flink
;
965 IntUnLockMessageQueue(MessageQueue
);
969 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
970 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
972 IntLockMessageQueue(MessageQueue
);
973 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
974 &NotifyMessage
->ListEntry
);
975 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
976 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
977 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
978 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
979 IntUnLockMessageQueue(MessageQueue
);
983 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
984 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
985 UINT uTimeout
, BOOL Block
, BOOL HookMessage
,
988 PUSER_SENT_MESSAGE Message
;
989 KEVENT CompletionEvent
;
992 PUSER_MESSAGE_QUEUE ThreadQueue
;
993 LARGE_INTEGER Timeout
;
996 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
998 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
999 return STATUS_INSUFFICIENT_RESOURCES
;
1002 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1004 ThreadQueue
= PsGetWin32Thread()->MessageQueue
;
1005 ASSERT(ThreadQueue
!= MessageQueue
);
1007 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1009 /* FIXME - increase reference counter of sender's message queue here */
1012 Message
->Msg
.hwnd
= Wnd
;
1013 Message
->Msg
.message
= Msg
;
1014 Message
->Msg
.wParam
= wParam
;
1015 Message
->Msg
.lParam
= lParam
;
1016 Message
->CompletionEvent
= &CompletionEvent
;
1017 Message
->Result
= &Result
;
1018 Message
->SenderQueue
= ThreadQueue
;
1019 IntReferenceMessageQueue(ThreadQueue
);
1020 Message
->CompletionCallback
= NULL
;
1021 Message
->HookMessage
= HookMessage
;
1023 IntReferenceMessageQueue(MessageQueue
);
1025 /* add it to the list of pending messages */
1026 IntLockMessageQueue(ThreadQueue
);
1027 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1028 IntUnLockMessageQueue(ThreadQueue
);
1030 /* queue it in the destination's message queue */
1031 IntLockMessageQueue(MessageQueue
);
1032 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1033 IntUnLockMessageQueue(MessageQueue
);
1035 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1036 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1037 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1038 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1040 /* we can't access the Message anymore since it could have already been deleted! */
1044 /* don't process messages sent to the thread */
1045 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1046 FALSE
, (uTimeout
? &Timeout
: NULL
));
1047 if(WaitStatus
== STATUS_TIMEOUT
)
1049 /* look up if the message has not yet dispatched, if so
1050 make sure it can't pass a result and it must not set the completion event anymore */
1051 IntLockMessageQueue(MessageQueue
);
1052 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1053 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1055 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1058 /* we can access Message here, it's secure because the message queue is locked
1059 and the message is still hasn't been dispatched */
1060 Message
->CompletionEvent
= NULL
;
1061 Message
->Result
= NULL
;
1064 Entry
= Entry
->Flink
;
1066 IntUnLockMessageQueue(MessageQueue
);
1068 /* remove from the local dispatching list so the other thread knows,
1069 it can't pass a result and it must not set the completion event anymore */
1070 IntLockMessageQueue(ThreadQueue
);
1071 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1072 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1074 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1077 /* we can access Message here, it's secure because the sender's message is locked
1078 and the message has definitely not yet been destroyed, otherwise it would
1079 have been removed from this list by the dispatching routine right after
1080 dispatching the message */
1081 Message
->CompletionEvent
= NULL
;
1082 Message
->Result
= NULL
;
1083 RemoveEntryList(&Message
->DispatchingListEntry
);
1086 Entry
= Entry
->Flink
;
1088 IntUnLockMessageQueue(ThreadQueue
);
1090 DPRINT("MsqSendMessage (blocked) timed out\n");
1092 while (MsqDispatchOneSentMessage(ThreadQueue
));
1096 PVOID WaitObjects
[2];
1098 WaitObjects
[0] = &CompletionEvent
;
1099 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1102 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1103 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1104 if(WaitStatus
== STATUS_TIMEOUT
)
1106 /* look up if the message has not yet been dispatched, if so
1107 make sure it can't pass a result and it must not set the completion event anymore */
1108 IntLockMessageQueue(MessageQueue
);
1109 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1110 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1112 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1115 /* we can access Message here, it's secure because the message queue is locked
1116 and the message is still hasn't been dispatched */
1117 Message
->CompletionEvent
= NULL
;
1118 Message
->Result
= NULL
;
1121 Entry
= Entry
->Flink
;
1123 IntUnLockMessageQueue(MessageQueue
);
1125 /* remove from the local dispatching list so the other thread knows,
1126 it can't pass a result and it must not set the completion event anymore */
1127 IntLockMessageQueue(ThreadQueue
);
1128 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1129 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1131 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1134 /* we can access Message here, it's secure because the sender's message is locked
1135 and the message has definitely not yet been destroyed, otherwise it would
1136 have been removed from this list by the dispatching routine right after
1137 dispatching the message */
1138 Message
->CompletionEvent
= NULL
;
1139 Message
->Result
= NULL
;
1140 RemoveEntryList(&Message
->DispatchingListEntry
);
1143 Entry
= Entry
->Flink
;
1145 IntUnLockMessageQueue(ThreadQueue
);
1147 DPRINT("MsqSendMessage timed out\n");
1150 while (MsqDispatchOneSentMessage(ThreadQueue
));
1152 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1155 if(WaitStatus
!= STATUS_TIMEOUT
)
1156 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1162 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1165 PUSER_MESSAGE Message
;
1167 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1171 IntLockMessageQueue(MessageQueue
);
1172 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1173 &Message
->ListEntry
);
1174 MessageQueue
->QueueBits
|= MessageBits
;
1175 MessageQueue
->ChangedBits
|= MessageBits
;
1176 if (MessageQueue
->WakeMask
& MessageBits
)
1177 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1178 IntUnLockMessageQueue(MessageQueue
);
1182 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1184 IntLockMessageQueue(MessageQueue
);
1185 MessageQueue
->QuitPosted
= TRUE
;
1186 MessageQueue
->QuitExitCode
= ExitCode
;
1187 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1188 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1189 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1190 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1191 IntUnLockMessageQueue(MessageQueue
);
1195 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1196 IN BOOLEAN Hardware
,
1199 IN UINT MsgFilterLow
,
1200 IN UINT MsgFilterHigh
,
1201 OUT PUSER_MESSAGE
* Message
)
1203 PLIST_ENTRY CurrentEntry
;
1204 PUSER_MESSAGE CurrentMessage
;
1205 PLIST_ENTRY ListHead
;
1209 return(MsqPeekHardwareMessage(MessageQueue
, Wnd
,
1210 MsgFilterLow
, MsgFilterHigh
,
1214 IntLockMessageQueue(MessageQueue
);
1215 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1216 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1217 while (CurrentEntry
!= ListHead
)
1219 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1221 if ((Wnd
== 0 || Wnd
== CurrentMessage
->Msg
.hwnd
) &&
1222 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1223 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1224 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1228 RemoveEntryList(&CurrentMessage
->ListEntry
);
1230 IntUnLockMessageQueue(MessageQueue
);
1231 *Message
= CurrentMessage
;
1234 CurrentEntry
= CurrentEntry
->Flink
;
1236 IntUnLockMessageQueue(MessageQueue
);
1241 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, HWND WndFilter
,
1242 UINT MsgFilterMin
, UINT MsgFilterMax
)
1244 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1245 LARGE_INTEGER TimerExpiry
;
1246 PLARGE_INTEGER Timeout
;
1248 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1250 Timeout
= &TimerExpiry
;
1257 return(KeWaitForMultipleObjects(2,
1268 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1270 LARGE_INTEGER LargeTickCount
;
1272 KeQueryTickCount(&LargeTickCount
);
1273 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1277 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1279 LARGE_INTEGER LargeTickCount
;
1282 MessageQueue
->Thread
= Thread
;
1283 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1284 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1285 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1286 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1287 InitializeListHead(&MessageQueue
->TimerListHead
);
1288 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1289 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1290 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1291 ExInitializeFastMutex(&MessageQueue
->Lock
);
1292 MessageQueue
->QuitPosted
= FALSE
;
1293 MessageQueue
->QuitExitCode
= 0;
1294 KeQueryTickCount(&LargeTickCount
);
1295 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1296 MessageQueue
->FocusWindow
= NULL
;
1297 MessageQueue
->PaintPosted
= FALSE
;
1298 MessageQueue
->PaintCount
= 0;
1299 MessageQueue
->WakeMask
= ~0;
1300 MessageQueue
->NewMessagesHandle
= NULL
;
1302 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1303 NULL
, SynchronizationEvent
, FALSE
);
1304 if (!NT_SUCCESS(Status
))
1309 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1310 ExEventObjectType
, KernelMode
,
1311 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1312 if (!NT_SUCCESS(Status
))
1314 ZwClose(MessageQueue
->NewMessagesHandle
);
1315 MessageQueue
->NewMessagesHandle
= NULL
;
1323 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1325 PLIST_ENTRY CurrentEntry
;
1326 PUSER_MESSAGE CurrentMessage
;
1327 PTIMER_ENTRY CurrentTimer
;
1328 PUSER_SENT_MESSAGE CurrentSentMessage
;
1330 IntLockMessageQueue(MessageQueue
);
1332 /* cleanup posted messages */
1333 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1335 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1336 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1338 MsqDestroyMessage(CurrentMessage
);
1341 /* remove the messages that have not yet been dispatched */
1342 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1344 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1345 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1348 IntLockMessageQueue(CurrentSentMessage
->SenderQueue
);
1349 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1351 /* remove the message from the dispatching list */
1352 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1354 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1357 /* wake the sender's thread */
1358 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1360 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1362 IntUnLockMessageQueue(CurrentSentMessage
->SenderQueue
);
1364 /* dereference our and the sender's message queue */
1365 IntDereferenceMessageQueue(MessageQueue
);
1366 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1368 /* free the message */
1369 ExFreePool(CurrentSentMessage
);
1372 /* cleanup timers */
1373 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1375 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1376 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1377 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1380 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1381 ExitThread() was called in a SendMessage() umode callback */
1382 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1384 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1385 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1388 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1390 /* wake the sender's thread */
1391 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1393 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1396 /* dereference our and the sender's message queue */
1397 IntDereferenceMessageQueue(MessageQueue
);
1398 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1400 /* free the message */
1401 ExFreePool(CurrentSentMessage
);
1404 /* tell other threads not to bother returning any info to us */
1405 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1407 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1408 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1409 DispatchingListEntry
);
1410 CurrentSentMessage
->CompletionEvent
= NULL
;
1411 CurrentSentMessage
->Result
= NULL
;
1413 /* do NOT dereference our message queue as it might get attempted to be
1417 IntUnLockMessageQueue(MessageQueue
);
1420 PUSER_MESSAGE_QUEUE FASTCALL
1421 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1423 PUSER_MESSAGE_QUEUE MessageQueue
;
1425 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(PagedPool
,
1426 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1434 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1435 /* hold at least one reference until it'll be destroyed */
1436 IntReferenceMessageQueue(MessageQueue
);
1437 /* initialize the queue */
1438 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1440 IntDereferenceMessageQueue(MessageQueue
);
1444 return MessageQueue
;
1448 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1450 PDESKTOP_OBJECT desk
;
1452 /* remove the message queue from any desktops */
1453 if ((desk
= (PDESKTOP_OBJECT
)InterlockedExchange((LONG
*)&MessageQueue
->Desktop
, 0)))
1455 InterlockedExchange((LONG
*)&desk
->ActiveMessageQueue
, 0);
1456 IntDereferenceMessageQueue(MessageQueue
);
1459 /* if this is the primitive message queue, deregister it */
1460 if (MessageQueue
== W32kGetPrimitiveMessageQueue())
1461 W32kUnregisterPrimitiveMessageQueue();
1464 MsqCleanupMessageQueue(MessageQueue
);
1466 /* decrease the reference counter, if it hits zero, the queue will be freed */
1467 IntDereferenceMessageQueue(MessageQueue
);
1471 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1473 return Queue
->Hooks
;
1477 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1479 Queue
->Hooks
= Hooks
;
1483 MsqSetMessageExtraInfo(LPARAM lParam
)
1486 PUSER_MESSAGE_QUEUE MessageQueue
;
1488 MessageQueue
= PsGetWin32Thread()->MessageQueue
;
1494 Ret
= MessageQueue
->ExtraInfo
;
1495 MessageQueue
->ExtraInfo
= lParam
;
1501 MsqGetMessageExtraInfo(VOID
)
1503 PUSER_MESSAGE_QUEUE MessageQueue
;
1505 MessageQueue
= PsGetWin32Thread()->MessageQueue
;
1511 return MessageQueue
->ExtraInfo
;
1515 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1521 case MSQ_STATE_CAPTURE
:
1522 Prev
= MessageQueue
->CaptureWindow
;
1523 MessageQueue
->CaptureWindow
= hWnd
;
1525 case MSQ_STATE_ACTIVE
:
1526 Prev
= MessageQueue
->ActiveWindow
;
1527 MessageQueue
->ActiveWindow
= hWnd
;
1529 case MSQ_STATE_FOCUS
:
1530 Prev
= MessageQueue
->FocusWindow
;
1531 MessageQueue
->FocusWindow
= hWnd
;
1533 case MSQ_STATE_MENUOWNER
:
1534 Prev
= MessageQueue
->MenuOwner
;
1535 MessageQueue
->MenuOwner
= hWnd
;
1537 case MSQ_STATE_MOVESIZE
:
1538 Prev
= MessageQueue
->MoveSize
;
1539 MessageQueue
->MoveSize
= hWnd
;
1541 case MSQ_STATE_CARET
:
1542 ASSERT(MessageQueue
->CaretInfo
);
1543 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1544 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1552 static VOID FASTCALL
1553 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1555 PLIST_ENTRY Current
;
1558 Current
= MessageQueue
->TimerListHead
.Flink
;
1559 if (Current
== &MessageQueue
->TimerListHead
)
1561 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1563 while (Current
!= &MessageQueue
->TimerListHead
)
1565 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1566 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1567 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1568 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1569 Current
= Current
->Flink
;
1572 #endif /* ! defined(NDEBUG) */
1574 /* Must have the message queue locked while calling this */
1575 static VOID FASTCALL
1576 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1578 PLIST_ENTRY Current
;
1580 Current
= MessageQueue
->TimerListHead
.Flink
;
1581 while (Current
!= &MessageQueue
->TimerListHead
)
1583 if (NewTimer
->ExpiryTime
.QuadPart
<
1584 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1588 Current
= Current
->Flink
;
1591 InsertTailList(Current
, &NewTimer
->ListEntry
);
1594 /* Must have the message queue locked while calling this */
1595 static PTIMER_ENTRY FASTCALL
1596 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1599 PLIST_ENTRY EnumEntry
;
1601 /* Remove timer if already in the queue */
1602 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1603 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1605 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1606 EnumEntry
= EnumEntry
->Flink
;
1608 if (Timer
->Wnd
== Wnd
&&
1609 Timer
->IDEvent
== IDEvent
&&
1612 RemoveEntryList(&Timer
->ListEntry
);
1621 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1622 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1626 LARGE_INTEGER CurrentTime
;
1628 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1629 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1631 IntLockMessageQueue(MessageQueue
);
1632 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1635 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1638 IntUnLockMessageQueue(MessageQueue
);
1639 DPRINT1("Failed to allocate timer entry\n");
1642 DPRINT("Allocated new timer entry %p\n", Timer
);
1644 Timer
->IDEvent
= IDEvent
;
1649 DPRINT("Updating existing timer entry %p\n", Timer
);
1652 KeQuerySystemTime(&CurrentTime
);
1653 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1654 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1655 Timer
->Period
= Period
;
1656 Timer
->TimerFunc
= TimerFunc
;
1657 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1658 Timer
->ExpiryTime
.QuadPart
);
1660 InsertTimer(MessageQueue
, Timer
);
1663 DumpTimerList(MessageQueue
);
1664 #endif /* ! defined(NDEBUG) */
1666 IntUnLockMessageQueue(MessageQueue
);
1672 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1673 UINT_PTR IDEvent
, UINT Msg
)
1677 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1678 MessageQueue
, Wnd
, IDEvent
, Msg
);
1680 IntLockMessageQueue(MessageQueue
);
1681 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1685 DPRINT("Failed to remove timer from list, not found\n");
1689 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1693 DumpTimerList(MessageQueue
);
1694 #endif /* ! defined(NDEBUG) */
1696 IntUnLockMessageQueue(MessageQueue
);
1698 return NULL
!= Timer
;
1702 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1703 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1704 MSG
*Msg
, BOOLEAN Restart
)
1707 LARGE_INTEGER CurrentTime
;
1708 PLIST_ENTRY EnumEntry
;
1711 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1712 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1714 IntLockMessageQueue(MessageQueue
);
1715 KeQuerySystemTime(&CurrentTime
);
1716 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1717 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1719 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1721 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1722 TIMER_ENTRY
, ListEntry
);
1723 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1724 Timer
->ExpiryTime
.QuadPart
);
1725 EnumEntry
= EnumEntry
->Flink
;
1726 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1727 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1728 (MsgFilterMin
<= Timer
->Msg
&&
1729 Timer
->Msg
<= MsgFilterMax
)))
1731 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1733 DPRINT("Timer is expired\n");
1739 DPRINT("No need to check later timers\n");
1745 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1746 Timer
, Timer
->Wnd
, Timer
->Msg
, WndFilter
, MsgFilterMin
, MsgFilterMax
);
1752 DPRINT("No timer pending\n");
1753 IntUnLockMessageQueue(MessageQueue
);
1757 Msg
->hwnd
= Timer
->Wnd
;
1758 Msg
->message
= Timer
->Msg
;
1759 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1760 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1764 RemoveEntryList(&Timer
->ListEntry
);
1765 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1766 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1767 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1768 InsertTimer(MessageQueue
, Timer
);
1771 DumpTimerList(MessageQueue
);
1772 #endif /* ! defined(NDEBUG) */
1775 IntUnLockMessageQueue(MessageQueue
);
1777 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1778 Msg
->wParam
, Msg
->lParam
);
1784 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1787 PLIST_ENTRY EnumEntry
;
1789 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1791 IntLockMessageQueue(MessageQueue
);
1792 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1793 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1795 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1796 EnumEntry
= EnumEntry
->Flink
;
1797 if (Timer
->Wnd
== Wnd
)
1799 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1800 RemoveEntryList(&Timer
->ListEntry
);
1801 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1806 DumpTimerList(MessageQueue
);
1807 #endif /* ! defined(NDEBUG) */
1809 IntUnLockMessageQueue(MessageQueue
);
1813 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1814 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1815 PLARGE_INTEGER FirstTimerExpiry
)
1818 PLIST_ENTRY EnumEntry
;
1820 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1821 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1823 IntLockMessageQueue(MessageQueue
);
1824 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1825 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1827 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1828 TIMER_ENTRY
, ListEntry
);
1829 EnumEntry
= EnumEntry
->Flink
;
1830 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1831 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1832 (MsgFilterMin
<= Timer
->Msg
&&
1833 Timer
->Msg
<= MsgFilterMax
)))
1835 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1836 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);
1837 IntUnLockMessageQueue(MessageQueue
);
1842 IntUnLockMessageQueue(MessageQueue
);