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 along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 PPROCESSINFO W32d
= PsGetCurrentProcessWin32Process();
76 hWnd
= UserGetForegroundWindow();
78 Window
= UserGetWindowObject(hWnd
);
80 if (Window
&& Window
->pti
)
82 if (Window
->pti
->fsHooks
& 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 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
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
)
178 * If we got WM_MOUSEMOVE and there are already messages in the
179 * system message queue, check if the last message is mouse move
180 * and if it is then just overwrite it.
182 IntLockSystemMessageQueue(OldIrql
);
185 * Bail out if the queue is full. FIXME: We should handle this case
189 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
191 IntUnLockSystemMessageQueue(OldIrql
);
195 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
197 if (SystemMessageQueueTail
== 0)
198 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
200 Prev
= SystemMessageQueueTail
- 1;
201 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
203 SystemMessageQueueTail
= Prev
;
204 SystemMessageQueueCount
--;
209 * Actually insert the message into the system message queue.
212 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
213 SystemMessageQueueTail
=
214 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
215 SystemMessageQueueCount
++;
217 IntUnLockSystemMessageQueue(OldIrql
);
219 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
223 MsqIsClkLck(LPMSG Msg
, BOOL Remove
)
226 PSYSTEM_CURSORINFO CurInfo
;
229 pti
= PsGetCurrentThreadWin32Thread();
230 if (pti
->rpdesk
== NULL
)
235 CurInfo
= IntGetSysCursorInfo();
237 switch (Msg
->message
)
240 Res
= ((Msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
241 if (Res
&& (!CurInfo
->ClickLockActive
))
243 CurInfo
->ClickLockActive
= TRUE
;
247 if (CurInfo
->ClickLockActive
)
250 CurInfo
->ClickLockActive
= FALSE
;
251 CurInfo
->ClickLockTime
= 0;
255 CurInfo
->ClickLockTime
= Msg
->time
;
263 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
266 PSYSTEM_CURSORINFO CurInfo
;
270 pti
= PsGetCurrentThreadWin32Thread();
271 if (pti
->rpdesk
== NULL
)
276 CurInfo
= IntGetSysCursorInfo();
277 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
278 ((Msg
->time
- CurInfo
->LastBtnDown
) < gspv
.iDblClickTime
);
282 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
283 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
289 Res
= (dX
<= gspv
.iDblClickWidth
) &&
290 (dY
<= gspv
.iDblClickHeight
);
294 if(CurInfo
->ButtonsDown
)
295 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
301 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
302 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
303 CurInfo
->ButtonsDown
= Msg
->message
;
306 CurInfo
->LastBtnDown
= 0;
307 CurInfo
->LastClkWnd
= NULL
;
311 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
312 CurInfo
->LastBtnDown
= Msg
->time
;
320 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
321 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
322 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
324 USHORT Msg
= Message
->Msg
.message
;
325 PWINDOW_OBJECT CaptureWindow
= NULL
;
328 ASSERT_REFS_CO(ScopeWin
);
331 co_WinPosWindowFromPoint can return a Window, and in that case
332 that window has a ref that we need to deref. Thats why we add "dummy"
333 refs in all other cases.
336 hCaptureWin
= IntGetCaptureWindow();
337 if (hCaptureWin
== NULL
)
339 if (Msg
== WM_MOUSEWHEEL
)
341 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
342 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
346 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
347 if(CaptureWindow
== NULL
)
349 CaptureWindow
= ScopeWin
;
350 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
354 /* this is the one case where we dont add a ref, since the returned
355 window is already referenced */
361 /* FIXME - window messages should go to the right window if no buttons are
363 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
364 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
369 if (CaptureWindow
== NULL
)
373 RemoveEntryList(&Message
->ListEntry
);
374 if(MessageQueue
->MouseMoveMsg
== Message
)
376 MessageQueue
->MouseMoveMsg
= NULL
;
379 // when FromGlobalQueue is true, the caller has already removed the Message
385 if (CaptureWindow
->pti
->MessageQueue
!= MessageQueue
)
387 if (! FromGlobalQueue
)
389 DPRINT("Moving msg between private queues\n");
390 /* This message is already queued in a private queue, but we need
391 * to move it to a different queue, perhaps because a new window
392 * was created which now covers the screen area previously taken
393 * by another window. To move it, we need to take it out of the
394 * old queue. Note that we're already holding the lock mutexes of the
396 RemoveEntryList(&Message
->ListEntry
);
398 /* remove the pointer for the current WM_MOUSEMOVE message in case we
400 if(MessageQueue
->MouseMoveMsg
== Message
)
402 MessageQueue
->MouseMoveMsg
= NULL
;
406 /* lock the destination message queue, so we don't get in trouble with other
407 threads, messing with it at the same time */
408 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
409 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
410 &Message
->ListEntry
);
411 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
413 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
415 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
417 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
418 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
420 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
421 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
423 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
424 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
425 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
426 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
430 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
431 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
432 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
433 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
435 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
438 UserDereferenceObject(CaptureWindow
);
442 /* From here on, we're in the same message queue as the caller! */
444 *ScreenPoint
= Message
->Msg
.pt
;
446 if((Window
!= NULL
&& PtrToInt(Window
) != 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
447 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
449 /* Reject the message because it doesn't match the filter */
453 /* Lock the message queue so no other thread can mess with it.
454 Our own message queue is not locked while fetching from the global
455 queue, so we have to make sure nothing interferes! */
456 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
457 /* if we're from the global queue, we need to add our message to our
458 private queue so we don't loose it! */
459 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
460 &Message
->ListEntry
);
463 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
465 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
&&
466 (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
!= Message
))
468 /* delete the old message */
469 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
470 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
471 if (!FromGlobalQueue
)
473 // We might have deleted the next one in our queue, so fix next
474 *Next
= Message
->ListEntry
.Flink
;
477 /* always save a pointer to this WM_MOUSEMOVE message here because we're
478 sure that the message is in the private queue */
479 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
483 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
486 UserDereferenceObject(CaptureWindow
);
491 /* FIXME - only assign if removing? */
492 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
493 Message
->Msg
.message
= Msg
;
494 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
496 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
498 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
499 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
503 /* Lock the message queue so no other thread can mess with it.
504 Our own message queue is not locked while fetching from the global
505 queue, so we have to make sure nothing interferes! */
506 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
507 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
509 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
510 with one that's been sent later */
511 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
512 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
513 /* our message is not in the private queue so we can remove the pointer
514 instead of setting it to the current message we're processing */
515 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
517 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
519 else if (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
== Message
)
521 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
525 UserDereferenceObject(CaptureWindow
);
531 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
532 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
533 PUSER_MESSAGE
* Message
)
538 PLIST_ENTRY CurrentEntry
;
539 PWINDOW_OBJECT DesktopWindow
= NULL
;
540 PVOID WaitObjects
[2];
542 DECLARE_RETURN(BOOL
);
543 USER_REFERENCE_ENTRY Ref
;
544 PDESKTOPINFO Desk
= NULL
;
546 WaitObjects
[1] = MessageQueue
->NewMessages
;
547 WaitObjects
[0] = &HardwareMessageQueueLock
;
554 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
555 UserMode
, FALSE
, NULL
, NULL
);
559 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
561 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
565 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
566 Desk
= DesktopWindow
->pti
->pDeskInfo
;
569 /* Process messages in the message queue itself. */
570 IntLockHardwareMessageQueue(MessageQueue
);
571 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
572 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
574 PUSER_MESSAGE Current
=
575 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
576 CurrentEntry
= CurrentEntry
->Flink
;
577 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
578 Current
->Msg
.message
<= WM_MOUSELAST
)
582 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
583 Current
, Remove
, &Freed
,
584 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
589 RemoveEntryList(&Current
->ListEntry
);
591 IntUnLockHardwareMessageQueue(MessageQueue
);
592 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
596 Desk
->LastInputWasKbd
= FALSE
;
603 IntUnLockHardwareMessageQueue(MessageQueue
);
605 /* Now try the global queue. */
607 /* Transfer all messages from the DPC accessible queue to the main queue. */
608 IntLockSystemMessageQueue(OldIrql
);
609 while (SystemMessageQueueCount
> 0)
611 PUSER_MESSAGE UserMsg
;
614 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
615 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
616 SystemMessageQueueHead
=
617 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
618 SystemMessageQueueCount
--;
619 IntUnLockSystemMessageQueue(OldIrql
);
621 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
622 /* What to do if out of memory? For now we just panic a bit in debug */
624 UserMsg
->FreeLParam
= FALSE
;
626 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
628 IntLockSystemMessageQueue(OldIrql
);
630 HardwareMessageQueueStamp
++;
631 IntUnLockSystemMessageQueue(OldIrql
);
633 /* Process messages in the queue until we find one to return. */
634 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
635 while (CurrentEntry
!= &HardwareMessageQueueHead
)
637 PUSER_MESSAGE Current
=
638 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
639 CurrentEntry
= CurrentEntry
->Flink
;
640 RemoveEntryList(&Current
->ListEntry
);
641 HardwareMessageQueueStamp
++;
642 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
643 Current
->Msg
.message
<= WM_MOUSELAST
)
645 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
646 /* Translate the message. */
647 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
648 Current
, Remove
, &Freed
,
649 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
652 /* Check for no more messages in the system queue. */
653 IntLockSystemMessageQueue(OldIrql
);
654 if (SystemMessageQueueCount
== 0 &&
655 IsListEmpty(&HardwareMessageQueueHead
))
657 KeClearEvent(&HardwareMessageEvent
);
659 IntUnLockSystemMessageQueue(OldIrql
);
662 If we aren't removing the message then add it to the private
667 IntLockHardwareMessageQueue(MessageQueue
);
668 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
670 if(MessageQueue
->MouseMoveMsg
)
672 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
673 ExFreePool(MessageQueue
->MouseMoveMsg
);
675 MessageQueue
->MouseMoveMsg
= Current
;
677 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
678 &Current
->ListEntry
);
679 IntUnLockHardwareMessageQueue(MessageQueue
);
681 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
686 /* If the contents of the queue changed then restart processing. */
687 if (HardwareMessageQueueStamp
!= ActiveStamp
)
689 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
695 /* Check if the system message queue is now empty. */
696 IntLockSystemMessageQueue(OldIrql
);
697 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
699 KeClearEvent(&HardwareMessageEvent
);
701 IntUnLockSystemMessageQueue(OldIrql
);
702 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
707 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
713 // Note: Only called from input.c.
716 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
718 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
720 LARGE_INTEGER LargeTickCount
;
721 KBDLLHOOKSTRUCT KbdHookData
;
722 BOOLEAN Entered
= FALSE
;
724 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
725 uMsg
, wParam
, lParam
);
727 // Condition may arise when calling MsqPostMessage and waiting for an event.
728 if (!UserIsEntered())
730 // Fixme: Not sure ATM if this thread is locked.
731 UserEnterExclusive();
735 FocusMessageQueue
= IntGetFocusMessageQueue();
739 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
740 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
746 KeQueryTickCount(&LargeTickCount
);
747 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
749 /* We can't get the Msg.pt point here since we don't know thread
750 (and thus the window station) the message will end up in yet. */
752 KbdHookData
.vkCode
= Msg
.wParam
;
753 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
754 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
755 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
756 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
757 KbdHookData
.time
= Msg
.time
;
758 KbdHookData
.dwExtraInfo
= 0;
759 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
761 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
762 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
763 if (Entered
) UserLeave();
767 if (FocusMessageQueue
== NULL
)
769 DPRINT("No focus message queue\n");
770 if (Entered
) UserLeave();
774 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
776 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
777 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
779 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
781 Msg
.pt
= gpsi
->ptCursor
;
782 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
786 DPRINT("Invalid focus window handle\n");
789 if (Entered
) UserLeave();
794 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
796 PWINDOW_OBJECT Window
;
797 PTHREADINFO Win32Thread
;
799 LARGE_INTEGER LargeTickCount
;
802 Status
= ObReferenceObjectByPointer (Thread
,
806 if (!NT_SUCCESS(Status
))
809 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
810 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
812 ObDereferenceObject ((PETHREAD
)Thread
);
816 Window
= IntGetWindowObject(hWnd
);
819 ObDereferenceObject ((PETHREAD
)Thread
);
824 Mesg
.message
= WM_HOTKEY
;
825 Mesg
.wParam
= wParam
;
826 Mesg
.lParam
= lParam
;
827 KeQueryTickCount(&LargeTickCount
);
828 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
829 Mesg
.pt
= gpsi
->ptCursor
;
830 MsqPostMessage(Window
->pti
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
831 UserDereferenceObject(Window
);
832 ObDereferenceObject (Thread
);
834 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
835 // &Message->ListEntry);
836 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
839 PUSER_MESSAGE FASTCALL
840 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
842 PUSER_MESSAGE Message
;
844 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
850 Message
->FreeLParam
= FreeLParam
;
851 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
857 MsqDestroyMessage(PUSER_MESSAGE Message
)
859 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
863 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
865 PLIST_ENTRY ListEntry
;
866 PUSER_SENT_MESSAGE_NOTIFY Message
;
868 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
870 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
871 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
874 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
877 Message
->CompletionCallbackContext
,
885 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
887 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
891 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
893 PUSER_SENT_MESSAGE Message
;
898 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
903 /* remove it from the list of pending messages */
904 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
905 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
907 /* insert it to the list of messages that are currently dispatched by this
909 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
910 &Message
->ListEntry
);
912 if (Message
->HookMessage
== MSQ_ISHOOK
)
914 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
915 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
917 Message
->Msg
.lParam
);
919 else if (Message
->HookMessage
== MSQ_ISEVENT
)
921 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
924 Message
->Msg
.lParam
);
928 /* Call the window procedure. */
929 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
930 Message
->Msg
.message
,
932 Message
->Msg
.lParam
);
935 /* remove the message from the local dispatching list, because it doesn't need
936 to be cleaned up on thread termination anymore */
937 RemoveEntryList(&Message
->ListEntry
);
939 /* remove the message from the dispatching list, so lock the sender's message queue */
940 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
943 /* only remove it from the dispatching list if not already removed by a timeout */
944 RemoveEntryList(&Message
->DispatchingListEntry
);
946 /* still keep the sender's message queue locked, so the sender can't exit the
947 MsqSendMessage() function (if timed out) */
949 /* Let the sender know the result. */
950 if (Message
->Result
!= NULL
)
952 *Message
->Result
= Result
;
955 if (Message
->HasPackedLParam
== TRUE
)
957 if (Message
->Msg
.lParam
)
958 ExFreePool((PVOID
)Message
->Msg
.lParam
);
961 /* Notify the sender. */
962 if (Message
->CompletionEvent
!= NULL
)
964 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
967 /* Call the callback if the message was sent with SendMessageCallback */
968 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
970 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
972 Message
->Msg
.message
,
973 Message
->CompletionCallbackContext
,
978 /* Only if it is not a no wait message */
979 if (!(Message
->HookMessage
& MSQ_SENTNOWAIT
))
981 IntDereferenceMessageQueue(Message
->SenderQueue
);
982 IntDereferenceMessageQueue(MessageQueue
);
985 /* free the message */
986 ExFreePoolWithTag(Message
, TAG_USRMSG
);
991 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
993 PUSER_SENT_MESSAGE SentMessage
;
994 PUSER_MESSAGE PostedMessage
;
995 PUSER_MESSAGE_QUEUE MessageQueue
;
996 PLIST_ENTRY CurrentEntry
, ListHead
;
997 PWINDOW_OBJECT Window
= pWindow
;
1001 MessageQueue
= Window
->pti
->MessageQueue
;
1002 ASSERT(MessageQueue
);
1004 /* remove the posted messages for this window */
1005 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1006 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1007 while (CurrentEntry
!= ListHead
)
1009 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1011 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1013 RemoveEntryList(&PostedMessage
->ListEntry
);
1014 MsqDestroyMessage(PostedMessage
);
1015 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1019 CurrentEntry
= CurrentEntry
->Flink
;
1023 /* remove the sent messages for this window */
1024 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1025 ListHead
= &MessageQueue
->SentMessagesListHead
;
1026 while (CurrentEntry
!= ListHead
)
1028 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1030 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1032 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1034 RemoveEntryList(&SentMessage
->ListEntry
);
1036 /* remove the message from the dispatching list */
1037 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1039 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1042 /* wake the sender's thread */
1043 if (SentMessage
->CompletionEvent
!= NULL
)
1045 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1048 if (SentMessage
->HasPackedLParam
== TRUE
)
1050 if (SentMessage
->Msg
.lParam
)
1051 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
1054 /* Only if it is not a no wait message */
1055 if (!(SentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1057 /* dereference our and the sender's message queue */
1058 IntDereferenceMessageQueue(MessageQueue
);
1059 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1062 /* free the message */
1063 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
1065 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1069 CurrentEntry
= CurrentEntry
->Flink
;
1075 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1076 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1078 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1079 &NotifyMessage
->ListEntry
);
1080 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1081 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1082 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1083 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1087 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1088 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1089 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1093 PUSER_SENT_MESSAGE Message
;
1094 KEVENT CompletionEvent
;
1095 NTSTATUS WaitStatus
;
1097 PUSER_MESSAGE_QUEUE ThreadQueue
;
1098 LARGE_INTEGER Timeout
;
1101 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1103 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1104 return STATUS_INSUFFICIENT_RESOURCES
;
1107 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1109 pti
= PsGetCurrentThreadWin32Thread();
1110 ThreadQueue
= pti
->MessageQueue
;
1111 ASSERT(ThreadQueue
!= MessageQueue
);
1113 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1115 /* FIXME - increase reference counter of sender's message queue here */
1118 Message
->Msg
.hwnd
= Wnd
;
1119 Message
->Msg
.message
= Msg
;
1120 Message
->Msg
.wParam
= wParam
;
1121 Message
->Msg
.lParam
= lParam
;
1122 Message
->CompletionEvent
= &CompletionEvent
;
1123 Message
->Result
= &Result
;
1124 Message
->SenderQueue
= ThreadQueue
;
1125 IntReferenceMessageQueue(ThreadQueue
);
1126 Message
->CompletionCallback
= NULL
;
1127 Message
->HookMessage
= HookMessage
;
1128 Message
->HasPackedLParam
= FALSE
;
1130 IntReferenceMessageQueue(MessageQueue
);
1132 /* add it to the list of pending messages */
1133 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1135 /* queue it in the destination's message queue */
1136 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1138 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1139 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1140 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1141 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1143 /* we can't access the Message anymore since it could have already been deleted! */
1151 /* don't process messages sent to the thread */
1152 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1153 FALSE
, (uTimeout
? &Timeout
: NULL
));
1157 if(WaitStatus
== STATUS_TIMEOUT
)
1159 /* look up if the message has not yet dispatched, if so
1160 make sure it can't pass a result and it must not set the completion event anymore */
1161 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1162 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1164 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1167 /* we can access Message here, it's secure because the message queue is locked
1168 and the message is still hasn't been dispatched */
1169 Message
->CompletionEvent
= NULL
;
1170 Message
->Result
= NULL
;
1173 Entry
= Entry
->Flink
;
1176 /* remove from the local dispatching list so the other thread knows,
1177 it can't pass a result and it must not set the completion event anymore */
1178 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1179 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1181 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1184 /* we can access Message here, it's secure because the sender's message is locked
1185 and the message has definitely not yet been destroyed, otherwise it would
1186 have been removed from this list by the dispatching routine right after
1187 dispatching the message */
1188 Message
->CompletionEvent
= NULL
;
1189 Message
->Result
= NULL
;
1190 RemoveEntryList(&Message
->DispatchingListEntry
);
1191 Message
->DispatchingListEntry
.Flink
= NULL
;
1194 Entry
= Entry
->Flink
;
1197 DPRINT("MsqSendMessage (blocked) timed out\n");
1199 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1204 PVOID WaitObjects
[2];
1206 WaitObjects
[0] = &CompletionEvent
;
1207 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1214 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1215 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1219 if(WaitStatus
== STATUS_TIMEOUT
)
1221 /* look up if the message has not yet been dispatched, if so
1222 make sure it can't pass a result and it must not set the completion event anymore */
1223 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1224 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1226 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1229 /* we can access Message here, it's secure because the message queue is locked
1230 and the message is still hasn't been dispatched */
1231 Message
->CompletionEvent
= NULL
;
1232 Message
->Result
= NULL
;
1235 Entry
= Entry
->Flink
;
1238 /* remove from the local dispatching list so the other thread knows,
1239 it can't pass a result and it must not set the completion event anymore */
1240 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1241 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1243 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1246 /* we can access Message here, it's secure because the sender's message is locked
1247 and the message has definitely not yet been destroyed, otherwise it would
1248 have been removed from this list by the dispatching routine right after
1249 dispatching the message */
1250 Message
->CompletionEvent
= NULL
;
1251 Message
->Result
= NULL
;
1252 RemoveEntryList(&Message
->DispatchingListEntry
);
1253 Message
->DispatchingListEntry
.Flink
= NULL
;
1256 Entry
= Entry
->Flink
;
1259 DPRINT("MsqSendMessage timed out\n");
1262 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1265 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1268 if(WaitStatus
!= STATUS_TIMEOUT
)
1269 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1275 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1278 PUSER_MESSAGE Message
;
1280 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1284 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1285 &Message
->ListEntry
);
1286 MessageQueue
->QueueBits
|= MessageBits
;
1287 MessageQueue
->ChangedBits
|= MessageBits
;
1288 if (MessageQueue
->WakeMask
& MessageBits
)
1289 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1293 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1295 MessageQueue
->QuitPosted
= TRUE
;
1296 MessageQueue
->QuitExitCode
= ExitCode
;
1297 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1298 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1299 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1300 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1304 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1305 IN BOOLEAN Hardware
,
1307 IN PWINDOW_OBJECT Window
,
1308 IN UINT MsgFilterLow
,
1309 IN UINT MsgFilterHigh
,
1310 OUT PUSER_MESSAGE
* Message
)
1312 PLIST_ENTRY CurrentEntry
;
1313 PUSER_MESSAGE CurrentMessage
;
1314 PLIST_ENTRY ListHead
;
1318 return(co_MsqPeekHardwareMessage( MessageQueue
,
1326 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1327 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1328 while (CurrentEntry
!= ListHead
)
1330 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1333 PtrToInt(Window
) == 1 ||
1334 Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1335 ( (MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1336 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1337 MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1341 RemoveEntryList(&CurrentMessage
->ListEntry
);
1344 *Message
= CurrentMessage
;
1347 CurrentEntry
= CurrentEntry
->Flink
;
1354 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1355 UINT MsgFilterMin
, UINT MsgFilterMax
)
1357 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1360 IdlePing(); // Going to wait so send Idle ping.
1364 ret
= KeWaitForMultipleObjects(2,
1377 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1379 LARGE_INTEGER LargeTickCount
;
1381 KeQueryTickCount(&LargeTickCount
);
1382 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1386 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1388 LARGE_INTEGER LargeTickCount
;
1391 MessageQueue
->Thread
= Thread
;
1392 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1393 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1394 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1395 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1396 InitializeListHead(&MessageQueue
->TimerListHead
);
1397 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1398 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1399 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1400 MessageQueue
->QuitPosted
= FALSE
;
1401 MessageQueue
->QuitExitCode
= 0;
1402 KeQueryTickCount(&LargeTickCount
);
1403 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1404 MessageQueue
->FocusWindow
= NULL
;
1405 MessageQueue
->PaintCount
= 0;
1406 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1407 MessageQueue
->WakeMask
= ~0;
1408 MessageQueue
->NewMessagesHandle
= NULL
;
1410 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1411 NULL
, SynchronizationEvent
, FALSE
);
1412 if (!NT_SUCCESS(Status
))
1417 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1418 ExEventObjectType
, KernelMode
,
1419 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1420 if (!NT_SUCCESS(Status
))
1422 ZwClose(MessageQueue
->NewMessagesHandle
);
1423 MessageQueue
->NewMessagesHandle
= NULL
;
1431 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1433 PLIST_ENTRY CurrentEntry
;
1434 PUSER_MESSAGE CurrentMessage
;
1435 PTIMER_ENTRY CurrentTimer
;
1436 PUSER_SENT_MESSAGE CurrentSentMessage
;
1438 /* cleanup posted messages */
1439 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1441 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1442 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1444 MsqDestroyMessage(CurrentMessage
);
1447 /* remove the messages that have not yet been dispatched */
1448 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1450 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1451 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1454 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1456 /* remove the message from the dispatching list */
1457 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1459 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1462 /* wake the sender's thread */
1463 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1465 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1468 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1470 if (CurrentSentMessage
->Msg
.lParam
)
1471 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1474 /* Only if it is not a no wait message */
1475 if (!(CurrentSentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1477 /* dereference our and the sender's message queue */
1478 IntDereferenceMessageQueue(MessageQueue
);
1479 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1482 /* free the message */
1483 ExFreePool(CurrentSentMessage
);
1486 /* cleanup timers */
1487 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1489 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1490 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1491 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1494 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1495 ExitThread() was called in a SendMessage() umode callback */
1496 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1498 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1499 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1502 /* remove the message from the dispatching list */
1503 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1505 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1508 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1510 /* wake the sender's thread */
1511 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1513 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1516 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1518 if (CurrentSentMessage
->Msg
.lParam
)
1519 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1522 /* Only if it is not a no wait message */
1523 if (!(CurrentSentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
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(NonPagedPool
,
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
)
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 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1826 MSG
*Msg
, BOOLEAN Restart
)
1829 LARGE_INTEGER CurrentTime
;
1830 LARGE_INTEGER LargeTickCount
;
1831 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
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
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
, WindowFilter
->hSelf
, 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 Msg
->pt
= gpsi
->ptCursor
;
1888 RemoveEntryList(&Timer
->ListEntry
);
1889 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1890 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1891 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1892 InsertTimer(MessageQueue
, Timer
);
1896 DumpTimerList(MessageQueue
);
1897 #endif /* ! defined(NDEBUG) */
1901 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1902 Msg
->wParam
, Msg
->lParam
);
1908 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1911 PLIST_ENTRY EnumEntry
;
1913 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1915 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1916 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1918 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1919 EnumEntry
= EnumEntry
->Flink
;
1920 if (Timer
->Wnd
== Wnd
)
1922 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1923 RemoveEntryList(&Timer
->ListEntry
);
1924 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1929 DumpTimerList(MessageQueue
);
1930 #endif /* ! defined(NDEBUG) */
1935 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1936 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1937 PLARGE_INTEGER FirstTimerExpiry
)
1940 PLIST_ENTRY EnumEntry
;
1942 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1943 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1945 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1946 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1948 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1949 TIMER_ENTRY
, ListEntry
);
1950 EnumEntry
= EnumEntry
->Flink
;
1951 if ((NULL
== WndFilter
|| PtrToInt(WndFilter
) == 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1952 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1953 (MsgFilterMin
<= Timer
->Msg
&&
1954 Timer
->Msg
<= MsgFilterMax
)))
1956 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1957 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);