[win32k]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / msgqueue.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
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.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Message queues
23 * FILE: subsystems/win32/win32k/ntuser/msgqueue.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 Alexandre Julliard
26 Maarten Lankhorst
27 * REVISION HISTORY:
28 * 06-06-2001 CSH Created
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <win32k.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 /* GLOBALS *******************************************************************/
39
40 static PAGED_LOOKASIDE_LIST MessageLookasideList;
41
42 /* FUNCTIONS *****************************************************************/
43
44 HANDLE FASTCALL
45 IntMsqSetWakeMask(DWORD WakeMask)
46 {
47 PTHREADINFO Win32Thread;
48 PUSER_MESSAGE_QUEUE MessageQueue;
49 HANDLE MessageEventHandle;
50
51 Win32Thread = PsGetCurrentThreadWin32Thread();
52 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
53 return 0;
54
55 MessageQueue = Win32Thread->MessageQueue;
56 MessageQueue->WakeMask = WakeMask;
57 MessageEventHandle = MessageQueue->NewMessagesHandle;
58
59 if (Win32Thread->pcti)
60 Win32Thread->pcti->fsWakeMask = WakeMask;
61
62 IdlePing();
63
64 return MessageEventHandle;
65 }
66
67 BOOL FASTCALL
68 IntMsqClearWakeMask(VOID)
69 {
70 PTHREADINFO Win32Thread;
71 PUSER_MESSAGE_QUEUE MessageQueue;
72
73 Win32Thread = PsGetCurrentThreadWin32Thread();
74 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
75 return FALSE;
76
77 MessageQueue = Win32Thread->MessageQueue;
78 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
79 MessageQueue->WakeMask = ~0;
80
81 if (Win32Thread->pcti)
82 Win32Thread->pcti->fsWakeMask = 0;
83
84 IdlePong();
85
86 return TRUE;
87 }
88
89 VOID FASTCALL
90 MsqWakeQueue(PUSER_MESSAGE_QUEUE Queue, DWORD MessageBits)
91 {
92 Queue->QueueBits |= MessageBits;
93 Queue->ChangedBits |= MessageBits;
94 if (Queue->WakeMask & MessageBits)
95 KeSetEvent(Queue->NewMessages, IO_NO_INCREMENT, FALSE);
96 }
97
98 VOID FASTCALL
99 MsqIncPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
100 {
101 Queue->PaintCount++;
102 MsqWakeQueue(Queue, QS_PAINT);
103 }
104
105 VOID FASTCALL
106 MsqDecPaintCountQueue(PUSER_MESSAGE_QUEUE Queue)
107 {
108 Queue->PaintCount--;
109 }
110
111
112 INIT_FUNCTION
113 NTSTATUS
114 NTAPI
115 MsqInitializeImpl(VOID)
116 {
117 ExInitializePagedLookasideList(&MessageLookasideList,
118 NULL,
119 NULL,
120 0,
121 sizeof(USER_MESSAGE),
122 TAG_USRMSG,
123 256);
124
125 return(STATUS_SUCCESS);
126 }
127
128 VOID FASTCALL
129 MsqPostMouseMove(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg)
130 {
131 MessageQueue->MouseMoveMsg = *Msg;
132 MessageQueue->MouseMoved = TRUE;
133 MsqWakeQueue(MessageQueue, QS_MOUSEMOVE);
134 }
135
136 VOID FASTCALL
137 co_MsqInsertMouseMessage(MSG* Msg)
138 {
139 LARGE_INTEGER LargeTickCount;
140 MSLLHOOKSTRUCT MouseHookData;
141 PWND pwnd, pwndDesktop;
142
143 KeQueryTickCount(&LargeTickCount);
144 Msg->time = MsqCalculateMessageTime(&LargeTickCount);
145
146 MouseHookData.pt.x = LOWORD(Msg->lParam);
147 MouseHookData.pt.y = HIWORD(Msg->lParam);
148 switch(Msg->message)
149 {
150 case WM_MOUSEWHEEL:
151 MouseHookData.mouseData = MAKELONG(0, GET_WHEEL_DELTA_WPARAM(Msg->wParam));
152 break;
153 case WM_XBUTTONDOWN:
154 case WM_XBUTTONUP:
155 case WM_XBUTTONDBLCLK:
156 case WM_NCXBUTTONDOWN:
157 case WM_NCXBUTTONUP:
158 case WM_NCXBUTTONDBLCLK:
159 MouseHookData.mouseData = MAKELONG(0, HIWORD(Msg->wParam));
160 break;
161 default:
162 MouseHookData.mouseData = 0;
163 break;
164 }
165
166 MouseHookData.flags = 0;
167 MouseHookData.time = Msg->time;
168 MouseHookData.dwExtraInfo = 0;
169
170 /* If the hook procedure returned non zero, dont send the message */
171 if (co_HOOK_CallHooks(WH_MOUSE_LL, HC_ACTION, Msg->message, (LPARAM) &MouseHookData))
172 return;
173
174 /* Get the desktop window */
175 pwndDesktop = UserGetDesktopWindow();
176 if(!pwndDesktop)
177 return;
178
179 /* Check if the mouse is captured */
180 Msg->hwnd = IntGetCaptureWindow();
181 if(Msg->hwnd != NULL)
182 {
183 pwnd = UserGetWindowObject(Msg->hwnd);
184 }
185 else
186 {
187 /* Loop all top level windows to find which one should receive input */
188 for( pwnd = pwndDesktop->spwndChild;
189 pwnd != NULL;
190 pwnd = pwnd->spwndNext )
191 {
192 if((pwnd->style & WS_VISIBLE) &&
193 IntPtInWindow(pwnd, Msg->pt.x, Msg->pt.y))
194 {
195 Msg->hwnd = pwnd->head.h;
196 break;
197 }
198 }
199 }
200
201 /* Check if we found a window */
202 if(Msg->hwnd != NULL && pwnd != NULL)
203 {
204 if(Msg->message == WM_MOUSEMOVE)
205 {
206 /* Mouse move is a special case*/
207 MsqPostMouseMove(pwnd->head.pti->MessageQueue, Msg);
208 }
209 else
210 {
211 DPRINT("Posting mouse message to hwnd=0x%x!\n", UserHMGetHandle(pwnd));
212 MsqPostMessage(pwnd->head.pti->MessageQueue, Msg, TRUE, QS_MOUSEBUTTON);
213 }
214 }
215 }
216
217 //
218 // Note: Only called from input.c.
219 //
220 VOID FASTCALL
221 co_MsqPostKeyboardMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
222 {
223 PUSER_MESSAGE_QUEUE FocusMessageQueue;
224 MSG Msg;
225 LARGE_INTEGER LargeTickCount;
226 KBDLLHOOKSTRUCT KbdHookData;
227 BOOLEAN Entered = FALSE;
228
229 DPRINT("MsqPostKeyboardMessage(uMsg 0x%x, wParam 0x%x, lParam 0x%x)\n",
230 uMsg, wParam, lParam);
231
232 // Condition may arise when calling MsqPostMessage and waiting for an event.
233 if (!UserIsEntered())
234 {
235 // Fixme: Not sure ATM if this thread is locked.
236 UserEnterExclusive();
237 Entered = TRUE;
238 }
239
240 FocusMessageQueue = IntGetFocusMessageQueue();
241
242 Msg.hwnd = 0;
243
244 if (FocusMessageQueue && (FocusMessageQueue->FocusWindow != (HWND)0))
245 Msg.hwnd = FocusMessageQueue->FocusWindow;
246
247 Msg.message = uMsg;
248 Msg.wParam = wParam;
249 Msg.lParam = lParam;
250
251 KeQueryTickCount(&LargeTickCount);
252 Msg.time = MsqCalculateMessageTime(&LargeTickCount);
253
254 /* We can't get the Msg.pt point here since we don't know thread
255 (and thus the window station) the message will end up in yet. */
256
257 KbdHookData.vkCode = Msg.wParam;
258 KbdHookData.scanCode = (Msg.lParam >> 16) & 0xff;
259 KbdHookData.flags = (0 == (Msg.lParam & 0x01000000) ? 0 : LLKHF_EXTENDED) |
260 (0 == (Msg.lParam & 0x20000000) ? 0 : LLKHF_ALTDOWN) |
261 (0 == (Msg.lParam & 0x80000000) ? 0 : LLKHF_UP);
262 KbdHookData.time = Msg.time;
263 KbdHookData.dwExtraInfo = 0;
264 if (co_HOOK_CallHooks(WH_KEYBOARD_LL, HC_ACTION, Msg.message, (LPARAM) &KbdHookData))
265 {
266 DPRINT1("Kbd msg %d wParam %d lParam 0x%08x dropped by WH_KEYBOARD_LL hook\n",
267 Msg.message, Msg.wParam, Msg.lParam);
268 if (Entered) UserLeave();
269 return;
270 }
271
272 if (FocusMessageQueue == NULL)
273 {
274 DPRINT("No focus message queue\n");
275 if (Entered) UserLeave();
276 return;
277 }
278
279 if (FocusMessageQueue->FocusWindow != (HWND)0)
280 {
281 Msg.hwnd = FocusMessageQueue->FocusWindow;
282 DPRINT("Msg.hwnd = %x\n", Msg.hwnd);
283
284 FocusMessageQueue->Desktop->pDeskInfo->LastInputWasKbd = TRUE;
285
286 Msg.pt = gpsi->ptCursor;
287 MsqPostMessage(FocusMessageQueue, &Msg, TRUE, QS_KEY);
288 }
289 else
290 {
291 DPRINT("Invalid focus window handle\n");
292 }
293
294 if (Entered) UserLeave();
295 return;
296 }
297
298 VOID FASTCALL
299 MsqPostHotKeyMessage(PVOID Thread, HWND hWnd, WPARAM wParam, LPARAM lParam)
300 {
301 PWND Window;
302 PTHREADINFO Win32Thread;
303 MSG Mesg;
304 LARGE_INTEGER LargeTickCount;
305 NTSTATUS Status;
306
307 Status = ObReferenceObjectByPointer (Thread,
308 THREAD_ALL_ACCESS,
309 PsThreadType,
310 KernelMode);
311 if (!NT_SUCCESS(Status))
312 return;
313
314 Win32Thread = ((PETHREAD)Thread)->Tcb.Win32Thread;
315 if (Win32Thread == NULL || Win32Thread->MessageQueue == NULL)
316 {
317 ObDereferenceObject ((PETHREAD)Thread);
318 return;
319 }
320
321 Window = IntGetWindowObject(hWnd);
322 if (!Window)
323 {
324 ObDereferenceObject ((PETHREAD)Thread);
325 return;
326 }
327
328 Mesg.hwnd = hWnd;
329 Mesg.message = WM_HOTKEY;
330 Mesg.wParam = wParam;
331 Mesg.lParam = lParam;
332 KeQueryTickCount(&LargeTickCount);
333 Mesg.time = MsqCalculateMessageTime(&LargeTickCount);
334 Mesg.pt = gpsi->ptCursor;
335 MsqPostMessage(Window->head.pti->MessageQueue, &Mesg, FALSE, QS_HOTKEY);
336 UserDereferenceObject(Window);
337 ObDereferenceObject (Thread);
338
339 }
340
341 PUSER_MESSAGE FASTCALL
342 MsqCreateMessage(LPMSG Msg)
343 {
344 PUSER_MESSAGE Message;
345
346 Message = ExAllocateFromPagedLookasideList(&MessageLookasideList);
347 if (!Message)
348 {
349 return NULL;
350 }
351
352 RtlMoveMemory(&Message->Msg, Msg, sizeof(MSG));
353
354 return Message;
355 }
356
357 VOID FASTCALL
358 MsqDestroyMessage(PUSER_MESSAGE Message)
359 {
360 ExFreeToPagedLookasideList(&MessageLookasideList, Message);
361 }
362
363 VOID FASTCALL
364 co_MsqDispatchSentNotifyMessages(PUSER_MESSAGE_QUEUE MessageQueue)
365 {
366 PLIST_ENTRY ListEntry;
367 PUSER_SENT_MESSAGE_NOTIFY Message;
368
369 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
370 {
371 ListEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
372 Message = CONTAINING_RECORD(ListEntry, USER_SENT_MESSAGE_NOTIFY,
373 ListEntry);
374
375 co_IntCallSentMessageCallback(Message->CompletionCallback,
376 Message->hWnd,
377 Message->Msg,
378 Message->CompletionCallbackContext,
379 Message->Result);
380
381 }
382
383 }
384
385 BOOLEAN FASTCALL
386 MsqPeekSentMessages(PUSER_MESSAGE_QUEUE MessageQueue)
387 {
388 return(!IsListEmpty(&MessageQueue->SentMessagesListHead));
389 }
390
391 BOOLEAN FASTCALL
392 co_MsqDispatchOneSentMessage(PUSER_MESSAGE_QUEUE MessageQueue)
393 {
394 PUSER_SENT_MESSAGE Message;
395 PLIST_ENTRY Entry;
396 LRESULT Result;
397
398 if (IsListEmpty(&MessageQueue->SentMessagesListHead))
399 {
400 return(FALSE);
401 }
402
403 /* remove it from the list of pending messages */
404 Entry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
405 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
406
407 /* insert it to the list of messages that are currently dispatched by this
408 message queue */
409 InsertTailList(&MessageQueue->LocalDispatchingMessagesHead,
410 &Message->ListEntry);
411
412 if (Message->HookMessage == MSQ_ISHOOK)
413 { // Direct Hook Call processor
414 Result = co_CallHook( Message->Msg.message, // HookId
415 (INT)(INT_PTR)Message->Msg.hwnd, // Code
416 Message->Msg.wParam,
417 Message->Msg.lParam);
418 }
419 else if (Message->HookMessage == MSQ_ISEVENT)
420 { // Direct Event Call processor
421 Result = co_EVENT_CallEvents( Message->Msg.message,
422 Message->Msg.hwnd,
423 Message->Msg.wParam,
424 Message->Msg.lParam);
425 }
426 else
427 { /* Call the window procedure. */
428 Result = co_IntSendMessage( Message->Msg.hwnd,
429 Message->Msg.message,
430 Message->Msg.wParam,
431 Message->Msg.lParam);
432 }
433
434 /* remove the message from the local dispatching list, because it doesn't need
435 to be cleaned up on thread termination anymore */
436 RemoveEntryList(&Message->ListEntry);
437
438 /* remove the message from the dispatching list if needed, so lock the sender's message queue */
439 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
440 {
441 if (Message->DispatchingListEntry.Flink != NULL)
442 {
443 /* only remove it from the dispatching list if not already removed by a timeout */
444 RemoveEntryList(&Message->DispatchingListEntry);
445 }
446 }
447 /* still keep the sender's message queue locked, so the sender can't exit the
448 MsqSendMessage() function (if timed out) */
449
450 /* Let the sender know the result. */
451 if (Message->Result != NULL)
452 {
453 *Message->Result = Result;
454 }
455
456 if (Message->HasPackedLParam == TRUE)
457 {
458 if (Message->Msg.lParam)
459 ExFreePool((PVOID)Message->Msg.lParam);
460 }
461
462 /* Notify the sender. */
463 if (Message->CompletionEvent != NULL)
464 {
465 KeSetEvent(Message->CompletionEvent, IO_NO_INCREMENT, FALSE);
466 }
467
468 /* Call the callback if the message was sent with SendMessageCallback */
469 if (Message->CompletionCallback != NULL)
470 {
471 co_IntCallSentMessageCallback(Message->CompletionCallback,
472 Message->Msg.hwnd,
473 Message->Msg.message,
474 Message->CompletionCallbackContext,
475 Result);
476 }
477
478 /* Only if it is not a no wait message */
479 if (!(Message->HookMessage & MSQ_SENTNOWAIT))
480 {
481 IntDereferenceMessageQueue(Message->SenderQueue);
482 IntDereferenceMessageQueue(MessageQueue);
483 }
484
485 /* free the message */
486 ExFreePoolWithTag(Message, TAG_USRMSG);
487 return(TRUE);
488 }
489
490 VOID APIENTRY
491 MsqRemoveWindowMessagesFromQueue(PVOID pWindow)
492 {
493 PUSER_SENT_MESSAGE SentMessage;
494 PUSER_MESSAGE PostedMessage;
495 PUSER_MESSAGE_QUEUE MessageQueue;
496 PLIST_ENTRY CurrentEntry, ListHead;
497 PWND Window = pWindow;
498
499 ASSERT(Window);
500
501 MessageQueue = Window->head.pti->MessageQueue;
502 ASSERT(MessageQueue);
503
504 /* remove the posted messages for this window */
505 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
506 ListHead = &MessageQueue->PostedMessagesListHead;
507 while (CurrentEntry != ListHead)
508 {
509 PostedMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
510 ListEntry);
511 if (PostedMessage->Msg.hwnd == Window->head.h)
512 {
513 RemoveEntryList(&PostedMessage->ListEntry);
514 MsqDestroyMessage(PostedMessage);
515 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
516 }
517 else
518 {
519 CurrentEntry = CurrentEntry->Flink;
520 }
521 }
522
523 /* remove the sent messages for this window */
524 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
525 ListHead = &MessageQueue->SentMessagesListHead;
526 while (CurrentEntry != ListHead)
527 {
528 SentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
529 ListEntry);
530 if(SentMessage->Msg.hwnd == Window->head.h)
531 {
532 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
533
534 RemoveEntryList(&SentMessage->ListEntry);
535
536 /* remove the message from the dispatching list if neede */
537 if ((!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
538 && (SentMessage->DispatchingListEntry.Flink != NULL))
539 {
540 RemoveEntryList(&SentMessage->DispatchingListEntry);
541 }
542
543 /* wake the sender's thread */
544 if (SentMessage->CompletionEvent != NULL)
545 {
546 KeSetEvent(SentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
547 }
548
549 if (SentMessage->HasPackedLParam == TRUE)
550 {
551 if (SentMessage->Msg.lParam)
552 ExFreePool((PVOID)SentMessage->Msg.lParam);
553 }
554
555 /* Only if it is not a no wait message */
556 if (!(SentMessage->HookMessage & MSQ_SENTNOWAIT))
557 {
558 /* dereference our and the sender's message queue */
559 IntDereferenceMessageQueue(MessageQueue);
560 IntDereferenceMessageQueue(SentMessage->SenderQueue);
561 }
562
563 /* free the message */
564 ExFreePoolWithTag(SentMessage, TAG_USRMSG);
565
566 CurrentEntry = MessageQueue->SentMessagesListHead.Flink;
567 }
568 else
569 {
570 CurrentEntry = CurrentEntry->Flink;
571 }
572 }
573 }
574
575 VOID FASTCALL
576 MsqSendNotifyMessage(PUSER_MESSAGE_QUEUE MessageQueue,
577 PUSER_SENT_MESSAGE_NOTIFY NotifyMessage)
578 {
579 InsertTailList(&MessageQueue->NotifyMessagesListHead,
580 &NotifyMessage->ListEntry);
581 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE);
582 }
583
584 NTSTATUS FASTCALL
585 co_MsqSendMessage(PUSER_MESSAGE_QUEUE MessageQueue,
586 HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam,
587 UINT uTimeout, BOOL Block, INT HookMessage,
588 ULONG_PTR *uResult)
589 {
590 PTHREADINFO pti;
591 PUSER_SENT_MESSAGE Message;
592 KEVENT CompletionEvent;
593 NTSTATUS WaitStatus;
594 PUSER_MESSAGE_QUEUE ThreadQueue;
595 LARGE_INTEGER Timeout;
596 PLIST_ENTRY Entry;
597 LRESULT Result = 0; //// Result could be trashed. ////
598
599 if(!(Message = ExAllocatePoolWithTag(PagedPool, sizeof(USER_SENT_MESSAGE), TAG_USRMSG)))
600 {
601 DPRINT1("MsqSendMessage(): Not enough memory to allocate a message");
602 return STATUS_INSUFFICIENT_RESOURCES;
603 }
604
605 KeInitializeEvent(&CompletionEvent, NotificationEvent, FALSE);
606
607 pti = PsGetCurrentThreadWin32Thread();
608 ThreadQueue = pti->MessageQueue;
609 ASSERT(ThreadQueue != MessageQueue);
610
611 Timeout.QuadPart = (LONGLONG) uTimeout * (LONGLONG) -10000;
612
613 /* FIXME - increase reference counter of sender's message queue here */
614
615 Message->Msg.hwnd = Wnd;
616 Message->Msg.message = Msg;
617 Message->Msg.wParam = wParam;
618 Message->Msg.lParam = lParam;
619 Message->CompletionEvent = &CompletionEvent;
620 Message->Result = &Result;
621 Message->SenderQueue = ThreadQueue;
622 IntReferenceMessageQueue(ThreadQueue);
623 Message->CompletionCallback = NULL;
624 Message->CompletionCallbackContext = 0;
625 Message->HookMessage = HookMessage;
626 Message->HasPackedLParam = FALSE;
627
628 IntReferenceMessageQueue(MessageQueue);
629
630 /* add it to the list of pending messages */
631 InsertTailList(&ThreadQueue->DispatchingMessagesHead, &Message->DispatchingListEntry);
632
633 /* queue it in the destination's message queue */
634 InsertTailList(&MessageQueue->SentMessagesListHead, &Message->ListEntry);
635
636 MsqWakeQueue(MessageQueue, QS_SENDMESSAGE);
637
638 /* we can't access the Message anymore since it could have already been deleted! */
639
640 if(Block)
641 {
642 UserLeaveCo();
643
644 /* don't process messages sent to the thread */
645 WaitStatus = KeWaitForSingleObject(&CompletionEvent, UserRequest, UserMode,
646 FALSE, (uTimeout ? &Timeout : NULL));
647
648 UserEnterCo();
649
650 if(WaitStatus == STATUS_TIMEOUT)
651 {
652 /* look up if the message has not yet dispatched, if so
653 make sure it can't pass a result and it must not set the completion event anymore */
654 Entry = MessageQueue->SentMessagesListHead.Flink;
655 while (Entry != &MessageQueue->SentMessagesListHead)
656 {
657 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
658 == Message)
659 {
660 /* we can access Message here, it's secure because the message queue is locked
661 and the message is still hasn't been dispatched */
662 Message->CompletionEvent = NULL;
663 Message->Result = NULL;
664 break;
665 }
666 Entry = Entry->Flink;
667 }
668
669 /* remove from the local dispatching list so the other thread knows,
670 it can't pass a result and it must not set the completion event anymore */
671 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
672 while (Entry != &ThreadQueue->DispatchingMessagesHead)
673 {
674 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
675 == Message)
676 {
677 /* we can access Message here, it's secure because the sender's message is locked
678 and the message has definitely not yet been destroyed, otherwise it would
679 have been removed from this list by the dispatching routine right after
680 dispatching the message */
681 Message->CompletionEvent = NULL;
682 Message->Result = NULL;
683 RemoveEntryList(&Message->DispatchingListEntry);
684 Message->DispatchingListEntry.Flink = NULL;
685 break;
686 }
687 Entry = Entry->Flink;
688 }
689
690 DPRINT("MsqSendMessage (blocked) timed out\n");
691 }
692 while (co_MsqDispatchOneSentMessage(ThreadQueue))
693 ;
694 }
695 else
696 {
697 PVOID WaitObjects[2];
698
699 WaitObjects[0] = &CompletionEvent;
700 WaitObjects[1] = ThreadQueue->NewMessages;
701 do
702 {
703 UserLeaveCo();
704
705 WaitStatus = KeWaitForMultipleObjects(2, WaitObjects, WaitAny, UserRequest,
706 UserMode, FALSE, (uTimeout ? &Timeout : NULL), NULL);
707
708 UserEnterCo();
709
710 if(WaitStatus == STATUS_TIMEOUT)
711 {
712 /* look up if the message has not yet been dispatched, if so
713 make sure it can't pass a result and it must not set the completion event anymore */
714 Entry = MessageQueue->SentMessagesListHead.Flink;
715 while (Entry != &MessageQueue->SentMessagesListHead)
716 {
717 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry)
718 == Message)
719 {
720 /* we can access Message here, it's secure because the message queue is locked
721 and the message is still hasn't been dispatched */
722 Message->CompletionEvent = NULL;
723 Message->Result = NULL;
724 break;
725 }
726 Entry = Entry->Flink;
727 }
728
729 /* remove from the local dispatching list so the other thread knows,
730 it can't pass a result and it must not set the completion event anymore */
731 Entry = ThreadQueue->DispatchingMessagesHead.Flink;
732 while (Entry != &ThreadQueue->DispatchingMessagesHead)
733 {
734 if ((PUSER_SENT_MESSAGE) CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, DispatchingListEntry)
735 == Message)
736 {
737 /* we can access Message here, it's secure because the sender's message is locked
738 and the message has definitely not yet been destroyed, otherwise it would
739 have been removed from this list by the dispatching routine right after
740 dispatching the message */
741 Message->CompletionEvent = NULL;
742 Message->Result = NULL;
743 RemoveEntryList(&Message->DispatchingListEntry);
744 Message->DispatchingListEntry.Flink = NULL;
745 break;
746 }
747 Entry = Entry->Flink;
748 }
749
750 DPRINT("MsqSendMessage timed out\n");
751 break;
752 }
753 while (co_MsqDispatchOneSentMessage(ThreadQueue))
754 ;
755 }
756 while (NT_SUCCESS(WaitStatus) && STATUS_WAIT_0 != WaitStatus);
757 }
758
759 if(WaitStatus != STATUS_TIMEOUT)
760 *uResult = (STATUS_WAIT_0 == WaitStatus ? Result : -1);
761
762 return WaitStatus;
763 }
764
765 VOID FASTCALL
766 MsqPostMessage(PUSER_MESSAGE_QUEUE MessageQueue, MSG* Msg, BOOLEAN HardwareMessage,
767 DWORD MessageBits)
768 {
769 PUSER_MESSAGE Message;
770
771 if(!(Message = MsqCreateMessage(Msg)))
772 {
773 return;
774 }
775
776 if(!HardwareMessage)
777 {
778 InsertTailList(&MessageQueue->PostedMessagesListHead,
779 &Message->ListEntry);
780 }
781 else
782 {
783 InsertTailList(&MessageQueue->HardwareMessagesListHead,
784 &Message->ListEntry);
785 }
786 MsqWakeQueue(MessageQueue, MessageBits);
787 }
788
789 VOID FASTCALL
790 MsqPostQuitMessage(PUSER_MESSAGE_QUEUE MessageQueue, ULONG ExitCode)
791 {
792 MessageQueue->QuitPosted = TRUE;
793 MessageQueue->QuitExitCode = ExitCode;
794 MsqWakeQueue(MessageQueue, QS_POSTMESSAGE);
795 }
796
797 /***********************************************************************
798 * MsqSendParentNotify
799 *
800 * Send a WM_PARENTNOTIFY to all ancestors of the given window, unless
801 * the window has the WS_EX_NOPARENTNOTIFY style.
802 */
803 static void MsqSendParentNotify( PWND pwnd, WORD event, WORD idChild, POINT pt )
804 {
805 PWND pwndDesktop = UserGetWindowObject(IntGetDesktopWindow());
806
807 /* pt has to be in the client coordinates of the parent window */
808 pt.x += pwndDesktop->rcClient.left - pwnd->rcClient.left;
809 pt.y += pwndDesktop->rcClient.top - pwnd->rcClient.top;
810
811 for (;;)
812 {
813 PWND pwndParent;
814
815 if (!(pwnd->style & WS_CHILD)) break;
816 if (pwnd->ExStyle & WS_EX_NOPARENTNOTIFY) break;
817 if (!(pwndParent = IntGetParent(pwnd))) break;
818 if (pwndParent == pwndDesktop) break;
819 pt.x += pwnd->rcClient.left - pwndParent->rcClient.left;
820 pt.y += pwnd->rcClient.top - pwndParent->rcClient.top;
821
822 pwnd = pwndParent;
823 co_IntSendMessage( UserHMGetHandle(pwnd), WM_PARENTNOTIFY,
824 MAKEWPARAM( event, idChild ), MAKELPARAM( pt.x, pt.y ) );
825 }
826 }
827
828 BOOL co_IntProcessMouseMessage(MSG* msg, BOOL* RemoveMessages, UINT first, UINT last)
829 {
830 MSG clk_msg;
831 POINT pt;
832 UINT message;
833 USHORT hittest;
834 EVENTMSG event;
835 MOUSEHOOKSTRUCT hook;
836 BOOL eatMsg;
837
838 PWND pwndMsg, pwndDesktop;
839 PUSER_MESSAGE_QUEUE MessageQueue;
840 PTHREADINFO pti;
841 PSYSTEM_CURSORINFO CurInfo;
842 DECLARE_RETURN(BOOL);
843
844 pti = PsGetCurrentThreadWin32Thread();
845 pwndDesktop = UserGetDesktopWindow();
846 MessageQueue = pti->MessageQueue;
847 CurInfo = IntGetSysCursorInfo();
848 pwndMsg = UserGetWindowObject(msg->hwnd);
849 clk_msg = MessageQueue->msgDblClk;
850
851 /* find the window to dispatch this mouse message to */
852 if (MessageQueue->CaptureWindow)
853 {
854 hittest = HTCLIENT;
855 pwndMsg = IntGetWindowObject(MessageQueue->CaptureWindow);
856 }
857 else
858 {
859 pwndMsg = co_WinPosWindowFromPoint(pwndMsg, &msg->pt, &hittest);
860 }
861
862 DPRINT("Got mouse message for 0x%x, hittest: 0x%x\n", msg->hwnd, hittest );
863
864 if (pwndMsg == NULL || pwndMsg->head.pti != pti)
865 {
866 /* Remove and ignore the message */
867 *RemoveMessages = TRUE;
868 RETURN(FALSE);
869 }
870
871 msg->hwnd = UserHMGetHandle(pwndMsg);
872
873 /* FIXME: is this really the right place for this hook? */
874 event.message = msg->message;
875 event.time = msg->time;
876 event.hwnd = msg->hwnd;
877 event.paramL = msg->pt.x;
878 event.paramH = msg->pt.y;
879 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&event );
880
881 #if 0
882 if (!check_hwnd_filter( msg, hwnd_filter )) RETURN(FALSE);
883 #endif
884
885 pt = msg->pt;
886 message = msg->message;
887 /* Note: windows has no concept of a non-client wheel message */
888 if (message != WM_MOUSEWHEEL)
889 {
890 if (hittest != HTCLIENT)
891 {
892 message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
893 msg->wParam = hittest;
894 }
895 else
896 {
897 /* coordinates don't get translated while tracking a menu */
898 /* FIXME: should differentiate popups and top-level menus */
899 if (!(MessageQueue->MenuOwner))
900 {
901 pt.x += pwndDesktop->rcClient.left - pwndMsg->rcClient.left;
902 pt.y += pwndDesktop->rcClient.top - pwndMsg->rcClient.top;
903 }
904 }
905 }
906 msg->lParam = MAKELONG( pt.x, pt.y );
907
908 /* translate double clicks */
909
910 if ((msg->message == WM_LBUTTONDOWN) ||
911 (msg->message == WM_RBUTTONDOWN) ||
912 (msg->message == WM_MBUTTONDOWN) ||
913 (msg->message == WM_XBUTTONDOWN))
914 {
915 BOOL update = *RemoveMessages;
916
917 /* translate double clicks -
918 * note that ...MOUSEMOVEs can slip in between
919 * ...BUTTONDOWN and ...BUTTONDBLCLK messages */
920
921 if ((MessageQueue->MenuOwner || MessageQueue->MoveSize) ||
922 hittest != HTCLIENT ||
923 (pwndMsg->pcls->style & CS_DBLCLKS))
924 {
925 if ((msg->message == clk_msg.message) &&
926 (msg->hwnd == clk_msg.hwnd) &&
927 (msg->wParam == clk_msg.wParam) &&
928 (msg->time - clk_msg.time < gspv.iDblClickTime) &&
929 (abs(msg->pt.x - clk_msg.pt.x) < UserGetSystemMetrics(SM_CXDOUBLECLK)/2) &&
930 (abs(msg->pt.y - clk_msg.pt.y) < UserGetSystemMetrics(SM_CYDOUBLECLK)/2))
931 {
932 message += (WM_LBUTTONDBLCLK - WM_LBUTTONDOWN);
933 if (update)
934 {
935 MessageQueue->msgDblClk.message = 0; /* clear the double click conditions */
936 update = FALSE;
937 }
938 }
939 }
940
941 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
942 {
943 DPRINT("Message out of range!!!\n");
944 RETURN(FALSE);
945 }
946
947 /* update static double click conditions */
948 if (update) MessageQueue->msgDblClk = *msg;
949 }
950 else
951 {
952 if (!((first == 0 && last == 0) || (message >= first || message <= last)))
953 {
954 DPRINT("Message out of range!!!\n");
955 RETURN(FALSE);
956 }
957 }
958
959 if(gspv.bMouseClickLock)
960 {
961 BOOL IsClkLck = FALSE;
962
963 if(msg->message == WM_LBUTTONUP)
964 {
965 IsClkLck = ((msg->time - CurInfo->ClickLockTime) >= gspv.dwMouseClickLockTime);
966 if (IsClkLck && (!CurInfo->ClickLockActive))
967 {
968 CurInfo->ClickLockActive = TRUE;
969 }
970 }
971 else if (msg->message == WM_LBUTTONDOWN)
972 {
973 if (CurInfo->ClickLockActive)
974 {
975 IsClkLck = TRUE;
976 CurInfo->ClickLockActive = FALSE;
977 }
978
979 CurInfo->ClickLockTime = msg->time;
980 }
981
982 if(IsClkLck)
983 {
984 /* Remove and ignore the message */
985 *RemoveMessages = TRUE;
986 RETURN(FALSE);
987 }
988 }
989
990 /* message is accepted now (but may still get dropped) */
991
992 hook.pt = msg->pt;
993 hook.hwnd = msg->hwnd;
994 hook.wHitTestCode = hittest;
995 hook.dwExtraInfo = 0/*extra_info*/;
996 if (co_HOOK_CallHooks( WH_MOUSE, *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
997 message, (LPARAM)&hook ))
998 {
999 hook.pt = msg->pt;
1000 hook.hwnd = msg->hwnd;
1001 hook.wHitTestCode = hittest;
1002 hook.dwExtraInfo = 0/*extra_info*/;
1003 co_HOOK_CallHooks( WH_CBT, HCBT_CLICKSKIPPED, message, (LPARAM)&hook );
1004
1005 DPRINT1("WH_MOUSE dorpped mouse message!\n");
1006
1007 /* Remove and skip message */
1008 *RemoveMessages = TRUE;
1009 RETURN(FALSE);
1010 }
1011
1012 if ((hittest == HTERROR) || (hittest == HTNOWHERE))
1013 {
1014 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd,
1015 MAKELONG( hittest, msg->message ));
1016
1017 /* Remove and skip message */
1018 *RemoveMessages = TRUE;
1019 RETURN(FALSE);
1020 }
1021
1022 if ((*RemoveMessages == FALSE) || MessageQueue->CaptureWindow)
1023 {
1024 /* Accept the message */
1025 msg->message = message;
1026 RETURN(TRUE);
1027 }
1028
1029 eatMsg = FALSE;
1030
1031 if ((msg->message == WM_LBUTTONDOWN) ||
1032 (msg->message == WM_RBUTTONDOWN) ||
1033 (msg->message == WM_MBUTTONDOWN) ||
1034 (msg->message == WM_XBUTTONDOWN))
1035 {
1036 /* Send the WM_PARENTNOTIFY,
1037 * note that even for double/nonclient clicks
1038 * notification message is still WM_L/M/RBUTTONDOWN.
1039 */
1040 MsqSendParentNotify(pwndMsg, msg->message, 0, msg->pt );
1041
1042 /* Activate the window if needed */
1043
1044 if (msg->hwnd != MessageQueue->ActiveWindow)
1045 {
1046 PWND pwndTop = pwndMsg;
1047 while (pwndTop)
1048 {
1049 if ((pwndTop->style & (WS_POPUP|WS_CHILD)) != WS_CHILD) break;
1050 pwndTop = IntGetParent( pwndTop );
1051 }
1052
1053 if (pwndTop && pwndTop != pwndDesktop)
1054 {
1055 LONG ret = co_IntSendMessage( msg->hwnd,
1056 WM_MOUSEACTIVATE,
1057 (WPARAM)UserHMGetHandle(pwndTop),
1058 MAKELONG( hittest, msg->message));
1059 switch(ret)
1060 {
1061 case MA_NOACTIVATEANDEAT:
1062 eatMsg = TRUE;
1063 /* fall through */
1064 case MA_NOACTIVATE:
1065 break;
1066 case MA_ACTIVATEANDEAT:
1067 eatMsg = TRUE;
1068 /* fall through */
1069 case MA_ACTIVATE:
1070 case 0:
1071 if(!co_IntMouseActivateWindow(pwndMsg)) eatMsg = TRUE;
1072 break;
1073 default:
1074 DPRINT1( "unknown WM_MOUSEACTIVATE code %d\n", ret );
1075 break;
1076 }
1077 }
1078 }
1079 }
1080
1081 /* send the WM_SETCURSOR message */
1082
1083 /* Windows sends the normal mouse message as the message parameter
1084 in the WM_SETCURSOR message even if it's non-client mouse message */
1085 co_IntSendMessage( msg->hwnd, WM_SETCURSOR, (WPARAM)msg->hwnd, MAKELONG( hittest, msg->message ));
1086
1087 msg->message = message;
1088 RETURN(!eatMsg);
1089
1090 CLEANUP:
1091 if(pwndMsg)
1092 UserDereferenceObject(pwndMsg);
1093
1094 END_CLEANUP;
1095 }
1096
1097 BOOL co_IntProcessKeyboardMessage(MSG* Msg, BOOL* RemoveMessages)
1098 {
1099 EVENTMSG Event;
1100
1101 Event.message = Msg->message;
1102 Event.hwnd = Msg->hwnd;
1103 Event.time = Msg->time;
1104 Event.paramL = (Msg->wParam & 0xFF) | (HIWORD(Msg->lParam) << 8);
1105 Event.paramH = Msg->lParam & 0x7FFF;
1106 if (HIWORD(Msg->lParam) & 0x0100) Event.paramH |= 0x8000;
1107 co_HOOK_CallHooks( WH_JOURNALRECORD, HC_ACTION, 0, (LPARAM)&Event);
1108
1109 if (co_HOOK_CallHooks( WH_KEYBOARD,
1110 *RemoveMessages ? HC_ACTION : HC_NOREMOVE,
1111 LOWORD(Msg->wParam),
1112 Msg->lParam))
1113 {
1114 /* skip this message */
1115 co_HOOK_CallHooks( WH_CBT,
1116 HCBT_KEYSKIPPED,
1117 LOWORD(Msg->wParam),
1118 Msg->lParam );
1119 DPRINT1("KeyboardMessage WH_CBT Call Hook return!\n");
1120 return FALSE;
1121 }
1122 return TRUE;
1123 }
1124
1125 BOOL co_IntProcessHardwareMessage(MSG* Msg, BOOL* RemoveMessages, UINT first, UINT last)
1126 {
1127 if ( IS_MOUSE_MESSAGE(Msg->message))
1128 {
1129 return co_IntProcessMouseMessage(Msg, RemoveMessages, first, last);
1130 }
1131 else if ( IS_KBD_MESSAGE(Msg->message))
1132 {
1133 return co_IntProcessKeyboardMessage(Msg, RemoveMessages);
1134 }
1135
1136 return TRUE;
1137 }
1138
1139 BOOL APIENTRY
1140 co_MsqPeekMouseMove(IN PUSER_MESSAGE_QUEUE MessageQueue,
1141 IN BOOL Remove,
1142 IN PWND Window,
1143 IN UINT MsgFilterLow,
1144 IN UINT MsgFilterHigh,
1145 OUT MSG* pMsg)
1146 {
1147 BOOL AcceptMessage;
1148 MSG msg;
1149
1150 if(!(MessageQueue->MouseMoved))
1151 return FALSE;
1152
1153 msg = MessageQueue->MouseMoveMsg;
1154
1155 AcceptMessage = co_IntProcessMouseMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1156
1157 if(AcceptMessage)
1158 *pMsg = msg;
1159
1160 if(Remove)
1161 MessageQueue->MouseMoved = FALSE;
1162
1163 return AcceptMessage;
1164 }
1165
1166 BOOL APIENTRY
1167 co_MsqPeekHardwareMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1168 IN BOOL Remove,
1169 IN PWND Window,
1170 IN UINT MsgFilterLow,
1171 IN UINT MsgFilterHigh,
1172 OUT MSG* pMsg)
1173 {
1174
1175 BOOL AcceptMessage;
1176 PUSER_MESSAGE CurrentMessage;
1177 PLIST_ENTRY ListHead, CurrentEntry = NULL;
1178 MSG msg;
1179
1180 ListHead = &MessageQueue->HardwareMessagesListHead;
1181 CurrentEntry = ListHead->Flink;
1182
1183 while(CurrentEntry != ListHead)
1184 {
1185 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1186 ListEntry);
1187
1188 msg = CurrentMessage->Msg;
1189
1190 AcceptMessage = co_IntProcessHardwareMessage(&msg, &Remove, MsgFilterLow, MsgFilterHigh);
1191
1192 CurrentEntry = CurrentMessage->ListEntry.Flink;
1193
1194 if (Remove)
1195 {
1196 RemoveEntryList(&CurrentMessage->ListEntry);
1197 MsqDestroyMessage(CurrentMessage);
1198 }
1199
1200 if(AcceptMessage)
1201 {
1202 *pMsg = msg;
1203 return TRUE;
1204 }
1205
1206 }
1207
1208 return FALSE;
1209 }
1210
1211 BOOLEAN APIENTRY
1212 MsqPeekMessage(IN PUSER_MESSAGE_QUEUE MessageQueue,
1213 IN BOOLEAN Remove,
1214 IN PWND Window,
1215 IN UINT MsgFilterLow,
1216 IN UINT MsgFilterHigh,
1217 OUT PMSG Message)
1218 {
1219 PLIST_ENTRY CurrentEntry;
1220 PUSER_MESSAGE CurrentMessage;
1221 PLIST_ENTRY ListHead;
1222
1223 CurrentEntry = MessageQueue->PostedMessagesListHead.Flink;
1224 ListHead = &MessageQueue->PostedMessagesListHead;
1225 while (CurrentEntry != ListHead)
1226 {
1227 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1228 ListEntry);
1229 if ( ( !Window ||
1230 PtrToInt(Window) == 1 ||
1231 Window->head.h == CurrentMessage->Msg.hwnd ) &&
1232 ( (MsgFilterLow == 0 && MsgFilterHigh == 0) ||
1233 ( MsgFilterLow <= CurrentMessage->Msg.message &&
1234 MsgFilterHigh >= CurrentMessage->Msg.message ) ) )
1235 {
1236 *Message= CurrentMessage->Msg;
1237
1238 if (Remove)
1239 {
1240 RemoveEntryList(&CurrentMessage->ListEntry);
1241 MsqDestroyMessage(CurrentMessage);
1242 }
1243
1244 return(TRUE);
1245 }
1246 CurrentEntry = CurrentEntry->Flink;
1247 }
1248
1249 return(FALSE);
1250 }
1251
1252 NTSTATUS FASTCALL
1253 co_MsqWaitForNewMessages(PUSER_MESSAGE_QUEUE MessageQueue, PWND WndFilter,
1254 UINT MsgFilterMin, UINT MsgFilterMax)
1255 {
1256 NTSTATUS ret;
1257
1258 UserLeaveCo();
1259 ret = KeWaitForSingleObject(MessageQueue->NewMessages,
1260 Executive,
1261 UserMode,
1262 FALSE,
1263 NULL);
1264 UserEnterCo();
1265 return ret;
1266 }
1267
1268 BOOL FASTCALL
1269 MsqIsHung(PUSER_MESSAGE_QUEUE MessageQueue)
1270 {
1271 LARGE_INTEGER LargeTickCount;
1272
1273 KeQueryTickCount(&LargeTickCount);
1274 return ((LargeTickCount.u.LowPart - MessageQueue->LastMsgRead) > MSQ_HUNG);
1275 }
1276
1277 BOOLEAN FASTCALL
1278 MsqInitializeMessageQueue(struct _ETHREAD *Thread, PUSER_MESSAGE_QUEUE MessageQueue)
1279 {
1280 LARGE_INTEGER LargeTickCount;
1281 NTSTATUS Status;
1282
1283 MessageQueue->Thread = Thread;
1284 MessageQueue->CaretInfo = (PTHRDCARETINFO)(MessageQueue + 1);
1285 InitializeListHead(&MessageQueue->PostedMessagesListHead);
1286 InitializeListHead(&MessageQueue->SentMessagesListHead);
1287 InitializeListHead(&MessageQueue->HardwareMessagesListHead);
1288 InitializeListHead(&MessageQueue->DispatchingMessagesHead);
1289 InitializeListHead(&MessageQueue->LocalDispatchingMessagesHead);
1290 KeInitializeMutex(&MessageQueue->HardwareLock, 0);
1291 MessageQueue->QuitPosted = FALSE;
1292 MessageQueue->QuitExitCode = 0;
1293 KeQueryTickCount(&LargeTickCount);
1294 MessageQueue->LastMsgRead = LargeTickCount.u.LowPart;
1295 MessageQueue->FocusWindow = NULL;
1296 MessageQueue->PaintCount = 0;
1297 // HACK!!!!!!! Newbies that wrote this should hold your head down in shame! (jt)
1298 MessageQueue->WakeMask = ~0;
1299 MessageQueue->NewMessagesHandle = NULL;
1300
1301 Status = ZwCreateEvent(&MessageQueue->NewMessagesHandle, EVENT_ALL_ACCESS,
1302 NULL, SynchronizationEvent, FALSE);
1303 if (!NT_SUCCESS(Status))
1304 {
1305 return FALSE;
1306 }
1307
1308 Status = ObReferenceObjectByHandle(MessageQueue->NewMessagesHandle, 0,
1309 ExEventObjectType, KernelMode,
1310 (PVOID*)&MessageQueue->NewMessages, NULL);
1311 if (!NT_SUCCESS(Status))
1312 {
1313 ZwClose(MessageQueue->NewMessagesHandle);
1314 MessageQueue->NewMessagesHandle = NULL;
1315 return FALSE;
1316 }
1317
1318 return TRUE;
1319 }
1320
1321 VOID FASTCALL
1322 MsqCleanupMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1323 {
1324 PLIST_ENTRY CurrentEntry;
1325 PUSER_MESSAGE CurrentMessage;
1326 PUSER_SENT_MESSAGE CurrentSentMessage;
1327
1328 /* cleanup posted messages */
1329 while (!IsListEmpty(&MessageQueue->PostedMessagesListHead))
1330 {
1331 CurrentEntry = RemoveHeadList(&MessageQueue->PostedMessagesListHead);
1332 CurrentMessage = CONTAINING_RECORD(CurrentEntry, USER_MESSAGE,
1333 ListEntry);
1334 MsqDestroyMessage(CurrentMessage);
1335 }
1336
1337 /* remove the messages that have not yet been dispatched */
1338 while (!IsListEmpty(&MessageQueue->SentMessagesListHead))
1339 {
1340 CurrentEntry = RemoveHeadList(&MessageQueue->SentMessagesListHead);
1341 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1342 ListEntry);
1343
1344 DPRINT("Notify the sender and remove a message from the queue that had not been dispatched\n");
1345
1346 /* remove the message from the dispatching list if needed */
1347 if ((!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1348 && (CurrentSentMessage->DispatchingListEntry.Flink != NULL))
1349 {
1350 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1351 }
1352
1353 /* wake the sender's thread */
1354 if (CurrentSentMessage->CompletionEvent != NULL)
1355 {
1356 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1357 }
1358
1359 if (CurrentSentMessage->HasPackedLParam == TRUE)
1360 {
1361 if (CurrentSentMessage->Msg.lParam)
1362 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1363 }
1364
1365 /* Only if it is not a no wait message */
1366 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1367 {
1368 /* dereference our and the sender's message queue */
1369 IntDereferenceMessageQueue(MessageQueue);
1370 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1371 }
1372
1373 /* free the message */
1374 ExFreePool(CurrentSentMessage);
1375 }
1376
1377 /* notify senders of dispatching messages. This needs to be cleaned up if e.g.
1378 ExitThread() was called in a SendMessage() umode callback */
1379 while (!IsListEmpty(&MessageQueue->LocalDispatchingMessagesHead))
1380 {
1381 CurrentEntry = RemoveHeadList(&MessageQueue->LocalDispatchingMessagesHead);
1382 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1383 ListEntry);
1384
1385 /* remove the message from the dispatching list */
1386 if(CurrentSentMessage->DispatchingListEntry.Flink != NULL)
1387 {
1388 RemoveEntryList(&CurrentSentMessage->DispatchingListEntry);
1389 }
1390
1391 DPRINT("Notify the sender, the thread has been terminated while dispatching a message!\n");
1392
1393 /* wake the sender's thread */
1394 if (CurrentSentMessage->CompletionEvent != NULL)
1395 {
1396 KeSetEvent(CurrentSentMessage->CompletionEvent, IO_NO_INCREMENT, FALSE);
1397 }
1398
1399 if (CurrentSentMessage->HasPackedLParam == TRUE)
1400 {
1401 if (CurrentSentMessage->Msg.lParam)
1402 ExFreePool((PVOID)CurrentSentMessage->Msg.lParam);
1403 }
1404
1405 /* Only if it is not a no wait message */
1406 if (!(CurrentSentMessage->HookMessage & MSQ_SENTNOWAIT))
1407 {
1408 /* dereference our and the sender's message queue */
1409 IntDereferenceMessageQueue(MessageQueue);
1410 IntDereferenceMessageQueue(CurrentSentMessage->SenderQueue);
1411 }
1412
1413 /* free the message */
1414 ExFreePool(CurrentSentMessage);
1415 }
1416
1417 /* tell other threads not to bother returning any info to us */
1418 while (! IsListEmpty(&MessageQueue->DispatchingMessagesHead))
1419 {
1420 CurrentEntry = RemoveHeadList(&MessageQueue->DispatchingMessagesHead);
1421 CurrentSentMessage = CONTAINING_RECORD(CurrentEntry, USER_SENT_MESSAGE,
1422 DispatchingListEntry);
1423 CurrentSentMessage->CompletionEvent = NULL;
1424 CurrentSentMessage->Result = NULL;
1425
1426 /* do NOT dereference our message queue as it might get attempted to be
1427 locked later */
1428 }
1429
1430 }
1431
1432 PUSER_MESSAGE_QUEUE FASTCALL
1433 MsqCreateMessageQueue(struct _ETHREAD *Thread)
1434 {
1435 PUSER_MESSAGE_QUEUE MessageQueue;
1436
1437 MessageQueue = (PUSER_MESSAGE_QUEUE)ExAllocatePoolWithTag(NonPagedPool,
1438 sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO),
1439 TAG_MSGQ);
1440
1441 if (!MessageQueue)
1442 {
1443 return NULL;
1444 }
1445
1446 RtlZeroMemory(MessageQueue, sizeof(USER_MESSAGE_QUEUE) + sizeof(THRDCARETINFO));
1447 /* hold at least one reference until it'll be destroyed */
1448 IntReferenceMessageQueue(MessageQueue);
1449 /* initialize the queue */
1450 if (!MsqInitializeMessageQueue(Thread, MessageQueue))
1451 {
1452 IntDereferenceMessageQueue(MessageQueue);
1453 return NULL;
1454 }
1455
1456 return MessageQueue;
1457 }
1458
1459 VOID FASTCALL
1460 MsqDestroyMessageQueue(PUSER_MESSAGE_QUEUE MessageQueue)
1461 {
1462 PDESKTOP desk;
1463
1464 /* remove the message queue from any desktops */
1465 if ((desk = InterlockedExchangePointer((PVOID*)&MessageQueue->Desktop, 0)))
1466 {
1467 (void)InterlockedExchangePointer((PVOID*)&desk->ActiveMessageQueue, 0);
1468 IntDereferenceMessageQueue(MessageQueue);
1469 }
1470
1471 /* clean it up */
1472 MsqCleanupMessageQueue(MessageQueue);
1473
1474 /* decrease the reference counter, if it hits zero, the queue will be freed */
1475 IntDereferenceMessageQueue(MessageQueue);
1476 }
1477
1478 LPARAM FASTCALL
1479 MsqSetMessageExtraInfo(LPARAM lParam)
1480 {
1481 LPARAM Ret;
1482 PTHREADINFO pti;
1483 PUSER_MESSAGE_QUEUE MessageQueue;
1484
1485 pti = PsGetCurrentThreadWin32Thread();
1486 MessageQueue = pti->MessageQueue;
1487 if(!MessageQueue)
1488 {
1489 return 0;
1490 }
1491
1492 Ret = MessageQueue->ExtraInfo;
1493 MessageQueue->ExtraInfo = lParam;
1494
1495 return Ret;
1496 }
1497
1498 LPARAM FASTCALL
1499 MsqGetMessageExtraInfo(VOID)
1500 {
1501 PTHREADINFO pti;
1502 PUSER_MESSAGE_QUEUE MessageQueue;
1503
1504 pti = PsGetCurrentThreadWin32Thread();
1505 MessageQueue = pti->MessageQueue;
1506 if(!MessageQueue)
1507 {
1508 return 0;
1509 }
1510
1511 return MessageQueue->ExtraInfo;
1512 }
1513
1514 HWND FASTCALL
1515 MsqSetStateWindow(PUSER_MESSAGE_QUEUE MessageQueue, ULONG Type, HWND hWnd)
1516 {
1517 HWND Prev;
1518
1519 switch(Type)
1520 {
1521 case MSQ_STATE_CAPTURE:
1522 Prev = MessageQueue->CaptureWindow;
1523 MessageQueue->CaptureWindow = hWnd;
1524 return Prev;
1525 case MSQ_STATE_ACTIVE:
1526 Prev = MessageQueue->ActiveWindow;
1527 MessageQueue->ActiveWindow = hWnd;
1528 return Prev;
1529 case MSQ_STATE_FOCUS:
1530 Prev = MessageQueue->FocusWindow;
1531 MessageQueue->FocusWindow = hWnd;
1532 return Prev;
1533 case MSQ_STATE_MENUOWNER:
1534 Prev = MessageQueue->MenuOwner;
1535 MessageQueue->MenuOwner = hWnd;
1536 return Prev;
1537 case MSQ_STATE_MOVESIZE:
1538 Prev = MessageQueue->MoveSize;
1539 MessageQueue->MoveSize = hWnd;
1540 return Prev;
1541 case MSQ_STATE_CARET:
1542 ASSERT(MessageQueue->CaretInfo);
1543 Prev = MessageQueue->CaretInfo->hWnd;
1544 MessageQueue->CaretInfo->hWnd = hWnd;
1545 return Prev;
1546 }
1547
1548 return NULL;
1549 }
1550
1551 /* EOF */