2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Message queues
24 * FILE: subsys/win32k/ntuser/msgqueue.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
27 * 06-06-2001 CSH Created
30 /* INCLUDES ******************************************************************/
37 /* GLOBALS *******************************************************************/
39 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
41 static MSG SystemMessageQueue
[SYSTEM_MESSAGE_QUEUE_SIZE
];
42 static ULONG SystemMessageQueueHead
= 0;
43 static ULONG SystemMessageQueueTail
= 0;
44 static ULONG SystemMessageQueueCount
= 0;
45 static KSPIN_LOCK SystemMessageQueueLock
;
47 static ULONG
volatile HardwareMessageQueueStamp
= 0;
48 static LIST_ENTRY HardwareMessageQueueHead
;
49 static KMUTANT HardwareMessageQueueLock
;
51 static KEVENT HardwareMessageEvent
;
53 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
54 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
56 #define IntLockSystemMessageQueue(OldIrql) \
57 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
59 #define IntUnLockSystemMessageQueue(OldIrql) \
60 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
62 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
63 KeReleaseMutant(&HardwareMessageQueueLock, IO_NO_INCREMENT, FALSE, Wait)
65 /* FUNCTIONS *****************************************************************/
68 IntMsqSetWakeMask(DWORD WakeMask
)
70 PW32THREAD Win32Thread
;
71 PUSER_MESSAGE_QUEUE MessageQueue
;
72 HANDLE MessageEventHandle
;
74 Win32Thread
= PsGetCurrentThreadWin32Thread();
75 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
78 MessageQueue
= Win32Thread
->MessageQueue
;
79 MessageQueue
->WakeMask
= WakeMask
;
80 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
82 return MessageEventHandle
;
86 IntMsqClearWakeMask(VOID
)
88 PW32THREAD Win32Thread
;
89 PUSER_MESSAGE_QUEUE MessageQueue
;
91 Win32Thread
= PsGetCurrentThreadWin32Thread();
92 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
95 MessageQueue
= Win32Thread
->MessageQueue
;
96 MessageQueue
->WakeMask
= ~0;
102 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
105 Queue
->QueueBits
|= QS_PAINT
;
106 Queue
->ChangedBits
|= QS_PAINT
;
107 if (Queue
->WakeMask
& QS_PAINT
)
108 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
112 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
119 MsqInitializeImpl(VOID
)
121 /*CurrentFocusMessageQueue = NULL;*/
122 InitializeListHead(&HardwareMessageQueueHead
);
123 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
124 KeInitializeSpinLock(&SystemMessageQueueLock
);
125 KeInitializeMutant(&HardwareMessageQueueLock
, 0);
127 ExInitializePagedLookasideList(&MessageLookasideList
,
131 sizeof(USER_MESSAGE
),
134 ExInitializePagedLookasideList(&TimerLookasideList
,
142 return(STATUS_SUCCESS
);
146 MsqInsertSystemMessage(MSG
* Msg
)
148 LARGE_INTEGER LargeTickCount
;
152 IntLockSystemMessageQueue(OldIrql
);
155 * Bail out if the queue is full. FIXME: We should handle this case
159 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
161 IntUnLockSystemMessageQueue(OldIrql
);
165 KeQueryTickCount(&LargeTickCount
);
166 Msg
->time
= LargeTickCount
.u
.LowPart
;
169 * If we got WM_MOUSEMOVE and there are already messages in the
170 * system message queue, check if the last message is mouse move
171 * and if it is then just overwrite it.
174 if (Msg
->message
== WM_MOUSEMOVE
&& SystemMessageQueueCount
)
176 if (SystemMessageQueueTail
== 0)
177 Prev
= SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
179 Prev
= SystemMessageQueueTail
- 1;
180 if (SystemMessageQueue
[Prev
].message
== WM_MOUSEMOVE
)
182 SystemMessageQueueTail
= Prev
;
183 SystemMessageQueueCount
--;
188 * Actually insert the message into the system message queue.
191 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
192 SystemMessageQueueTail
=
193 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
194 SystemMessageQueueCount
++;
196 IntUnLockSystemMessageQueue(OldIrql
);
198 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
202 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
204 PWINSTATION_OBJECT WinStaObject
;
205 PSYSTEM_CURSORINFO CurInfo
;
209 if (PsGetCurrentThreadWin32Thread()->Desktop
== NULL
)
214 WinStaObject
= PsGetCurrentThreadWin32Thread()->Desktop
->WindowStation
;
216 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
217 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
218 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
222 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
223 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
229 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
230 (dY
<= CurInfo
->DblClickHeight
);
234 if(CurInfo
->ButtonsDown
)
235 Res
= (CurInfo
->ButtonsDown
== Msg
->message
);
243 CurInfo
->LastBtnDown
= 0;
244 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
245 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
246 CurInfo
->LastClkWnd
= NULL
;
247 CurInfo
->ButtonsDown
= Msg
->message
;
251 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
252 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
253 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
254 CurInfo
->LastBtnDown
= Msg
->time
;
255 CurInfo
->ButtonsDown
= Msg
->message
;
263 co_MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
, UINT FilterLow
, UINT FilterHigh
,
264 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
265 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
)
267 USHORT Msg
= Message
->Msg
.message
;
268 PWINDOW_OBJECT Window
= NULL
;
271 ASSERT_REFS_CO(ScopeWin
);
274 co_WinPosWindowFromPoint can return a Window, and in that case
275 that window has a ref that we need to deref. Thats why we add "dummy"
276 refs in all other cases.
279 hCaptureWin
= IntGetCaptureWindow();
280 if (hCaptureWin
== NULL
)
282 if(Msg
== WM_MOUSEWHEEL
)
284 Window
= UserGetWindowObject(IntGetFocusWindow());
285 if (Window
) UserRefObject(Window
);
289 co_WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &Window
);
293 if (Window
) UserRefObject(Window
);
297 /* this is the one case where we dont add a ref, since the returned
298 window is already referenced */
304 /* FIXME - window messages should go to the right window if no buttons are
306 Window
= UserGetWindowObject(hCaptureWin
);
307 if (Window
) UserRefObject(Window
);
316 RemoveEntryList(&Message
->ListEntry
);
317 if(MessageQueue
->MouseMoveMsg
== Message
)
319 MessageQueue
->MouseMoveMsg
= NULL
;
327 if (Window
->MessageQueue
!= MessageQueue
)
329 if (! FromGlobalQueue
)
331 DPRINT("Moving msg between private queues\n");
332 /* This message is already queued in a private queue, but we need
333 * to move it to a different queue, perhaps because a new window
334 * was created which now covers the screen area previously taken
335 * by another window. To move it, we need to take it out of the
336 * old queue. Note that we're already holding the lock mutexes of the
338 RemoveEntryList(&Message
->ListEntry
);
340 /* remove the pointer for the current WM_MOUSEMOVE message in case we
342 if(MessageQueue
->MouseMoveMsg
== Message
)
344 MessageQueue
->MouseMoveMsg
= NULL
;
348 /* lock the destination message queue, so we don't get in trouble with other
349 threads, messing with it at the same time */
350 IntLockHardwareMessageQueue(Window
->MessageQueue
);
351 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
352 &Message
->ListEntry
);
353 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
355 if(Window
->MessageQueue
->MouseMoveMsg
)
357 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
359 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
360 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
362 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
363 Window
->MessageQueue
->MouseMoveMsg
= Message
;
365 Window
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
366 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
367 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
368 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
372 Window
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
373 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
374 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
375 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
377 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
380 UserDerefObject(Window
);
384 /* From here on, we're in the same message queue as the caller! */
386 *ScreenPoint
= Message
->Msg
.pt
;
388 if((hWnd
!= NULL
&& Window
->hSelf
!= hWnd
) ||
389 ((FilterLow
!= 0 || FilterLow
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
391 /* Reject the message because it doesn't match the filter */
395 /* Lock the message queue so no other thread can mess with it.
396 Our own message queue is not locked while fetching from the global
397 queue, so we have to make sure nothing interferes! */
398 IntLockHardwareMessageQueue(Window
->MessageQueue
);
399 /* if we're from the global queue, we need to add our message to our
400 private queue so we don't loose it! */
401 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
402 &Message
->ListEntry
);
405 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
407 if(Window
->MessageQueue
->MouseMoveMsg
&&
408 (Window
->MessageQueue
->MouseMoveMsg
!= Message
))
410 /* delete the old message */
411 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
412 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
414 /* always save a pointer to this WM_MOUSEMOVE message here because we're
415 sure that the message is in the private queue */
416 Window
->MessageQueue
->MouseMoveMsg
= Message
;
420 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
423 UserDerefObject(Window
);
428 /* FIXME - only assign if removing? */
429 Message
->Msg
.hwnd
= Window
->hSelf
;
430 Message
->Msg
.message
= Msg
;
431 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
433 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
435 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
436 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
440 /* Lock the message queue so no other thread can mess with it.
441 Our own message queue is not locked while fetching from the global
442 queue, so we have to make sure nothing interferes! */
443 IntLockHardwareMessageQueue(Window
->MessageQueue
);
444 if(Window
->MessageQueue
->MouseMoveMsg
)
446 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
447 with one that's been sent later */
448 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
449 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
450 /* our message is not in the private queue so we can remove the pointer
451 instead of setting it to the current message we're processing */
452 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
454 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
456 else if(Window
->MessageQueue
->MouseMoveMsg
== Message
)
458 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
462 UserDerefObject(Window
);
468 co_MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
,
469 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
470 PUSER_MESSAGE
* Message
)
475 PLIST_ENTRY CurrentEntry
;
476 PWINDOW_OBJECT DesktopWindow
= NULL
;
477 PVOID WaitObjects
[2];
479 DECLARE_RETURN(BOOL
);
480 USER_REFERENCE_ENTRY Ref
;
482 if( !IntGetScreenDC() ||
483 PsGetCurrentThreadWin32Thread()->MessageQueue
== W32kGetPrimitiveMessageQueue() )
488 WaitObjects
[1] = MessageQueue
->NewMessages
;
489 WaitObjects
[0] = &HardwareMessageQueueLock
;
494 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
495 UserMode
, FALSE
, NULL
, NULL
);
499 while (co_MsqDispatchOneSentMessage(MessageQueue
))
504 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
506 DesktopWindow
= UserGetWindowObject(IntGetDesktopWindow());
508 if (DesktopWindow
) UserRefObjectCo(DesktopWindow
, &Ref
);//can DesktopWindow be NULL?
510 /* Process messages in the message queue itself. */
511 IntLockHardwareMessageQueue(MessageQueue
);
512 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
513 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
515 PUSER_MESSAGE Current
=
516 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
517 CurrentEntry
= CurrentEntry
->Flink
;
518 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
519 Current
->Msg
.message
<= WM_MOUSELAST
)
524 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
525 Current
, Remove
, &Freed
,
526 DesktopWindow
, &ScreenPoint
, FALSE
);
531 RemoveEntryList(&Current
->ListEntry
);
533 IntUnLockHardwareMessageQueue(MessageQueue
);
534 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
542 IntUnLockHardwareMessageQueue(MessageQueue
);
544 /* Now try the global queue. */
546 /* Transfer all messages from the DPC accessible queue to the main queue. */
547 IntLockSystemMessageQueue(OldIrql
);
548 while (SystemMessageQueueCount
> 0)
550 PUSER_MESSAGE UserMsg
;
554 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
555 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
556 SystemMessageQueueHead
=
557 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
558 SystemMessageQueueCount
--;
559 IntUnLockSystemMessageQueue(OldIrql
);
560 if (WM_MOUSEFIRST
<= Msg
.message
&& Msg
.message
<= WM_MOUSELAST
)
562 MSLLHOOKSTRUCT MouseHookData
;
564 MouseHookData
.pt
.x
= LOWORD(Msg
.lParam
);
565 MouseHookData
.pt
.y
= HIWORD(Msg
.lParam
);
569 MouseHookData
.mouseData
= MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg
.wParam
));
573 case WM_XBUTTONDBLCLK
:
574 case WM_NCXBUTTONDOWN
:
576 case WM_NCXBUTTONDBLCLK
:
577 MouseHookData
.mouseData
= MAKELONG(0, HIWORD(Msg
.wParam
));
580 MouseHookData
.mouseData
= 0;
583 MouseHookData
.flags
= 0;
584 MouseHookData
.time
= Msg
.time
;
585 MouseHookData
.dwExtraInfo
= 0;
586 ProcessMessage
= (0 == co_HOOK_CallHooks(WH_MOUSE_LL
, HC_ACTION
,
587 Msg
.message
, (LPARAM
) &MouseHookData
));
591 ProcessMessage
= TRUE
;
595 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
596 /* What to do if out of memory? For now we just panic a bit in debug */
598 UserMsg
->FreeLParam
= FALSE
;
600 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
602 IntLockSystemMessageQueue(OldIrql
);
604 HardwareMessageQueueStamp
++;
605 IntUnLockSystemMessageQueue(OldIrql
);
607 /* Process messages in the queue until we find one to return. */
608 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
609 while (CurrentEntry
!= &HardwareMessageQueueHead
)
611 PUSER_MESSAGE Current
=
612 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
613 CurrentEntry
= CurrentEntry
->Flink
;
614 RemoveEntryList(&Current
->ListEntry
);
615 HardwareMessageQueueStamp
++;
616 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
617 Current
->Msg
.message
<= WM_MOUSELAST
)
619 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
620 /* Translate the message. */
621 Accept
= co_MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
622 Current
, Remove
, &Freed
,
623 DesktopWindow
, &ScreenPoint
, TRUE
);
626 /* Check for no more messages in the system queue. */
627 IntLockSystemMessageQueue(OldIrql
);
628 if (SystemMessageQueueCount
== 0 &&
629 IsListEmpty(&HardwareMessageQueueHead
))
631 KeClearEvent(&HardwareMessageEvent
);
633 IntUnLockSystemMessageQueue(OldIrql
);
636 If we aren't removing the message then add it to the private
641 IntLockHardwareMessageQueue(MessageQueue
);
642 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
644 if(MessageQueue
->MouseMoveMsg
)
646 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
647 ExFreePool(MessageQueue
->MouseMoveMsg
);
649 MessageQueue
->MouseMoveMsg
= Current
;
651 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
652 &Current
->ListEntry
);
653 IntUnLockHardwareMessageQueue(MessageQueue
);
655 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
660 /* If the contents of the queue changed then restart processing. */
661 if (HardwareMessageQueueStamp
!= ActiveStamp
)
663 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
669 /* Check if the system message queue is now empty. */
670 IntLockSystemMessageQueue(OldIrql
);
671 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
673 KeClearEvent(&HardwareMessageEvent
);
675 IntUnLockSystemMessageQueue(OldIrql
);
676 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
681 if (DesktopWindow
) UserDerefObjectCo(DesktopWindow
);
687 co_MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
689 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
691 LARGE_INTEGER LargeTickCount
;
692 KBDLLHOOKSTRUCT KbdHookData
;
694 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
695 uMsg
, wParam
, lParam
);
702 KeQueryTickCount(&LargeTickCount
);
703 Msg
.time
= LargeTickCount
.u
.LowPart
;
704 /* We can't get the Msg.pt point here since we don't know thread
705 (and thus the window station) the message will end up in yet. */
707 KbdHookData
.vkCode
= Msg
.wParam
;
708 KbdHookData
.scanCode
= (Msg
.lParam
>> 16) & 0xff;
709 KbdHookData
.flags
= (0 == (Msg
.lParam
& 0x01000000) ? 0 : LLKHF_EXTENDED
) |
710 (0 == (Msg
.lParam
& 0x20000000) ? 0 : LLKHF_ALTDOWN
) |
711 (0 == (Msg
.lParam
& 0x80000000) ? 0 : LLKHF_UP
);
712 KbdHookData
.time
= Msg
.time
;
713 KbdHookData
.dwExtraInfo
= 0;
714 if (co_HOOK_CallHooks(WH_KEYBOARD_LL
, HC_ACTION
, Msg
.message
, (LPARAM
) &KbdHookData
))
716 DPRINT("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
717 Msg
.message
, Msg
.wParam
, Msg
.lParam
);
721 FocusMessageQueue
= IntGetFocusMessageQueue();
722 if( !IntGetScreenDC() )
724 /* FIXME: What to do about Msg.pt here? */
725 if( W32kGetPrimitiveMessageQueue() )
727 MsqPostMessage(W32kGetPrimitiveMessageQueue(), &Msg
, FALSE
, QS_KEY
);
732 if (FocusMessageQueue
== NULL
)
734 DPRINT("No focus message queue\n");
738 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
740 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
741 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
742 IntGetCursorLocation(FocusMessageQueue
->Desktop
->WindowStation
,
744 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
748 DPRINT("Invalid focus window handle\n");
754 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
756 PWINDOW_OBJECT Window
;
757 PW32THREAD Win32Thread
;
758 PWINSTATION_OBJECT WinSta
;
760 LARGE_INTEGER LargeTickCount
;
763 Status
= ObReferenceObjectByPointer (Thread
,
767 if (!NT_SUCCESS(Status
))
770 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
771 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
773 ObDereferenceObject ((PETHREAD
)Thread
);
777 WinSta
= Win32Thread
->Desktop
->WindowStation
;
778 Window
= IntGetWindowObject(hWnd
);
781 ObDereferenceObject ((PETHREAD
)Thread
);
786 Mesg
.message
= WM_HOTKEY
;
787 Mesg
.wParam
= wParam
;
788 Mesg
.lParam
= lParam
;
789 KeQueryTickCount(&LargeTickCount
);
790 Mesg
.time
= LargeTickCount
.u
.LowPart
;
791 IntGetCursorLocation(WinSta
, &Mesg
.pt
);
792 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
793 ObmDereferenceObject(Window
);
794 ObDereferenceObject (Thread
);
796 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
797 // &Message->ListEntry);
798 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
801 PUSER_MESSAGE FASTCALL
802 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
804 PUSER_MESSAGE Message
;
806 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
812 Message
->FreeLParam
= FreeLParam
;
813 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
819 MsqDestroyMessage(PUSER_MESSAGE Message
)
821 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
825 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
827 PLIST_ENTRY ListEntry
;
828 PUSER_SENT_MESSAGE_NOTIFY Message
;
830 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
832 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
833 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
836 co_IntCallSentMessageCallback(Message
->CompletionCallback
,
839 Message
->CompletionCallbackContext
,
847 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
849 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
853 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
855 PUSER_SENT_MESSAGE Message
;
859 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
861 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
866 /* remove it from the list of pending messages */
867 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
868 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
870 /* insert it to the list of messages that are currently dispatched by this
872 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
873 &Message
->ListEntry
);
875 if (Message
->HookMessage
)
877 Result
= co_HOOK_CallHooks(Message
->Msg
.message
,
878 (INT
) Message
->Msg
.hwnd
,
880 Message
->Msg
.lParam
);
884 /* Call the window procedure. */
885 Result
= co_IntSendMessage(Message
->Msg
.hwnd
,
886 Message
->Msg
.message
,
888 Message
->Msg
.lParam
);
891 /* remove the message from the local dispatching list, because it doesn't need
892 to be cleaned up on thread termination anymore */
893 RemoveEntryList(&Message
->ListEntry
);
895 /* remove the message from the dispatching list, so lock the sender's message queue */
896 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
899 /* only remove it from the dispatching list if not already removed by a timeout */
900 RemoveEntryList(&Message
->DispatchingListEntry
);
902 /* still keep the sender's message queue locked, so the sender can't exit the
903 MsqSendMessage() function (if timed out) */
905 /* Let the sender know the result. */
906 if (Message
->Result
!= NULL
)
908 *Message
->Result
= Result
;
911 /* Notify the sender. */
912 if (Message
->CompletionEvent
!= NULL
)
914 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
917 /* Notify the sender if they specified a callback. */
918 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
920 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
921 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
923 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
926 NotifyMessage
->CompletionCallback
=
927 Message
->CompletionCallback
;
928 NotifyMessage
->CompletionCallbackContext
=
929 Message
->CompletionCallbackContext
;
930 NotifyMessage
->Result
= Result
;
931 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
932 NotifyMessage
->Msg
= Message
->Msg
.message
;
933 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
938 /* dereference both sender and our queue */
939 IntDereferenceMessageQueue(MessageQueue
);
940 IntDereferenceMessageQueue(Message
->SenderQueue
);
942 /* free the message */
948 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
950 PUSER_SENT_MESSAGE SentMessage
;
951 PUSER_MESSAGE PostedMessage
;
952 PUSER_MESSAGE_QUEUE MessageQueue
;
953 PLIST_ENTRY CurrentEntry
, ListHead
;
954 PWINDOW_OBJECT Window
= pWindow
;
958 MessageQueue
= Window
->MessageQueue
;
959 ASSERT(MessageQueue
);
961 /* remove the posted messages for this window */
962 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
963 ListHead
= &MessageQueue
->PostedMessagesListHead
;
964 while (CurrentEntry
!= ListHead
)
966 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
968 if (PostedMessage
->Msg
.hwnd
== Window
->hSelf
)
970 RemoveEntryList(&PostedMessage
->ListEntry
);
971 MsqDestroyMessage(PostedMessage
);
972 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
976 CurrentEntry
= CurrentEntry
->Flink
;
980 /* remove the sent messages for this window */
981 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
982 ListHead
= &MessageQueue
->SentMessagesListHead
;
983 while (CurrentEntry
!= ListHead
)
985 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
986 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
988 if(SentMessage
->Msg
.hwnd
== Window
->hSelf
)
990 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
992 /* remove the message from the dispatching list */
993 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
995 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
998 /* wake the sender's thread */
999 if (SentMessage
->CompletionEvent
!= NULL
)
1001 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1004 /* dereference our and the sender's message queue */
1005 IntDereferenceMessageQueue(MessageQueue
);
1006 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
1008 /* free the message */
1009 ExFreePool(SentMessage
);
1011 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
1015 CurrentEntry
= CurrentEntry
->Flink
;
1021 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1022 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
1024 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
1025 &NotifyMessage
->ListEntry
);
1026 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1027 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1028 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1029 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1033 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1034 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
1035 UINT uTimeout
, BOOL Block
, BOOL HookMessage
,
1038 PUSER_SENT_MESSAGE Message
;
1039 KEVENT CompletionEvent
;
1040 NTSTATUS WaitStatus
;
1042 PUSER_MESSAGE_QUEUE ThreadQueue
;
1043 LARGE_INTEGER Timeout
;
1046 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
1048 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
1049 return STATUS_INSUFFICIENT_RESOURCES
;
1052 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
1054 ThreadQueue
= PsGetCurrentThreadWin32Thread()->MessageQueue
;
1055 ASSERT(ThreadQueue
!= MessageQueue
);
1057 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
1059 /* FIXME - increase reference counter of sender's message queue here */
1062 Message
->Msg
.hwnd
= Wnd
;
1063 Message
->Msg
.message
= Msg
;
1064 Message
->Msg
.wParam
= wParam
;
1065 Message
->Msg
.lParam
= lParam
;
1066 Message
->CompletionEvent
= &CompletionEvent
;
1067 Message
->Result
= &Result
;
1068 Message
->SenderQueue
= ThreadQueue
;
1069 IntReferenceMessageQueue(ThreadQueue
);
1070 Message
->CompletionCallback
= NULL
;
1071 Message
->HookMessage
= HookMessage
;
1073 IntReferenceMessageQueue(MessageQueue
);
1075 /* add it to the list of pending messages */
1076 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
1078 /* queue it in the destination's message queue */
1079 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
1081 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1082 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1083 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1084 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1086 /* we can't access the Message anymore since it could have already been deleted! */
1093 /* don't process messages sent to the thread */
1094 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1095 FALSE
, (uTimeout
? &Timeout
: NULL
));
1099 if(WaitStatus
== STATUS_TIMEOUT
)
1101 /* look up if the message has not yet dispatched, if so
1102 make sure it can't pass a result and it must not set the completion event anymore */
1103 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1104 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1106 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1109 /* we can access Message here, it's secure because the message queue is locked
1110 and the message is still hasn't been dispatched */
1111 Message
->CompletionEvent
= NULL
;
1112 Message
->Result
= NULL
;
1115 Entry
= Entry
->Flink
;
1118 /* remove from the local dispatching list so the other thread knows,
1119 it can't pass a result and it must not set the completion event anymore */
1120 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1121 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1123 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1126 /* we can access Message here, it's secure because the sender's message is locked
1127 and the message has definitely not yet been destroyed, otherwise it would
1128 have been removed from this list by the dispatching routine right after
1129 dispatching the message */
1130 Message
->CompletionEvent
= NULL
;
1131 Message
->Result
= NULL
;
1132 RemoveEntryList(&Message
->DispatchingListEntry
);
1133 Message
->DispatchingListEntry
.Flink
= NULL
;
1136 Entry
= Entry
->Flink
;
1139 DPRINT("MsqSendMessage (blocked) timed out\n");
1141 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1146 PVOID WaitObjects
[2];
1148 WaitObjects
[0] = &CompletionEvent
;
1149 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1155 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1156 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1160 if(WaitStatus
== STATUS_TIMEOUT
)
1162 /* look up if the message has not yet been dispatched, if so
1163 make sure it can't pass a result and it must not set the completion event anymore */
1164 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1165 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1167 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1170 /* we can access Message here, it's secure because the message queue is locked
1171 and the message is still hasn't been dispatched */
1172 Message
->CompletionEvent
= NULL
;
1173 Message
->Result
= NULL
;
1176 Entry
= Entry
->Flink
;
1179 /* remove from the local dispatching list so the other thread knows,
1180 it can't pass a result and it must not set the completion event anymore */
1181 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1182 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1184 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1187 /* we can access Message here, it's secure because the sender's message is locked
1188 and the message has definitely not yet been destroyed, otherwise it would
1189 have been removed from this list by the dispatching routine right after
1190 dispatching the message */
1191 Message
->CompletionEvent
= NULL
;
1192 Message
->Result
= NULL
;
1193 RemoveEntryList(&Message
->DispatchingListEntry
);
1194 Message
->DispatchingListEntry
.Flink
= NULL
;
1197 Entry
= Entry
->Flink
;
1200 DPRINT("MsqSendMessage timed out\n");
1203 while (co_MsqDispatchOneSentMessage(ThreadQueue
))
1206 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1209 if(WaitStatus
!= STATUS_TIMEOUT
)
1210 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1216 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1219 PUSER_MESSAGE Message
;
1221 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1225 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1226 &Message
->ListEntry
);
1227 MessageQueue
->QueueBits
|= MessageBits
;
1228 MessageQueue
->ChangedBits
|= MessageBits
;
1229 if (MessageQueue
->WakeMask
& MessageBits
)
1230 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1234 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1236 MessageQueue
->QuitPosted
= TRUE
;
1237 MessageQueue
->QuitExitCode
= ExitCode
;
1238 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1239 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1240 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1241 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1245 co_MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1246 IN BOOLEAN Hardware
,
1249 IN UINT MsgFilterLow
,
1250 IN UINT MsgFilterHigh
,
1251 OUT PUSER_MESSAGE
* Message
)
1253 PLIST_ENTRY CurrentEntry
;
1254 PUSER_MESSAGE CurrentMessage
;
1255 PLIST_ENTRY ListHead
;
1259 return(co_MsqPeekHardwareMessage(MessageQueue
, Wnd
,
1260 MsgFilterLow
, MsgFilterHigh
,
1264 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1265 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1266 while (CurrentEntry
!= ListHead
)
1268 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1270 if ((Wnd
== 0 || Wnd
== CurrentMessage
->Msg
.hwnd
) &&
1271 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1272 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1273 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1277 RemoveEntryList(&CurrentMessage
->ListEntry
);
1280 *Message
= CurrentMessage
;
1283 CurrentEntry
= CurrentEntry
->Flink
;
1290 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, HWND WndFilter
,
1291 UINT MsgFilterMin
, UINT MsgFilterMax
)
1293 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1294 LARGE_INTEGER TimerExpiry
;
1295 PLARGE_INTEGER Timeout
;
1298 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1300 Timeout
= &TimerExpiry
;
1309 ret
= KeWaitForMultipleObjects(2,
1324 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1326 LARGE_INTEGER LargeTickCount
;
1328 KeQueryTickCount(&LargeTickCount
);
1329 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1333 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1335 LARGE_INTEGER LargeTickCount
;
1338 MessageQueue
->Thread
= Thread
;
1339 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1340 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1341 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1342 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1343 InitializeListHead(&MessageQueue
->TimerListHead
);
1344 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1345 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1346 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1347 MessageQueue
->QuitPosted
= FALSE
;
1348 MessageQueue
->QuitExitCode
= 0;
1349 KeQueryTickCount(&LargeTickCount
);
1350 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1351 MessageQueue
->FocusWindow
= NULL
;
1352 MessageQueue
->PaintCount
= 0;
1353 MessageQueue
->WakeMask
= ~0;
1354 MessageQueue
->NewMessagesHandle
= NULL
;
1356 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1357 NULL
, SynchronizationEvent
, FALSE
);
1358 if (!NT_SUCCESS(Status
))
1363 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1364 ExEventObjectType
, KernelMode
,
1365 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1366 if (!NT_SUCCESS(Status
))
1368 ZwClose(MessageQueue
->NewMessagesHandle
);
1369 MessageQueue
->NewMessagesHandle
= NULL
;
1377 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1379 PLIST_ENTRY CurrentEntry
;
1380 PUSER_MESSAGE CurrentMessage
;
1381 PTIMER_ENTRY CurrentTimer
;
1382 PUSER_SENT_MESSAGE CurrentSentMessage
;
1384 /* cleanup posted messages */
1385 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1387 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1388 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1390 MsqDestroyMessage(CurrentMessage
);
1393 /* remove the messages that have not yet been dispatched */
1394 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1396 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1397 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1400 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1402 /* remove the message from the dispatching list */
1403 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1405 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1408 /* wake the sender's thread */
1409 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1411 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1414 /* dereference our and the sender's message queue */
1415 IntDereferenceMessageQueue(MessageQueue
);
1416 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1418 /* free the message */
1419 ExFreePool(CurrentSentMessage
);
1422 /* cleanup timers */
1423 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1425 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1426 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1427 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1430 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1431 ExitThread() was called in a SendMessage() umode callback */
1432 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1434 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1435 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1438 /* remove the message from the dispatching list */
1439 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1441 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1444 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1446 /* wake the sender's thread */
1447 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1449 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1452 /* dereference our and the sender's message queue */
1453 IntDereferenceMessageQueue(MessageQueue
);
1454 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1456 /* free the message */
1457 ExFreePool(CurrentSentMessage
);
1460 /* tell other threads not to bother returning any info to us */
1461 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1463 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1464 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1465 DispatchingListEntry
);
1466 CurrentSentMessage
->CompletionEvent
= NULL
;
1467 CurrentSentMessage
->Result
= NULL
;
1469 /* do NOT dereference our message queue as it might get attempted to be
1475 PUSER_MESSAGE_QUEUE FASTCALL
1476 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1478 PUSER_MESSAGE_QUEUE MessageQueue
;
1480 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(PagedPool
,
1481 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1489 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1490 /* hold at least one reference until it'll be destroyed */
1491 IntReferenceMessageQueue(MessageQueue
);
1492 /* initialize the queue */
1493 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1495 IntDereferenceMessageQueue(MessageQueue
);
1499 return MessageQueue
;
1503 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1505 PDESKTOP_OBJECT desk
;
1507 /* remove the message queue from any desktops */
1508 if ((desk
= (PDESKTOP_OBJECT
)InterlockedExchange((LONG
*)&MessageQueue
->Desktop
, 0)))
1510 InterlockedExchange((LONG
*)&desk
->ActiveMessageQueue
, 0);
1511 IntDereferenceMessageQueue(MessageQueue
);
1514 /* if this is the primitive message queue, deregister it */
1515 if (MessageQueue
== W32kGetPrimitiveMessageQueue())
1516 W32kUnregisterPrimitiveMessageQueue();
1519 MsqCleanupMessageQueue(MessageQueue
);
1521 /* decrease the reference counter, if it hits zero, the queue will be freed */
1522 IntDereferenceMessageQueue(MessageQueue
);
1526 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1528 return Queue
->Hooks
;
1532 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1534 Queue
->Hooks
= Hooks
;
1538 MsqSetMessageExtraInfo(LPARAM lParam
)
1541 PUSER_MESSAGE_QUEUE MessageQueue
;
1543 MessageQueue
= PsGetCurrentThreadWin32Thread()->MessageQueue
;
1549 Ret
= MessageQueue
->ExtraInfo
;
1550 MessageQueue
->ExtraInfo
= lParam
;
1556 MsqGetMessageExtraInfo(VOID
)
1558 PUSER_MESSAGE_QUEUE MessageQueue
;
1560 MessageQueue
= PsGetCurrentThreadWin32Thread()->MessageQueue
;
1566 return MessageQueue
->ExtraInfo
;
1570 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1576 case MSQ_STATE_CAPTURE
:
1577 Prev
= MessageQueue
->CaptureWindow
;
1578 MessageQueue
->CaptureWindow
= hWnd
;
1580 case MSQ_STATE_ACTIVE
:
1581 Prev
= MessageQueue
->ActiveWindow
;
1582 MessageQueue
->ActiveWindow
= hWnd
;
1584 case MSQ_STATE_FOCUS
:
1585 Prev
= MessageQueue
->FocusWindow
;
1586 MessageQueue
->FocusWindow
= hWnd
;
1588 case MSQ_STATE_MENUOWNER
:
1589 Prev
= MessageQueue
->MenuOwner
;
1590 MessageQueue
->MenuOwner
= hWnd
;
1592 case MSQ_STATE_MOVESIZE
:
1593 Prev
= MessageQueue
->MoveSize
;
1594 MessageQueue
->MoveSize
= hWnd
;
1596 case MSQ_STATE_CARET
:
1597 ASSERT(MessageQueue
->CaretInfo
);
1598 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1599 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1607 static VOID FASTCALL
1608 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1610 PLIST_ENTRY Current
;
1613 Current
= MessageQueue
->TimerListHead
.Flink
;
1614 if (Current
== &MessageQueue
->TimerListHead
)
1616 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1618 while (Current
!= &MessageQueue
->TimerListHead
)
1620 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1621 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1622 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1623 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1624 Current
= Current
->Flink
;
1627 #endif /* ! defined(NDEBUG) */
1629 /* Must have the message queue locked while calling this */
1630 static VOID FASTCALL
1631 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1633 PLIST_ENTRY Current
;
1635 Current
= MessageQueue
->TimerListHead
.Flink
;
1636 while (Current
!= &MessageQueue
->TimerListHead
)
1638 if (NewTimer
->ExpiryTime
.QuadPart
<
1639 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1643 Current
= Current
->Flink
;
1646 InsertTailList(Current
, &NewTimer
->ListEntry
);
1649 /* Must have the message queue locked while calling this */
1650 static PTIMER_ENTRY FASTCALL
1651 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1654 PLIST_ENTRY EnumEntry
;
1656 /* Remove timer if already in the queue */
1657 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1658 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1660 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1661 EnumEntry
= EnumEntry
->Flink
;
1663 if (Timer
->Wnd
== Wnd
&&
1664 Timer
->IDEvent
== IDEvent
&&
1667 RemoveEntryList(&Timer
->ListEntry
);
1676 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1677 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1681 LARGE_INTEGER CurrentTime
;
1683 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1684 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1686 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1689 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1692 DPRINT1("Failed to allocate timer entry\n");
1695 DPRINT("Allocated new timer entry %p\n", Timer
);
1697 Timer
->IDEvent
= IDEvent
;
1702 DPRINT("Updating existing timer entry %p\n", Timer
);
1705 KeQuerySystemTime(&CurrentTime
);
1706 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1707 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1708 Timer
->Period
= Period
;
1709 Timer
->TimerFunc
= TimerFunc
;
1710 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1711 Timer
->ExpiryTime
.QuadPart
);
1713 InsertTimer(MessageQueue
, Timer
);
1717 DumpTimerList(MessageQueue
);
1718 #endif /* ! defined(NDEBUG) */
1724 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1725 UINT_PTR IDEvent
, UINT Msg
)
1729 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1730 MessageQueue
, Wnd
, IDEvent
, Msg
);
1732 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1736 DPRINT("Failed to remove timer from list, not found\n");
1740 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1744 DumpTimerList(MessageQueue
);
1745 #endif /* ! defined(NDEBUG) */
1747 return NULL
!= Timer
;
1751 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1752 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1753 MSG
*Msg
, BOOLEAN Restart
)
1756 LARGE_INTEGER CurrentTime
;
1757 LARGE_INTEGER LargeTickCount
;
1758 PLIST_ENTRY EnumEntry
;
1761 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1762 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1764 KeQuerySystemTime(&CurrentTime
);
1765 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1766 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1768 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1770 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1771 TIMER_ENTRY
, ListEntry
);
1772 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1773 Timer
->ExpiryTime
.QuadPart
);
1774 EnumEntry
= EnumEntry
->Flink
;
1775 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1776 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1777 (MsgFilterMin
<= Timer
->Msg
&&
1778 Timer
->Msg
<= MsgFilterMax
)))
1780 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1782 DPRINT("Timer is expired\n");
1788 DPRINT("No need to check later timers\n");
1794 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1795 Timer
, Timer
->Wnd
, Timer
->Msg
, WndFilter
, MsgFilterMin
, MsgFilterMax
);
1801 DPRINT("No timer pending\n");
1805 Msg
->hwnd
= Timer
->Wnd
;
1806 Msg
->message
= Timer
->Msg
;
1807 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1808 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1809 KeQueryTickCount(&LargeTickCount
);
1810 Msg
->time
= LargeTickCount
.u
.LowPart
;
1811 IntGetCursorLocation(PsGetCurrentThreadWin32Thread()->Desktop
->WindowStation
,
1816 RemoveEntryList(&Timer
->ListEntry
);
1817 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1818 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1819 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1820 InsertTimer(MessageQueue
, Timer
);
1824 DumpTimerList(MessageQueue
);
1825 #endif /* ! defined(NDEBUG) */
1829 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1830 Msg
->wParam
, Msg
->lParam
);
1836 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1839 PLIST_ENTRY EnumEntry
;
1841 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1843 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1844 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1846 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1847 EnumEntry
= EnumEntry
->Flink
;
1848 if (Timer
->Wnd
== Wnd
)
1850 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1851 RemoveEntryList(&Timer
->ListEntry
);
1852 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1857 DumpTimerList(MessageQueue
);
1858 #endif /* ! defined(NDEBUG) */
1863 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1864 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1865 PLARGE_INTEGER FirstTimerExpiry
)
1868 PLIST_ENTRY EnumEntry
;
1870 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1871 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1873 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1874 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1876 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1877 TIMER_ENTRY
, ListEntry
);
1878 EnumEntry
= EnumEntry
->Flink
;
1879 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1880 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1881 (MsgFilterMin
<= Timer
->Msg
&&
1882 Timer
->Msg
<= MsgFilterMax
)))
1884 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1885 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);