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 ******************************************************************/
35 #include <win32k/debug1.h>
38 /* GLOBALS *******************************************************************/
40 #define SYSTEM_MESSAGE_QUEUE_SIZE (256)
42 static MSG SystemMessageQueue
[SYSTEM_MESSAGE_QUEUE_SIZE
];
43 static ULONG SystemMessageQueueHead
= 0;
44 static ULONG SystemMessageQueueTail
= 0;
45 static ULONG SystemMessageQueueCount
= 0;
46 static ULONG SystemMessageQueueMouseMove
= -1;
47 static KSPIN_LOCK SystemMessageQueueLock
;
49 static ULONG
volatile HardwareMessageQueueStamp
= 0;
50 static LIST_ENTRY HardwareMessageQueueHead
;
51 static KMUTEX HardwareMessageQueueLock
;
53 static KEVENT HardwareMessageEvent
;
55 static PAGED_LOOKASIDE_LIST MessageLookasideList
;
56 static PAGED_LOOKASIDE_LIST TimerLookasideList
;
58 #define IntLockSystemMessageQueue(OldIrql) \
59 KeAcquireSpinLock(&SystemMessageQueueLock, &OldIrql)
61 #define IntUnLockSystemMessageQueue(OldIrql) \
62 KeReleaseSpinLock(&SystemMessageQueueLock, OldIrql)
64 #define IntUnLockSystemHardwareMessageQueueLock(Wait) \
65 KeReleaseMutex(&HardwareMessageQueueLock, Wait)
67 /* FUNCTIONS *****************************************************************/
70 IntMsqSetWakeMask(DWORD WakeMask
)
72 PW32THREAD Win32Thread
;
73 PUSER_MESSAGE_QUEUE MessageQueue
;
74 HANDLE MessageEventHandle
;
76 Win32Thread
= PsGetWin32Thread();
77 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
80 MessageQueue
= Win32Thread
->MessageQueue
;
81 IntLockMessageQueue(MessageQueue
);
82 MessageQueue
->WakeMask
= WakeMask
;
83 MessageEventHandle
= MessageQueue
->NewMessagesHandle
;
84 IntUnLockMessageQueue(MessageQueue
);
86 return MessageEventHandle
;
90 IntMsqClearWakeMask(VOID
)
92 PW32THREAD Win32Thread
;
93 PUSER_MESSAGE_QUEUE MessageQueue
;
95 Win32Thread
= PsGetWin32Thread();
96 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
99 MessageQueue
= Win32Thread
->MessageQueue
;
100 IntLockMessageQueue(MessageQueue
);
101 MessageQueue
->WakeMask
= ~0;
102 IntUnLockMessageQueue(MessageQueue
);
108 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
110 IntLockMessageQueue(Queue
);
112 Queue
->PaintPosted
= TRUE
;
113 Queue
->QueueBits
|= QS_PAINT
;
114 Queue
->ChangedBits
|= QS_PAINT
;
115 if (Queue
->WakeMask
& QS_PAINT
)
116 KeSetEvent(Queue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
117 IntUnLockMessageQueue(Queue
);
121 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue
)
123 IntLockMessageQueue(Queue
);
125 if (Queue
->PaintCount
== 0)
127 Queue
->PaintPosted
= FALSE
;
129 IntUnLockMessageQueue(Queue
);
134 MsqInitializeImpl(VOID
)
136 /*CurrentFocusMessageQueue = NULL;*/
137 InitializeListHead(&HardwareMessageQueueHead
);
138 KeInitializeEvent(&HardwareMessageEvent
, NotificationEvent
, 0);
139 KeInitializeSpinLock(&SystemMessageQueueLock
);
140 KeInitializeMutex(&HardwareMessageQueueLock
, 0);
142 ExInitializePagedLookasideList(&MessageLookasideList
,
146 sizeof(USER_MESSAGE
),
149 ExInitializePagedLookasideList(&TimerLookasideList
,
157 return(STATUS_SUCCESS
);
161 MsqInsertSystemMessage(MSG
* Msg
)
163 LARGE_INTEGER LargeTickCount
;
165 ULONG mmov
= (ULONG
)-1;
167 KeQueryTickCount(&LargeTickCount
);
168 Msg
->time
= LargeTickCount
.u
.LowPart
;
170 IntLockSystemMessageQueue(OldIrql
);
172 /* only insert WM_MOUSEMOVE messages if not already in system message queue */
173 if(Msg
->message
== WM_MOUSEMOVE
)
174 mmov
= SystemMessageQueueMouseMove
;
176 if(mmov
!= (ULONG
)-1)
178 /* insert message at the queue head */
179 while (mmov
!= SystemMessageQueueHead
)
181 ULONG prev
= mmov
? mmov
- 1 : SYSTEM_MESSAGE_QUEUE_SIZE
- 1;
182 ASSERT((LONG
) mmov
>= 0);
183 ASSERT(mmov
< SYSTEM_MESSAGE_QUEUE_SIZE
);
184 SystemMessageQueue
[mmov
] = SystemMessageQueue
[prev
];
187 SystemMessageQueue
[SystemMessageQueueHead
] = *Msg
;
191 if (SystemMessageQueueCount
== SYSTEM_MESSAGE_QUEUE_SIZE
)
193 IntUnLockSystemMessageQueue(OldIrql
);
196 SystemMessageQueue
[SystemMessageQueueTail
] = *Msg
;
197 if(Msg
->message
== WM_MOUSEMOVE
)
198 SystemMessageQueueMouseMove
= SystemMessageQueueTail
;
199 SystemMessageQueueTail
=
200 (SystemMessageQueueTail
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
201 SystemMessageQueueCount
++;
203 IntUnLockSystemMessageQueue(OldIrql
);
204 KeSetEvent(&HardwareMessageEvent
, IO_NO_INCREMENT
, FALSE
);
208 MsqIsDblClk(LPMSG Msg
, BOOL Remove
)
210 PWINSTATION_OBJECT WinStaObject
;
211 PSYSTEM_CURSORINFO CurInfo
;
215 if (PsGetWin32Thread()->Desktop
== NULL
)
219 WinStaObject
= PsGetWin32Thread()->Desktop
->WindowStation
;
221 CurInfo
= IntGetSysCursorInfo(WinStaObject
);
222 Res
= (Msg
->hwnd
== (HWND
)CurInfo
->LastClkWnd
) &&
223 ((Msg
->time
- CurInfo
->LastBtnDown
) < CurInfo
->DblClickSpeed
);
227 dX
= CurInfo
->LastBtnDownX
- Msg
->pt
.x
;
228 dY
= CurInfo
->LastBtnDownY
- Msg
->pt
.y
;
232 Res
= (dX
<= CurInfo
->DblClickWidth
) &&
233 (dY
<= CurInfo
->DblClickHeight
);
240 CurInfo
->LastBtnDown
= 0;
241 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
242 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
243 CurInfo
->LastClkWnd
= NULL
;
247 CurInfo
->LastBtnDownX
= Msg
->pt
.x
;
248 CurInfo
->LastBtnDownY
= Msg
->pt
.y
;
249 CurInfo
->LastClkWnd
= (HANDLE
)Msg
->hwnd
;
250 CurInfo
->LastBtnDown
= Msg
->time
;
258 MsqTranslateMouseMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
, UINT FilterLow
, UINT FilterHigh
,
259 PUSER_MESSAGE Message
, BOOL Remove
, PBOOL Freed
,
260 PWINDOW_OBJECT ScopeWin
, PPOINT ScreenPoint
, BOOL FromGlobalQueue
)
262 USHORT Msg
= Message
->Msg
.message
;
263 PWINDOW_OBJECT Window
= NULL
;
266 CaptureWin
= IntGetCaptureWindow();
267 if (CaptureWin
== NULL
)
269 if(Msg
== WM_MOUSEWHEEL
)
271 Window
= IntGetWindowObject(IntGetFocusWindow());
275 WinPosWindowFromPoint(ScopeWin
, NULL
, &Message
->Msg
.pt
, &Window
);
279 IntReferenceWindowObject(Window
);
285 /* FIXME - window messages should go to the right window if no buttons are
287 Window
= IntGetWindowObject(CaptureWin
);
294 RemoveEntryList(&Message
->ListEntry
);
295 if(MessageQueue
->MouseMoveMsg
== Message
)
297 MessageQueue
->MouseMoveMsg
= NULL
;
305 if (Window
->MessageQueue
!= MessageQueue
)
307 if (! FromGlobalQueue
)
309 DPRINT("Moving msg between private queues\n");
310 /* This message is already queued in a private queue, but we need
311 * to move it to a different queue, perhaps because a new window
312 * was created which now covers the screen area previously taken
313 * by another window. To move it, we need to take it out of the
314 * old queue. Note that we're already holding the lock mutexes of the
316 RemoveEntryList(&Message
->ListEntry
);
318 /* remove the pointer for the current WM_MOUSEMOVE message in case we
320 if(MessageQueue
->MouseMoveMsg
== Message
)
322 MessageQueue
->MouseMoveMsg
= NULL
;
326 /* lock the destination message queue, so we don't get in trouble with other
327 threads, messing with it at the same time */
328 IntLockHardwareMessageQueue(Window
->MessageQueue
);
329 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
330 &Message
->ListEntry
);
331 if(Message
->Msg
.message
== WM_MOUSEMOVE
)
333 if(Window
->MessageQueue
->MouseMoveMsg
)
335 /* remove the old WM_MOUSEMOVE message, we're processing a more recent
337 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
338 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
340 /* save the pointer to the WM_MOUSEMOVE message in the new queue */
341 Window
->MessageQueue
->MouseMoveMsg
= Message
;
343 Window
->MessageQueue
->QueueBits
|= QS_MOUSEMOVE
;
344 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEMOVE
;
345 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEMOVE
)
346 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
350 Window
->MessageQueue
->QueueBits
|= QS_MOUSEBUTTON
;
351 Window
->MessageQueue
->ChangedBits
|= QS_MOUSEBUTTON
;
352 if (Window
->MessageQueue
->WakeMask
& QS_MOUSEBUTTON
)
353 KeSetEvent(Window
->MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
355 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
358 IntReleaseWindowObject(Window
);
362 /* From here on, we're in the same message queue as the caller! */
364 *ScreenPoint
= Message
->Msg
.pt
;
366 if((hWnd
!= NULL
&& Window
->Self
!= hWnd
) ||
367 ((FilterLow
!= 0 || FilterLow
!= 0) && (Msg
< FilterLow
|| Msg
> FilterHigh
)))
369 /* Reject the message because it doesn't match the filter */
373 /* Lock the message queue so no other thread can mess with it.
374 Our own message queue is not locked while fetching from the global
375 queue, so we have to make sure nothing interferes! */
376 IntLockHardwareMessageQueue(Window
->MessageQueue
);
377 /* if we're from the global queue, we need to add our message to our
378 private queue so we don't loose it! */
379 InsertTailList(&Window
->MessageQueue
->HardwareMessagesListHead
,
380 &Message
->ListEntry
);
383 if (Message
->Msg
.message
== WM_MOUSEMOVE
)
385 if(Window
->MessageQueue
->MouseMoveMsg
&&
386 (Window
->MessageQueue
->MouseMoveMsg
!= Message
))
388 /* delete the old message */
389 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
390 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
392 /* always save a pointer to this WM_MOUSEMOVE message here because we're
393 sure that the message is in the private queue */
394 Window
->MessageQueue
->MouseMoveMsg
= Message
;
398 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
401 IntReleaseWindowObject(Window
);
406 /* FIXME - only assign if removing? */
407 Message
->Msg
.hwnd
= Window
->Self
;
408 Message
->Msg
.message
= Msg
;
409 Message
->Msg
.lParam
= MAKELONG(Message
->Msg
.pt
.x
, Message
->Msg
.pt
.y
);
411 /* remove the reference to the current WM_(NC)MOUSEMOVE message, if this message
413 if (Message
->Msg
.message
== WM_MOUSEMOVE
||
414 Message
->Msg
.message
== WM_NCMOUSEMOVE
)
418 /* Lock the message queue so no other thread can mess with it.
419 Our own message queue is not locked while fetching from the global
420 queue, so we have to make sure nothing interferes! */
421 IntLockHardwareMessageQueue(Window
->MessageQueue
);
422 if(Window
->MessageQueue
->MouseMoveMsg
)
424 /* delete the WM_(NC)MOUSEMOVE message in the private queue, we're dealing
425 with one that's been sent later */
426 RemoveEntryList(&Window
->MessageQueue
->MouseMoveMsg
->ListEntry
);
427 ExFreePool(Window
->MessageQueue
->MouseMoveMsg
);
428 /* our message is not in the private queue so we can remove the pointer
429 instead of setting it to the current message we're processing */
430 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
432 IntUnLockHardwareMessageQueue(Window
->MessageQueue
);
434 else if(Window
->MessageQueue
->MouseMoveMsg
== Message
)
436 Window
->MessageQueue
->MouseMoveMsg
= NULL
;
440 IntReleaseWindowObject(Window
);
446 MsqPeekHardwareMessage(PUSER_MESSAGE_QUEUE MessageQueue
, HWND hWnd
,
447 UINT FilterLow
, UINT FilterHigh
, BOOL Remove
,
448 PUSER_MESSAGE
* Message
)
453 PLIST_ENTRY CurrentEntry
;
454 PWINDOW_OBJECT DesktopWindow
;
455 PVOID WaitObjects
[2];
458 if( !IntGetScreenDC() ||
459 PsGetWin32Thread()->MessageQueue
== W32kGetPrimitiveMessageQueue() )
464 WaitObjects
[1] = MessageQueue
->NewMessages
;
465 WaitObjects
[0] = &HardwareMessageQueueLock
;
468 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
469 UserMode
, FALSE
, NULL
, NULL
);
470 while (MsqDispatchOneSentMessage(MessageQueue
))
475 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
477 DesktopWindow
= IntGetWindowObject(IntGetDesktopWindow());
479 /* Process messages in the message queue itself. */
480 IntLockHardwareMessageQueue(MessageQueue
);
481 CurrentEntry
= MessageQueue
->HardwareMessagesListHead
.Flink
;
482 while (CurrentEntry
!= &MessageQueue
->HardwareMessagesListHead
)
484 PUSER_MESSAGE Current
=
485 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
486 CurrentEntry
= CurrentEntry
->Flink
;
487 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
488 Current
->Msg
.message
<= WM_MOUSELAST
)
490 Accept
= MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
491 Current
, Remove
, &Freed
,
492 DesktopWindow
, &ScreenPoint
, FALSE
);
497 RemoveEntryList(&Current
->ListEntry
);
499 IntUnLockHardwareMessageQueue(MessageQueue
);
500 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
502 IntReleaseWindowObject(DesktopWindow
);
508 IntUnLockHardwareMessageQueue(MessageQueue
);
510 /* Now try the global queue. */
512 /* Transfer all messages from the DPC accessible queue to the main queue. */
513 IntLockSystemMessageQueue(OldIrql
);
514 while (SystemMessageQueueCount
> 0)
516 PUSER_MESSAGE UserMsg
;
519 ASSERT(SystemMessageQueueHead
< SYSTEM_MESSAGE_QUEUE_SIZE
);
520 Msg
= SystemMessageQueue
[SystemMessageQueueHead
];
521 SystemMessageQueueHead
=
522 (SystemMessageQueueHead
+ 1) % SYSTEM_MESSAGE_QUEUE_SIZE
;
523 SystemMessageQueueCount
--;
524 IntUnLockSystemMessageQueue(OldIrql
);
525 UserMsg
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
526 /* What to do if out of memory? For now we just panic a bit in debug */
528 UserMsg
->FreeLParam
= FALSE
;
530 InsertTailList(&HardwareMessageQueueHead
, &UserMsg
->ListEntry
);
531 IntLockSystemMessageQueue(OldIrql
);
534 * we could set this to -1 conditionally if we find one, but
535 * this is more efficient and just as effective.
537 SystemMessageQueueMouseMove
= -1;
538 HardwareMessageQueueStamp
++;
539 IntUnLockSystemMessageQueue(OldIrql
);
541 /* Process messages in the queue until we find one to return. */
542 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
543 while (CurrentEntry
!= &HardwareMessageQueueHead
)
545 PUSER_MESSAGE Current
=
546 CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
, ListEntry
);
547 CurrentEntry
= CurrentEntry
->Flink
;
548 RemoveEntryList(&Current
->ListEntry
);
549 HardwareMessageQueueStamp
++;
550 if (Current
->Msg
.message
>= WM_MOUSEFIRST
&&
551 Current
->Msg
.message
<= WM_MOUSELAST
)
553 const ULONG ActiveStamp
= HardwareMessageQueueStamp
;
554 /* Translate the message. */
555 Accept
= MsqTranslateMouseMessage(MessageQueue
, hWnd
, FilterLow
, FilterHigh
,
556 Current
, Remove
, &Freed
,
557 DesktopWindow
, &ScreenPoint
, TRUE
);
560 /* Check for no more messages in the system queue. */
561 IntLockSystemMessageQueue(OldIrql
);
562 if (SystemMessageQueueCount
== 0 &&
563 IsListEmpty(&HardwareMessageQueueHead
))
565 KeClearEvent(&HardwareMessageEvent
);
567 IntUnLockSystemMessageQueue(OldIrql
);
570 If we aren't removing the message then add it to the private
575 IntLockHardwareMessageQueue(MessageQueue
);
576 if(Current
->Msg
.message
== WM_MOUSEMOVE
)
578 if(MessageQueue
->MouseMoveMsg
)
580 RemoveEntryList(&MessageQueue
->MouseMoveMsg
->ListEntry
);
581 ExFreePool(MessageQueue
->MouseMoveMsg
);
583 MessageQueue
->MouseMoveMsg
= Current
;
585 InsertTailList(&MessageQueue
->HardwareMessagesListHead
,
586 &Current
->ListEntry
);
587 IntUnLockHardwareMessageQueue(MessageQueue
);
589 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
591 IntReleaseWindowObject(DesktopWindow
);
594 /* If the contents of the queue changed then restart processing. */
595 if (HardwareMessageQueueStamp
!= ActiveStamp
)
597 CurrentEntry
= HardwareMessageQueueHead
.Flink
;
602 IntReleaseWindowObject(DesktopWindow
);
603 /* Check if the system message queue is now empty. */
604 IntLockSystemMessageQueue(OldIrql
);
605 if (SystemMessageQueueCount
== 0 && IsListEmpty(&HardwareMessageQueueHead
))
607 KeClearEvent(&HardwareMessageEvent
);
609 IntUnLockSystemMessageQueue(OldIrql
);
610 IntUnLockSystemHardwareMessageQueueLock(FALSE
);
616 MsqPostKeyboardMessage(UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
618 PUSER_MESSAGE_QUEUE FocusMessageQueue
;
621 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
622 uMsg
, wParam
, lParam
);
628 /* FIXME: Initialize time and point. */
630 FocusMessageQueue
= IntGetFocusMessageQueue();
631 if( !IntGetScreenDC() ) {
632 if( W32kGetPrimitiveMessageQueue() ) {
633 MsqPostMessage(W32kGetPrimitiveMessageQueue(), &Msg
, FALSE
, QS_KEY
);
636 if (FocusMessageQueue
== NULL
)
638 DPRINT("No focus message queue\n");
642 if (FocusMessageQueue
->FocusWindow
!= (HWND
)0)
644 Msg
.hwnd
= FocusMessageQueue
->FocusWindow
;
645 DPRINT("Msg.hwnd = %x\n", Msg
.hwnd
);
646 MsqPostMessage(FocusMessageQueue
, &Msg
, FALSE
, QS_KEY
);
650 DPRINT("Invalid focus window handle\n");
656 MsqPostHotKeyMessage(PVOID Thread
, HWND hWnd
, WPARAM wParam
, LPARAM lParam
)
658 PWINDOW_OBJECT Window
;
659 PW32THREAD Win32Thread
;
660 PWINSTATION_OBJECT WinSta
;
664 Status
= ObReferenceObjectByPointer (Thread
,
668 if (!NT_SUCCESS(Status
))
671 Win32Thread
= ((PETHREAD
)Thread
)->Tcb
.Win32Thread
;
672 if (Win32Thread
== NULL
|| Win32Thread
->MessageQueue
== NULL
)
674 ObDereferenceObject ((PETHREAD
)Thread
);
678 WinSta
= Win32Thread
->Desktop
->WindowStation
;
679 Status
= ObmReferenceObjectByHandle(WinSta
->HandleTable
,
680 hWnd
, otWindow
, (PVOID
*)&Window
);
681 if (!NT_SUCCESS(Status
))
683 ObDereferenceObject ((PETHREAD
)Thread
);
688 Mesg
.message
= WM_HOTKEY
;
689 Mesg
.wParam
= wParam
;
690 Mesg
.lParam
= lParam
;
691 // Mesg.pt.x = PsGetWin32Process()->WindowStation->SystemCursor.x;
692 // Mesg.pt.y = PsGetWin32Process()->WindowStation->SystemCursor.y;
693 // KeQueryTickCount(&LargeTickCount);
694 // Mesg.time = LargeTickCount.u.LowPart;
695 MsqPostMessage(Window
->MessageQueue
, &Mesg
, FALSE
, QS_HOTKEY
);
696 ObmDereferenceObject(Window
);
697 ObDereferenceObject (Thread
);
699 // IntLockMessageQueue(pThread->MessageQueue);
700 // InsertHeadList(&pThread->MessageQueue->PostedMessagesListHead,
701 // &Message->ListEntry);
702 // KeSetEvent(pThread->MessageQueue->NewMessages, IO_NO_INCREMENT, FALSE);
703 // IntUnLockMessageQueue(pThread->MessageQueue);
707 PUSER_MESSAGE FASTCALL
708 MsqCreateMessage(LPMSG Msg
, BOOLEAN FreeLParam
)
710 PUSER_MESSAGE Message
;
712 Message
= ExAllocateFromPagedLookasideList(&MessageLookasideList
);
718 Message
->FreeLParam
= FreeLParam
;
719 RtlMoveMemory(&Message
->Msg
, Msg
, sizeof(MSG
));
725 MsqDestroyMessage(PUSER_MESSAGE Message
)
727 ExFreeToPagedLookasideList(&MessageLookasideList
, Message
);
731 MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
733 PLIST_ENTRY ListEntry
;
734 PUSER_SENT_MESSAGE_NOTIFY Message
;
736 IntLockMessageQueue(MessageQueue
);
737 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
739 ListEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
740 Message
= CONTAINING_RECORD(ListEntry
, USER_SENT_MESSAGE_NOTIFY
,
742 IntUnLockMessageQueue(MessageQueue
);
744 IntCallSentMessageCallback(Message
->CompletionCallback
,
747 Message
->CompletionCallbackContext
,
750 IntLockMessageQueue(MessageQueue
);
752 IntUnLockMessageQueue(MessageQueue
);
756 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue
)
758 return(!IsListEmpty(&MessageQueue
->SentMessagesListHead
));
762 MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue
)
764 PUSER_SENT_MESSAGE Message
;
768 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
;
770 IntLockMessageQueue(MessageQueue
);
771 if (IsListEmpty(&MessageQueue
->SentMessagesListHead
))
773 IntUnLockMessageQueue(MessageQueue
);
777 /* remove it from the list of pending messages */
778 Entry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
779 Message
= CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
);
781 /* insert it to the list of messages that are currently dispatched by this
783 InsertTailList(&MessageQueue
->LocalDispatchingMessagesHead
,
784 &Message
->ListEntry
);
786 IntUnLockMessageQueue(MessageQueue
);
788 /* Call the window procedure. */
789 Result
= IntSendMessage(Message
->Msg
.hwnd
,
790 Message
->Msg
.message
,
792 Message
->Msg
.lParam
);
794 /* remove the message from the local dispatching list, because it doesn't need
795 to be cleaned up on thread termination anymore */
796 IntLockMessageQueue(MessageQueue
);
797 RemoveEntryList(&Message
->ListEntry
);
798 IntUnLockMessageQueue(MessageQueue
);
800 /* remove the message from the dispatching list, so lock the sender's message queue */
801 IntLockMessageQueue(Message
->SenderQueue
);
803 SenderReturned
= (Message
->DispatchingListEntry
.Flink
== NULL
);
806 /* only remove it from the dispatching list if not already removed by a timeout */
807 RemoveEntryList(&Message
->DispatchingListEntry
);
809 /* still keep the sender's message queue locked, so the sender can't exit the
810 MsqSendMessage() function (if timed out) */
812 /* Let the sender know the result. */
813 if (Message
->Result
!= NULL
)
815 *Message
->Result
= Result
;
818 /* Notify the sender. */
819 if (Message
->CompletionEvent
!= NULL
)
821 KeSetEvent(Message
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
824 /* unlock the sender's message queue, the safe operation is done */
825 IntUnLockMessageQueue(Message
->SenderQueue
);
827 /* Notify the sender if they specified a callback. */
828 if (!SenderReturned
&& Message
->CompletionCallback
!= NULL
)
830 if(!(NotifyMessage
= ExAllocatePoolWithTag(NonPagedPool
,
831 sizeof(USER_SENT_MESSAGE_NOTIFY
), TAG_USRMSG
)))
833 DPRINT1("MsqDispatchOneSentMessage(): Not enough memory to create a callback notify message\n");
836 NotifyMessage
->CompletionCallback
=
837 Message
->CompletionCallback
;
838 NotifyMessage
->CompletionCallbackContext
=
839 Message
->CompletionCallbackContext
;
840 NotifyMessage
->Result
= Result
;
841 NotifyMessage
->hWnd
= Message
->Msg
.hwnd
;
842 NotifyMessage
->Msg
= Message
->Msg
.message
;
843 MsqSendNotifyMessage(Message
->SenderQueue
, NotifyMessage
);
848 /* dereference both sender and our queue */
849 IntDereferenceMessageQueue(MessageQueue
);
850 IntDereferenceMessageQueue(Message
->SenderQueue
);
852 /* free the message */
858 MsqRemoveWindowMessagesFromQueue(PVOID pWindow
)
860 PUSER_SENT_MESSAGE SentMessage
;
861 PUSER_MESSAGE PostedMessage
;
862 PUSER_MESSAGE_QUEUE MessageQueue
;
863 PLIST_ENTRY CurrentEntry
, ListHead
;
864 PWINDOW_OBJECT Window
= pWindow
;
868 MessageQueue
= Window
->MessageQueue
;
869 ASSERT(MessageQueue
);
871 IntLockMessageQueue(MessageQueue
);
873 /* remove the posted messages for this window */
874 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
875 ListHead
= &MessageQueue
->PostedMessagesListHead
;
876 while (CurrentEntry
!= ListHead
)
878 PostedMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
880 if (PostedMessage
->Msg
.hwnd
== Window
->Self
)
882 RemoveEntryList(&PostedMessage
->ListEntry
);
883 MsqDestroyMessage(PostedMessage
);
884 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
888 CurrentEntry
= CurrentEntry
->Flink
;
892 /* remove the sent messages for this window */
893 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
894 ListHead
= &MessageQueue
->SentMessagesListHead
;
895 while (CurrentEntry
!= ListHead
)
897 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
898 SentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
900 if(SentMessage
->Msg
.hwnd
== Window
->Self
)
902 IntLockMessageQueue(SentMessage
->SenderQueue
);
903 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
905 /* remove the message from the dispatching list */
906 if(SentMessage
->DispatchingListEntry
.Flink
!= NULL
)
908 RemoveEntryList(&SentMessage
->DispatchingListEntry
);
911 /* wake the sender's thread */
912 if (SentMessage
->CompletionEvent
!= NULL
)
914 KeSetEvent(SentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
916 IntUnLockMessageQueue(SentMessage
->SenderQueue
);
918 /* dereference our and the sender's message queue */
919 IntDereferenceMessageQueue(MessageQueue
);
920 IntDereferenceMessageQueue(SentMessage
->SenderQueue
);
922 /* free the message */
923 ExFreePool(SentMessage
);
925 CurrentEntry
= MessageQueue
->SentMessagesListHead
.Flink
;
929 CurrentEntry
= CurrentEntry
->Flink
;
932 IntUnLockMessageQueue(MessageQueue
);
936 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
937 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage
)
939 IntLockMessageQueue(MessageQueue
);
940 InsertTailList(&MessageQueue
->NotifyMessagesListHead
,
941 &NotifyMessage
->ListEntry
);
942 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
943 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
944 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
945 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
946 IntUnLockMessageQueue(MessageQueue
);
950 MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
951 HWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
,
952 UINT uTimeout
, BOOL Block
, ULONG_PTR
*uResult
)
954 PUSER_SENT_MESSAGE Message
;
955 KEVENT CompletionEvent
;
958 PUSER_MESSAGE_QUEUE ThreadQueue
;
959 LARGE_INTEGER Timeout
;
962 if(!(Message
= ExAllocatePoolWithTag(PagedPool
, sizeof(USER_SENT_MESSAGE
), TAG_USRMSG
)))
964 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
965 return STATUS_INSUFFICIENT_RESOURCES
;
968 KeInitializeEvent(&CompletionEvent
, NotificationEvent
, FALSE
);
970 ThreadQueue
= PsGetWin32Thread()->MessageQueue
;
971 ASSERT(ThreadQueue
!= MessageQueue
);
973 Timeout
.QuadPart
= (LONGLONG
) uTimeout
* (LONGLONG
) -10000;
975 /* FIXME - increase reference counter of sender's message queue here */
978 Message
->Msg
.hwnd
= Wnd
;
979 Message
->Msg
.message
= Msg
;
980 Message
->Msg
.wParam
= wParam
;
981 Message
->Msg
.lParam
= lParam
;
982 Message
->CompletionEvent
= &CompletionEvent
;
983 Message
->Result
= &Result
;
984 Message
->SenderQueue
= ThreadQueue
;
985 IntReferenceMessageQueue(ThreadQueue
);
986 Message
->CompletionCallback
= NULL
;
988 IntReferenceMessageQueue(MessageQueue
);
990 /* add it to the list of pending messages */
991 IntLockMessageQueue(ThreadQueue
);
992 InsertTailList(&ThreadQueue
->DispatchingMessagesHead
, &Message
->DispatchingListEntry
);
993 IntUnLockMessageQueue(ThreadQueue
);
995 /* queue it in the destination's message queue */
996 IntLockMessageQueue(MessageQueue
);
997 InsertTailList(&MessageQueue
->SentMessagesListHead
, &Message
->ListEntry
);
998 IntUnLockMessageQueue(MessageQueue
);
1000 MessageQueue
->QueueBits
|= QS_SENDMESSAGE
;
1001 MessageQueue
->ChangedBits
|= QS_SENDMESSAGE
;
1002 if (MessageQueue
->WakeMask
& QS_SENDMESSAGE
)
1003 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1005 /* we can't access the Message anymore since it could have already been deleted! */
1009 /* don't process messages sent to the thread */
1010 WaitStatus
= KeWaitForSingleObject(&CompletionEvent
, UserRequest
, UserMode
,
1011 FALSE
, (uTimeout
? &Timeout
: NULL
));
1012 if(WaitStatus
== STATUS_TIMEOUT
)
1014 /* look up if the message has not yet dispatched, if so
1015 make sure it can't pass a result and it must not set the completion event anymore */
1016 IntLockMessageQueue(MessageQueue
);
1017 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1018 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1020 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1023 /* we can access Message here, it's secure because the message queue is locked
1024 and the message is still hasn't been dispatched */
1025 Message
->CompletionEvent
= NULL
;
1026 Message
->Result
= NULL
;
1029 Entry
= Entry
->Flink
;
1031 IntUnLockMessageQueue(MessageQueue
);
1033 /* remove from the local dispatching list so the other thread knows,
1034 it can't pass a result and it must not set the completion event anymore */
1035 IntLockMessageQueue(ThreadQueue
);
1036 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1037 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1039 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1042 /* we can access Message here, it's secure because the sender's message is locked
1043 and the message has definitely not yet been destroyed, otherwise it would
1044 have been removed from this list by the dispatching routine right after
1045 dispatching the message */
1046 Message
->CompletionEvent
= NULL
;
1047 Message
->Result
= NULL
;
1048 RemoveEntryList(&Message
->DispatchingListEntry
);
1051 Entry
= Entry
->Flink
;
1053 IntUnLockMessageQueue(ThreadQueue
);
1055 DPRINT("MsqSendMessage (blocked) timed out\n");
1057 while (MsqDispatchOneSentMessage(ThreadQueue
));
1061 PVOID WaitObjects
[2];
1063 WaitObjects
[0] = &CompletionEvent
;
1064 WaitObjects
[1] = ThreadQueue
->NewMessages
;
1067 WaitStatus
= KeWaitForMultipleObjects(2, WaitObjects
, WaitAny
, UserRequest
,
1068 UserMode
, FALSE
, (uTimeout
? &Timeout
: NULL
), NULL
);
1069 if(WaitStatus
== STATUS_TIMEOUT
)
1071 /* look up if the message has not yet been dispatched, if so
1072 make sure it can't pass a result and it must not set the completion event anymore */
1073 IntLockMessageQueue(MessageQueue
);
1074 Entry
= MessageQueue
->SentMessagesListHead
.Flink
;
1075 while (Entry
!= &MessageQueue
->SentMessagesListHead
)
1077 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, ListEntry
)
1080 /* we can access Message here, it's secure because the message queue is locked
1081 and the message is still hasn't been dispatched */
1082 Message
->CompletionEvent
= NULL
;
1083 Message
->Result
= NULL
;
1086 Entry
= Entry
->Flink
;
1088 IntUnLockMessageQueue(MessageQueue
);
1090 /* remove from the local dispatching list so the other thread knows,
1091 it can't pass a result and it must not set the completion event anymore */
1092 IntLockMessageQueue(ThreadQueue
);
1093 Entry
= ThreadQueue
->DispatchingMessagesHead
.Flink
;
1094 while (Entry
!= &ThreadQueue
->DispatchingMessagesHead
)
1096 if ((PUSER_SENT_MESSAGE
) CONTAINING_RECORD(Entry
, USER_SENT_MESSAGE
, DispatchingListEntry
)
1099 /* we can access Message here, it's secure because the sender's message is locked
1100 and the message has definitely not yet been destroyed, otherwise it would
1101 have been removed from this list by the dispatching routine right after
1102 dispatching the message */
1103 Message
->CompletionEvent
= NULL
;
1104 Message
->Result
= NULL
;
1105 RemoveEntryList(&Message
->DispatchingListEntry
);
1108 Entry
= Entry
->Flink
;
1110 IntUnLockMessageQueue(ThreadQueue
);
1112 DPRINT("MsqSendMessage timed out\n");
1115 while (MsqDispatchOneSentMessage(ThreadQueue
));
1117 while (NT_SUCCESS(WaitStatus
) && STATUS_WAIT_0
!= WaitStatus
);
1120 if(WaitStatus
!= STATUS_TIMEOUT
)
1121 *uResult
= (STATUS_WAIT_0
== WaitStatus
? Result
: -1);
1127 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue
, MSG
* Msg
, BOOLEAN FreeLParam
,
1130 PUSER_MESSAGE Message
;
1132 if(!(Message
= MsqCreateMessage(Msg
, FreeLParam
)))
1136 IntLockMessageQueue(MessageQueue
);
1137 InsertTailList(&MessageQueue
->PostedMessagesListHead
,
1138 &Message
->ListEntry
);
1139 MessageQueue
->QueueBits
|= MessageBits
;
1140 MessageQueue
->ChangedBits
|= MessageBits
;
1141 if (MessageQueue
->WakeMask
& MessageBits
)
1142 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1143 IntUnLockMessageQueue(MessageQueue
);
1147 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG ExitCode
)
1149 IntLockMessageQueue(MessageQueue
);
1150 MessageQueue
->QuitPosted
= TRUE
;
1151 MessageQueue
->QuitExitCode
= ExitCode
;
1152 MessageQueue
->QueueBits
|= QS_POSTMESSAGE
;
1153 MessageQueue
->ChangedBits
|= QS_POSTMESSAGE
;
1154 if (MessageQueue
->WakeMask
& QS_POSTMESSAGE
)
1155 KeSetEvent(MessageQueue
->NewMessages
, IO_NO_INCREMENT
, FALSE
);
1156 IntUnLockMessageQueue(MessageQueue
);
1160 MsqFindMessage(IN PUSER_MESSAGE_QUEUE MessageQueue
,
1161 IN BOOLEAN Hardware
,
1164 IN UINT MsgFilterLow
,
1165 IN UINT MsgFilterHigh
,
1166 OUT PUSER_MESSAGE
* Message
)
1168 PLIST_ENTRY CurrentEntry
;
1169 PUSER_MESSAGE CurrentMessage
;
1170 PLIST_ENTRY ListHead
;
1174 return(MsqPeekHardwareMessage(MessageQueue
, Wnd
,
1175 MsgFilterLow
, MsgFilterHigh
,
1179 IntLockMessageQueue(MessageQueue
);
1180 CurrentEntry
= MessageQueue
->PostedMessagesListHead
.Flink
;
1181 ListHead
= &MessageQueue
->PostedMessagesListHead
;
1182 while (CurrentEntry
!= ListHead
)
1184 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1186 if ((Wnd
== 0 || Wnd
== CurrentMessage
->Msg
.hwnd
) &&
1187 ((MsgFilterLow
== 0 && MsgFilterHigh
== 0) ||
1188 (MsgFilterLow
<= CurrentMessage
->Msg
.message
&&
1189 MsgFilterHigh
>= CurrentMessage
->Msg
.message
)))
1193 RemoveEntryList(&CurrentMessage
->ListEntry
);
1195 IntUnLockMessageQueue(MessageQueue
);
1196 *Message
= CurrentMessage
;
1199 CurrentEntry
= CurrentEntry
->Flink
;
1201 IntUnLockMessageQueue(MessageQueue
);
1206 MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue
, HWND WndFilter
,
1207 UINT MsgFilterMin
, UINT MsgFilterMax
)
1209 PVOID WaitObjects
[2] = {MessageQueue
->NewMessages
, &HardwareMessageEvent
};
1210 LARGE_INTEGER TimerExpiry
;
1211 PLARGE_INTEGER Timeout
;
1213 if (MsqGetFirstTimerExpiry(MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, &TimerExpiry
))
1215 Timeout
= &TimerExpiry
;
1222 return(KeWaitForMultipleObjects(2,
1233 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue
)
1235 LARGE_INTEGER LargeTickCount
;
1237 KeQueryTickCount(&LargeTickCount
);
1238 return ((LargeTickCount
.u
.LowPart
- MessageQueue
->LastMsgRead
) > MSQ_HUNG
);
1242 MsqInitializeMessageQueue(struct _ETHREAD
*Thread
, PUSER_MESSAGE_QUEUE MessageQueue
)
1244 LARGE_INTEGER LargeTickCount
;
1247 MessageQueue
->Thread
= Thread
;
1248 MessageQueue
->CaretInfo
= (PTHRDCARETINFO
)(MessageQueue
+ 1);
1249 InitializeListHead(&MessageQueue
->PostedMessagesListHead
);
1250 InitializeListHead(&MessageQueue
->SentMessagesListHead
);
1251 InitializeListHead(&MessageQueue
->HardwareMessagesListHead
);
1252 InitializeListHead(&MessageQueue
->TimerListHead
);
1253 InitializeListHead(&MessageQueue
->DispatchingMessagesHead
);
1254 InitializeListHead(&MessageQueue
->LocalDispatchingMessagesHead
);
1255 KeInitializeMutex(&MessageQueue
->HardwareLock
, 0);
1256 ExInitializeFastMutex(&MessageQueue
->Lock
);
1257 MessageQueue
->QuitPosted
= FALSE
;
1258 MessageQueue
->QuitExitCode
= 0;
1259 KeQueryTickCount(&LargeTickCount
);
1260 MessageQueue
->LastMsgRead
= LargeTickCount
.u
.LowPart
;
1261 MessageQueue
->FocusWindow
= NULL
;
1262 MessageQueue
->PaintPosted
= FALSE
;
1263 MessageQueue
->PaintCount
= 0;
1264 MessageQueue
->WakeMask
= ~0;
1265 MessageQueue
->NewMessagesHandle
= NULL
;
1267 Status
= ZwCreateEvent(&MessageQueue
->NewMessagesHandle
, EVENT_ALL_ACCESS
,
1268 NULL
, SynchronizationEvent
, FALSE
);
1269 if (!NT_SUCCESS(Status
))
1274 Status
= ObReferenceObjectByHandle(MessageQueue
->NewMessagesHandle
, 0,
1275 ExEventObjectType
, KernelMode
,
1276 (PVOID
*)&MessageQueue
->NewMessages
, NULL
);
1277 if (!NT_SUCCESS(Status
))
1279 ZwClose(MessageQueue
->NewMessagesHandle
);
1280 MessageQueue
->NewMessagesHandle
= NULL
;
1288 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1290 PLIST_ENTRY CurrentEntry
;
1291 PUSER_MESSAGE CurrentMessage
;
1292 PTIMER_ENTRY CurrentTimer
;
1293 PUSER_SENT_MESSAGE CurrentSentMessage
;
1295 IntLockMessageQueue(MessageQueue
);
1297 /* cleanup posted messages */
1298 while (!IsListEmpty(&MessageQueue
->PostedMessagesListHead
))
1300 CurrentEntry
= RemoveHeadList(&MessageQueue
->PostedMessagesListHead
);
1301 CurrentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_MESSAGE
,
1303 MsqDestroyMessage(CurrentMessage
);
1306 /* remove the messages that have not yet been dispatched */
1307 while (!IsListEmpty(&MessageQueue
->SentMessagesListHead
))
1309 CurrentEntry
= RemoveHeadList(&MessageQueue
->SentMessagesListHead
);
1310 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1313 IntLockMessageQueue(CurrentSentMessage
->SenderQueue
);
1314 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1316 /* remove the message from the dispatching list */
1317 if(CurrentSentMessage
->DispatchingListEntry
.Flink
!= NULL
)
1319 RemoveEntryList(&CurrentSentMessage
->DispatchingListEntry
);
1322 /* wake the sender's thread */
1323 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1325 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1327 IntUnLockMessageQueue(CurrentSentMessage
->SenderQueue
);
1329 /* dereference our and the sender's message queue */
1330 IntDereferenceMessageQueue(MessageQueue
);
1331 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1333 /* free the message */
1334 ExFreePool(CurrentSentMessage
);
1337 /* cleanup timers */
1338 while (! IsListEmpty(&MessageQueue
->TimerListHead
))
1340 CurrentEntry
= RemoveHeadList(&MessageQueue
->TimerListHead
);
1341 CurrentTimer
= CONTAINING_RECORD(CurrentEntry
, TIMER_ENTRY
, ListEntry
);
1342 ExFreeToPagedLookasideList(&TimerLookasideList
, CurrentTimer
);
1345 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1346 ExitThread() was called in a SendMessage() umode callback */
1347 while (!IsListEmpty(&MessageQueue
->LocalDispatchingMessagesHead
))
1349 CurrentEntry
= RemoveHeadList(&MessageQueue
->LocalDispatchingMessagesHead
);
1350 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1353 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1355 /* wake the sender's thread */
1356 if (CurrentSentMessage
->CompletionEvent
!= NULL
)
1358 KeSetEvent(CurrentSentMessage
->CompletionEvent
, IO_NO_INCREMENT
, FALSE
);
1361 /* dereference our and the sender's message queue */
1362 IntDereferenceMessageQueue(MessageQueue
);
1363 IntDereferenceMessageQueue(CurrentSentMessage
->SenderQueue
);
1365 /* free the message */
1366 ExFreePool(CurrentSentMessage
);
1369 /* tell other threads not to bother returning any info to us */
1370 while (! IsListEmpty(&MessageQueue
->DispatchingMessagesHead
))
1372 CurrentEntry
= RemoveHeadList(&MessageQueue
->DispatchingMessagesHead
);
1373 CurrentSentMessage
= CONTAINING_RECORD(CurrentEntry
, USER_SENT_MESSAGE
,
1374 DispatchingListEntry
);
1375 CurrentSentMessage
->CompletionEvent
= NULL
;
1376 CurrentSentMessage
->Result
= NULL
;
1378 /* do NOT dereference our message queue as it might get attempted to be
1382 IntUnLockMessageQueue(MessageQueue
);
1385 PUSER_MESSAGE_QUEUE FASTCALL
1386 MsqCreateMessageQueue(struct _ETHREAD
*Thread
)
1388 PUSER_MESSAGE_QUEUE MessageQueue
;
1390 MessageQueue
= (PUSER_MESSAGE_QUEUE
)ExAllocatePoolWithTag(PagedPool
,
1391 sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
),
1399 RtlZeroMemory(MessageQueue
, sizeof(USER_MESSAGE_QUEUE
) + sizeof(THRDCARETINFO
));
1400 /* hold at least one reference until it'll be destroyed */
1401 IntReferenceMessageQueue(MessageQueue
);
1402 /* initialize the queue */
1403 if (!MsqInitializeMessageQueue(Thread
, MessageQueue
))
1405 IntDereferenceMessageQueue(MessageQueue
);
1409 return MessageQueue
;
1413 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue
)
1415 PDESKTOP_OBJECT desk
;
1417 /* remove the message queue from any desktops */
1418 if ((desk
= (PDESKTOP_OBJECT
)InterlockedExchange((LONG
*)&MessageQueue
->Desktop
, 0)))
1420 InterlockedExchange((LONG
*)&desk
->ActiveMessageQueue
, 0);
1421 IntDereferenceMessageQueue(MessageQueue
);
1424 /* if this is the primitive message queue, deregister it */
1425 if (MessageQueue
== W32kGetPrimitiveMessageQueue())
1426 W32kUnregisterPrimitiveMessageQueue();
1429 MsqCleanupMessageQueue(MessageQueue
);
1431 /* decrease the reference counter, if it hits zero, the queue will be freed */
1432 IntDereferenceMessageQueue(MessageQueue
);
1436 MsqGetHooks(PUSER_MESSAGE_QUEUE Queue
)
1438 return Queue
->Hooks
;
1442 MsqSetHooks(PUSER_MESSAGE_QUEUE Queue
, PHOOKTABLE Hooks
)
1444 Queue
->Hooks
= Hooks
;
1448 MsqSetMessageExtraInfo(LPARAM lParam
)
1451 PUSER_MESSAGE_QUEUE MessageQueue
;
1453 MessageQueue
= PsGetWin32Thread()->MessageQueue
;
1459 Ret
= MessageQueue
->ExtraInfo
;
1460 MessageQueue
->ExtraInfo
= lParam
;
1466 MsqGetMessageExtraInfo(VOID
)
1468 PUSER_MESSAGE_QUEUE MessageQueue
;
1470 MessageQueue
= PsGetWin32Thread()->MessageQueue
;
1476 return MessageQueue
->ExtraInfo
;
1480 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue
, ULONG Type
, HWND hWnd
)
1486 case MSQ_STATE_CAPTURE
:
1487 Prev
= MessageQueue
->CaptureWindow
;
1488 MessageQueue
->CaptureWindow
= hWnd
;
1490 case MSQ_STATE_ACTIVE
:
1491 Prev
= MessageQueue
->ActiveWindow
;
1492 MessageQueue
->ActiveWindow
= hWnd
;
1494 case MSQ_STATE_FOCUS
:
1495 Prev
= MessageQueue
->FocusWindow
;
1496 MessageQueue
->FocusWindow
= hWnd
;
1498 case MSQ_STATE_MENUOWNER
:
1499 Prev
= MessageQueue
->MenuOwner
;
1500 MessageQueue
->MenuOwner
= hWnd
;
1502 case MSQ_STATE_MOVESIZE
:
1503 Prev
= MessageQueue
->MoveSize
;
1504 MessageQueue
->MoveSize
= hWnd
;
1506 case MSQ_STATE_CARET
:
1507 ASSERT(MessageQueue
->CaretInfo
);
1508 Prev
= MessageQueue
->CaretInfo
->hWnd
;
1509 MessageQueue
->CaretInfo
->hWnd
= hWnd
;
1517 static VOID FASTCALL
1518 DumpTimerList(PUSER_MESSAGE_QUEUE MessageQueue
)
1520 PLIST_ENTRY Current
;
1523 Current
= MessageQueue
->TimerListHead
.Flink
;
1524 if (Current
== &MessageQueue
->TimerListHead
)
1526 DPRINT("timer list is empty for queue %p\n", MessageQueue
);
1528 while (Current
!= &MessageQueue
->TimerListHead
)
1530 Timer
= CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
);
1531 DPRINT("queue %p timer %p expiry %I64d wnd %x id %p period %u timerproc %p msg %u\n",
1532 MessageQueue
, Timer
, Timer
->ExpiryTime
.QuadPart
, Timer
->Wnd
, Timer
->IDEvent
,
1533 Timer
->Period
, Timer
->TimerFunc
, Timer
->Msg
);
1534 Current
= Current
->Flink
;
1537 #endif /* ! defined(NDEBUG) */
1539 /* Must have the message queue locked while calling this */
1540 static VOID FASTCALL
1541 InsertTimer(PUSER_MESSAGE_QUEUE MessageQueue
, PTIMER_ENTRY NewTimer
)
1543 PLIST_ENTRY Current
;
1545 Current
= MessageQueue
->TimerListHead
.Flink
;
1546 while (Current
!= &MessageQueue
->TimerListHead
)
1548 if (NewTimer
->ExpiryTime
.QuadPart
<
1549 CONTAINING_RECORD(Current
, TIMER_ENTRY
, ListEntry
)->ExpiryTime
.QuadPart
)
1553 Current
= Current
->Flink
;
1556 InsertTailList(Current
, &NewTimer
->ListEntry
);
1559 /* Must have the message queue locked while calling this */
1560 static PTIMER_ENTRY FASTCALL
1561 RemoveTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
, UINT_PTR IDEvent
, UINT Msg
)
1564 PLIST_ENTRY EnumEntry
;
1566 /* Remove timer if already in the queue */
1567 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1568 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1570 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1571 EnumEntry
= EnumEntry
->Flink
;
1573 if (Timer
->Wnd
== Wnd
&&
1574 Timer
->IDEvent
== IDEvent
&&
1577 RemoveEntryList(&Timer
->ListEntry
);
1586 MsqSetTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1587 UINT_PTR IDEvent
, UINT Period
, TIMERPROC TimerFunc
,
1591 LARGE_INTEGER CurrentTime
;
1593 DPRINT("MsqSetTimer queue %p wnd %x id %p period %u timerproc %p msg %d\n",
1594 MessageQueue
, Wnd
, IDEvent
, Period
, TimerFunc
, Msg
);
1596 IntLockMessageQueue(MessageQueue
);
1597 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1600 Timer
= ExAllocateFromPagedLookasideList(&TimerLookasideList
);
1603 IntUnLockMessageQueue(MessageQueue
);
1604 DPRINT1("Failed to allocate timer entry\n");
1607 DPRINT("Allocated new timer entry %p\n", Timer
);
1609 Timer
->IDEvent
= IDEvent
;
1614 DPRINT("Updating existing timer entry %p\n", Timer
);
1617 KeQuerySystemTime(&CurrentTime
);
1618 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1619 (ULONGLONG
) Period
* (ULONGLONG
) 10000;
1620 Timer
->Period
= Period
;
1621 Timer
->TimerFunc
= TimerFunc
;
1622 DPRINT("Insert timer now %I64d expiry %I64d\n", CurrentTime
.QuadPart
,
1623 Timer
->ExpiryTime
.QuadPart
);
1625 InsertTimer(MessageQueue
, Timer
);
1628 DumpTimerList(MessageQueue
);
1629 #endif /* ! defined(NDEBUG) */
1631 IntUnLockMessageQueue(MessageQueue
);
1637 MsqKillTimer(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
,
1638 UINT_PTR IDEvent
, UINT Msg
)
1642 DPRINT("MsqKillTimer queue %p wnd %x id %p msg %d\n",
1643 MessageQueue
, Wnd
, IDEvent
, Msg
);
1645 IntLockMessageQueue(MessageQueue
);
1646 Timer
= RemoveTimer(MessageQueue
, Wnd
, IDEvent
, Msg
);
1650 DPRINT("Failed to remove timer from list, not found\n");
1654 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1658 DumpTimerList(MessageQueue
);
1659 #endif /* ! defined(NDEBUG) */
1661 IntUnLockMessageQueue(MessageQueue
);
1663 return NULL
!= Timer
;
1667 MsqGetTimerMessage(PUSER_MESSAGE_QUEUE MessageQueue
,
1668 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1669 MSG
*Msg
, BOOLEAN Restart
)
1672 LARGE_INTEGER CurrentTime
;
1673 PLIST_ENTRY EnumEntry
;
1676 DPRINT("MsqGetTimerMessage queue %p msg %p restart %s\n",
1677 MessageQueue
, Msg
, Restart
? "TRUE" : "FALSE");
1679 IntLockMessageQueue(MessageQueue
);
1680 KeQuerySystemTime(&CurrentTime
);
1681 DPRINT("Current time %I64d\n", CurrentTime
.QuadPart
);
1682 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1684 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1686 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1687 TIMER_ENTRY
, ListEntry
);
1688 DPRINT("Checking timer %p wnd %x expiry %I64d\n", Timer
, Timer
->Wnd
,
1689 Timer
->ExpiryTime
.QuadPart
);
1690 EnumEntry
= EnumEntry
->Flink
;
1691 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1692 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1693 (MsgFilterMin
<= Timer
->Msg
&&
1694 Timer
->Msg
<= MsgFilterMax
)))
1696 if (Timer
->ExpiryTime
.QuadPart
<= CurrentTime
.QuadPart
)
1698 DPRINT("Timer is expired\n");
1704 DPRINT("No need to check later timers\n");
1710 DPRINT("timer %p (wnd %x msg %d) failed filter wnd %x msgmin %d msgmax %d\n",
1711 Timer
, Timer
->Wnd
, Timer
->Msg
, WndFilter
, MsgFilterMin
, MsgFilterMax
);
1717 DPRINT("No timer pending\n");
1718 IntUnLockMessageQueue(MessageQueue
);
1722 Msg
->hwnd
= Timer
->Wnd
;
1723 Msg
->message
= Timer
->Msg
;
1724 Msg
->wParam
= (WPARAM
) Timer
->IDEvent
;
1725 Msg
->lParam
= (LPARAM
) Timer
->TimerFunc
;
1729 RemoveEntryList(&Timer
->ListEntry
);
1730 Timer
->ExpiryTime
.QuadPart
= CurrentTime
.QuadPart
+
1731 (ULONGLONG
) Timer
->Period
* (ULONGLONG
) 10000;
1732 DPRINT("Restarting timer %p expires %I64d\n", Timer
, Timer
->ExpiryTime
.QuadPart
);
1733 InsertTimer(MessageQueue
, Timer
);
1736 DumpTimerList(MessageQueue
);
1737 #endif /* ! defined(NDEBUG) */
1740 IntUnLockMessageQueue(MessageQueue
);
1742 DPRINT("Created message wnd %x msg %d wParam %u lParam %u\n", Msg
->hwnd
, Msg
->message
,
1743 Msg
->wParam
, Msg
->lParam
);
1749 MsqRemoveTimersWindow(PUSER_MESSAGE_QUEUE MessageQueue
, HWND Wnd
)
1752 PLIST_ENTRY EnumEntry
;
1754 DPRINT("MsqRemoveTimersWindow queue %p wnd %x\n", MessageQueue
, Wnd
);
1756 IntLockMessageQueue(MessageQueue
);
1757 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1758 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1760 Timer
= CONTAINING_RECORD(EnumEntry
, TIMER_ENTRY
, ListEntry
);
1761 EnumEntry
= EnumEntry
->Flink
;
1762 if (Timer
->Wnd
== Wnd
)
1764 DPRINT("Removing timer %p because its window is going away\n", Timer
);
1765 RemoveEntryList(&Timer
->ListEntry
);
1766 ExFreeToPagedLookasideList(&TimerLookasideList
, Timer
);
1771 DumpTimerList(MessageQueue
);
1772 #endif /* ! defined(NDEBUG) */
1774 IntUnLockMessageQueue(MessageQueue
);
1778 MsqGetFirstTimerExpiry(PUSER_MESSAGE_QUEUE MessageQueue
,
1779 HWND WndFilter
, UINT MsgFilterMin
, UINT MsgFilterMax
,
1780 PLARGE_INTEGER FirstTimerExpiry
)
1783 PLIST_ENTRY EnumEntry
;
1785 DPRINT("MsqGetFirstTimerExpiry queue %p wndfilter %x msgfiltermin %d msgfiltermax %d expiry %p\n",
1786 MessageQueue
, WndFilter
, MsgFilterMin
, MsgFilterMax
, FirstTimerExpiry
);
1788 IntLockMessageQueue(MessageQueue
);
1789 EnumEntry
= MessageQueue
->TimerListHead
.Flink
;
1790 while (EnumEntry
!= &MessageQueue
->TimerListHead
)
1792 Timer
= CONTAINING_RECORD(MessageQueue
->TimerListHead
.Flink
,
1793 TIMER_ENTRY
, ListEntry
);
1794 EnumEntry
= EnumEntry
->Flink
;
1795 if ((NULL
== WndFilter
|| Timer
->Wnd
== WndFilter
) &&
1796 ((MsgFilterMin
== 0 && MsgFilterMax
== 0) ||
1797 (MsgFilterMin
<= Timer
->Msg
&&
1798 Timer
->Msg
<= MsgFilterMax
)))
1800 *FirstTimerExpiry
= Timer
->ExpiryTime
;
1801 DPRINT("First timer expires %I64d\n", Timer
->ExpiryTime
);
1802 IntUnLockMessageQueue(MessageQueue
);
1807 IntUnLockMessageQueue(MessageQueue
);