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;
201 MouseHookData
.flags
= 0;
202 MouseHookData
.time
= Msg
->time
;
203 MouseHookData
.dwExtraInfo
= 0;
204 if( co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
, Msg
->message
, (LPARAM
) &MouseHookData
))
208 * If we got WM_MOUSEMOVE and there are already messages in the
209 * system message queue, check if the last message is mouse move
210 * and if it is then just overwrite it.
212 IntLockSystemMessageQueue(OldIrql
);
215 * Bail out if the queue is full. FIXME: We should handle this case
219 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
221 IntUnLockSystemMessageQueue(OldIrql
);
225 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
227 if (SystemMessageQueueTail
== 0)
228 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
230 Prev
= SystemMessageQueueTail
- 1;
231 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
233 SystemMessageQueueTail
= Prev
;
234 SystemMessageQueueCount
--;
239 * Actually insert the message into the system message queue.
242 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
243 SystemMessageQueueTail
=
244 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
245 SystemMessageQueueCount
++;
247 IntUnLockSystemMessageQueue(OldIrql
);
249 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
253 MsqIsClkLck(LPMSG Msg
, BOOL Remove
)
256 PSYSTEM_CURSORINFO CurInfo
;
259 pti
= PsGetCurrentThreadWin32Thread();
260 if (pti
->rpdesk
== NULL
)
265 CurInfo
= IntGetSysCursorInfo();
267 switch (Msg
->message
)
270 Res
= ((Msg
->time
- CurInfo
->ClickLockTime
) >= gspv
.dwMouseClickLockTime
);
271 if (Res
&& (!CurInfo
->ClickLockActive
))
273 CurInfo
->ClickLockActive
= TRUE
;
277 if (CurInfo
->ClickLockActive
)
280 CurInfo
->ClickLockActive
= FALSE
;
281 CurInfo
->ClickLockTime
= 0;
285 CurInfo
->ClickLockTime
= Msg
->time
;
293 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
296 PSYSTEM_CURSORINFO CurInfo
;
300 pti
= PsGetCurrentThreadWin32Thread();
301 if (pti
->rpdesk
== NULL
)
306 CurInfo
= IntGetSysCursorInfo();
307 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
308 ((Msg
->time
- CurInfo
->LastBtnDown
) < gspv
.iDblClickTime
);
312 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
313 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
319 Res
= (dX
<= gspv
.iDblClickWidth
) &&
320 (dY
<= gspv
.iDblClickHeight
);
324 if(CurInfo
->ButtonsDown
)
325 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
331 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
332 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
333 CurInfo
->ButtonsDown
= Msg
->message
;
336 CurInfo
->LastBtnDown
= 0;
337 CurInfo
->LastClkWnd
= NULL
;
341 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
342 CurInfo
->LastBtnDown
= Msg
->time
;
350 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
, UINT FilterLow
, UINT FilterHigh
,
351 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
352 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
, PLIST_ENTRY
*Next
)
354 USHORT Msg
= Message
->Msg
.message
;
355 PWINDOW_OBJECT CaptureWindow
= NULL
;
358 ASSERT_REFS_CO(ScopeWin
);
361 co_WinPosWindowFromPoint can return a Window, and in that case
362 that window has a ref that we need to deref. Thats why we add "dummy"
363 refs in all other cases.
366 hCaptureWin
= IntGetCaptureWindow();
367 if (hCaptureWin
== NULL
)
369 if (Msg
== WM_MOUSEWHEEL
)
371 CaptureWindow
= UserGetWindowObject(IntGetFocusWindow());
372 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
376 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &CaptureWindow
);
377 if(CaptureWindow
== NULL
)
379 CaptureWindow
= ScopeWin
;
380 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
384 /* this is the one case where we dont add a ref, since the returned
385 window is already referenced */
391 /* FIXME - window messages should go to the right window if no buttons are
393 CaptureWindow
= UserGetWindowObject(hCaptureWin
);
394 if (CaptureWindow
) UserReferenceObject(CaptureWindow
);
399 if (CaptureWindow
== NULL
)
403 RemoveEntryList(&Message
->ListEntry
);
404 if(MessageQueue
->MouseMoveMsg
== Message
)
406 MessageQueue
->MouseMoveMsg
= NULL
;
409 // when FromGlobalQueue is true, the caller has already removed the Message
415 if (CaptureWindow
->pti
->MessageQueue
!= MessageQueue
)
417 if (! FromGlobalQueue
)
419 DPRINT("Moving msg between private queues\n");
420 /* This message is already queued in a private queue, but we need
421 * to move it to a different queue, perhaps because a new window
422 * was created which now covers the screen area previously taken
423 * by another window. To move it, we need to take it out of the
424 * old queue. Note that we're already holding the lock mutexes of the
426 RemoveEntryList(&Message
->ListEntry
);
428 /* remove the pointer for the current WM_MOUSEMOVE message in case we
430 if(MessageQueue
->MouseMoveMsg
== Message
)
432 MessageQueue
->MouseMoveMsg
= NULL
;
436 /* lock the destination message queue, so we don't get in trouble with other
437 threads, messing with it at the same time */
438 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
439 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
440 &Message
->ListEntry
);
441 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
443 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
445 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
447 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
448 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
450 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
451 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
453 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
454 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
455 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
456 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
460 CaptureWindow
->pti
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
461 CaptureWindow
->pti
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
462 if (CaptureWindow
->pti
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
463 KeSetEvent(CaptureWindow
->pti
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
465 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
468 UserDereferenceObject(CaptureWindow
);
472 /* From here on, we're in the same message queue as the caller! */
474 *ScreenPoint
= Message
->Msg
.pt
;
476 if((Window
!= NULL
&& PtrToInt(Window
) != 1 && CaptureWindow
->hSelf
!= Window
->hSelf
) ||
477 ((FilterLow
!= 0 || FilterHigh
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
479 /* Reject the message because it doesn't match the filter */
483 /* Lock the message queue so no other thread can mess with it.
484 Our own message queue is not locked while fetching from the global
485 queue, so we have to make sure nothing interferes! */
486 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
487 /* if we're from the global queue, we need to add our message to our
488 private queue so we don't loose it! */
489 InsertTailList(&CaptureWindow
->pti
->MessageQueue
->HardwareMessagesListHead
,
490 &Message
->ListEntry
);
493 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
495 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
&&
496 (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
!= Message
))
498 /* delete the old message */
499 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
500 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
501 if (!FromGlobalQueue
)
503 // We might have deleted the next one in our queue, so fix next
504 *Next
= Message
->ListEntry
.Flink
;
507 /* always save a pointer to this WM_MOUSEMOVE message here because we're
508 sure that the message is in the private queue */
509 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= Message
;
513 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
516 UserDereferenceObject(CaptureWindow
);
521 /* FIXME - only assign if removing? */
522 Message
->Msg
.hwnd
= CaptureWindow
->hSelf
;
523 Message
->Msg
.message
= Msg
;
524 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
526 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
528 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
529 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
533 /* Lock the message queue so no other thread can mess with it.
534 Our own message queue is not locked while fetching from the global
535 queue, so we have to make sure nothing interferes! */
536 IntLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
537 if(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
)
539 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
540 with one that's been sent later */
541 RemoveEntryList(&CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
->ListEntry
);
542 ExFreePool(CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
);
543 /* our message is not in the private queue so we can remove the pointer
544 instead of setting it to the current message we're processing */
545 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
547 IntUnLockHardwareMessageQueue(CaptureWindow
->pti
->MessageQueue
);
549 else if (CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
== Message
)
551 CaptureWindow
->pti
->MessageQueue
->MouseMoveMsg
= NULL
;
555 UserDereferenceObject(CaptureWindow
);
561 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT Window
,
562 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
563 PUSER_MESSAGE
* Message
)
568 PLIST_ENTRY CurrentEntry
;
569 PWINDOW_OBJECT DesktopWindow
= NULL
;
570 PVOID WaitObjects
[2];
572 DECLARE_RETURN(BOOL
);
573 USER_REFERENCE_ENTRY Ref
;
574 PDESKTOPINFO Desk
= NULL
;
576 WaitObjects
[1] = MessageQueue
->NewMessages
;
577 WaitObjects
[0] = &HardwareMessageQueueLock
;
584 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
585 UserMode
, FALSE
, NULL
, NULL
);
589 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
591 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
595 UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
596 Desk
= DesktopWindow
->pti
->pDeskInfo
;
599 /* Process messages in the message queue itself. */
600 IntLockHardwareMessageQueue(MessageQueue
);
601 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
602 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
604 PUSER_MESSAGE Current
=
605 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
606 CurrentEntry
= CurrentEntry
->Flink
;
607 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
608 Current
->Msg
.message
<= WM_MOUSELAST
)
612 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
613 Current
, Remove
, &Freed
,
614 DesktopWindow
, &ScreenPoint
, FALSE
, &CurrentEntry
);
619 RemoveEntryList(&Current
->ListEntry
);
621 IntUnLockHardwareMessageQueue(MessageQueue
);
622 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
626 Desk
->LastInputWasKbd
= FALSE
;
633 IntUnLockHardwareMessageQueue(MessageQueue
);
635 /* Now try the global queue. */
637 /* Transfer all messages from the DPC accessible queue to the main queue. */
638 IntLockSystemMessageQueue(OldIrql
);
639 while (SystemMessageQueueCount
> 0)
641 PUSER_MESSAGE UserMsg
;
644 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
645 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
646 SystemMessageQueueHead
=
647 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
648 SystemMessageQueueCount
--;
649 IntUnLockSystemMessageQueue(OldIrql
);
651 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
652 /* What to do if out of memory? For now we just panic a bit in debug */
654 UserMsg
->FreeLParam
= FALSE
;
656 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
658 IntLockSystemMessageQueue(OldIrql
);
660 HardwareMessageQueueStamp
++;
661 IntUnLockSystemMessageQueue(OldIrql
);
663 /* Process messages in the queue until we find one to return. */
664 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
665 while (CurrentEntry
!= &HardwareMessageQueueHead
)
667 PUSER_MESSAGE Current
=
668 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
669 CurrentEntry
= CurrentEntry
->Flink
;
670 RemoveEntryList(&Current
->ListEntry
);
671 HardwareMessageQueueStamp
++;
672 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
673 Current
->Msg
.message
<= WM_MOUSELAST
)
675 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
676 /* Translate the message. */
677 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, Window
, FilterLow
, FilterHigh
,
678 Current
, Remove
, &Freed
,
679 DesktopWindow
, &ScreenPoint
, TRUE
, NULL
);
682 /* Check for no more messages in the system queue. */
683 IntLockSystemMessageQueue(OldIrql
);
684 if (SystemMessageQueueCount
== 0 &&
685 IsListEmpty(&HardwareMessageQueueHead
))
687 KeClearEvent(&HardwareMessageEvent
);
689 IntUnLockSystemMessageQueue(OldIrql
);
692 If we aren't removing the message then add it to the private
697 IntLockHardwareMessageQueue(MessageQueue
);
698 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
700 if(MessageQueue
->MouseMoveMsg
)
702 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
703 ExFreePool(MessageQueue
->MouseMoveMsg
);
705 MessageQueue
->MouseMoveMsg
= Current
;
707 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
708 &Current
->ListEntry
);
709 IntUnLockHardwareMessageQueue(MessageQueue
);
711 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
716 /* If the contents of the queue changed then restart processing. */
717 if (HardwareMessageQueueStamp
!= ActiveStamp
)
719 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
725 /* Check if the system message queue is now empty. */
726 IntLockSystemMessageQueue(OldIrql
);
727 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
729 KeClearEvent(&HardwareMessageEvent
);
731 IntUnLockSystemMessageQueue(OldIrql
);
732 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
737 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
743 // Note: Only called from input.c.
746 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
748 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
750 LARGE_INTEGER LargeTickCount
;
751 KBDLLHOOKSTRUCT KbdHookData
;
752 BOOLEAN Entered
= FALSE
;
754 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
755 uMsg
, wParam
, lParam
);
757 // Condition may arise when calling MsqPostMessage and waiting for an event.
758 if (!UserIsEntered())
760 // Fixme: Not sure ATM if this thread is locked.
761 UserEnterExclusive();
765 FocusMessageQueue
= IntGetFocusMessageQueue();
769 if (FocusMessageQueue
&& (FocusMessageQueue
->FocusWindow
!= (HWND
)0))
770 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
776 KeQueryTickCount(&LargeTickCount
);
777 Msg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
779 /* We can't get the Msg.pt point here since we don't know thread
780 (and thus the window station) the message will end up in yet. */
782 KbdHookData
.vkCode
= Msg
.wParam
;
783 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
784 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
785 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
786 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
787 KbdHookData
.time
= Msg
.time
;
788 KbdHookData
.dwExtraInfo
= 0;
789 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
791 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
792 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
793 if (Entered
) UserLeave();
797 if (FocusMessageQueue
== NULL
)
799 DPRINT("No focus message queue\n");
800 if (Entered
) UserLeave();
804 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
806 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
807 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
809 FocusMessageQueue
->Desktop
->pDeskInfo
->LastInputWasKbd
= TRUE
;
811 Msg
.pt
= gpsi
->ptCursor
;
812 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
816 DPRINT("Invalid focus window handle\n");
819 if (Entered
) UserLeave();
824 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
826 PWINDOW_OBJECT Window
;
827 PTHREADINFO Win32Thread
;
829 LARGE_INTEGER LargeTickCount
;
832 Status
= ObReferenceObjectByPointer (Thread
,
836 if (!NT_SUCCESS(Status
))
839 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
840 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
842 ObDereferenceObject ((PETHREAD
)Thread
);
846 Window
= IntGetWindowObject(hWnd
);
849 ObDereferenceObject ((PETHREAD
)Thread
);
854 Mesg
.message
= WM_HOTKEY
;
855 Mesg
.wParam
= wParam
;
856 Mesg
.lParam
= lParam
;
857 KeQueryTickCount(&LargeTickCount
);
858 Mesg
.time
= MsqCalculateMessageTime(&LargeTickCount
);
859 Mesg
.pt
= gpsi
->ptCursor
;
860 MsqPostMessage(Window
->pti
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
861 UserDereferenceObject(Window
);
862 ObDereferenceObject (Thread
);
864 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
865 // &Message->ListEntry);
866 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
869 PUSER_MESSAGE FASTCALL
870 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
872 PUSER_MESSAGE Message
;
874 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
880 Message
->FreeLParam
= FreeLParam
;
881 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
887 MsqDestroyMessage(PUSER_MESSAGE Message
)
889 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
893 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
895 PLIST_ENTRY ListEntry
;
896 PUSER_SENT_MESSAGE_NOTIFY Message
;
898 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
900 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
901 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
904 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
907 Message
->CompletionCallbackContext
,
915 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
917 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
921 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
923 PUSER_SENT_MESSAGE Message
;
927 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
929 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
934 /* remove it from the list of pending messages */
935 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
936 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
938 /* insert it to the list of messages that are currently dispatched by this
940 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
941 &Message
->ListEntry
);
943 if (Message
->HookMessage
== MSQ_ISHOOK
)
945 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
946 (INT
)(INT_PTR
)Message
->Msg
.hwnd
,
948 Message
->Msg
.lParam
);
950 else if (Message
->HookMessage
== MSQ_ISEVENT
)
952 Result
= co_EVENT_CallEvents( Message
->Msg
.message
,
955 Message
->Msg
.lParam
);
959 /* Call the window procedure. */
960 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
961 Message
->Msg
.message
,
963 Message
->Msg
.lParam
);
966 /* remove the message from the local dispatching list, because it doesn't need
967 to be cleaned up on thread termination anymore */
968 RemoveEntryList(&Message
->ListEntry
);
970 /* remove the message from the dispatching list, so lock the sender's message queue */
971 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
974 /* only remove it from the dispatching list if not already removed by a timeout */
975 RemoveEntryList(&Message
->DispatchingListEntry
);
977 /* still keep the sender's message queue locked, so the sender can't exit the
978 MsqSendMessage() function (if timed out) */
980 /* Let the sender know the result. */
981 if (Message
->Result
!= NULL
)
983 *Message
->Result
= Result
;
986 /* Notify the sender. */
987 if (Message
->CompletionEvent
!= NULL
)
989 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
992 /* Notify the sender if they specified a callback. */
993 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
995 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
996 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
998 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
1001 NotifyMessage
->CompletionCallback
=
1002 Message
->CompletionCallback
;
1003 NotifyMessage
->CompletionCallbackContext
=
1004 Message
->CompletionCallbackContext
;
1005 NotifyMessage
->Result
= Result
;
1006 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
1007 NotifyMessage
->Msg
= Message
->Msg
.message
;
1008 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
1013 /* dereference both sender and our queue */
1014 IntDereferenceMessageQueue(MessageQueue
);
1015 IntDereferenceMessageQueue(Message
->SenderQueue
);
1017 /* free the message */
1018 ExFreePool(Message
);
1023 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
1025 PUSER_SENT_MESSAGE SentMessage
;
1026 PUSER_MESSAGE PostedMessage
;
1027 PUSER_MESSAGE_QUEUE MessageQueue
;
1028 PLIST_ENTRY CurrentEntry
, ListHead
;
1029 PWINDOW_OBJECT Window
= pWindow
;
1033 MessageQueue
= Window
->pti
->MessageQueue
;
1034 ASSERT(MessageQueue
);
1036 /* remove the posted messages for this window */
1037 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1038 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1039 while (CurrentEntry
!= ListHead
)
1041 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1043 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
1045 RemoveEntryList(&PostedMessage
->ListEntry
);
1046 MsqDestroyMessage(PostedMessage
);
1047 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1051 CurrentEntry
= CurrentEntry
->Flink
;
1055 /* remove the sent messages for this window */
1056 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1057 ListHead
= &MessageQueue
->SentMessagesListHead
;
1058 while (CurrentEntry
!= ListHead
)
1060 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1062 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
1064 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1066 RemoveEntryList(&SentMessage
->ListEntry
);
1068 /* remove the message from the dispatching list */
1069 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1071 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
1074 /* wake the sender's thread */
1075 if (SentMessage
->CompletionEvent
!= NULL
)
1077 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1080 /* dereference our and the sender's message queue */
1081 IntDereferenceMessageQueue(MessageQueue
);
1082 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1084 /* free the message */
1085 ExFreePool(SentMessage
);
1087 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1091 CurrentEntry
= CurrentEntry
->Flink
;
1097 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1098 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1100 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1101 &NotifyMessage
->ListEntry
);
1102 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1103 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1104 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1105 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1109 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1110 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1111 UINT uTimeout
, BOOL Block
, INT HookMessage
,
1115 PUSER_SENT_MESSAGE Message
;
1116 KEVENT CompletionEvent
;
1117 NTSTATUS WaitStatus
;
1119 PUSER_MESSAGE_QUEUE ThreadQueue
;
1120 LARGE_INTEGER Timeout
;
1123 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1125 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1126 return STATUS_INSUFFICIENT_RESOURCES
;
1129 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1131 pti
= PsGetCurrentThreadWin32Thread();
1132 ThreadQueue
= pti
->MessageQueue
;
1133 ASSERT(ThreadQueue
!= MessageQueue
);
1135 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1137 /* FIXME - increase reference counter of sender's message queue here */
1140 Message
->Msg
.hwnd
= Wnd
;
1141 Message
->Msg
.message
= Msg
;
1142 Message
->Msg
.wParam
= wParam
;
1143 Message
->Msg
.lParam
= lParam
;
1144 Message
->CompletionEvent
= &CompletionEvent
;
1145 Message
->Result
= &Result
;
1146 Message
->SenderQueue
= ThreadQueue
;
1147 IntReferenceMessageQueue(ThreadQueue
);
1148 Message
->CompletionCallback
= NULL
;
1149 Message
->HookMessage
= HookMessage
;
1151 IntReferenceMessageQueue(MessageQueue
);
1153 /* add it to the list of pending messages */
1154 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1156 /* queue it in the destination's message queue */
1157 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1159 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1160 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1161 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1162 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1164 /* we can't access the Message anymore since it could have already been deleted! */
1172 /* don't process messages sent to the thread */
1173 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1174 FALSE
, (uTimeout
? &Timeout
: NULL
));
1178 if(WaitStatus
== STATUS_TIMEOUT
)
1180 /* look up if the message has not yet dispatched, if so
1181 make sure it can't pass a result and it must not set the completion event anymore */
1182 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1183 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1185 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1188 /* we can access Message here, it's secure because the message queue is locked
1189 and the message is still hasn't been dispatched */
1190 Message
->CompletionEvent
= NULL
;
1191 Message
->Result
= NULL
;
1194 Entry
= Entry
->Flink
;
1197 /* remove from the local dispatching list so the other thread knows,
1198 it can't pass a result and it must not set the completion event anymore */
1199 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1200 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1202 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1205 /* we can access Message here, it's secure because the sender's message is locked
1206 and the message has definitely not yet been destroyed, otherwise it would
1207 have been removed from this list by the dispatching routine right after
1208 dispatching the message */
1209 Message
->CompletionEvent
= NULL
;
1210 Message
->Result
= NULL
;
1211 RemoveEntryList(&Message
->DispatchingListEntry
);
1212 Message
->DispatchingListEntry
.Flink
= NULL
;
1215 Entry
= Entry
->Flink
;
1218 DPRINT("MsqSendMessage (blocked) timed out\n");
1220 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1225 PVOID WaitObjects
[2];
1227 WaitObjects
[0] = &CompletionEvent
;
1228 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1235 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1236 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1240 if(WaitStatus
== STATUS_TIMEOUT
)
1242 /* look up if the message has not yet been dispatched, if so
1243 make sure it can't pass a result and it must not set the completion event anymore */
1244 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1245 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1247 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1250 /* we can access Message here, it's secure because the message queue is locked
1251 and the message is still hasn't been dispatched */
1252 Message
->CompletionEvent
= NULL
;
1253 Message
->Result
= NULL
;
1256 Entry
= Entry
->Flink
;
1259 /* remove from the local dispatching list so the other thread knows,
1260 it can't pass a result and it must not set the completion event anymore */
1261 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1262 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1264 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1267 /* we can access Message here, it's secure because the sender's message is locked
1268 and the message has definitely not yet been destroyed, otherwise it would
1269 have been removed from this list by the dispatching routine right after
1270 dispatching the message */
1271 Message
->CompletionEvent
= NULL
;
1272 Message
->Result
= NULL
;
1273 RemoveEntryList(&Message
->DispatchingListEntry
);
1274 Message
->DispatchingListEntry
.Flink
= NULL
;
1277 Entry
= Entry
->Flink
;
1280 DPRINT("MsqSendMessage timed out\n");
1283 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1286 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1289 if(WaitStatus
!= STATUS_TIMEOUT
)
1290 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1296 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1299 PUSER_MESSAGE Message
;
1301 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1305 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1306 &Message
->ListEntry
);
1307 MessageQueue
->QueueBits
|= MessageBits
;
1308 MessageQueue
->ChangedBits
|= MessageBits
;
1309 if (MessageQueue
->WakeMask
& MessageBits
)
1310 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1314 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1316 MessageQueue
->QuitPosted
= TRUE
;
1317 MessageQueue
->QuitExitCode
= ExitCode
;
1318 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1319 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1320 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1321 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1325 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1326 IN BOOLEAN Hardware
,
1328 IN PWINDOW_OBJECT Window
,
1329 IN UINT MsgFilterLow
,
1330 IN UINT MsgFilterHigh
,
1331 OUT PUSER_MESSAGE
* Message
)
1333 PLIST_ENTRY CurrentEntry
;
1334 PUSER_MESSAGE CurrentMessage
;
1335 PLIST_ENTRY ListHead
;
1339 return(co_MsqPeekHardwareMessage( MessageQueue
,
1347 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1348 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1349 while (CurrentEntry
!= ListHead
)
1351 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1354 PtrToInt(Window
) == 1 ||
1355 Window
->hSelf
== CurrentMessage
->Msg
.hwnd
) &&
1356 ( (MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1357 ( MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1358 MsgFilterHigh
>= CurrentMessage
->Msg
.message
) ) )
1362 RemoveEntryList(&CurrentMessage
->ListEntry
);
1365 *Message
= CurrentMessage
;
1368 CurrentEntry
= CurrentEntry
->Flink
;
1375 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, PWINDOW_OBJECT WndFilter
,
1376 UINT MsgFilterMin
, UINT MsgFilterMax
)
1378 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1379 LARGE_INTEGER TimerExpiry
;
1380 PLARGE_INTEGER Timeout
;
1383 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1385 Timeout
= &TimerExpiry
;
1392 IdlePing(); // Going to wait so send Idle ping.
1396 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 */
1491 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1493 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1496 /* wake the sender's thread */
1497 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1499 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1502 /* dereference our and the sender's message queue */
1503 IntDereferenceMessageQueue(MessageQueue
);
1504 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1506 /* free the message */
1507 ExFreePool(CurrentSentMessage
);
1510 /* cleanup timers */
1511 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1513 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1514 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1515 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1518 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1519 ExitThread() was called in a SendMessage() umode callback */
1520 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1522 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1523 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1526 /* remove the message from the dispatching list */
1527 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1529 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1532 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1534 /* wake the sender's thread */
1535 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1537 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1540 /* dereference our and the sender's message queue */
1541 IntDereferenceMessageQueue(MessageQueue
);
1542 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1544 /* free the message */
1545 ExFreePool(CurrentSentMessage
);
1548 /* tell other threads not to bother returning any info to us */
1549 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1551 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1552 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1553 DispatchingListEntry
);
1554 CurrentSentMessage
->CompletionEvent
= NULL
;
1555 CurrentSentMessage
->Result
= NULL
;
1557 /* do NOT dereference our message queue as it might get attempted to be
1563 PUSER_MESSAGE_QUEUE FASTCALL
1564 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1566 PUSER_MESSAGE_QUEUE MessageQueue
;
1568 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(NonPagedPool
,
1569 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1577 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1578 /* hold at least one reference until it'll be destroyed */
1579 IntReferenceMessageQueue(MessageQueue
);
1580 /* initialize the queue */
1581 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1583 IntDereferenceMessageQueue(MessageQueue
);
1587 return MessageQueue
;
1591 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1595 /* remove the message queue from any desktops */
1596 if ((desk
= InterlockedExchangePointer((PVOID
*)&MessageQueue
->Desktop
, 0)))
1598 (void)InterlockedExchangePointer((PVOID
*)&desk
->ActiveMessageQueue
, 0);
1599 IntDereferenceMessageQueue(MessageQueue
);
1603 MsqCleanupMessageQueue(MessageQueue
);
1605 /* decrease the reference counter, if it hits zero, the queue will be freed */
1606 IntDereferenceMessageQueue(MessageQueue
);
1610 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1612 return Queue
->Hooks
;
1616 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1618 Queue
->Hooks
= Hooks
;
1622 MsqSetMessageExtraInfo(LPARAM lParam
)
1626 PUSER_MESSAGE_QUEUE MessageQueue
;
1628 pti
= PsGetCurrentThreadWin32Thread();
1629 MessageQueue
= pti
->MessageQueue
;
1635 Ret
= MessageQueue
->ExtraInfo
;
1636 MessageQueue
->ExtraInfo
= lParam
;
1642 MsqGetMessageExtraInfo(VOID
)
1645 PUSER_MESSAGE_QUEUE MessageQueue
;
1647 pti
= PsGetCurrentThreadWin32Thread();
1648 MessageQueue
= pti
->MessageQueue
;
1654 return MessageQueue
->ExtraInfo
;
1658 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1664 case MSQ_STATE_CAPTURE
:
1665 Prev
= MessageQueue
->CaptureWindow
;
1666 MessageQueue
->CaptureWindow
= hWnd
;
1668 case MSQ_STATE_ACTIVE
:
1669 Prev
= MessageQueue
->ActiveWindow
;
1670 MessageQueue
->ActiveWindow
= hWnd
;
1672 case MSQ_STATE_FOCUS
:
1673 Prev
= MessageQueue
->FocusWindow
;
1674 MessageQueue
->FocusWindow
= hWnd
;
1676 case MSQ_STATE_MENUOWNER
:
1677 Prev
= MessageQueue
->MenuOwner
;
1678 MessageQueue
->MenuOwner
= hWnd
;
1680 case MSQ_STATE_MOVESIZE
:
1681 Prev
= MessageQueue
->MoveSize
;
1682 MessageQueue
->MoveSize
= hWnd
;
1684 case MSQ_STATE_CARET
:
1685 ASSERT(MessageQueue
->CaretInfo
);
1686 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1687 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1695 static VOID FASTCALL
1696 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1698 PLIST_ENTRY Current
;
1701 Current
= MessageQueue
->TimerListHead
.Flink
;
1702 if (Current
== &MessageQueue
->TimerListHead
)
1704 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1706 while (Current
!= &MessageQueue
->TimerListHead
)
1708 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1709 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1710 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1711 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1712 Current
= Current
->Flink
;
1715 #endif /* ! defined(NDEBUG) */
1717 /* Must have the message queue locked while calling this */
1718 static VOID FASTCALL
1719 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1721 PLIST_ENTRY Current
;
1723 Current
= MessageQueue
->TimerListHead
.Flink
;
1724 while (Current
!= &MessageQueue
->TimerListHead
)
1726 if (NewTimer
->ExpiryTime
.QuadPart
<
1727 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1731 Current
= Current
->Flink
;
1734 InsertTailList(Current
, &NewTimer
->ListEntry
);
1737 /* Must have the message queue locked while calling this */
1738 static PTIMER_ENTRY FASTCALL
1739 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1742 PLIST_ENTRY EnumEntry
;
1744 /* Remove timer if already in the queue */
1745 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1746 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1748 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1749 EnumEntry
= EnumEntry
->Flink
;
1751 if (Timer
->Wnd
== Wnd
&&
1752 Timer
->IDEvent
== IDEvent
&&
1755 RemoveEntryList(&Timer
->ListEntry
);
1764 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1765 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1769 LARGE_INTEGER CurrentTime
;
1771 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1772 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1774 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1777 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1780 DPRINT1("Failed to allocate timer entry\n");
1783 DPRINT("Allocated new timer entry %p\n", Timer
);
1785 Timer
->IDEvent
= IDEvent
;
1790 DPRINT("Updating existing timer entry %p\n", Timer
);
1793 KeQuerySystemTime(&CurrentTime
);
1794 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1795 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1796 Timer
->Period
= Period
;
1797 Timer
->TimerFunc
= TimerFunc
;
1798 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1799 Timer
->ExpiryTime
.QuadPart
);
1801 InsertTimer(MessageQueue
, Timer
);
1805 DumpTimerList(MessageQueue
);
1806 #endif /* ! defined(NDEBUG) */
1812 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1813 UINT_PTR IDEvent
, UINT Msg
)
1817 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1818 MessageQueue
, Wnd
, IDEvent
, Msg
);
1820 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1824 DPRINT("Failed to remove timer from list, not found\n");
1828 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1832 DumpTimerList(MessageQueue
);
1833 #endif /* ! defined(NDEBUG) */
1835 return NULL
!= Timer
;
1839 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1840 PWINDOW_OBJECT WindowFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1841 MSG
*Msg
, BOOLEAN Restart
)
1844 LARGE_INTEGER CurrentTime
;
1845 LARGE_INTEGER LargeTickCount
;
1846 PLIST_ENTRY EnumEntry
;
1849 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1850 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1852 KeQuerySystemTime(&CurrentTime
);
1853 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1854 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1856 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1858 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1859 TIMER_ENTRY
, ListEntry
);
1860 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1861 Timer
->ExpiryTime
.QuadPart
);
1862 EnumEntry
= EnumEntry
->Flink
;
1863 if ((NULL
== WindowFilter
|| Timer
->Wnd
== WindowFilter
->hSelf
) &&
1864 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1865 (MsgFilterMin
<= Timer
->Msg
&&
1866 Timer
->Msg
<= MsgFilterMax
)))
1868 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1870 DPRINT("Timer is expired\n");
1876 DPRINT("No need to check later timers\n");
1882 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1883 Timer
, Timer
->Wnd
, Timer
->Msg
, WindowFilter
->hSelf
, MsgFilterMin
, MsgFilterMax
);
1889 DPRINT("No timer pending\n");
1893 Msg
->hwnd
= Timer
->Wnd
;
1894 Msg
->message
= Timer
->Msg
;
1895 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1896 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1897 KeQueryTickCount(&LargeTickCount
);
1898 Msg
->time
= MsqCalculateMessageTime(&LargeTickCount
);
1899 Msg
->pt
= gpsi
->ptCursor
;
1903 RemoveEntryList(&Timer
->ListEntry
);
1904 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1905 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1906 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1907 InsertTimer(MessageQueue
, Timer
);
1911 DumpTimerList(MessageQueue
);
1912 #endif /* ! defined(NDEBUG) */
1916 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1917 Msg
->wParam
, Msg
->lParam
);
1923 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1926 PLIST_ENTRY EnumEntry
;
1928 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1930 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1931 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1933 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1934 EnumEntry
= EnumEntry
->Flink
;
1935 if (Timer
->Wnd
== Wnd
)
1937 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1938 RemoveEntryList(&Timer
->ListEntry
);
1939 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1944 DumpTimerList(MessageQueue
);
1945 #endif /* ! defined(NDEBUG) */
1950 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1951 PWINDOW_OBJECT WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1952 PLARGE_INTEGER FirstTimerExpiry
)
1955 PLIST_ENTRY EnumEntry
;
1957 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1958 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1960 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1961 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1963 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1964 TIMER_ENTRY
, ListEntry
);
1965 EnumEntry
= EnumEntry
->Flink
;
1966 if ((NULL
== WndFilter
|| PtrToInt(WndFilter
) == 1 || Timer
->Wnd
== WndFilter
->hSelf
) &&
1967 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1968 (MsgFilterMin
<= Timer
->Msg
&&
1969 Timer
->Msg
<= MsgFilterMax
)))
1971 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1972 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);