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.
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * 06-06-2001 CSH Created
29 /* INCLUDES ******************************************************************/
36 /* GLOBALS *******************************************************************/
38 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
40 static MSG SystemMessageQueue
[SYSTEM_MESSAGE_QUEUE_SIZE
];
41 static ULONG SystemMessageQueueHead
= 0;
42 static ULONG SystemMessageQueueTail
= 0;
43 static ULONG SystemMessageQueueCount
= 0;
44 static KSPIN_LOCK SystemMessageQueueLock
;
46 static ULONG
volatile HardwareMessageQueueStamp
= 0;
47 static LIST_ENTRY HardwareMessageQueueHead
;
48 static KMUTANT HardwareMessageQueueLock
;
50 static KEVENT HardwareMessageEvent
;
52 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
53 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
55 #define IntLockSystemMessageQueue(OldIrql) \
56 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
58 #define IntUnLockSystemMessageQueue(OldIrql) \
59 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
61 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
62 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
64 /* FUNCTIONS *****************************************************************/
67 // Wakeup any thread/process waiting on idle input.
73 PWINDOW_OBJECT Window
;
74 PW32PROCESS W32d
= PsGetCurrentProcessWin32Process();
76 hWnd
= UserGetForegroundWindow();
78 Window
= UserGetWindowObject(hWnd
);
80 if (Window
&& Window
->ti
)
82 if (Window
->ti
->Hooks
& HOOKID_TO_FLAG(WH_FOREGROUNDIDLE
))
84 co_HOOK_CallHooks(WH_FOREGROUNDIDLE
,HC_ACTION
,0,0);
88 if (W32d
&& W32d
->InputIdleEvent
)
89 KePulseEvent( W32d
->InputIdleEvent
, EVENT_INCREMENT
, TRUE
);
93 IntMsqSetWakeMask(DWORD WakeMask
)
95 PTHREADINFO Win32Thread
;
96 PUSER_MESSAGE_QUEUE MessageQueue
;
97 HANDLE MessageEventHandle
;
99 Win32Thread
= PsGetCurrentThreadWin32Thread();
100 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
103 MessageQueue
= Win32Thread
->MessageQueue
;
104 MessageQueue
->WakeMask
= WakeMask
;
105 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
107 return MessageEventHandle
;
111 IntMsqClearWakeMask(VOID
)
113 PTHREADINFO Win32Thread
;
114 PUSER_MESSAGE_QUEUE MessageQueue
;
116 Win32Thread
= PsGetCurrentThreadWin32Thread();
117 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
120 MessageQueue
= Win32Thread
->MessageQueue
;
121 MessageQueue
->WakeMask
= ~0;
127 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
130 Queue
->QueueBits
|= QS_PAINT
;
131 Queue
->ChangedBits
|= QS_PAINT
;
132 if (Queue
->WakeMask
& QS_PAINT
)
133 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
137 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
144 MsqInitializeImpl(VOID
)
146 /*CurrentFocusMessageQueue = NULL;*/
147 InitializeListHead(&HardwareMessageQueueHead
);
148 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
149 KeInitializeSpinLock(&SystemMessageQueueLock
);
150 KeInitializeMutant(&HardwareMessageQueueLock
, 0);
152 ExInitializePagedLookasideList(&MessageLookasideList
,
156 sizeof(USER_MESSAGE
),
159 ExInitializePagedLookasideList(&TimerLookasideList
,
167 return(STATUS_SUCCESS
);
171 MsqInsertSystemMessage(MSG
* Msg
)
173 LARGE_INTEGER LargeTickCount
;
178 IntLockSystemMessageQueue(OldIrql
);
181 * Bail out if the queue is full. FIXME: We should handle this case
185 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
187 IntUnLockSystemMessageQueue(OldIrql
);
191 KeQueryTickCount(&LargeTickCount
);
192 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
194 Event
.message
= Msg
->message
;
195 Event
.time
= Msg
->time
;
196 Event
.hwnd
= Msg
->hwnd
;
197 Event
.paramL
= Msg
->pt
.x
;
198 Event
.paramH
= Msg
->pt
.y
;
199 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
202 * If we got WM_MOUSEMOVE and there are already messages in the
203 * system message queue, check if the last message is mouse move
204 * and if it is then just overwrite it.
207 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
209 if (SystemMessageQueueTail
== 0)
210 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
212 Prev
= SystemMessageQueueTail
- 1;
213 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
215 SystemMessageQueueTail
= Prev
;
216 SystemMessageQueueCount
--;
221 * Actually insert the message into the system message queue.
224 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
225 SystemMessageQueueTail
=
226 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
227 SystemMessageQueueCount
++;
229 IntUnLockSystemMessageQueue(OldIrql
);
231 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
235 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
238 PWINSTATION_OBJECT WinStaObject
;
239 PSYSTEM_CURSORINFO CurInfo
;
243 pti
= PsGetCurrentThreadWin32Thread();
244 if (pti
->Desktop
== NULL
)
249 WinStaObject
= pti
->Desktop
->WindowStation
;
251 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
252 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
253 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
257 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
258 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
264 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
265 (dY
<= CurInfo
->DblClickHeight
);
269 if(CurInfo
->ButtonsDown
)
270 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
278 CurInfo
->LastBtnDown
= 0;
279 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
280 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
281 CurInfo
->LastClkWnd
= NULL
;
282 CurInfo
->ButtonsDown
= Msg
->message
;
286 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
287 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
288 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
289 CurInfo
->LastBtnDown
= Msg
->time
;
290 CurInfo
->ButtonsDown
= Msg
->message
;
298 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
, UINT FilterLow
, UINT FilterHigh
,
299 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
300 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
302 USHORT Msg
= Message
->Msg
.message
;
303 PWINDOW_OBJECT Window
= NULL
;
306 ASSERT_REFS_CO(ScopeWin
);
309 co_WinPosWindowFromPoint can return a Window, and in that case
310 that window has a ref that we need to deref. Thats why we add "dummy"
311 refs in all other cases.
314 hCaptureWin
= IntGetCaptureWindow();
315 if (hCaptureWin
== NULL
)
317 if(Msg
== WM_MOUSEWHEEL
)
319 Window
= UserGetWindowObject(IntGetFocusWindow());
320 if (Window
) UserReferenceObject(Window
);
324 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &Window
);
328 if (Window
) UserReferenceObject(Window
);
332 /* this is the one case where we dont add a ref, since the returned
333 window is already referenced */
339 /* FIXME - window messages should go to the right window if no buttons are
341 Window
= UserGetWindowObject(hCaptureWin
);
342 if (Window
) UserReferenceObject(Window
);
351 RemoveEntryList(&Message
->ListEntry
);
352 if(MessageQueue
->MouseMoveMsg
== Message
)
354 MessageQueue
->MouseMoveMsg
= NULL
;
357 // when FromGlobalQueue is true, the caller has already removed the Message
363 if (Window
->MessageQueue
!= MessageQueue
)
365 if (! FromGlobalQueue
)
367 DPRINT("Moving msg between private queues\n");
368 /* This message is already queued in a private queue, but we need
369 * to move it to a different queue, perhaps because a new window
370 * was created which now covers the screen area previously taken
371 * by another window. To move it, we need to take it out of the
372 * old queue. Note that we're already holding the lock mutexes of the
374 RemoveEntryList(&Message
->ListEntry
);
376 /* remove the pointer for the current WM_MOUSEMOVE message in case we
378 if(MessageQueue
->MouseMoveMsg
== Message
)
380 MessageQueue
->MouseMoveMsg
= NULL
;
384 /* lock the destination message queue, so we don't get in trouble with other
385 threads, messing with it at the same time */
386 IntLockHardwareMessageQueue(Window
->MessageQueue
);
387 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
388 &Message
->ListEntry
);
389 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
391 if(Window
->MessageQueue
->MouseMoveMsg
)
393 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
395 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
396 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
398 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
399 Window
->MessageQueue
->MouseMoveMsg
= Message
;
401 Window
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
402 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
403 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
404 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
408 Window
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
409 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
410 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
411 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
413 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
416 UserDereferenceObject(Window
);
420 /* From here on, we're in the same message queue as the caller! */
422 *ScreenPoint
= Message
->Msg
.pt
;
424 if((hWnd
!= NULL
&& Window
->hSelf
!= hWnd
) ||
425 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
427 /* Reject the message because it doesn't match the filter */
431 /* Lock the message queue so no other thread can mess with it.
432 Our own message queue is not locked while fetching from the global
433 queue, so we have to make sure nothing interferes! */
434 IntLockHardwareMessageQueue(Window
->MessageQueue
);
435 /* if we're from the global queue, we need to add our message to our
436 private queue so we don't loose it! */
437 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
438 &Message
->ListEntry
);
441 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
443 if(Window
->MessageQueue
->MouseMoveMsg
&&
444 (Window
->MessageQueue
->MouseMoveMsg
!= Message
))
446 /* delete the old message */
447 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
448 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
449 if (!FromGlobalQueue
)
451 // We might have deleted the next one in our queue, so fix next
452 *Next
= Message
->ListEntry
.Flink
;
455 /* always save a pointer to this WM_MOUSEMOVE message here because we're
456 sure that the message is in the private queue */
457 Window
->MessageQueue
->MouseMoveMsg
= Message
;
461 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
464 UserDereferenceObject(Window
);
469 /* FIXME - only assign if removing? */
470 Message
->Msg
.hwnd
= Window
->hSelf
;
471 Message
->Msg
.message
= Msg
;
472 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
474 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
476 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
477 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
481 /* Lock the message queue so no other thread can mess with it.
482 Our own message queue is not locked while fetching from the global
483 queue, so we have to make sure nothing interferes! */
484 IntLockHardwareMessageQueue(Window
->MessageQueue
);
485 if(Window
->MessageQueue
->MouseMoveMsg
)
487 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
488 with one that's been sent later */
489 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
490 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
491 /* our message is not in the private queue so we can remove the pointer
492 instead of setting it to the current message we're processing */
493 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
495 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
497 else if(Window
->MessageQueue
->MouseMoveMsg
== Message
)
499 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
503 UserDereferenceObject(Window
);
509 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
,
510 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
511 PUSER_MESSAGE
* Message
)
516 PLIST_ENTRY CurrentEntry
;
517 PWINDOW_OBJECT DesktopWindow
= NULL
;
518 PVOID WaitObjects
[2];
520 DECLARE_RETURN(BOOL
);
521 USER_REFERENCE_ENTRY Ref
;
522 PDESKTOPINFO Desk
= NULL
;
524 WaitObjects
[1] = MessageQueue
->NewMessages
;
525 WaitObjects
[0] = &HardwareMessageQueueLock
;
532 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
533 UserMode
, FALSE
, NULL
, NULL
);
537 while (co_MsqDispatchOneSentMessage(MessageQueue
))
542 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
544 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
548 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
549 Desk
= DesktopWindow
->ti
->Desktop
;
552 /* Process messages in the message queue itself. */
553 IntLockHardwareMessageQueue(MessageQueue
);
554 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
555 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
557 PUSER_MESSAGE Current
=
558 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
559 CurrentEntry
= CurrentEntry
->Flink
;
560 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
561 Current
->Msg
.message
<= WM_MOUSELAST
)
565 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
566 Current
, Remove
, &Freed
,
567 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
572 RemoveEntryList(&Current
->ListEntry
);
574 IntUnLockHardwareMessageQueue(MessageQueue
);
575 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
579 Desk
->LastInputWasKbd
= FALSE
;
586 IntUnLockHardwareMessageQueue(MessageQueue
);
588 /* Now try the global queue. */
590 /* Transfer all messages from the DPC accessible queue to the main queue. */
591 IntLockSystemMessageQueue(OldIrql
);
592 while (SystemMessageQueueCount
> 0)
594 PUSER_MESSAGE UserMsg
;
598 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
599 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
600 SystemMessageQueueHead
=
601 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
602 SystemMessageQueueCount
--;
603 IntUnLockSystemMessageQueue(OldIrql
);
604 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
606 MSLLHOOKSTRUCT MouseHookData
;
608 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
609 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
613 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
617 case WM_XBUTTONDBLCLK
:
618 case WM_NCXBUTTONDOWN
:
620 case WM_NCXBUTTONDBLCLK
:
621 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
624 MouseHookData
.mouseData
= 0;
627 MouseHookData
.flags
= 0;
628 MouseHookData
.time
= Msg
.time
;
629 MouseHookData
.dwExtraInfo
= 0;
630 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
631 Msg
.message
, (LPARAM
) &MouseHookData
));
635 ProcessMessage
= TRUE
;
639 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
640 /* What to do if out of memory? For now we just panic a bit in debug */
642 UserMsg
->FreeLParam
= FALSE
;
644 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
646 IntLockSystemMessageQueue(OldIrql
);
648 HardwareMessageQueueStamp
++;
649 IntUnLockSystemMessageQueue(OldIrql
);
651 /* Process messages in the queue until we find one to return. */
652 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
653 while (CurrentEntry
!= &HardwareMessageQueueHead
)
655 PUSER_MESSAGE Current
=
656 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
657 CurrentEntry
= CurrentEntry
->Flink
;
658 RemoveEntryList(&Current
->ListEntry
);
659 HardwareMessageQueueStamp
++;
660 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
661 Current
->Msg
.message
<= WM_MOUSELAST
)
663 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
664 /* Translate the message. */
665 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
666 Current
, Remove
, &Freed
,
667 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
670 /* Check for no more messages in the system queue. */
671 IntLockSystemMessageQueue(OldIrql
);
672 if (SystemMessageQueueCount
== 0 &&
673 IsListEmpty(&HardwareMessageQueueHead
))
675 KeClearEvent(&HardwareMessageEvent
);
677 IntUnLockSystemMessageQueue(OldIrql
);
680 If we aren't removing the message then add it to the private
685 IntLockHardwareMessageQueue(MessageQueue
);
686 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
688 if(MessageQueue
->MouseMoveMsg
)
690 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
691 ExFreePool(MessageQueue
->MouseMoveMsg
);
693 MessageQueue
->MouseMoveMsg
= Current
;
695 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
696 &Current
->ListEntry
);
697 IntUnLockHardwareMessageQueue(MessageQueue
);
699 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
704 /* If the contents of the queue changed then restart processing. */
705 if (HardwareMessageQueueStamp
!= ActiveStamp
)
707 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
713 /* Check if the system message queue is now empty. */
714 IntLockSystemMessageQueue(OldIrql
);
715 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
717 KeClearEvent(&HardwareMessageEvent
);
719 IntUnLockSystemMessageQueue(OldIrql
);
720 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
725 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
731 // Note: Only called from input.c.
734 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
736 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
738 LARGE_INTEGER LargeTickCount
;
739 KBDLLHOOKSTRUCT KbdHookData
;
742 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
743 uMsg
, wParam
, lParam
);
745 // Condition may arise when calling MsqPostMessage and waiting for an event.
746 if (!UserIsEntered()) UserEnterExclusive(); // Fixme: Not sure ATM if this thread is locked.
748 FocusMessageQueue
= IntGetFocusMessageQueue();
752 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
753 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
759 KeQueryTickCount(&LargeTickCount
);
760 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
762 Event
.message
= Msg
.message
;
763 Event
.hwnd
= Msg
.hwnd
;
764 Event
.time
= Msg
.time
;
765 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
766 Event
.paramH
= Msg
.lParam
& 0x7FFF;
767 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
768 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
770 /* We can't get the Msg.pt point here since we don't know thread
771 (and thus the window station) the message will end up in yet. */
773 KbdHookData
.vkCode
= Msg
.wParam
;
774 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
775 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
776 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
777 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
778 KbdHookData
.time
= Msg
.time
;
779 KbdHookData
.dwExtraInfo
= 0;
780 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
782 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
783 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
787 if (FocusMessageQueue
== NULL
)
789 DPRINT("No focus message queue\n");
793 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
795 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
796 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
798 FocusMessageQueue
->Desktop
->DesktopInfo
->LastInputWasKbd
= TRUE
;
800 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
802 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
806 DPRINT("Invalid focus window handle\n");
808 if (UserIsEntered()) UserLeave();
812 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
814 PWINDOW_OBJECT Window
;
815 PTHREADINFO Win32Thread
;
816 PWINSTATION_OBJECT WinSta
;
818 LARGE_INTEGER LargeTickCount
;
821 Status
= ObReferenceObjectByPointer (Thread
,
825 if (!NT_SUCCESS(Status
))
828 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
829 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
831 ObDereferenceObject ((PETHREAD
)Thread
);
835 WinSta
= Win32Thread
->Desktop
->WindowStation
;
836 Window
= IntGetWindowObject(hWnd
);
839 ObDereferenceObject ((PETHREAD
)Thread
);
844 Mesg
.message
= WM_HOTKEY
;
845 Mesg
.wParam
= wParam
;
846 Mesg
.lParam
= lParam
;
847 KeQueryTickCount(&LargeTickCount
);
848 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
849 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
850 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
851 UserDereferenceObject(Window
);
852 ObDereferenceObject (Thread
);
854 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
855 // &Message->ListEntry);
856 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
859 PUSER_MESSAGE FASTCALL
860 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
862 PUSER_MESSAGE Message
;
864 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
870 Message
->FreeLParam
= FreeLParam
;
871 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
877 MsqDestroyMessage(PUSER_MESSAGE Message
)
879 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
883 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
885 PLIST_ENTRY ListEntry
;
886 PUSER_SENT_MESSAGE_NOTIFY Message
;
888 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
890 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
891 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
894 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
897 Message
->CompletionCallbackContext
,
905 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
907 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
911 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
913 PUSER_SENT_MESSAGE Message
;
917 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
919 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
924 /* remove it from the list of pending messages */
925 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
926 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
928 /* insert it to the list of messages that are currently dispatched by this
930 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
931 &Message
->ListEntry
);
933 if (Message
->HookMessage
== MSQ_ISHOOK
)
935 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
936 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
938 Message
->Msg
.lParam
);
940 else if (Message
->HookMessage
== MSQ_ISEVENT
)
942 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
945 Message
->Msg
.lParam
);
949 /* Call the window procedure. */
950 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
951 Message
->Msg
.message
,
953 Message
->Msg
.lParam
);
956 /* remove the message from the local dispatching list, because it doesn't need
957 to be cleaned up on thread termination anymore */
958 RemoveEntryList(&Message
->ListEntry
);
960 /* remove the message from the dispatching list, so lock the sender's message queue */
961 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
964 /* only remove it from the dispatching list if not already removed by a timeout */
965 RemoveEntryList(&Message
->DispatchingListEntry
);
967 /* still keep the sender's message queue locked, so the sender can't exit the
968 MsqSendMessage() function (if timed out) */
970 /* Let the sender know the result. */
971 if (Message
->Result
!= NULL
)
973 *Message
->Result
= Result
;
976 /* Notify the sender. */
977 if (Message
->CompletionEvent
!= NULL
)
979 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
982 /* Notify the sender if they specified a callback. */
983 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
985 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
986 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
988 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
991 NotifyMessage
->CompletionCallback
=
992 Message
->CompletionCallback
;
993 NotifyMessage
->CompletionCallbackContext
=
994 Message
->CompletionCallbackContext
;
995 NotifyMessage
->Result
= Result
;
996 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
997 NotifyMessage
->Msg
= Message
->Msg
.message
;
998 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1003 /* dereference both sender and our queue */
1004 IntDereferenceMessageQueue(MessageQueue
);
1005 IntDereferenceMessageQueue(Message
->SenderQueue
);
1007 /* free the message */
1008 ExFreePool(Message
);
1013 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1015 PUSER_SENT_MESSAGE SentMessage
;
1016 PUSER_MESSAGE PostedMessage
;
1017 PUSER_MESSAGE_QUEUE MessageQueue
;
1018 PLIST_ENTRY CurrentEntry
, ListHead
;
1019 PWINDOW_OBJECT Window
= pWindow
;
1023 MessageQueue
= Window
->MessageQueue
;
1024 ASSERT(MessageQueue
);
1026 /* remove the posted messages for this window */
1027 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1028 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1029 while (CurrentEntry
!= ListHead
)
1031 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1033 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1035 RemoveEntryList(&PostedMessage
->ListEntry
);
1036 MsqDestroyMessage(PostedMessage
);
1037 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1041 CurrentEntry
= CurrentEntry
->Flink
;
1045 /* remove the sent messages for this window */
1046 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1047 ListHead
= &MessageQueue
->SentMessagesListHead
;
1048 while (CurrentEntry
!= ListHead
)
1050 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1052 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1054 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1056 RemoveEntryList(&SentMessage
->ListEntry
);
1058 /* remove the message from the dispatching list */
1059 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1061 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1064 /* wake the sender's thread */
1065 if (SentMessage
->CompletionEvent
!= NULL
)
1067 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1070 /* dereference our and the sender's message queue */
1071 IntDereferenceMessageQueue(MessageQueue
);
1072 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1074 /* free the message */
1075 ExFreePool(SentMessage
);
1077 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1081 CurrentEntry
= CurrentEntry
->Flink
;
1087 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1088 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1090 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1091 &NotifyMessage
->ListEntry
);
1092 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1093 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1094 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1095 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1099 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1100 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1101 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1105 PUSER_SENT_MESSAGE Message
;
1106 KEVENT CompletionEvent
;
1107 NTSTATUS WaitStatus
;
1109 PUSER_MESSAGE_QUEUE ThreadQueue
;
1110 LARGE_INTEGER Timeout
;
1113 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1115 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1116 return STATUS_INSUFFICIENT_RESOURCES
;
1119 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1121 pti
= PsGetCurrentThreadWin32Thread();
1122 ThreadQueue
= pti
->MessageQueue
;
1123 ASSERT(ThreadQueue
!= MessageQueue
);
1125 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1127 /* FIXME - increase reference counter of sender's message queue here */
1130 Message
->Msg
.hwnd
= Wnd
;
1131 Message
->Msg
.message
= Msg
;
1132 Message
->Msg
.wParam
= wParam
;
1133 Message
->Msg
.lParam
= lParam
;
1134 Message
->CompletionEvent
= &CompletionEvent
;
1135 Message
->Result
= &Result
;
1136 Message
->SenderQueue
= ThreadQueue
;
1137 IntReferenceMessageQueue(ThreadQueue
);
1138 Message
->CompletionCallback
= NULL
;
1139 Message
->HookMessage
= HookMessage
;
1141 IntReferenceMessageQueue(MessageQueue
);
1143 /* add it to the list of pending messages */
1144 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1146 /* queue it in the destination's message queue */
1147 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1149 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1150 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1151 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1152 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1154 /* we can't access the Message anymore since it could have already been deleted! */
1162 /* don't process messages sent to the thread */
1163 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1164 FALSE
, (uTimeout
? &Timeout
: NULL
));
1168 if(WaitStatus
== STATUS_TIMEOUT
)
1170 /* look up if the message has not yet dispatched, if so
1171 make sure it can't pass a result and it must not set the completion event anymore */
1172 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1173 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1175 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1178 /* we can access Message here, it's secure because the message queue is locked
1179 and the message is still hasn't been dispatched */
1180 Message
->CompletionEvent
= NULL
;
1181 Message
->Result
= NULL
;
1184 Entry
= Entry
->Flink
;
1187 /* remove from the local dispatching list so the other thread knows,
1188 it can't pass a result and it must not set the completion event anymore */
1189 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1190 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1192 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1195 /* we can access Message here, it's secure because the sender's message is locked
1196 and the message has definitely not yet been destroyed, otherwise it would
1197 have been removed from this list by the dispatching routine right after
1198 dispatching the message */
1199 Message
->CompletionEvent
= NULL
;
1200 Message
->Result
= NULL
;
1201 RemoveEntryList(&Message
->DispatchingListEntry
);
1202 Message
->DispatchingListEntry
.Flink
= NULL
;
1205 Entry
= Entry
->Flink
;
1208 DPRINT("MsqSendMessage (blocked) timed out\n");
1210 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1215 PVOID WaitObjects
[2];
1217 WaitObjects
[0] = &CompletionEvent
;
1218 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1225 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1226 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1230 if(WaitStatus
== STATUS_TIMEOUT
)
1232 /* look up if the message has not yet been dispatched, if so
1233 make sure it can't pass a result and it must not set the completion event anymore */
1234 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1235 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1237 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1240 /* we can access Message here, it's secure because the message queue is locked
1241 and the message is still hasn't been dispatched */
1242 Message
->CompletionEvent
= NULL
;
1243 Message
->Result
= NULL
;
1246 Entry
= Entry
->Flink
;
1249 /* remove from the local dispatching list so the other thread knows,
1250 it can't pass a result and it must not set the completion event anymore */
1251 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1252 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1254 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1257 /* we can access Message here, it's secure because the sender's message is locked
1258 and the message has definitely not yet been destroyed, otherwise it would
1259 have been removed from this list by the dispatching routine right after
1260 dispatching the message */
1261 Message
->CompletionEvent
= NULL
;
1262 Message
->Result
= NULL
;
1263 RemoveEntryList(&Message
->DispatchingListEntry
);
1264 Message
->DispatchingListEntry
.Flink
= NULL
;
1267 Entry
= Entry
->Flink
;
1270 DPRINT("MsqSendMessage timed out\n");
1273 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1276 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1279 if(WaitStatus
!= STATUS_TIMEOUT
)
1280 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1286 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1289 PUSER_MESSAGE Message
;
1291 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1295 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1296 &Message
->ListEntry
);
1297 MessageQueue
->QueueBits
|= MessageBits
;
1298 MessageQueue
->ChangedBits
|= MessageBits
;
1299 if (MessageQueue
->WakeMask
& MessageBits
)
1300 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1304 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1306 MessageQueue
->QuitPosted
= TRUE
;
1307 MessageQueue
->QuitExitCode
= ExitCode
;
1308 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1309 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1310 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1311 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1315 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1316 IN BOOLEAN Hardware
,
1319 IN UINT MsgFilterLow
,
1320 IN UINT MsgFilterHigh
,
1321 OUT PUSER_MESSAGE
* Message
)
1323 PLIST_ENTRY CurrentEntry
;
1324 PUSER_MESSAGE CurrentMessage
;
1325 PLIST_ENTRY ListHead
;
1329 return(co_MsqPeekHardwareMessage(MessageQueue
, Wnd
,
1330 MsgFilterLow
, MsgFilterHigh
,
1334 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1335 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1336 while (CurrentEntry
!= ListHead
)
1338 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1340 if ((Wnd
== 0 || Wnd
== CurrentMessage
->Msg
.hwnd
) &&
1341 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1342 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1343 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1347 RemoveEntryList(&CurrentMessage
->ListEntry
);
1350 *Message
= CurrentMessage
;
1353 CurrentEntry
= CurrentEntry
->Flink
;
1360 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, HWND WndFilter
,
1361 UINT MsgFilterMin
, UINT MsgFilterMax
)
1363 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1364 LARGE_INTEGER TimerExpiry
;
1365 PLARGE_INTEGER Timeout
;
1368 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1370 Timeout
= &TimerExpiry
;
1377 IdlePing(); // Going to wait so send Idle ping.
1381 ret
= KeWaitForMultipleObjects(2,
1396 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1398 LARGE_INTEGER LargeTickCount
;
1400 KeQueryTickCount(&LargeTickCount
);
1401 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1405 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1407 LARGE_INTEGER LargeTickCount
;
1410 MessageQueue
->Thread
= Thread
;
1411 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1412 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1413 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1414 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1415 InitializeListHead(&MessageQueue
->TimerListHead
);
1416 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1417 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1418 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1419 MessageQueue
->QuitPosted
= FALSE
;
1420 MessageQueue
->QuitExitCode
= 0;
1421 KeQueryTickCount(&LargeTickCount
);
1422 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1423 MessageQueue
->FocusWindow
= NULL
;
1424 MessageQueue
->PaintCount
= 0;
1425 MessageQueue
->WakeMask
= ~0;
1426 MessageQueue
->NewMessagesHandle
= NULL
;
1428 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1429 NULL
, SynchronizationEvent
, FALSE
);
1430 if (!NT_SUCCESS(Status
))
1435 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1436 ExEventObjectType
, KernelMode
,
1437 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1438 if (!NT_SUCCESS(Status
))
1440 ZwClose(MessageQueue
->NewMessagesHandle
);
1441 MessageQueue
->NewMessagesHandle
= NULL
;
1449 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1451 PLIST_ENTRY CurrentEntry
;
1452 PUSER_MESSAGE CurrentMessage
;
1453 PTIMER_ENTRY CurrentTimer
;
1454 PUSER_SENT_MESSAGE CurrentSentMessage
;
1456 /* cleanup posted messages */
1457 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1459 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1460 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1462 MsqDestroyMessage(CurrentMessage
);
1465 /* remove the messages that have not yet been dispatched */
1466 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1468 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1469 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1472 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1474 /* remove the message from the dispatching list */
1475 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1477 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1480 /* wake the sender's thread */
1481 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1483 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1486 /* dereference our and the sender's message queue */
1487 IntDereferenceMessageQueue(MessageQueue
);
1488 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1490 /* free the message */
1491 ExFreePool(CurrentSentMessage
);
1494 /* cleanup timers */
1495 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1497 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1498 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1499 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1502 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1503 ExitThread() was called in a SendMessage() umode callback */
1504 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1506 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1507 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1510 /* remove the message from the dispatching list */
1511 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1513 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1516 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1518 /* wake the sender's thread */
1519 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1521 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1524 /* dereference our and the sender's message queue */
1525 IntDereferenceMessageQueue(MessageQueue
);
1526 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1528 /* free the message */
1529 ExFreePool(CurrentSentMessage
);
1532 /* tell other threads not to bother returning any info to us */
1533 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1535 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1536 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1537 DispatchingListEntry
);
1538 CurrentSentMessage
->CompletionEvent
= NULL
;
1539 CurrentSentMessage
->Result
= NULL
;
1541 /* do NOT dereference our message queue as it might get attempted to be
1547 PUSER_MESSAGE_QUEUE FASTCALL
1548 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1550 PUSER_MESSAGE_QUEUE MessageQueue
;
1552 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(PagedPool
,
1553 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1561 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1562 /* hold at least one reference until it'll be destroyed */
1563 IntReferenceMessageQueue(MessageQueue
);
1564 /* initialize the queue */
1565 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1567 IntDereferenceMessageQueue(MessageQueue
);
1571 return MessageQueue
;
1575 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1579 /* remove the message queue from any desktops */
1580 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1582 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1583 IntDereferenceMessageQueue(MessageQueue
);
1587 MsqCleanupMessageQueue(MessageQueue
);
1589 /* decrease the reference counter, if it hits zero, the queue will be freed */
1590 IntDereferenceMessageQueue(MessageQueue
);
1594 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1596 return Queue
->Hooks
;
1600 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1602 Queue
->Hooks
= Hooks
;
1606 MsqSetMessageExtraInfo(LPARAM lParam
)
1610 PUSER_MESSAGE_QUEUE MessageQueue
;
1612 pti
= PsGetCurrentThreadWin32Thread();
1613 MessageQueue
= pti
->MessageQueue
;
1619 Ret
= MessageQueue
->ExtraInfo
;
1620 MessageQueue
->ExtraInfo
= lParam
;
1626 MsqGetMessageExtraInfo(VOID
)
1629 PUSER_MESSAGE_QUEUE MessageQueue
;
1631 pti
= PsGetCurrentThreadWin32Thread();
1632 MessageQueue
= pti
->MessageQueue
;
1638 return MessageQueue
->ExtraInfo
;
1642 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1648 case MSQ_STATE_CAPTURE
:
1649 Prev
= MessageQueue
->CaptureWindow
;
1650 MessageQueue
->CaptureWindow
= hWnd
;
1652 case MSQ_STATE_ACTIVE
:
1653 Prev
= MessageQueue
->ActiveWindow
;
1654 MessageQueue
->ActiveWindow
= hWnd
;
1656 case MSQ_STATE_FOCUS
:
1657 Prev
= MessageQueue
->FocusWindow
;
1658 MessageQueue
->FocusWindow
= hWnd
;
1660 case MSQ_STATE_MENUOWNER
:
1661 Prev
= MessageQueue
->MenuOwner
;
1662 MessageQueue
->MenuOwner
= hWnd
;
1664 case MSQ_STATE_MOVESIZE
:
1665 Prev
= MessageQueue
->MoveSize
;
1666 MessageQueue
->MoveSize
= hWnd
;
1668 case MSQ_STATE_CARET
:
1669 ASSERT(MessageQueue
->CaretInfo
);
1670 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1671 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1679 static VOID FASTCALL
1680 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1682 PLIST_ENTRY Current
;
1685 Current
= MessageQueue
->TimerListHead
.Flink
;
1686 if (Current
== &MessageQueue
->TimerListHead
)
1688 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1690 while (Current
!= &MessageQueue
->TimerListHead
)
1692 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1693 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1694 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1695 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1696 Current
= Current
->Flink
;
1699 #endif /* ! defined(NDEBUG) */
1701 /* Must have the message queue locked while calling this */
1702 static VOID FASTCALL
1703 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1705 PLIST_ENTRY Current
;
1707 Current
= MessageQueue
->TimerListHead
.Flink
;
1708 while (Current
!= &MessageQueue
->TimerListHead
)
1710 if (NewTimer
->ExpiryTime
.QuadPart
<
1711 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1715 Current
= Current
->Flink
;
1718 InsertTailList(Current
, &NewTimer
->ListEntry
);
1721 /* Must have the message queue locked while calling this */
1722 static PTIMER_ENTRY FASTCALL
1723 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1726 PLIST_ENTRY EnumEntry
;
1728 /* Remove timer if already in the queue */
1729 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1730 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1732 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1733 EnumEntry
= EnumEntry
->Flink
;
1735 if (Timer
->Wnd
== Wnd
&&
1736 Timer
->IDEvent
== IDEvent
&&
1739 RemoveEntryList(&Timer
->ListEntry
);
1748 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1749 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1753 LARGE_INTEGER CurrentTime
;
1755 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1756 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1758 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1761 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1764 DPRINT1("Failed to allocate timer entry\n");
1767 DPRINT("Allocated new timer entry %p\n", Timer
);
1769 Timer
->IDEvent
= IDEvent
;
1774 DPRINT("Updating existing timer entry %p\n", Timer
);
1777 KeQuerySystemTime(&CurrentTime
);
1778 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1779 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1780 Timer
->Period
= Period
;
1781 Timer
->TimerFunc
= TimerFunc
;
1782 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1783 Timer
->ExpiryTime
.QuadPart
);
1785 InsertTimer(MessageQueue
, Timer
);
1789 DumpTimerList(MessageQueue
);
1790 #endif /* ! defined(NDEBUG) */
1796 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1797 UINT_PTR IDEvent
, UINT Msg
)
1801 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1802 MessageQueue
, Wnd
, IDEvent
, Msg
);
1804 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1808 DPRINT("Failed to remove timer from list, not found\n");
1812 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1816 DumpTimerList(MessageQueue
);
1817 #endif /* ! defined(NDEBUG) */
1819 return NULL
!= Timer
;
1823 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1824 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1825 MSG
*Msg
, BOOLEAN Restart
)
1828 LARGE_INTEGER CurrentTime
;
1829 LARGE_INTEGER LargeTickCount
;
1830 PLIST_ENTRY EnumEntry
;
1834 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1835 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1837 KeQuerySystemTime(&CurrentTime
);
1838 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1839 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1841 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1843 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1844 TIMER_ENTRY
, ListEntry
);
1845 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1846 Timer
->ExpiryTime
.QuadPart
);
1847 EnumEntry
= EnumEntry
->Flink
;
1848 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1849 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1850 (MsgFilterMin
<= Timer
->Msg
&&
1851 Timer
->Msg
<= MsgFilterMax
)))
1853 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1855 DPRINT("Timer is expired\n");
1861 DPRINT("No need to check later timers\n");
1867 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1868 Timer
, Timer
->Wnd
, Timer
->Msg
, WndFilter
, MsgFilterMin
, MsgFilterMax
);
1874 DPRINT("No timer pending\n");
1878 Msg
->hwnd
= Timer
->Wnd
;
1879 Msg
->message
= Timer
->Msg
;
1880 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1881 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1882 KeQueryTickCount(&LargeTickCount
);
1883 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1884 pti
= PsGetCurrentThreadWin32Thread();
1885 IntGetCursorLocation(pti
->Desktop
->WindowStation
,
1890 RemoveEntryList(&Timer
->ListEntry
);
1891 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1892 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1893 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1894 InsertTimer(MessageQueue
, Timer
);
1898 DumpTimerList(MessageQueue
);
1899 #endif /* ! defined(NDEBUG) */
1903 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1904 Msg
->wParam
, Msg
->lParam
);
1910 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1913 PLIST_ENTRY EnumEntry
;
1915 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1917 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1918 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1920 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1921 EnumEntry
= EnumEntry
->Flink
;
1922 if (Timer
->Wnd
== Wnd
)
1924 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1925 RemoveEntryList(&Timer
->ListEntry
);
1926 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1931 DumpTimerList(MessageQueue
);
1932 #endif /* ! defined(NDEBUG) */
1937 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1938 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1939 PLARGE_INTEGER FirstTimerExpiry
)
1942 PLIST_ENTRY EnumEntry
;
1944 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1945 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1947 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1948 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1950 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1951 TIMER_ENTRY
, ListEntry
);
1952 EnumEntry
= EnumEntry
->Flink
;
1953 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1954 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1955 (MsgFilterMin
<= Timer
->Msg
&&
1956 Timer
->Msg
<= MsgFilterMax
)))
1958 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1959 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);