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
)
174 LARGE_INTEGER LargeTickCount
;
177 MSLLHOOKSTRUCT MouseHookData
;
179 KeQueryTickCount(&LargeTickCount
);
180 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
182 MouseHookData
.pt
.x
= LOWORD(Msg
->lParam
);
183 MouseHookData
.pt
.y
= HIWORD(Msg
->lParam
);
187 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
->wParam
));
191 case WM_XBUTTONDBLCLK
:
192 case WM_NCXBUTTONDOWN
:
194 case WM_NCXBUTTONDBLCLK
:
195 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
->wParam
));
198 MouseHookData
.mouseData
= 0;
202 MouseHookData
.flags
= 0;
203 MouseHookData
.time
= Msg
->time
;
204 MouseHookData
.dwExtraInfo
= 0;
206 /* If the hook procedure returned non zero, dont send the message */
207 if (co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
211 * If we got WM_MOUSEMOVE and there are already messages in the
212 * system message queue, check if the last message is mouse move
213 * and if it is then just overwrite it.
215 IntLockSystemMessageQueue(OldIrql
);
218 * Bail out if the queue is full. FIXME: We should handle this case
222 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
224 IntUnLockSystemMessageQueue(OldIrql
);
228 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
230 if (SystemMessageQueueTail
== 0)
231 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
233 Prev
= SystemMessageQueueTail
- 1;
234 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
236 SystemMessageQueueTail
= Prev
;
237 SystemMessageQueueCount
--;
242 * Actually insert the message into the system message queue.
245 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
246 SystemMessageQueueTail
=
247 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
248 SystemMessageQueueCount
++;
250 IntUnLockSystemMessageQueue(OldIrql
);
252 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
256 MsqIsClkLck(LPMSG Msg
, BOOL Remove
)
259 PSYSTEM_CURSORINFO CurInfo
;
262 pti
= PsGetCurrentThreadWin32Thread();
263 if (pti
->rpdesk
== NULL
)
268 CurInfo
= IntGetSysCursorInfo();
270 switch (Msg
->message
)
273 Res
= ((Msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
274 if (Res
&& (!CurInfo
->ClickLockActive
))
276 CurInfo
->ClickLockActive
= TRUE
;
280 if (CurInfo
->ClickLockActive
)
283 CurInfo
->ClickLockActive
= FALSE
;
284 CurInfo
->ClickLockTime
= 0;
288 CurInfo
->ClickLockTime
= Msg
->time
;
296 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
299 PSYSTEM_CURSORINFO CurInfo
;
303 pti
= PsGetCurrentThreadWin32Thread();
304 if (pti
->rpdesk
== NULL
)
309 CurInfo
= IntGetSysCursorInfo();
310 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
311 ((Msg
->time
- CurInfo
->LastBtnDown
) < gspv
.iDblClickTime
);
315 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
316 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
322 Res
= (dX
<= gspv
.iDblClickWidth
) &&
323 (dY
<= gspv
.iDblClickHeight
);
327 if(CurInfo
->ButtonsDown
)
328 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
334 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
335 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
336 CurInfo
->ButtonsDown
= Msg
->message
;
339 CurInfo
->LastBtnDown
= 0;
340 CurInfo
->LastClkWnd
= NULL
;
344 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
345 CurInfo
->LastBtnDown
= Msg
->time
;
353 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
354 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
355 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
357 USHORT Msg
= Message
->Msg
.message
;
358 PWINDOW_OBJECT CaptureWindow
= NULL
;
361 ASSERT_REFS_CO(ScopeWin
);
364 co_WinPosWindowFromPoint can return a Window, and in that case
365 that window has a ref that we need to deref. Thats why we add "dummy"
366 refs in all other cases.
369 hCaptureWin
= IntGetCaptureWindow();
370 if (hCaptureWin
== NULL
)
372 if (Msg
== WM_MOUSEWHEEL
)
374 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
375 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
379 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
380 if(CaptureWindow
== NULL
)
382 CaptureWindow
= ScopeWin
;
383 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
387 /* this is the one case where we dont add a ref, since the returned
388 window is already referenced */
394 /* FIXME - window messages should go to the right window if no buttons are
396 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
397 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
402 if (CaptureWindow
== NULL
)
406 RemoveEntryList(&Message
->ListEntry
);
407 if(MessageQueue
->MouseMoveMsg
== Message
)
409 MessageQueue
->MouseMoveMsg
= NULL
;
412 // when FromGlobalQueue is true, the caller has already removed the Message
418 if (CaptureWindow
->pti
->MessageQueue
!= MessageQueue
)
420 if (! FromGlobalQueue
)
422 DPRINT("Moving msg between private queues\n");
423 /* This message is already queued in a private queue, but we need
424 * to move it to a different queue, perhaps because a new window
425 * was created which now covers the screen area previously taken
426 * by another window. To move it, we need to take it out of the
427 * old queue. Note that we're already holding the lock mutexes of the
429 RemoveEntryList(&Message
->ListEntry
);
431 /* remove the pointer for the current WM_MOUSEMOVE message in case we
433 if(MessageQueue
->MouseMoveMsg
== Message
)
435 MessageQueue
->MouseMoveMsg
= NULL
;
439 /* lock the destination message queue, so we don't get in trouble with other
440 threads, messing with it at the same time */
441 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
442 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
443 &Message
->ListEntry
);
444 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
446 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
448 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
450 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
451 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
453 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
454 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
456 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
457 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
458 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
459 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
463 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
464 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
465 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
466 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
468 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
471 UserDereferenceObject(CaptureWindow
);
475 /* From here on, we're in the same message queue as the caller! */
477 *ScreenPoint
= Message
->Msg
.pt
;
479 if((Window
!= NULL
&& PtrToInt(Window
) != 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
480 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
482 /* Reject the message because it doesn't match the filter */
486 /* Lock the message queue so no other thread can mess with it.
487 Our own message queue is not locked while fetching from the global
488 queue, so we have to make sure nothing interferes! */
489 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
490 /* if we're from the global queue, we need to add our message to our
491 private queue so we don't loose it! */
492 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
493 &Message
->ListEntry
);
496 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
498 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
&&
499 (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
!= Message
))
501 /* delete the old message */
502 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
503 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
504 if (!FromGlobalQueue
)
506 // We might have deleted the next one in our queue, so fix next
507 *Next
= Message
->ListEntry
.Flink
;
510 /* always save a pointer to this WM_MOUSEMOVE message here because we're
511 sure that the message is in the private queue */
512 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
516 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
519 UserDereferenceObject(CaptureWindow
);
524 /* FIXME - only assign if removing? */
525 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
526 Message
->Msg
.message
= Msg
;
527 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
529 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
531 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
532 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
536 /* Lock the message queue so no other thread can mess with it.
537 Our own message queue is not locked while fetching from the global
538 queue, so we have to make sure nothing interferes! */
539 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
540 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
542 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
543 with one that's been sent later */
544 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
545 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
546 /* our message is not in the private queue so we can remove the pointer
547 instead of setting it to the current message we're processing */
548 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
550 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
552 else if (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
== Message
)
554 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
558 UserDereferenceObject(CaptureWindow
);
564 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
565 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
566 PUSER_MESSAGE
* Message
)
571 PLIST_ENTRY CurrentEntry
;
572 PWINDOW_OBJECT DesktopWindow
= NULL
;
573 PVOID WaitObjects
[2];
575 DECLARE_RETURN(BOOL
);
576 USER_REFERENCE_ENTRY Ref
;
577 PDESKTOPINFO Desk
= NULL
;
579 WaitObjects
[1] = MessageQueue
->NewMessages
;
580 WaitObjects
[0] = &HardwareMessageQueueLock
;
587 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
588 UserMode
, FALSE
, NULL
, NULL
);
592 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
594 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
598 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
599 Desk
= DesktopWindow
->pti
->pDeskInfo
;
602 /* Process messages in the message queue itself. */
603 IntLockHardwareMessageQueue(MessageQueue
);
604 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
605 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
607 PUSER_MESSAGE Current
=
608 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
609 CurrentEntry
= CurrentEntry
->Flink
;
610 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
611 Current
->Msg
.message
<= WM_MOUSELAST
)
615 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
616 Current
, Remove
, &Freed
,
617 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
622 RemoveEntryList(&Current
->ListEntry
);
624 IntUnLockHardwareMessageQueue(MessageQueue
);
625 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
629 Desk
->LastInputWasKbd
= FALSE
;
636 IntUnLockHardwareMessageQueue(MessageQueue
);
638 /* Now try the global queue. */
640 /* Transfer all messages from the DPC accessible queue to the main queue. */
641 IntLockSystemMessageQueue(OldIrql
);
642 while (SystemMessageQueueCount
> 0)
644 PUSER_MESSAGE UserMsg
;
647 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
648 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
649 SystemMessageQueueHead
=
650 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
651 SystemMessageQueueCount
--;
652 IntUnLockSystemMessageQueue(OldIrql
);
654 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
655 /* What to do if out of memory? For now we just panic a bit in debug */
657 UserMsg
->FreeLParam
= FALSE
;
659 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
661 IntLockSystemMessageQueue(OldIrql
);
663 HardwareMessageQueueStamp
++;
664 IntUnLockSystemMessageQueue(OldIrql
);
666 /* Process messages in the queue until we find one to return. */
667 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
668 while (CurrentEntry
!= &HardwareMessageQueueHead
)
670 PUSER_MESSAGE Current
=
671 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
672 CurrentEntry
= CurrentEntry
->Flink
;
673 RemoveEntryList(&Current
->ListEntry
);
674 HardwareMessageQueueStamp
++;
675 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
676 Current
->Msg
.message
<= WM_MOUSELAST
)
678 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
679 /* Translate the message. */
680 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
681 Current
, Remove
, &Freed
,
682 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
685 /* Check for no more messages in the system queue. */
686 IntLockSystemMessageQueue(OldIrql
);
687 if (SystemMessageQueueCount
== 0 &&
688 IsListEmpty(&HardwareMessageQueueHead
))
690 KeClearEvent(&HardwareMessageEvent
);
692 IntUnLockSystemMessageQueue(OldIrql
);
695 If we aren't removing the message then add it to the private
700 IntLockHardwareMessageQueue(MessageQueue
);
701 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
703 if(MessageQueue
->MouseMoveMsg
)
705 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
706 ExFreePool(MessageQueue
->MouseMoveMsg
);
708 MessageQueue
->MouseMoveMsg
= Current
;
710 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
711 &Current
->ListEntry
);
712 IntUnLockHardwareMessageQueue(MessageQueue
);
714 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
719 /* If the contents of the queue changed then restart processing. */
720 if (HardwareMessageQueueStamp
!= ActiveStamp
)
722 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
728 /* Check if the system message queue is now empty. */
729 IntLockSystemMessageQueue(OldIrql
);
730 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
732 KeClearEvent(&HardwareMessageEvent
);
734 IntUnLockSystemMessageQueue(OldIrql
);
735 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
740 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
746 // Note: Only called from input.c.
749 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
751 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
753 LARGE_INTEGER LargeTickCount
;
754 KBDLLHOOKSTRUCT KbdHookData
;
755 BOOLEAN Entered
= FALSE
;
757 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
758 uMsg
, wParam
, lParam
);
760 // Condition may arise when calling MsqPostMessage and waiting for an event.
761 if (!UserIsEntered())
763 // Fixme: Not sure ATM if this thread is locked.
764 UserEnterExclusive();
768 FocusMessageQueue
= IntGetFocusMessageQueue();
772 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
773 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
779 KeQueryTickCount(&LargeTickCount
);
780 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
782 /* We can't get the Msg.pt point here since we don't know thread
783 (and thus the window station) the message will end up in yet. */
785 KbdHookData
.vkCode
= Msg
.wParam
;
786 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
787 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
788 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
789 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
790 KbdHookData
.time
= Msg
.time
;
791 KbdHookData
.dwExtraInfo
= 0;
792 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
794 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
795 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
796 if (Entered
) UserLeave();
800 if (FocusMessageQueue
== NULL
)
802 DPRINT("No focus message queue\n");
803 if (Entered
) UserLeave();
807 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
809 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
810 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
812 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
814 Msg
.pt
= gpsi
->ptCursor
;
815 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
819 DPRINT("Invalid focus window handle\n");
822 if (Entered
) UserLeave();
827 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
829 PWINDOW_OBJECT Window
;
830 PTHREADINFO Win32Thread
;
832 LARGE_INTEGER LargeTickCount
;
835 Status
= ObReferenceObjectByPointer (Thread
,
839 if (!NT_SUCCESS(Status
))
842 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
843 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
845 ObDereferenceObject ((PETHREAD
)Thread
);
849 Window
= IntGetWindowObject(hWnd
);
852 ObDereferenceObject ((PETHREAD
)Thread
);
857 Mesg
.message
= WM_HOTKEY
;
858 Mesg
.wParam
= wParam
;
859 Mesg
.lParam
= lParam
;
860 KeQueryTickCount(&LargeTickCount
);
861 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
862 Mesg
.pt
= gpsi
->ptCursor
;
863 MsqPostMessage(Window
->pti
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
864 UserDereferenceObject(Window
);
865 ObDereferenceObject (Thread
);
867 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
868 // &Message->ListEntry);
869 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
872 PUSER_MESSAGE FASTCALL
873 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
875 PUSER_MESSAGE Message
;
877 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
883 Message
->FreeLParam
= FreeLParam
;
884 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
890 MsqDestroyMessage(PUSER_MESSAGE Message
)
892 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
896 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
898 PLIST_ENTRY ListEntry
;
899 PUSER_SENT_MESSAGE_NOTIFY Message
;
901 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
903 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
904 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
907 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
910 Message
->CompletionCallbackContext
,
918 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
920 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
924 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
926 PUSER_SENT_MESSAGE Message
;
930 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
935 /* remove it from the list of pending messages */
936 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
937 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
939 /* insert it to the list of messages that are currently dispatched by this
941 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
942 &Message
->ListEntry
);
944 if (Message
->HookMessage
== MSQ_ISHOOK
)
946 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
947 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
949 Message
->Msg
.lParam
);
951 else if (Message
->HookMessage
== MSQ_ISEVENT
)
953 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
956 Message
->Msg
.lParam
);
960 /* Call the window procedure. */
961 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
962 Message
->Msg
.message
,
964 Message
->Msg
.lParam
);
967 /* remove the message from the local dispatching list, because it doesn't need
968 to be cleaned up on thread termination anymore */
969 RemoveEntryList(&Message
->ListEntry
);
971 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
972 if (!(Message
->HookMessage
& MSQ_SENTNOWAIT
))
974 if (Message
->DispatchingListEntry
.Flink
!= NULL
)
976 /* only remove it from the dispatching list if not already removed by a timeout */
977 RemoveEntryList(&Message
->DispatchingListEntry
);
980 /* still keep the sender's message queue locked, so the sender can't exit the
981 MsqSendMessage() function (if timed out) */
983 /* Let the sender know the result. */
984 if (Message
->Result
!= NULL
)
986 *Message
->Result
= Result
;
989 if (Message
->HasPackedLParam
== TRUE
)
991 if (Message
->Msg
.lParam
)
992 ExFreePool((PVOID
)Message
->Msg
.lParam
);
995 /* Notify the sender. */
996 if (Message
->CompletionEvent
!= NULL
)
998 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1001 /* Call the callback if the message was sent with SendMessageCallback */
1002 if (Message
->CompletionCallback
!= NULL
)
1004 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
1006 Message
->Msg
.message
,
1007 Message
->CompletionCallbackContext
,
1011 /* Only if it is not a no wait message */
1012 if (!(Message
->HookMessage
& MSQ_SENTNOWAIT
))
1014 IntDereferenceMessageQueue(Message
->SenderQueue
);
1015 IntDereferenceMessageQueue(MessageQueue
);
1018 /* free the message */
1019 ExFreePoolWithTag(Message
, TAG_USRMSG
);
1024 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1026 PUSER_SENT_MESSAGE SentMessage
;
1027 PUSER_MESSAGE PostedMessage
;
1028 PUSER_MESSAGE_QUEUE MessageQueue
;
1029 PLIST_ENTRY CurrentEntry
, ListHead
;
1030 PWINDOW_OBJECT Window
= pWindow
;
1034 MessageQueue
= Window
->pti
->MessageQueue
;
1035 ASSERT(MessageQueue
);
1037 /* remove the posted messages for this window */
1038 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1039 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1040 while (CurrentEntry
!= ListHead
)
1042 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1044 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1046 RemoveEntryList(&PostedMessage
->ListEntry
);
1047 MsqDestroyMessage(PostedMessage
);
1048 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1052 CurrentEntry
= CurrentEntry
->Flink
;
1056 /* remove the sent messages for this window */
1057 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1058 ListHead
= &MessageQueue
->SentMessagesListHead
;
1059 while (CurrentEntry
!= ListHead
)
1061 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1063 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1065 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1067 RemoveEntryList(&SentMessage
->ListEntry
);
1069 /* remove the message from the dispatching list if neede */
1070 if ((!(SentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1071 && (SentMessage
->DispatchingListEntry
.Flink
!= NULL
))
1073 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1076 /* wake the sender's thread */
1077 if (SentMessage
->CompletionEvent
!= NULL
)
1079 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1082 if (SentMessage
->HasPackedLParam
== TRUE
)
1084 if (SentMessage
->Msg
.lParam
)
1085 ExFreePool((PVOID
)SentMessage
->Msg
.lParam
);
1088 /* Only if it is not a no wait message */
1089 if (!(SentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1091 /* dereference our and the sender's message queue */
1092 IntDereferenceMessageQueue(MessageQueue
);
1093 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1096 /* free the message */
1097 ExFreePoolWithTag(SentMessage
, TAG_USRMSG
);
1099 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1103 CurrentEntry
= CurrentEntry
->Flink
;
1109 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1110 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1112 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1113 &NotifyMessage
->ListEntry
);
1114 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1115 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1116 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1117 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1121 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1122 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1123 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1127 PUSER_SENT_MESSAGE Message
;
1128 KEVENT CompletionEvent
;
1129 NTSTATUS WaitStatus
;
1131 PUSER_MESSAGE_QUEUE ThreadQueue
;
1132 LARGE_INTEGER Timeout
;
1135 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1137 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1138 return STATUS_INSUFFICIENT_RESOURCES
;
1141 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1143 pti
= PsGetCurrentThreadWin32Thread();
1144 ThreadQueue
= pti
->MessageQueue
;
1145 ASSERT(ThreadQueue
!= MessageQueue
);
1147 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1149 /* FIXME - increase reference counter of sender's message queue here */
1152 Message
->Msg
.hwnd
= Wnd
;
1153 Message
->Msg
.message
= Msg
;
1154 Message
->Msg
.wParam
= wParam
;
1155 Message
->Msg
.lParam
= lParam
;
1156 Message
->CompletionEvent
= &CompletionEvent
;
1157 Message
->Result
= &Result
;
1158 Message
->SenderQueue
= ThreadQueue
;
1159 IntReferenceMessageQueue(ThreadQueue
);
1160 Message
->CompletionCallback
= NULL
;
1161 Message
->HookMessage
= HookMessage
;
1162 Message
->HasPackedLParam
= FALSE
;
1164 IntReferenceMessageQueue(MessageQueue
);
1166 /* add it to the list of pending messages */
1167 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1169 /* queue it in the destination's message queue */
1170 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1172 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1173 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1174 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1175 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1177 /* we can't access the Message anymore since it could have already been deleted! */
1185 /* don't process messages sent to the thread */
1186 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1187 FALSE
, (uTimeout
? &Timeout
: NULL
));
1191 if(WaitStatus
== STATUS_TIMEOUT
)
1193 /* look up if the message has not yet dispatched, if so
1194 make sure it can't pass a result and it must not set the completion event anymore */
1195 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1196 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1198 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1201 /* we can access Message here, it's secure because the message queue is locked
1202 and the message is still hasn't been dispatched */
1203 Message
->CompletionEvent
= NULL
;
1204 Message
->Result
= NULL
;
1207 Entry
= Entry
->Flink
;
1210 /* remove from the local dispatching list so the other thread knows,
1211 it can't pass a result and it must not set the completion event anymore */
1212 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1213 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1215 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1218 /* we can access Message here, it's secure because the sender's message is locked
1219 and the message has definitely not yet been destroyed, otherwise it would
1220 have been removed from this list by the dispatching routine right after
1221 dispatching the message */
1222 Message
->CompletionEvent
= NULL
;
1223 Message
->Result
= NULL
;
1224 RemoveEntryList(&Message
->DispatchingListEntry
);
1225 Message
->DispatchingListEntry
.Flink
= NULL
;
1228 Entry
= Entry
->Flink
;
1231 DPRINT("MsqSendMessage (blocked) timed out\n");
1233 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1238 PVOID WaitObjects
[2];
1240 WaitObjects
[0] = &CompletionEvent
;
1241 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1248 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1249 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1253 if(WaitStatus
== STATUS_TIMEOUT
)
1255 /* look up if the message has not yet been dispatched, if so
1256 make sure it can't pass a result and it must not set the completion event anymore */
1257 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1258 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1260 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1263 /* we can access Message here, it's secure because the message queue is locked
1264 and the message is still hasn't been dispatched */
1265 Message
->CompletionEvent
= NULL
;
1266 Message
->Result
= NULL
;
1269 Entry
= Entry
->Flink
;
1272 /* remove from the local dispatching list so the other thread knows,
1273 it can't pass a result and it must not set the completion event anymore */
1274 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1275 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1277 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1280 /* we can access Message here, it's secure because the sender's message is locked
1281 and the message has definitely not yet been destroyed, otherwise it would
1282 have been removed from this list by the dispatching routine right after
1283 dispatching the message */
1284 Message
->CompletionEvent
= NULL
;
1285 Message
->Result
= NULL
;
1286 RemoveEntryList(&Message
->DispatchingListEntry
);
1287 Message
->DispatchingListEntry
.Flink
= NULL
;
1290 Entry
= Entry
->Flink
;
1293 DPRINT("MsqSendMessage timed out\n");
1296 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1299 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1302 if(WaitStatus
!= STATUS_TIMEOUT
)
1303 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1309 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1312 PUSER_MESSAGE Message
;
1314 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1318 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1319 &Message
->ListEntry
);
1320 MessageQueue
->QueueBits
|= MessageBits
;
1321 MessageQueue
->ChangedBits
|= MessageBits
;
1322 if (MessageQueue
->WakeMask
& MessageBits
)
1323 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1327 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1329 MessageQueue
->QuitPosted
= TRUE
;
1330 MessageQueue
->QuitExitCode
= ExitCode
;
1331 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1332 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1333 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1334 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1338 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1339 IN BOOLEAN Hardware
,
1341 IN PWINDOW_OBJECT Window
,
1342 IN UINT MsgFilterLow
,
1343 IN UINT MsgFilterHigh
,
1344 OUT PUSER_MESSAGE
* Message
)
1346 PLIST_ENTRY CurrentEntry
;
1347 PUSER_MESSAGE CurrentMessage
;
1348 PLIST_ENTRY ListHead
;
1352 return(co_MsqPeekHardwareMessage( MessageQueue
,
1360 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1361 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1362 while (CurrentEntry
!= ListHead
)
1364 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1367 PtrToInt(Window
) == 1 ||
1368 Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1369 ( (MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1370 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1371 MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1375 RemoveEntryList(&CurrentMessage
->ListEntry
);
1378 *Message
= CurrentMessage
;
1381 CurrentEntry
= CurrentEntry
->Flink
;
1388 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1389 UINT MsgFilterMin
, UINT MsgFilterMax
)
1391 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1394 IdlePing(); // Going to wait so send Idle ping.
1398 ret
= KeWaitForMultipleObjects(2,
1411 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1413 LARGE_INTEGER LargeTickCount
;
1415 KeQueryTickCount(&LargeTickCount
);
1416 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1420 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1422 LARGE_INTEGER LargeTickCount
;
1425 MessageQueue
->Thread
= Thread
;
1426 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1427 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1428 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1429 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1430 InitializeListHead(&MessageQueue
->TimerListHead
);
1431 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1432 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1433 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1434 MessageQueue
->QuitPosted
= FALSE
;
1435 MessageQueue
->QuitExitCode
= 0;
1436 KeQueryTickCount(&LargeTickCount
);
1437 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1438 MessageQueue
->FocusWindow
= NULL
;
1439 MessageQueue
->PaintCount
= 0;
1440 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1441 MessageQueue
->WakeMask
= ~0;
1442 MessageQueue
->NewMessagesHandle
= NULL
;
1444 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1445 NULL
, SynchronizationEvent
, FALSE
);
1446 if (!NT_SUCCESS(Status
))
1451 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1452 ExEventObjectType
, KernelMode
,
1453 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1454 if (!NT_SUCCESS(Status
))
1456 ZwClose(MessageQueue
->NewMessagesHandle
);
1457 MessageQueue
->NewMessagesHandle
= NULL
;
1465 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1467 PLIST_ENTRY CurrentEntry
;
1468 PUSER_MESSAGE CurrentMessage
;
1469 PTIMER_ENTRY CurrentTimer
;
1470 PUSER_SENT_MESSAGE CurrentSentMessage
;
1472 /* cleanup posted messages */
1473 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1475 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1476 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1478 MsqDestroyMessage(CurrentMessage
);
1481 /* remove the messages that have not yet been dispatched */
1482 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1484 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1485 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1488 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1490 /* remove the message from the dispatching list if needed */
1491 if ((!(CurrentSentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1492 && (CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
))
1494 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1497 /* wake the sender's thread */
1498 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1500 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1503 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1505 if (CurrentSentMessage
->Msg
.lParam
)
1506 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1509 /* Only if it is not a no wait message */
1510 if (!(CurrentSentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1512 /* dereference our and the sender's message queue */
1513 IntDereferenceMessageQueue(MessageQueue
);
1514 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1517 /* free the message */
1518 ExFreePool(CurrentSentMessage
);
1521 /* cleanup timers */
1522 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1524 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1525 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1526 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1529 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1530 ExitThread() was called in a SendMessage() umode callback */
1531 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1533 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1534 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1537 /* remove the message from the dispatching list */
1538 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1540 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1543 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1545 /* wake the sender's thread */
1546 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1548 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1551 if (CurrentSentMessage
->HasPackedLParam
== TRUE
)
1553 if (CurrentSentMessage
->Msg
.lParam
)
1554 ExFreePool((PVOID
)CurrentSentMessage
->Msg
.lParam
);
1557 /* Only if it is not a no wait message */
1558 if (!(CurrentSentMessage
->HookMessage
& MSQ_SENTNOWAIT
))
1560 /* dereference our and the sender's message queue */
1561 IntDereferenceMessageQueue(MessageQueue
);
1562 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1565 /* free the message */
1566 ExFreePool(CurrentSentMessage
);
1569 /* tell other threads not to bother returning any info to us */
1570 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1572 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1573 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1574 DispatchingListEntry
);
1575 CurrentSentMessage
->CompletionEvent
= NULL
;
1576 CurrentSentMessage
->Result
= NULL
;
1578 /* do NOT dereference our message queue as it might get attempted to be
1584 PUSER_MESSAGE_QUEUE FASTCALL
1585 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1587 PUSER_MESSAGE_QUEUE MessageQueue
;
1589 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1590 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1598 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1599 /* hold at least one reference until it'll be destroyed */
1600 IntReferenceMessageQueue(MessageQueue
);
1601 /* initialize the queue */
1602 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1604 IntDereferenceMessageQueue(MessageQueue
);
1608 return MessageQueue
;
1612 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1616 /* remove the message queue from any desktops */
1617 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1619 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1620 IntDereferenceMessageQueue(MessageQueue
);
1624 MsqCleanupMessageQueue(MessageQueue
);
1626 /* decrease the reference counter, if it hits zero, the queue will be freed */
1627 IntDereferenceMessageQueue(MessageQueue
);
1631 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1633 return Queue
->Hooks
;
1637 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1639 Queue
->Hooks
= Hooks
;
1643 MsqSetMessageExtraInfo(LPARAM lParam
)
1647 PUSER_MESSAGE_QUEUE MessageQueue
;
1649 pti
= PsGetCurrentThreadWin32Thread();
1650 MessageQueue
= pti
->MessageQueue
;
1656 Ret
= MessageQueue
->ExtraInfo
;
1657 MessageQueue
->ExtraInfo
= lParam
;
1663 MsqGetMessageExtraInfo(VOID
)
1666 PUSER_MESSAGE_QUEUE MessageQueue
;
1668 pti
= PsGetCurrentThreadWin32Thread();
1669 MessageQueue
= pti
->MessageQueue
;
1675 return MessageQueue
->ExtraInfo
;
1679 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1685 case MSQ_STATE_CAPTURE
:
1686 Prev
= MessageQueue
->CaptureWindow
;
1687 MessageQueue
->CaptureWindow
= hWnd
;
1689 case MSQ_STATE_ACTIVE
:
1690 Prev
= MessageQueue
->ActiveWindow
;
1691 MessageQueue
->ActiveWindow
= hWnd
;
1693 case MSQ_STATE_FOCUS
:
1694 Prev
= MessageQueue
->FocusWindow
;
1695 MessageQueue
->FocusWindow
= hWnd
;
1697 case MSQ_STATE_MENUOWNER
:
1698 Prev
= MessageQueue
->MenuOwner
;
1699 MessageQueue
->MenuOwner
= hWnd
;
1701 case MSQ_STATE_MOVESIZE
:
1702 Prev
= MessageQueue
->MoveSize
;
1703 MessageQueue
->MoveSize
= hWnd
;
1705 case MSQ_STATE_CARET
:
1706 ASSERT(MessageQueue
->CaretInfo
);
1707 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1708 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1716 static VOID FASTCALL
1717 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1719 PLIST_ENTRY Current
;
1722 Current
= MessageQueue
->TimerListHead
.Flink
;
1723 if (Current
== &MessageQueue
->TimerListHead
)
1725 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1727 while (Current
!= &MessageQueue
->TimerListHead
)
1729 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1730 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1731 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1732 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1733 Current
= Current
->Flink
;
1736 #endif /* ! defined(NDEBUG) */
1738 /* Must have the message queue locked while calling this */
1739 static VOID FASTCALL
1740 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1742 PLIST_ENTRY Current
;
1744 Current
= MessageQueue
->TimerListHead
.Flink
;
1745 while (Current
!= &MessageQueue
->TimerListHead
)
1747 if (NewTimer
->ExpiryTime
.QuadPart
<
1748 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1752 Current
= Current
->Flink
;
1755 InsertTailList(Current
, &NewTimer
->ListEntry
);
1758 /* Must have the message queue locked while calling this */
1759 static PTIMER_ENTRY FASTCALL
1760 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1763 PLIST_ENTRY EnumEntry
;
1765 /* Remove timer if already in the queue */
1766 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1767 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1769 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1770 EnumEntry
= EnumEntry
->Flink
;
1772 if (Timer
->Wnd
== Wnd
&&
1773 Timer
->IDEvent
== IDEvent
&&
1776 RemoveEntryList(&Timer
->ListEntry
);
1785 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1786 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1790 LARGE_INTEGER CurrentTime
;
1792 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1793 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1795 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1798 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1801 DPRINT1("Failed to allocate timer entry\n");
1804 DPRINT("Allocated new timer entry %p\n", Timer
);
1806 Timer
->IDEvent
= IDEvent
;
1811 DPRINT("Updating existing timer entry %p\n", Timer
);
1814 KeQuerySystemTime(&CurrentTime
);
1815 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1816 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1817 Timer
->Period
= Period
;
1818 Timer
->TimerFunc
= TimerFunc
;
1819 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1820 Timer
->ExpiryTime
.QuadPart
);
1822 InsertTimer(MessageQueue
, Timer
);
1826 DumpTimerList(MessageQueue
);
1827 #endif /* ! defined(NDEBUG) */
1833 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1834 UINT_PTR IDEvent
, UINT Msg
)
1838 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1839 MessageQueue
, Wnd
, IDEvent
, Msg
);
1841 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1845 DPRINT("Failed to remove timer from list, not found\n");
1849 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1853 DumpTimerList(MessageQueue
);
1854 #endif /* ! defined(NDEBUG) */
1856 return NULL
!= Timer
;
1860 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1861 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1862 MSG
*Msg
, BOOLEAN Restart
)
1865 LARGE_INTEGER CurrentTime
;
1866 LARGE_INTEGER LargeTickCount
;
1867 PLIST_ENTRY EnumEntry
;
1870 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1871 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1873 KeQuerySystemTime(&CurrentTime
);
1874 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1875 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1877 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1879 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1880 TIMER_ENTRY
, ListEntry
);
1881 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1882 Timer
->ExpiryTime
.QuadPart
);
1883 EnumEntry
= EnumEntry
->Flink
;
1884 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1885 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1886 (MsgFilterMin
<= Timer
->Msg
&&
1887 Timer
->Msg
<= MsgFilterMax
)))
1889 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1891 DPRINT("Timer is expired\n");
1897 DPRINT("No need to check later timers\n");
1903 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1904 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1910 DPRINT("No timer pending\n");
1914 Msg
->hwnd
= Timer
->Wnd
;
1915 Msg
->message
= Timer
->Msg
;
1916 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1917 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1918 KeQueryTickCount(&LargeTickCount
);
1919 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1920 Msg
->pt
= gpsi
->ptCursor
;
1924 RemoveEntryList(&Timer
->ListEntry
);
1925 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1926 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1927 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1928 InsertTimer(MessageQueue
, Timer
);
1932 DumpTimerList(MessageQueue
);
1933 #endif /* ! defined(NDEBUG) */
1937 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1938 Msg
->wParam
, Msg
->lParam
);
1944 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1947 PLIST_ENTRY EnumEntry
;
1949 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1951 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1952 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1954 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1955 EnumEntry
= EnumEntry
->Flink
;
1956 if (Timer
->Wnd
== Wnd
)
1958 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1959 RemoveEntryList(&Timer
->ListEntry
);
1960 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1965 DumpTimerList(MessageQueue
);
1966 #endif /* ! defined(NDEBUG) */
1971 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1972 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1973 PLARGE_INTEGER FirstTimerExpiry
)
1976 PLIST_ENTRY EnumEntry
;
1978 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1979 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1981 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1982 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1984 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1985 TIMER_ENTRY
, ListEntry
);
1986 EnumEntry
= EnumEntry
->Flink
;
1987 if ((NULL
== WndFilter
|| PtrToInt(WndFilter
) == 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1988 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1989 (MsgFilterMin
<= Timer
->Msg
&&
1990 Timer
->Msg
<= MsgFilterMax
)))
1992 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1993 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);