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 ******************************************************************/
37 /* GLOBALS *******************************************************************/
39 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
41 static MSG SystemMessageQueue
[SYSTEM_MESSAGE_QUEUE_SIZE
];
42 static ULONG SystemMessageQueueHead
= 0;
43 static ULONG SystemMessageQueueTail
= 0;
44 static ULONG SystemMessageQueueCount
= 0;
45 static KSPIN_LOCK SystemMessageQueueLock
;
47 static ULONG
volatile HardwareMessageQueueStamp
= 0;
48 static LIST_ENTRY HardwareMessageQueueHead
;
49 static KMUTANT HardwareMessageQueueLock
;
51 static KEVENT HardwareMessageEvent
;
53 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
54 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
56 #define IntLockSystemMessageQueue(OldIrql) \
57 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
59 #define IntUnLockSystemMessageQueue(OldIrql) \
60 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
62 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
63 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
65 /* FUNCTIONS *****************************************************************/
68 // Wakeup any thread/process waiting on idle input.
74 PWINDOW_OBJECT Window
;
75 PW32PROCESS W32d
= PsGetCurrentProcessWin32Process();
77 hWnd
= UserGetForegroundWindow();
79 Window
= UserGetWindowObject(hWnd
);
81 if (Window
&& Window
->ti
)
83 if (Window
->ti
->Hooks
& HOOKID_TO_FLAG(WH_FOREGROUNDIDLE
))
85 co_HOOK_CallHooks(WH_FOREGROUNDIDLE
,HC_ACTION
,0,0);
89 if (W32d
&& W32d
->InputIdleEvent
)
90 KePulseEvent( W32d
->InputIdleEvent
, EVENT_INCREMENT
, TRUE
);
94 IntMsqSetWakeMask(DWORD WakeMask
)
96 PTHREADINFO Win32Thread
;
97 PUSER_MESSAGE_QUEUE MessageQueue
;
98 HANDLE MessageEventHandle
;
100 Win32Thread
= PsGetCurrentThreadWin32Thread();
101 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
104 MessageQueue
= Win32Thread
->MessageQueue
;
105 MessageQueue
->WakeMask
= WakeMask
;
106 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
108 return MessageEventHandle
;
112 IntMsqClearWakeMask(VOID
)
114 PTHREADINFO Win32Thread
;
115 PUSER_MESSAGE_QUEUE MessageQueue
;
117 Win32Thread
= PsGetCurrentThreadWin32Thread();
118 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
121 MessageQueue
= Win32Thread
->MessageQueue
;
122 MessageQueue
->WakeMask
= ~0;
128 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
131 Queue
->QueueBits
|= QS_PAINT
;
132 Queue
->ChangedBits
|= QS_PAINT
;
133 if (Queue
->WakeMask
& QS_PAINT
)
134 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
138 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
145 MsqInitializeImpl(VOID
)
147 /*CurrentFocusMessageQueue = NULL;*/
148 InitializeListHead(&HardwareMessageQueueHead
);
149 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
150 KeInitializeSpinLock(&SystemMessageQueueLock
);
151 KeInitializeMutant(&HardwareMessageQueueLock
, 0);
153 ExInitializePagedLookasideList(&MessageLookasideList
,
157 sizeof(USER_MESSAGE
),
160 ExInitializePagedLookasideList(&TimerLookasideList
,
168 return(STATUS_SUCCESS
);
172 MsqInsertSystemMessage(MSG
* Msg
)
174 LARGE_INTEGER LargeTickCount
;
179 IntLockSystemMessageQueue(OldIrql
);
182 * Bail out if the queue is full. FIXME: We should handle this case
186 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
188 IntUnLockSystemMessageQueue(OldIrql
);
192 KeQueryTickCount(&LargeTickCount
);
193 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
195 Event
.message
= Msg
->message
;
196 Event
.time
= Msg
->time
;
197 Event
.hwnd
= Msg
->hwnd
;
198 Event
.paramL
= Msg
->pt
.x
;
199 Event
.paramH
= Msg
->pt
.y
;
200 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
203 * If we got WM_MOUSEMOVE and there are already messages in the
204 * system message queue, check if the last message is mouse move
205 * and if it is then just overwrite it.
208 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
210 if (SystemMessageQueueTail
== 0)
211 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
213 Prev
= SystemMessageQueueTail
- 1;
214 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
216 SystemMessageQueueTail
= Prev
;
217 SystemMessageQueueCount
--;
222 * Actually insert the message into the system message queue.
225 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
226 SystemMessageQueueTail
=
227 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
228 SystemMessageQueueCount
++;
230 IntUnLockSystemMessageQueue(OldIrql
);
232 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
236 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
239 PWINSTATION_OBJECT WinStaObject
;
240 PSYSTEM_CURSORINFO CurInfo
;
244 pti
= PsGetCurrentThreadWin32Thread();
245 if (pti
->Desktop
== NULL
)
250 WinStaObject
= pti
->Desktop
->WindowStation
;
252 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
253 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
254 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
258 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
259 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
265 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
266 (dY
<= CurInfo
->DblClickHeight
);
270 if(CurInfo
->ButtonsDown
)
271 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
279 CurInfo
->LastBtnDown
= 0;
280 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
281 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
282 CurInfo
->LastClkWnd
= NULL
;
283 CurInfo
->ButtonsDown
= Msg
->message
;
287 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
288 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
289 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
290 CurInfo
->LastBtnDown
= Msg
->time
;
291 CurInfo
->ButtonsDown
= Msg
->message
;
299 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
, UINT FilterLow
, UINT FilterHigh
,
300 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
301 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
303 USHORT Msg
= Message
->Msg
.message
;
304 PWINDOW_OBJECT Window
= NULL
;
307 ASSERT_REFS_CO(ScopeWin
);
310 co_WinPosWindowFromPoint can return a Window, and in that case
311 that window has a ref that we need to deref. Thats why we add "dummy"
312 refs in all other cases.
315 hCaptureWin
= IntGetCaptureWindow();
316 if (hCaptureWin
== NULL
)
318 if(Msg
== WM_MOUSEWHEEL
)
320 Window
= UserGetWindowObject(IntGetFocusWindow());
321 if (Window
) UserReferenceObject(Window
);
325 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &Window
);
329 if (Window
) UserReferenceObject(Window
);
333 /* this is the one case where we dont add a ref, since the returned
334 window is already referenced */
340 /* FIXME - window messages should go to the right window if no buttons are
342 Window
= UserGetWindowObject(hCaptureWin
);
343 if (Window
) UserReferenceObject(Window
);
352 RemoveEntryList(&Message
->ListEntry
);
353 if(MessageQueue
->MouseMoveMsg
== Message
)
355 MessageQueue
->MouseMoveMsg
= NULL
;
358 // when FromGlobalQueue is true, the caller has already removed the Message
364 if (Window
->MessageQueue
!= MessageQueue
)
366 if (! FromGlobalQueue
)
368 DPRINT("Moving msg between private queues\n");
369 /* This message is already queued in a private queue, but we need
370 * to move it to a different queue, perhaps because a new window
371 * was created which now covers the screen area previously taken
372 * by another window. To move it, we need to take it out of the
373 * old queue. Note that we're already holding the lock mutexes of the
375 RemoveEntryList(&Message
->ListEntry
);
377 /* remove the pointer for the current WM_MOUSEMOVE message in case we
379 if(MessageQueue
->MouseMoveMsg
== Message
)
381 MessageQueue
->MouseMoveMsg
= NULL
;
385 /* lock the destination message queue, so we don't get in trouble with other
386 threads, messing with it at the same time */
387 IntLockHardwareMessageQueue(Window
->MessageQueue
);
388 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
389 &Message
->ListEntry
);
390 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
392 if(Window
->MessageQueue
->MouseMoveMsg
)
394 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
396 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
397 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
399 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
400 Window
->MessageQueue
->MouseMoveMsg
= Message
;
402 Window
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
403 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
404 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
405 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
409 Window
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
410 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
411 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
412 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
414 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
417 UserDereferenceObject(Window
);
421 /* From here on, we're in the same message queue as the caller! */
423 *ScreenPoint
= Message
->Msg
.pt
;
425 if((hWnd
!= NULL
&& Window
->hSelf
!= hWnd
) ||
426 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
428 /* Reject the message because it doesn't match the filter */
432 /* Lock the message queue so no other thread can mess with it.
433 Our own message queue is not locked while fetching from the global
434 queue, so we have to make sure nothing interferes! */
435 IntLockHardwareMessageQueue(Window
->MessageQueue
);
436 /* if we're from the global queue, we need to add our message to our
437 private queue so we don't loose it! */
438 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
439 &Message
->ListEntry
);
442 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
444 if(Window
->MessageQueue
->MouseMoveMsg
&&
445 (Window
->MessageQueue
->MouseMoveMsg
!= Message
))
447 /* delete the old message */
448 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
449 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
450 if (!FromGlobalQueue
)
452 // We might have deleted the next one in our queue, so fix next
453 *Next
= Message
->ListEntry
.Flink
;
456 /* always save a pointer to this WM_MOUSEMOVE message here because we're
457 sure that the message is in the private queue */
458 Window
->MessageQueue
->MouseMoveMsg
= Message
;
462 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
465 UserDereferenceObject(Window
);
470 /* FIXME - only assign if removing? */
471 Message
->Msg
.hwnd
= Window
->hSelf
;
472 Message
->Msg
.message
= Msg
;
473 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
475 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
477 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
478 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
482 /* Lock the message queue so no other thread can mess with it.
483 Our own message queue is not locked while fetching from the global
484 queue, so we have to make sure nothing interferes! */
485 IntLockHardwareMessageQueue(Window
->MessageQueue
);
486 if(Window
->MessageQueue
->MouseMoveMsg
)
488 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
489 with one that's been sent later */
490 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
491 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
492 /* our message is not in the private queue so we can remove the pointer
493 instead of setting it to the current message we're processing */
494 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
496 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
498 else if(Window
->MessageQueue
->MouseMoveMsg
== Message
)
500 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
504 UserDereferenceObject(Window
);
510 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
,
511 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
512 PUSER_MESSAGE
* Message
)
517 PLIST_ENTRY CurrentEntry
;
518 PWINDOW_OBJECT DesktopWindow
= NULL
;
519 PVOID WaitObjects
[2];
521 DECLARE_RETURN(BOOL
);
522 USER_REFERENCE_ENTRY Ref
;
523 PDESKTOPINFO Desk
= NULL
;
525 WaitObjects
[1] = MessageQueue
->NewMessages
;
526 WaitObjects
[0] = &HardwareMessageQueueLock
;
533 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
534 UserMode
, FALSE
, NULL
, NULL
);
538 while (co_MsqDispatchOneSentMessage(MessageQueue
))
543 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
545 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
549 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
550 Desk
= DesktopWindow
->ti
->Desktop
;
553 /* Process messages in the message queue itself. */
554 IntLockHardwareMessageQueue(MessageQueue
);
555 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
556 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
558 PUSER_MESSAGE Current
=
559 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
560 CurrentEntry
= CurrentEntry
->Flink
;
561 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
562 Current
->Msg
.message
<= WM_MOUSELAST
)
566 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
567 Current
, Remove
, &Freed
,
568 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
573 RemoveEntryList(&Current
->ListEntry
);
575 IntUnLockHardwareMessageQueue(MessageQueue
);
576 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
580 Desk
->LastInputWasKbd
= FALSE
;
587 IntUnLockHardwareMessageQueue(MessageQueue
);
589 /* Now try the global queue. */
591 /* Transfer all messages from the DPC accessible queue to the main queue. */
592 IntLockSystemMessageQueue(OldIrql
);
593 while (SystemMessageQueueCount
> 0)
595 PUSER_MESSAGE UserMsg
;
599 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
600 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
601 SystemMessageQueueHead
=
602 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
603 SystemMessageQueueCount
--;
604 IntUnLockSystemMessageQueue(OldIrql
);
605 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
607 MSLLHOOKSTRUCT MouseHookData
;
609 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
610 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
614 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
618 case WM_XBUTTONDBLCLK
:
619 case WM_NCXBUTTONDOWN
:
621 case WM_NCXBUTTONDBLCLK
:
622 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
625 MouseHookData
.mouseData
= 0;
628 MouseHookData
.flags
= 0;
629 MouseHookData
.time
= Msg
.time
;
630 MouseHookData
.dwExtraInfo
= 0;
631 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
632 Msg
.message
, (LPARAM
) &MouseHookData
));
636 ProcessMessage
= TRUE
;
640 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
641 /* What to do if out of memory? For now we just panic a bit in debug */
643 UserMsg
->FreeLParam
= FALSE
;
645 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
647 IntLockSystemMessageQueue(OldIrql
);
649 HardwareMessageQueueStamp
++;
650 IntUnLockSystemMessageQueue(OldIrql
);
652 /* Process messages in the queue until we find one to return. */
653 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
654 while (CurrentEntry
!= &HardwareMessageQueueHead
)
656 PUSER_MESSAGE Current
=
657 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
658 CurrentEntry
= CurrentEntry
->Flink
;
659 RemoveEntryList(&Current
->ListEntry
);
660 HardwareMessageQueueStamp
++;
661 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
662 Current
->Msg
.message
<= WM_MOUSELAST
)
664 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
665 /* Translate the message. */
666 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
667 Current
, Remove
, &Freed
,
668 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
671 /* Check for no more messages in the system queue. */
672 IntLockSystemMessageQueue(OldIrql
);
673 if (SystemMessageQueueCount
== 0 &&
674 IsListEmpty(&HardwareMessageQueueHead
))
676 KeClearEvent(&HardwareMessageEvent
);
678 IntUnLockSystemMessageQueue(OldIrql
);
681 If we aren't removing the message then add it to the private
686 IntLockHardwareMessageQueue(MessageQueue
);
687 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
689 if(MessageQueue
->MouseMoveMsg
)
691 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
692 ExFreePool(MessageQueue
->MouseMoveMsg
);
694 MessageQueue
->MouseMoveMsg
= Current
;
696 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
697 &Current
->ListEntry
);
698 IntUnLockHardwareMessageQueue(MessageQueue
);
700 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
705 /* If the contents of the queue changed then restart processing. */
706 if (HardwareMessageQueueStamp
!= ActiveStamp
)
708 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
714 /* Check if the system message queue is now empty. */
715 IntLockSystemMessageQueue(OldIrql
);
716 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
718 KeClearEvent(&HardwareMessageEvent
);
720 IntUnLockSystemMessageQueue(OldIrql
);
721 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
726 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
732 // Note: Only called from input.c.
735 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
737 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
739 LARGE_INTEGER LargeTickCount
;
740 KBDLLHOOKSTRUCT KbdHookData
;
743 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
744 uMsg
, wParam
, lParam
);
746 // Condition may arise when calling MsqPostMessage and waiting for an event.
747 if (!UserIsEntered()) UserEnterExclusive(); // Fixme: Not sure ATM if this thread is locked.
749 FocusMessageQueue
= IntGetFocusMessageQueue();
753 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
754 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
760 KeQueryTickCount(&LargeTickCount
);
761 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
763 Event
.message
= Msg
.message
;
764 Event
.hwnd
= Msg
.hwnd
;
765 Event
.time
= Msg
.time
;
766 Event
.paramL
= (Msg
.wParam
& 0xFF) | (HIWORD(Msg
.lParam
) << 8);
767 Event
.paramH
= Msg
.lParam
& 0x7FFF;
768 if (HIWORD(Msg
.lParam
) & 0x0100) Event
.paramH
|= 0x8000;
769 co_HOOK_CallHooks( WH_JOURNALRECORD
, HC_ACTION
, 0, (LPARAM
)&Event
);
771 /* We can't get the Msg.pt point here since we don't know thread
772 (and thus the window station) the message will end up in yet. */
774 KbdHookData
.vkCode
= Msg
.wParam
;
775 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
776 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
777 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
778 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
779 KbdHookData
.time
= Msg
.time
;
780 KbdHookData
.dwExtraInfo
= 0;
781 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
783 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
784 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
788 if (FocusMessageQueue
== NULL
)
790 DPRINT("No focus message queue\n");
794 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
796 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
797 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
799 FocusMessageQueue
->Desktop
->DesktopInfo
->LastInputWasKbd
= TRUE
;
801 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
803 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
807 DPRINT("Invalid focus window handle\n");
809 if (UserIsEntered()) UserLeave();
813 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
815 PWINDOW_OBJECT Window
;
816 PTHREADINFO Win32Thread
;
817 PWINSTATION_OBJECT WinSta
;
819 LARGE_INTEGER LargeTickCount
;
822 Status
= ObReferenceObjectByPointer (Thread
,
826 if (!NT_SUCCESS(Status
))
829 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
830 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
832 ObDereferenceObject ((PETHREAD
)Thread
);
836 WinSta
= Win32Thread
->Desktop
->WindowStation
;
837 Window
= IntGetWindowObject(hWnd
);
840 ObDereferenceObject ((PETHREAD
)Thread
);
845 Mesg
.message
= WM_HOTKEY
;
846 Mesg
.wParam
= wParam
;
847 Mesg
.lParam
= lParam
;
848 KeQueryTickCount(&LargeTickCount
);
849 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
850 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
851 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
852 UserDereferenceObject(Window
);
853 ObDereferenceObject (Thread
);
855 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
856 // &Message->ListEntry);
857 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
860 PUSER_MESSAGE FASTCALL
861 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
863 PUSER_MESSAGE Message
;
865 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
871 Message
->FreeLParam
= FreeLParam
;
872 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
878 MsqDestroyMessage(PUSER_MESSAGE Message
)
880 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
884 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
886 PLIST_ENTRY ListEntry
;
887 PUSER_SENT_MESSAGE_NOTIFY Message
;
889 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
891 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
892 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
895 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
898 Message
->CompletionCallbackContext
,
906 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
908 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
912 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
914 PUSER_SENT_MESSAGE Message
;
918 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
920 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
925 /* remove it from the list of pending messages */
926 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
927 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
929 /* insert it to the list of messages that are currently dispatched by this
931 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
932 &Message
->ListEntry
);
934 if (Message
->HookMessage
== MSQ_ISHOOK
)
936 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
937 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
939 Message
->Msg
.lParam
);
941 else if (Message
->HookMessage
== MSQ_ISEVENT
)
943 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
945 (LONG
) Message
->Msg
.wParam
,
946 (LONG
) Message
->Msg
.lParam
);
950 /* Call the window procedure. */
951 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
952 Message
->Msg
.message
,
954 Message
->Msg
.lParam
);
957 /* remove the message from the local dispatching list, because it doesn't need
958 to be cleaned up on thread termination anymore */
959 RemoveEntryList(&Message
->ListEntry
);
961 /* remove the message from the dispatching list, so lock the sender's message queue */
962 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
965 /* only remove it from the dispatching list if not already removed by a timeout */
966 RemoveEntryList(&Message
->DispatchingListEntry
);
968 /* still keep the sender's message queue locked, so the sender can't exit the
969 MsqSendMessage() function (if timed out) */
971 /* Let the sender know the result. */
972 if (Message
->Result
!= NULL
)
974 *Message
->Result
= Result
;
977 /* Notify the sender. */
978 if (Message
->CompletionEvent
!= NULL
)
980 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
983 /* Notify the sender if they specified a callback. */
984 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
986 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
987 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
989 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
992 NotifyMessage
->CompletionCallback
=
993 Message
->CompletionCallback
;
994 NotifyMessage
->CompletionCallbackContext
=
995 Message
->CompletionCallbackContext
;
996 NotifyMessage
->Result
= Result
;
997 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
998 NotifyMessage
->Msg
= Message
->Msg
.message
;
999 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1004 /* dereference both sender and our queue */
1005 IntDereferenceMessageQueue(MessageQueue
);
1006 IntDereferenceMessageQueue(Message
->SenderQueue
);
1008 /* free the message */
1009 ExFreePool(Message
);
1014 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1016 PUSER_SENT_MESSAGE SentMessage
;
1017 PUSER_MESSAGE PostedMessage
;
1018 PUSER_MESSAGE_QUEUE MessageQueue
;
1019 PLIST_ENTRY CurrentEntry
, ListHead
;
1020 PWINDOW_OBJECT Window
= pWindow
;
1024 MessageQueue
= Window
->MessageQueue
;
1025 ASSERT(MessageQueue
);
1027 /* remove the posted messages for this window */
1028 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1029 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1030 while (CurrentEntry
!= ListHead
)
1032 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1034 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1036 RemoveEntryList(&PostedMessage
->ListEntry
);
1037 MsqDestroyMessage(PostedMessage
);
1038 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1042 CurrentEntry
= CurrentEntry
->Flink
;
1046 /* remove the sent messages for this window */
1047 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1048 ListHead
= &MessageQueue
->SentMessagesListHead
;
1049 while (CurrentEntry
!= ListHead
)
1051 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1053 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1055 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1057 RemoveEntryList(&SentMessage
->ListEntry
);
1059 /* remove the message from the dispatching list */
1060 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1062 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1065 /* wake the sender's thread */
1066 if (SentMessage
->CompletionEvent
!= NULL
)
1068 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1071 /* dereference our and the sender's message queue */
1072 IntDereferenceMessageQueue(MessageQueue
);
1073 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1075 /* free the message */
1076 ExFreePool(SentMessage
);
1078 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1082 CurrentEntry
= CurrentEntry
->Flink
;
1088 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1089 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1091 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1092 &NotifyMessage
->ListEntry
);
1093 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1094 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1095 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1096 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1100 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1101 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1102 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1106 PUSER_SENT_MESSAGE Message
;
1107 KEVENT CompletionEvent
;
1108 NTSTATUS WaitStatus
;
1110 PUSER_MESSAGE_QUEUE ThreadQueue
;
1111 LARGE_INTEGER Timeout
;
1114 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1116 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1117 return STATUS_INSUFFICIENT_RESOURCES
;
1120 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1122 pti
= PsGetCurrentThreadWin32Thread();
1123 ThreadQueue
= pti
->MessageQueue
;
1124 ASSERT(ThreadQueue
!= MessageQueue
);
1126 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1128 /* FIXME - increase reference counter of sender's message queue here */
1131 Message
->Msg
.hwnd
= Wnd
;
1132 Message
->Msg
.message
= Msg
;
1133 Message
->Msg
.wParam
= wParam
;
1134 Message
->Msg
.lParam
= lParam
;
1135 Message
->CompletionEvent
= &CompletionEvent
;
1136 Message
->Result
= &Result
;
1137 Message
->SenderQueue
= ThreadQueue
;
1138 IntReferenceMessageQueue(ThreadQueue
);
1139 Message
->CompletionCallback
= NULL
;
1140 Message
->HookMessage
= HookMessage
;
1142 IntReferenceMessageQueue(MessageQueue
);
1144 /* add it to the list of pending messages */
1145 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1147 /* queue it in the destination's message queue */
1148 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1150 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1151 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1152 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1153 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1155 /* we can't access the Message anymore since it could have already been deleted! */
1163 /* don't process messages sent to the thread */
1164 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1165 FALSE
, (uTimeout
? &Timeout
: NULL
));
1169 if(WaitStatus
== STATUS_TIMEOUT
)
1171 /* look up if the message has not yet dispatched, if so
1172 make sure it can't pass a result and it must not set the completion event anymore */
1173 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1174 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1176 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1179 /* we can access Message here, it's secure because the message queue is locked
1180 and the message is still hasn't been dispatched */
1181 Message
->CompletionEvent
= NULL
;
1182 Message
->Result
= NULL
;
1185 Entry
= Entry
->Flink
;
1188 /* remove from the local dispatching list so the other thread knows,
1189 it can't pass a result and it must not set the completion event anymore */
1190 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1191 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1193 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1196 /* we can access Message here, it's secure because the sender's message is locked
1197 and the message has definitely not yet been destroyed, otherwise it would
1198 have been removed from this list by the dispatching routine right after
1199 dispatching the message */
1200 Message
->CompletionEvent
= NULL
;
1201 Message
->Result
= NULL
;
1202 RemoveEntryList(&Message
->DispatchingListEntry
);
1203 Message
->DispatchingListEntry
.Flink
= NULL
;
1206 Entry
= Entry
->Flink
;
1209 DPRINT("MsqSendMessage (blocked) timed out\n");
1211 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1216 PVOID WaitObjects
[2];
1218 WaitObjects
[0] = &CompletionEvent
;
1219 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1226 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1227 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1231 if(WaitStatus
== STATUS_TIMEOUT
)
1233 /* look up if the message has not yet been dispatched, if so
1234 make sure it can't pass a result and it must not set the completion event anymore */
1235 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1236 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1238 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1241 /* we can access Message here, it's secure because the message queue is locked
1242 and the message is still hasn't been dispatched */
1243 Message
->CompletionEvent
= NULL
;
1244 Message
->Result
= NULL
;
1247 Entry
= Entry
->Flink
;
1250 /* remove from the local dispatching list so the other thread knows,
1251 it can't pass a result and it must not set the completion event anymore */
1252 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1253 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1255 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1258 /* we can access Message here, it's secure because the sender's message is locked
1259 and the message has definitely not yet been destroyed, otherwise it would
1260 have been removed from this list by the dispatching routine right after
1261 dispatching the message */
1262 Message
->CompletionEvent
= NULL
;
1263 Message
->Result
= NULL
;
1264 RemoveEntryList(&Message
->DispatchingListEntry
);
1265 Message
->DispatchingListEntry
.Flink
= NULL
;
1268 Entry
= Entry
->Flink
;
1271 DPRINT("MsqSendMessage timed out\n");
1274 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1277 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1280 if(WaitStatus
!= STATUS_TIMEOUT
)
1281 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1287 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1290 PUSER_MESSAGE Message
;
1292 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1296 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1297 &Message
->ListEntry
);
1298 MessageQueue
->QueueBits
|= MessageBits
;
1299 MessageQueue
->ChangedBits
|= MessageBits
;
1300 if (MessageQueue
->WakeMask
& MessageBits
)
1301 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1305 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1307 MessageQueue
->QuitPosted
= TRUE
;
1308 MessageQueue
->QuitExitCode
= ExitCode
;
1309 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1310 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1311 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1312 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1316 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1317 IN BOOLEAN Hardware
,
1320 IN UINT MsgFilterLow
,
1321 IN UINT MsgFilterHigh
,
1322 OUT PUSER_MESSAGE
* Message
)
1324 PLIST_ENTRY CurrentEntry
;
1325 PUSER_MESSAGE CurrentMessage
;
1326 PLIST_ENTRY ListHead
;
1330 return(co_MsqPeekHardwareMessage(MessageQueue
, Wnd
,
1331 MsgFilterLow
, MsgFilterHigh
,
1335 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1336 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1337 while (CurrentEntry
!= ListHead
)
1339 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1341 if ((Wnd
== 0 || Wnd
== CurrentMessage
->Msg
.hwnd
) &&
1342 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1343 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1344 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1348 RemoveEntryList(&CurrentMessage
->ListEntry
);
1351 *Message
= CurrentMessage
;
1354 CurrentEntry
= CurrentEntry
->Flink
;
1361 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, HWND WndFilter
,
1362 UINT MsgFilterMin
, UINT MsgFilterMax
)
1364 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1365 LARGE_INTEGER TimerExpiry
;
1366 PLARGE_INTEGER Timeout
;
1369 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1371 Timeout
= &TimerExpiry
;
1378 IdlePing(); // Going to wait so send Idle ping.
1382 ret
= KeWaitForMultipleObjects(2,
1397 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1399 LARGE_INTEGER LargeTickCount
;
1401 KeQueryTickCount(&LargeTickCount
);
1402 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1406 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1408 LARGE_INTEGER LargeTickCount
;
1411 MessageQueue
->Thread
= Thread
;
1412 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1413 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1414 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1415 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1416 InitializeListHead(&MessageQueue
->TimerListHead
);
1417 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1418 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1419 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1420 MessageQueue
->QuitPosted
= FALSE
;
1421 MessageQueue
->QuitExitCode
= 0;
1422 KeQueryTickCount(&LargeTickCount
);
1423 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1424 MessageQueue
->FocusWindow
= NULL
;
1425 MessageQueue
->PaintCount
= 0;
1426 MessageQueue
->WakeMask
= ~0;
1427 MessageQueue
->NewMessagesHandle
= NULL
;
1429 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1430 NULL
, SynchronizationEvent
, FALSE
);
1431 if (!NT_SUCCESS(Status
))
1436 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1437 ExEventObjectType
, KernelMode
,
1438 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1439 if (!NT_SUCCESS(Status
))
1441 ZwClose(MessageQueue
->NewMessagesHandle
);
1442 MessageQueue
->NewMessagesHandle
= NULL
;
1450 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1452 PLIST_ENTRY CurrentEntry
;
1453 PUSER_MESSAGE CurrentMessage
;
1454 PTIMER_ENTRY CurrentTimer
;
1455 PUSER_SENT_MESSAGE CurrentSentMessage
;
1457 /* cleanup posted messages */
1458 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1460 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1461 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1463 MsqDestroyMessage(CurrentMessage
);
1466 /* remove the messages that have not yet been dispatched */
1467 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1469 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1470 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1473 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1475 /* remove the message from the dispatching list */
1476 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1478 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1481 /* wake the sender's thread */
1482 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1484 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1487 /* dereference our and the sender's message queue */
1488 IntDereferenceMessageQueue(MessageQueue
);
1489 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1491 /* free the message */
1492 ExFreePool(CurrentSentMessage
);
1495 /* cleanup timers */
1496 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1498 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1499 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1500 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1503 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1504 ExitThread() was called in a SendMessage() umode callback */
1505 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1507 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1508 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1511 /* remove the message from the dispatching list */
1512 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1514 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1517 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1519 /* wake the sender's thread */
1520 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1522 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1525 /* dereference our and the sender's message queue */
1526 IntDereferenceMessageQueue(MessageQueue
);
1527 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1529 /* free the message */
1530 ExFreePool(CurrentSentMessage
);
1533 /* tell other threads not to bother returning any info to us */
1534 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1536 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1537 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1538 DispatchingListEntry
);
1539 CurrentSentMessage
->CompletionEvent
= NULL
;
1540 CurrentSentMessage
->Result
= NULL
;
1542 /* do NOT dereference our message queue as it might get attempted to be
1548 PUSER_MESSAGE_QUEUE FASTCALL
1549 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1551 PUSER_MESSAGE_QUEUE MessageQueue
;
1553 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(PagedPool
,
1554 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1562 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1563 /* hold at least one reference until it'll be destroyed */
1564 IntReferenceMessageQueue(MessageQueue
);
1565 /* initialize the queue */
1566 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1568 IntDereferenceMessageQueue(MessageQueue
);
1572 return MessageQueue
;
1576 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1578 PDESKTOP_OBJECT desk
;
1580 /* remove the message queue from any desktops */
1581 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1583 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1584 IntDereferenceMessageQueue(MessageQueue
);
1588 MsqCleanupMessageQueue(MessageQueue
);
1590 /* decrease the reference counter, if it hits zero, the queue will be freed */
1591 IntDereferenceMessageQueue(MessageQueue
);
1595 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1597 return Queue
->Hooks
;
1601 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1603 Queue
->Hooks
= Hooks
;
1607 MsqSetMessageExtraInfo(LPARAM lParam
)
1611 PUSER_MESSAGE_QUEUE MessageQueue
;
1613 pti
= PsGetCurrentThreadWin32Thread();
1614 MessageQueue
= pti
->MessageQueue
;
1620 Ret
= MessageQueue
->ExtraInfo
;
1621 MessageQueue
->ExtraInfo
= lParam
;
1627 MsqGetMessageExtraInfo(VOID
)
1630 PUSER_MESSAGE_QUEUE MessageQueue
;
1632 pti
= PsGetCurrentThreadWin32Thread();
1633 MessageQueue
= pti
->MessageQueue
;
1639 return MessageQueue
->ExtraInfo
;
1643 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1649 case MSQ_STATE_CAPTURE
:
1650 Prev
= MessageQueue
->CaptureWindow
;
1651 MessageQueue
->CaptureWindow
= hWnd
;
1653 case MSQ_STATE_ACTIVE
:
1654 Prev
= MessageQueue
->ActiveWindow
;
1655 MessageQueue
->ActiveWindow
= hWnd
;
1657 case MSQ_STATE_FOCUS
:
1658 Prev
= MessageQueue
->FocusWindow
;
1659 MessageQueue
->FocusWindow
= hWnd
;
1661 case MSQ_STATE_MENUOWNER
:
1662 Prev
= MessageQueue
->MenuOwner
;
1663 MessageQueue
->MenuOwner
= hWnd
;
1665 case MSQ_STATE_MOVESIZE
:
1666 Prev
= MessageQueue
->MoveSize
;
1667 MessageQueue
->MoveSize
= hWnd
;
1669 case MSQ_STATE_CARET
:
1670 ASSERT(MessageQueue
->CaretInfo
);
1671 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1672 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1680 static VOID FASTCALL
1681 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1683 PLIST_ENTRY Current
;
1686 Current
= MessageQueue
->TimerListHead
.Flink
;
1687 if (Current
== &MessageQueue
->TimerListHead
)
1689 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1691 while (Current
!= &MessageQueue
->TimerListHead
)
1693 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1694 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1695 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1696 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1697 Current
= Current
->Flink
;
1700 #endif /* ! defined(NDEBUG) */
1702 /* Must have the message queue locked while calling this */
1703 static VOID FASTCALL
1704 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1706 PLIST_ENTRY Current
;
1708 Current
= MessageQueue
->TimerListHead
.Flink
;
1709 while (Current
!= &MessageQueue
->TimerListHead
)
1711 if (NewTimer
->ExpiryTime
.QuadPart
<
1712 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1716 Current
= Current
->Flink
;
1719 InsertTailList(Current
, &NewTimer
->ListEntry
);
1722 /* Must have the message queue locked while calling this */
1723 static PTIMER_ENTRY FASTCALL
1724 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1727 PLIST_ENTRY EnumEntry
;
1729 /* Remove timer if already in the queue */
1730 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1731 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1733 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1734 EnumEntry
= EnumEntry
->Flink
;
1736 if (Timer
->Wnd
== Wnd
&&
1737 Timer
->IDEvent
== IDEvent
&&
1740 RemoveEntryList(&Timer
->ListEntry
);
1749 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1750 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1754 LARGE_INTEGER CurrentTime
;
1756 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1757 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1759 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1762 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1765 DPRINT1("Failed to allocate timer entry\n");
1768 DPRINT("Allocated new timer entry %p\n", Timer
);
1770 Timer
->IDEvent
= IDEvent
;
1775 DPRINT("Updating existing timer entry %p\n", Timer
);
1778 KeQuerySystemTime(&CurrentTime
);
1779 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1780 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1781 Timer
->Period
= Period
;
1782 Timer
->TimerFunc
= TimerFunc
;
1783 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1784 Timer
->ExpiryTime
.QuadPart
);
1786 InsertTimer(MessageQueue
, Timer
);
1790 DumpTimerList(MessageQueue
);
1791 #endif /* ! defined(NDEBUG) */
1797 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1798 UINT_PTR IDEvent
, UINT Msg
)
1802 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1803 MessageQueue
, Wnd
, IDEvent
, Msg
);
1805 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1809 DPRINT("Failed to remove timer from list, not found\n");
1813 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1817 DumpTimerList(MessageQueue
);
1818 #endif /* ! defined(NDEBUG) */
1820 return NULL
!= Timer
;
1824 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1825 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1826 MSG
*Msg
, BOOLEAN Restart
)
1829 LARGE_INTEGER CurrentTime
;
1830 LARGE_INTEGER LargeTickCount
;
1831 PLIST_ENTRY EnumEntry
;
1835 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1836 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1838 KeQuerySystemTime(&CurrentTime
);
1839 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1840 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1842 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1844 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1845 TIMER_ENTRY
, ListEntry
);
1846 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1847 Timer
->ExpiryTime
.QuadPart
);
1848 EnumEntry
= EnumEntry
->Flink
;
1849 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1850 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1851 (MsgFilterMin
<= Timer
->Msg
&&
1852 Timer
->Msg
<= MsgFilterMax
)))
1854 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1856 DPRINT("Timer is expired\n");
1862 DPRINT("No need to check later timers\n");
1868 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1869 Timer
, Timer
->Wnd
, Timer
->Msg
, WndFilter
, MsgFilterMin
, MsgFilterMax
);
1875 DPRINT("No timer pending\n");
1879 Msg
->hwnd
= Timer
->Wnd
;
1880 Msg
->message
= Timer
->Msg
;
1881 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1882 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1883 KeQueryTickCount(&LargeTickCount
);
1884 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1885 pti
= PsGetCurrentThreadWin32Thread();
1886 IntGetCursorLocation(pti
->Desktop
->WindowStation
,
1891 RemoveEntryList(&Timer
->ListEntry
);
1892 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1893 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1894 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1895 InsertTimer(MessageQueue
, Timer
);
1899 DumpTimerList(MessageQueue
);
1900 #endif /* ! defined(NDEBUG) */
1904 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1905 Msg
->wParam
, Msg
->lParam
);
1911 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1914 PLIST_ENTRY EnumEntry
;
1916 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1918 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1919 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1921 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1922 EnumEntry
= EnumEntry
->Flink
;
1923 if (Timer
->Wnd
== Wnd
)
1925 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1926 RemoveEntryList(&Timer
->ListEntry
);
1927 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1932 DumpTimerList(MessageQueue
);
1933 #endif /* ! defined(NDEBUG) */
1938 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1939 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1940 PLARGE_INTEGER FirstTimerExpiry
)
1943 PLIST_ENTRY EnumEntry
;
1945 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1946 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1948 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1949 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1951 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1952 TIMER_ENTRY
, ListEntry
);
1953 EnumEntry
= EnumEntry
->Flink
;
1954 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1955 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1956 (MsgFilterMin
<= Timer
->Msg
&&
1957 Timer
->Msg
<= MsgFilterMax
)))
1959 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1960 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);