- Partial implementation of RealMsgWaitForMultipleObjectsEx and message queue wake...
[reactos.git] / reactos / subsys / win32k / ntuser / message.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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id: message.c,v 1.78 2004/12/25 22:59:10 navaraf Exp $
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Messages
24 * FILE: subsys/win32k/ntuser/message.c
25 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
26 * REVISION HISTORY:
27 * 06-06-2001 CSH Created
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <w32k.h>
33
34 #define NDEBUG
35 #include <debug.h>
36
37 typedef struct
38 {
39 UINT uFlags;
40 UINT uTimeout;
41 ULONG_PTR Result;
42 } DOSENDMESSAGE, *PDOSENDMESSAGE;
43
44 /* FUNCTIONS *****************************************************************/
45
46 NTSTATUS FASTCALL
47 IntInitMessageImpl(VOID)
48 {
49 return STATUS_SUCCESS;
50 }
51
52 NTSTATUS FASTCALL
53 IntCleanupMessageImpl(VOID)
54 {
55 return STATUS_SUCCESS;
56 }
57
58 #define MMS_SIZE_WPARAM -1
59 #define MMS_SIZE_WPARAMWCHAR -2
60 #define MMS_SIZE_LPARAMSZ -3
61 #define MMS_SIZE_SPECIAL -4
62 #define MMS_FLAG_READ 0x01
63 #define MMS_FLAG_WRITE 0x02
64 #define MMS_FLAG_READWRITE (MMS_FLAG_READ | MMS_FLAG_WRITE)
65 typedef struct tagMSGMEMORY
66 {
67 UINT Message;
68 UINT Size;
69 INT Flags;
70 } MSGMEMORY, *PMSGMEMORY;
71
72 static MSGMEMORY MsgMemory[] =
73 {
74 { WM_CREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
75 { WM_DDE_ACK, sizeof(KMDDELPARAM), MMS_FLAG_READ },
76 { WM_DDE_EXECUTE, MMS_SIZE_WPARAM, MMS_FLAG_READ },
77 { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
78 { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
79 { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
80 { WM_NCCREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
81 { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
82 { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
83 { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
84 { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ },
85 { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READ },
86 { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
87 };
88
89 static PMSGMEMORY FASTCALL
90 FindMsgMemory(UINT Msg)
91 {
92 PMSGMEMORY MsgMemoryEntry;
93
94 /* See if this message type is present in the table */
95 for (MsgMemoryEntry = MsgMemory;
96 MsgMemoryEntry < MsgMemory + sizeof(MsgMemory) / sizeof(MSGMEMORY);
97 MsgMemoryEntry++)
98 {
99 if (Msg == MsgMemoryEntry->Message)
100 {
101 return MsgMemoryEntry;
102 }
103 }
104
105 return NULL;
106 }
107
108 static UINT FASTCALL
109 MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
110 {
111 CREATESTRUCTW *Cs;
112 PUNICODE_STRING WindowName;
113 PUNICODE_STRING ClassName;
114 UINT Size;
115
116 if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
117 {
118 return (UINT) wParam;
119 }
120 else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
121 {
122 return (UINT) (wParam * sizeof(WCHAR));
123 }
124 else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
125 {
126 return (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
127 }
128 else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
129 {
130 switch(MsgMemoryEntry->Message)
131 {
132 case WM_CREATE:
133 case WM_NCCREATE:
134 Cs = (CREATESTRUCTW *) lParam;
135 WindowName = (PUNICODE_STRING) Cs->lpszName;
136 ClassName = (PUNICODE_STRING) Cs->lpszClass;
137 Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
138 if (IS_ATOM(ClassName->Buffer))
139 {
140 Size += sizeof(WCHAR) + sizeof(ATOM);
141 }
142 else
143 {
144 Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
145 }
146 return Size;
147 break;
148
149 case WM_NCCALCSIZE:
150 return wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
151 break;
152
153 case WM_COPYDATA:
154 return sizeof(COPYDATASTRUCT) + ((PCOPYDATASTRUCT)lParam)->cbData;
155
156 default:
157 assert(FALSE);
158 return 0;
159 break;
160 }
161 }
162 else
163 {
164 return MsgMemoryEntry->Size;
165 }
166 }
167
168 static FASTCALL NTSTATUS
169 PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
170 {
171 NCCALCSIZE_PARAMS *UnpackedNcCalcsize;
172 NCCALCSIZE_PARAMS *PackedNcCalcsize;
173 CREATESTRUCTW *UnpackedCs;
174 CREATESTRUCTW *PackedCs;
175 PUNICODE_STRING WindowName;
176 PUNICODE_STRING ClassName;
177 UINT Size;
178 PCHAR CsData;
179
180 *lParamPacked = lParam;
181 if (WM_NCCALCSIZE == Msg && wParam)
182 {
183 UnpackedNcCalcsize = (NCCALCSIZE_PARAMS *) lParam;
184 if (UnpackedNcCalcsize->lppos != (PWINDOWPOS) (UnpackedNcCalcsize + 1))
185 {
186 PackedNcCalcsize = ExAllocatePoolWithTag(PagedPool,
187 sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
188 TAG_MSG);
189 if (NULL == PackedNcCalcsize)
190 {
191 DPRINT1("Not enough memory to pack lParam\n");
192 return STATUS_NO_MEMORY;
193 }
194 RtlCopyMemory(PackedNcCalcsize, UnpackedNcCalcsize, sizeof(NCCALCSIZE_PARAMS));
195 PackedNcCalcsize->lppos = (PWINDOWPOS) (PackedNcCalcsize + 1);
196 RtlCopyMemory(PackedNcCalcsize->lppos, UnpackedNcCalcsize->lppos, sizeof(WINDOWPOS));
197 *lParamPacked = (LPARAM) PackedNcCalcsize;
198 }
199 }
200 else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
201 {
202 UnpackedCs = (CREATESTRUCTW *) lParam;
203 WindowName = (PUNICODE_STRING) UnpackedCs->lpszName;
204 ClassName = (PUNICODE_STRING) UnpackedCs->lpszClass;
205 Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
206 if (IS_ATOM(ClassName->Buffer))
207 {
208 Size += sizeof(WCHAR) + sizeof(ATOM);
209 }
210 else
211 {
212 Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
213 }
214 PackedCs = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
215 if (NULL == PackedCs)
216 {
217 DPRINT1("Not enough memory to pack lParam\n");
218 return STATUS_NO_MEMORY;
219 }
220 RtlCopyMemory(PackedCs, UnpackedCs, sizeof(CREATESTRUCTW));
221 CsData = (PCHAR) (PackedCs + 1);
222 PackedCs->lpszName = (LPCWSTR) (CsData - (PCHAR) PackedCs);
223 RtlCopyMemory(CsData, WindowName->Buffer, WindowName->Length);
224 CsData += WindowName->Length;
225 *((WCHAR *) CsData) = L'\0';
226 CsData += sizeof(WCHAR);
227 PackedCs->lpszClass = (LPCWSTR) (CsData - (PCHAR) PackedCs);
228 if (IS_ATOM(ClassName->Buffer))
229 {
230 *((WCHAR *) CsData) = L'A';
231 CsData += sizeof(WCHAR);
232 *((ATOM *) CsData) = (ATOM)(DWORD_PTR) ClassName->Buffer;
233 CsData += sizeof(ATOM);
234 }
235 else
236 {
237 *((WCHAR *) CsData) = L'S';
238 CsData += sizeof(WCHAR);
239 RtlCopyMemory(CsData, ClassName->Buffer, ClassName->Length);
240 CsData += ClassName->Length;
241 *((WCHAR *) CsData) = L'\0';
242 CsData += sizeof(WCHAR);
243 }
244 ASSERT(CsData == (PCHAR) PackedCs + Size);
245 *lParamPacked = (LPARAM) PackedCs;
246 }
247
248 return STATUS_SUCCESS;
249 }
250
251 static FASTCALL NTSTATUS
252 UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
253 {
254 NCCALCSIZE_PARAMS *UnpackedParams;
255 NCCALCSIZE_PARAMS *PackedParams;
256 PWINDOWPOS UnpackedWindowPos;
257
258 if (lParamPacked == lParam)
259 {
260 return STATUS_SUCCESS;
261 }
262
263 if (WM_NCCALCSIZE == Msg && wParam)
264 {
265 PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
266 UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
267 UnpackedWindowPos = UnpackedParams->lppos;
268 RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
269 UnpackedParams->lppos = UnpackedWindowPos;
270 RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
271 ExFreePool((PVOID) lParamPacked);
272
273 return STATUS_SUCCESS;
274 }
275 else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
276 {
277 ExFreePool((PVOID) lParamPacked);
278
279 return STATUS_SUCCESS;
280 }
281
282 ASSERT(FALSE);
283
284 return STATUS_INVALID_PARAMETER;
285 }
286
287
288 LRESULT STDCALL
289 NtUserDispatchMessage(PNTUSERDISPATCHMESSAGEINFO UnsafeMsgInfo)
290 {
291 NTSTATUS Status;
292 NTUSERDISPATCHMESSAGEINFO MsgInfo;
293 PWINDOW_OBJECT WindowObject;
294 LRESULT Result = TRUE;
295
296 Status = MmCopyFromCaller(&MsgInfo, UnsafeMsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
297 if (! NT_SUCCESS(Status))
298 {
299 SetLastNtError(Status);
300 return 0;
301 }
302
303 /* Process timer messages. */
304 if (WM_TIMER == MsgInfo.Msg.message && 0 != MsgInfo.Msg.lParam)
305 {
306 LARGE_INTEGER LargeTickCount;
307 /* FIXME: Call hooks. */
308
309 /* FIXME: Check for continuing validity of timer. */
310
311 MsgInfo.HandledByKernel = FALSE;
312 KeQueryTickCount(&LargeTickCount);
313 MsgInfo.Proc = (WNDPROC) MsgInfo.Msg.lParam;
314 MsgInfo.Msg.lParam = (LPARAM)LargeTickCount.u.LowPart;
315 }
316 else if (NULL == MsgInfo.Msg.hwnd)
317 {
318 MsgInfo.HandledByKernel = TRUE;
319 Result = 0;
320 }
321 else
322 {
323 /* Get the window object. */
324 WindowObject = IntGetWindowObject(MsgInfo.Msg.hwnd);
325 if (NULL == WindowObject)
326 {
327 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
328 MsgInfo.HandledByKernel = TRUE;
329 Result = 0;
330 }
331 else
332 {
333 if (WindowObject->OwnerThread != PsGetCurrentThread())
334 {
335 IntReleaseWindowObject(WindowObject);
336 DPRINT1("Window doesn't belong to the calling thread!\n");
337 MsgInfo.HandledByKernel = TRUE;
338 Result = 0;
339 }
340 else
341 {
342 /* FIXME: Call hook procedures. */
343
344 MsgInfo.HandledByKernel = FALSE;
345 Result = 0;
346 if (0xFFFF0000 != ((DWORD) WindowObject->WndProcW & 0xFFFF0000))
347 {
348 if (0xFFFF0000 != ((DWORD) WindowObject->WndProcA & 0xFFFF0000))
349 {
350 /* Both Unicode and Ansi winprocs are real, use whatever
351 usermode prefers */
352 MsgInfo.Proc = (MsgInfo.Ansi ? WindowObject->WndProcA
353 : WindowObject->WndProcW);
354 }
355 else
356 {
357 /* Real Unicode winproc */
358 MsgInfo.Ansi = FALSE;
359 MsgInfo.Proc = WindowObject->WndProcW;
360 }
361 }
362 else
363 {
364 /* Must have real Ansi winproc */
365 MsgInfo.Ansi = TRUE;
366 MsgInfo.Proc = WindowObject->WndProcA;
367 }
368 }
369 IntReleaseWindowObject(WindowObject);
370 }
371 }
372 Status = MmCopyToCaller(UnsafeMsgInfo, &MsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
373 if (! NT_SUCCESS(Status))
374 {
375 SetLastNtError(Status);
376 return 0;
377 }
378
379 return Result;
380 }
381
382
383 BOOL STDCALL
384 NtUserTranslateMessage(LPMSG lpMsg,
385 HKL dwhkl)
386 {
387 NTSTATUS Status;
388 MSG SafeMsg;
389
390 Status = MmCopyFromCaller(&SafeMsg, lpMsg, sizeof(MSG));
391 if(!NT_SUCCESS(Status))
392 {
393 SetLastNtError(Status);
394 return FALSE;
395 }
396
397 return IntTranslateKbdMessage(&SafeMsg, dwhkl);
398 }
399
400
401 VOID FASTCALL
402 IntSendHitTestMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
403 {
404 if(!Msg->hwnd || ThreadQueue->CaptureWindow)
405 {
406 return;
407 }
408
409 switch(Msg->message)
410 {
411 case WM_MOUSEMOVE:
412 {
413 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
414 break;
415 }
416 case WM_NCMOUSEMOVE:
417 {
418 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
419 break;
420 }
421 case WM_LBUTTONDOWN:
422 case WM_MBUTTONDOWN:
423 case WM_RBUTTONDOWN:
424 case WM_XBUTTONDOWN:
425 case WM_LBUTTONDBLCLK:
426 case WM_MBUTTONDBLCLK:
427 case WM_RBUTTONDBLCLK:
428 case WM_XBUTTONDBLCLK:
429 {
430 WPARAM wParam;
431 PSYSTEM_CURSORINFO CurInfo;
432
433 if(!IntGetWindowStationObject(InputWindowStation))
434 {
435 break;
436 }
437 CurInfo = IntGetSysCursorInfo(InputWindowStation);
438 wParam = (WPARAM)(CurInfo->ButtonsDown);
439 ObDereferenceObject(InputWindowStation);
440
441 IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam);
442 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
443 break;
444 }
445 case WM_NCLBUTTONDOWN:
446 case WM_NCMBUTTONDOWN:
447 case WM_NCRBUTTONDOWN:
448 case WM_NCXBUTTONDOWN:
449 case WM_NCLBUTTONDBLCLK:
450 case WM_NCMBUTTONDBLCLK:
451 case WM_NCRBUTTONDBLCLK:
452 case WM_NCXBUTTONDBLCLK:
453 {
454 IntSendMessage(Msg->hwnd, WM_NCMOUSEMOVE, (WPARAM)Msg->wParam, Msg->lParam);
455 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
456 break;
457 }
458 }
459 }
460
461 BOOL FASTCALL
462 IntActivateWindowMouse(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, PWINDOW_OBJECT MsgWindow,
463 USHORT *HitTest)
464 {
465 ULONG Result;
466
467 if(*HitTest == (USHORT)HTTRANSPARENT)
468 {
469 /* eat the message, search again! */
470 return TRUE;
471 }
472
473 Result = IntSendMessage(MsgWindow->Self, WM_MOUSEACTIVATE, (WPARAM)NtUserGetParent(MsgWindow->Self), (LPARAM)MAKELONG(*HitTest, Msg->message));
474 switch (Result)
475 {
476 case MA_NOACTIVATEANDEAT:
477 return TRUE;
478 case MA_NOACTIVATE:
479 break;
480 case MA_ACTIVATEANDEAT:
481 IntMouseActivateWindow(MsgWindow);
482 return TRUE;
483 default:
484 /* MA_ACTIVATE */
485 IntMouseActivateWindow(MsgWindow);
486 break;
487 }
488
489 return FALSE;
490 }
491
492 BOOL FASTCALL
493 IntTranslateMouseMessage(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, USHORT *HitTest, BOOL Remove)
494 {
495 PWINDOW_OBJECT Window;
496
497 if(!(Window = IntGetWindowObject(Msg->hwnd)))
498 {
499 /* let's just eat the message?! */
500 return TRUE;
501 }
502
503 if(ThreadQueue == Window->MessageQueue &&
504 ThreadQueue->CaptureWindow != Window->Self)
505 {
506 /* only send WM_NCHITTEST messages if we're not capturing the window! */
507 *HitTest = IntSendMessage(Window->Self, WM_NCHITTEST, 0,
508 MAKELONG(Msg->pt.x, Msg->pt.y));
509
510 if(*HitTest == (USHORT)HTTRANSPARENT)
511 {
512 PWINDOW_OBJECT DesktopWindow;
513 HWND hDesktop = IntGetDesktopWindow();
514
515 if((DesktopWindow = IntGetWindowObject(hDesktop)))
516 {
517 PWINDOW_OBJECT Wnd;
518
519 WinPosWindowFromPoint(DesktopWindow, Window->MessageQueue, &Msg->pt, &Wnd);
520 if(Wnd)
521 {
522 if(Wnd != Window)
523 {
524 /* post the message to the other window */
525 Msg->hwnd = Wnd->Self;
526 if(!(Wnd->Status & WINDOWSTATUS_DESTROYING))
527 {
528 MsqPostMessage(Wnd->MessageQueue, Msg, FALSE,
529 Msg->message == WM_MOUSEMOVE ? QS_MOUSEMOVE :
530 QS_MOUSEBUTTON);
531 }
532
533 /* eat the message */
534 IntReleaseWindowObject(Wnd);
535 IntReleaseWindowObject(Window);
536 IntReleaseWindowObject(DesktopWindow);
537 return TRUE;
538 }
539 IntReleaseWindowObject(Wnd);
540 }
541
542 IntReleaseWindowObject(DesktopWindow);
543 }
544 }
545 }
546 else
547 {
548 *HitTest = HTCLIENT;
549 }
550
551 if(IS_BTN_MESSAGE(Msg->message, DOWN))
552 {
553 /* generate double click messages, if necessary */
554 if ((((*HitTest) != HTCLIENT) ||
555 (IntGetClassLong(Window, GCL_STYLE, FALSE) & CS_DBLCLKS)) &&
556 MsqIsDblClk(Msg, Remove))
557 {
558 Msg->message += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
559 }
560 }
561
562 if(Msg->message != WM_MOUSEWHEEL)
563 {
564
565 if ((*HitTest) != HTCLIENT)
566 {
567 Msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
568 if((Msg->message == WM_NCRBUTTONUP) &&
569 (((*HitTest) == HTCAPTION) || ((*HitTest) == HTSYSMENU)))
570 {
571 Msg->message = WM_CONTEXTMENU;
572 Msg->wParam = (WPARAM)Window->Self;
573 }
574 else
575 {
576 Msg->wParam = *HitTest;
577 }
578 Msg->lParam = MAKELONG(Msg->pt.x, Msg->pt.y);
579 }
580 else if(ThreadQueue->MoveSize == NULL &&
581 ThreadQueue->MenuOwner == NULL)
582 {
583 /* NOTE: Msg->pt should remain in screen coordinates. -- FiN */
584 Msg->lParam = MAKELONG(
585 Msg->pt.x - (WORD)Window->ClientRect.left,
586 Msg->pt.y - (WORD)Window->ClientRect.top);
587 }
588 }
589
590 IntReleaseWindowObject(Window);
591 return FALSE;
592 }
593
594
595 /*
596 * Internal version of PeekMessage() doing all the work
597 */
598 BOOL FASTCALL
599 IntPeekMessage(PUSER_MESSAGE Msg,
600 HWND Wnd,
601 UINT MsgFilterMin,
602 UINT MsgFilterMax,
603 UINT RemoveMsg)
604 {
605 LARGE_INTEGER LargeTickCount;
606 PUSER_MESSAGE_QUEUE ThreadQueue;
607 PUSER_MESSAGE Message;
608 BOOL Present, RemoveMessages;
609
610 /* The queues and order in which they are checked are documented in the MSDN
611 article on GetMessage() */
612
613 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
614
615 /* Inspect RemoveMsg flags */
616 /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
617 RemoveMessages = RemoveMsg & PM_REMOVE;
618
619 CheckMessages:
620
621 Present = FALSE;
622
623 KeQueryTickCount(&LargeTickCount);
624 ThreadQueue->LastMsgRead = LargeTickCount.u.LowPart;
625
626 /* Dispatch sent messages here. */
627 while (MsqDispatchOneSentMessage(ThreadQueue));
628
629 /* Now look for a quit message. */
630
631 if (ThreadQueue->QuitPosted)
632 {
633 /* According to the PSDK, WM_QUIT messages are always returned, regardless
634 of the filter specified */
635 Msg->Msg.hwnd = NULL;
636 Msg->Msg.message = WM_QUIT;
637 Msg->Msg.wParam = ThreadQueue->QuitExitCode;
638 Msg->Msg.lParam = 0;
639 Msg->FreeLParam = FALSE;
640 if (RemoveMessages)
641 {
642 ThreadQueue->QuitPosted = FALSE;
643 }
644 return TRUE;
645 }
646
647 /* Now check for normal messages. */
648 Present = MsqFindMessage(ThreadQueue,
649 FALSE,
650 RemoveMessages,
651 Wnd,
652 MsgFilterMin,
653 MsgFilterMax,
654 &Message);
655 if (Present)
656 {
657 RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
658 if (RemoveMessages)
659 {
660 MsqDestroyMessage(Message);
661 }
662 goto MessageFound;
663 }
664
665 /* Check for hardware events. */
666 Present = MsqFindMessage(ThreadQueue,
667 TRUE,
668 RemoveMessages,
669 Wnd,
670 MsgFilterMin,
671 MsgFilterMax,
672 &Message);
673 if (Present)
674 {
675 RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
676 if (RemoveMessages)
677 {
678 MsqDestroyMessage(Message);
679 }
680 goto MessageFound;
681 }
682
683 /* Check for sent messages again. */
684 while (MsqDispatchOneSentMessage(ThreadQueue));
685
686 /* Check for paint messages. */
687 if (IntGetPaintMessage(Wnd, MsgFilterMin, MsgFilterMax, PsGetWin32Thread(), &Msg->Msg, RemoveMessages))
688 {
689 Msg->FreeLParam = FALSE;
690 return TRUE;
691 }
692
693 /* FIXME - get WM_(SYS)TIMER messages */
694
695 if(Present)
696 {
697 MessageFound:
698
699 if(RemoveMessages)
700 {
701 PWINDOW_OBJECT MsgWindow = NULL;;
702
703 if(Msg->Msg.hwnd && (MsgWindow = IntGetWindowObject(Msg->Msg.hwnd)) &&
704 Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST)
705 {
706 USHORT HitTest;
707
708 if(IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, TRUE))
709 /* FIXME - check message filter again, if the message doesn't match anymore,
710 search again */
711 {
712 IntReleaseWindowObject(MsgWindow);
713 /* eat the message, search again */
714 goto CheckMessages;
715 }
716 if(ThreadQueue->CaptureWindow == NULL)
717 {
718 IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
719 if((Msg->Msg.message != WM_MOUSEMOVE && Msg->Msg.message != WM_NCMOUSEMOVE) &&
720 IS_BTN_MESSAGE(Msg->Msg.message, DOWN) &&
721 IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest))
722 {
723 IntReleaseWindowObject(MsgWindow);
724 /* eat the message, search again */
725 goto CheckMessages;
726 }
727 }
728 }
729 else
730 {
731 IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
732 }
733
734 if(MsgWindow)
735 {
736 IntReleaseWindowObject(MsgWindow);
737 }
738
739 return TRUE;
740 }
741
742 USHORT HitTest;
743 if((Msg->Msg.hwnd && Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST) &&
744 IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, FALSE))
745 /* FIXME - check message filter again, if the message doesn't match anymore,
746 search again */
747 {
748 /* eat the message, search again */
749 goto CheckMessages;
750 }
751
752 return TRUE;
753 }
754
755 return Present;
756 }
757
758 BOOL STDCALL
759 NtUserPeekMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
760 HWND Wnd,
761 UINT MsgFilterMin,
762 UINT MsgFilterMax,
763 UINT RemoveMsg)
764 {
765 NTSTATUS Status;
766 BOOL Present;
767 NTUSERGETMESSAGEINFO Info;
768 PWINDOW_OBJECT Window;
769 PMSGMEMORY MsgMemoryEntry;
770 PVOID UserMem;
771 UINT Size;
772 USER_MESSAGE Msg;
773
774 /* Validate input */
775 if (NULL != Wnd)
776 {
777 Window = IntGetWindowObject(Wnd);
778 if (NULL == Window)
779 {
780 Wnd = NULL;
781 }
782 else
783 {
784 IntReleaseWindowObject(Window);
785 }
786 }
787
788 if (MsgFilterMax < MsgFilterMin)
789 {
790 MsgFilterMin = 0;
791 MsgFilterMax = 0;
792 }
793
794 Present = IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
795 if (Present)
796 {
797 Info.Msg = Msg.Msg;
798 /* See if this message type is present in the table */
799 MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
800 if (NULL == MsgMemoryEntry)
801 {
802 /* Not present, no copying needed */
803 Info.LParamSize = 0;
804 }
805 else
806 {
807 /* Determine required size */
808 Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
809 Info.Msg.lParam);
810 /* Allocate required amount of user-mode memory */
811 Info.LParamSize = Size;
812 UserMem = NULL;
813 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
814 &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
815 if (! NT_SUCCESS(Status))
816 {
817 SetLastNtError(Status);
818 return (BOOL) -1;
819 }
820 /* Transfer lParam data to user-mode mem */
821 Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
822 if (! NT_SUCCESS(Status))
823 {
824 ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
825 &Info.LParamSize, MEM_DECOMMIT);
826 SetLastNtError(Status);
827 return (BOOL) -1;
828 }
829 Info.Msg.lParam = (LPARAM) UserMem;
830 }
831 if (Msg.FreeLParam && 0 != Msg.Msg.lParam)
832 {
833 ExFreePool((void *) Msg.Msg.lParam);
834 }
835 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
836 if (! NT_SUCCESS(Status))
837 {
838 SetLastNtError(Status);
839 return (BOOL) -1;
840 }
841 }
842
843 return Present;
844 }
845
846 static BOOL FASTCALL
847 IntWaitMessage(HWND Wnd,
848 UINT MsgFilterMin,
849 UINT MsgFilterMax)
850 {
851 PUSER_MESSAGE_QUEUE ThreadQueue;
852 NTSTATUS Status;
853 USER_MESSAGE Msg;
854
855 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
856
857 do
858 {
859 if (IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
860 {
861 return TRUE;
862 }
863
864 /* Nothing found. Wait for new messages. */
865 Status = MsqWaitForNewMessages(ThreadQueue);
866 }
867 while (STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63);
868
869 SetLastNtError(Status);
870
871 return FALSE;
872 }
873
874 BOOL STDCALL
875 NtUserGetMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
876 HWND Wnd,
877 UINT MsgFilterMin,
878 UINT MsgFilterMax)
879 /*
880 * FUNCTION: Get a message from the calling thread's message queue.
881 * ARGUMENTS:
882 * UnsafeMsg - Pointer to the structure which receives the returned message.
883 * Wnd - Window whose messages are to be retrieved.
884 * MsgFilterMin - Integer value of the lowest message value to be
885 * retrieved.
886 * MsgFilterMax - Integer value of the highest message value to be
887 * retrieved.
888 */
889 {
890 BOOL GotMessage;
891 NTUSERGETMESSAGEINFO Info;
892 NTSTATUS Status;
893 PWINDOW_OBJECT Window;
894 PMSGMEMORY MsgMemoryEntry;
895 PVOID UserMem;
896 UINT Size;
897 USER_MESSAGE Msg;
898
899 /* Validate input */
900 if (NULL != Wnd)
901 {
902 Window = IntGetWindowObject(Wnd);
903 if(!Window)
904 Wnd = NULL;
905 else
906 IntReleaseWindowObject(Window);
907 }
908 if (MsgFilterMax < MsgFilterMin)
909 {
910 MsgFilterMin = 0;
911 MsgFilterMax = 0;
912 }
913
914 do
915 {
916 GotMessage = IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
917 if (GotMessage)
918 {
919 Info.Msg = Msg.Msg;
920 /* See if this message type is present in the table */
921 MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
922 if (NULL == MsgMemoryEntry)
923 {
924 /* Not present, no copying needed */
925 Info.LParamSize = 0;
926 }
927 else
928 {
929 /* Determine required size */
930 Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
931 Info.Msg.lParam);
932 /* Allocate required amount of user-mode memory */
933 Info.LParamSize = Size;
934 UserMem = NULL;
935 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
936 &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
937
938 if (! NT_SUCCESS(Status))
939 {
940 SetLastNtError(Status);
941 return (BOOL) -1;
942 }
943 /* Transfer lParam data to user-mode mem */
944 Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
945 if (! NT_SUCCESS(Status))
946 {
947 ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
948 &Info.LParamSize, MEM_DECOMMIT);
949 SetLastNtError(Status);
950 return (BOOL) -1;
951 }
952 Info.Msg.lParam = (LPARAM) UserMem;
953 }
954 if (Msg.FreeLParam && 0 != Msg.Msg.lParam)
955 {
956 ExFreePool((void *) Msg.Msg.lParam);
957 }
958 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
959 if (! NT_SUCCESS(Status))
960 {
961 SetLastNtError(Status);
962 return (BOOL) -1;
963 }
964 }
965 else if (! IntWaitMessage(Wnd, MsgFilterMin, MsgFilterMax))
966 {
967 return (BOOL) -1;
968 }
969 }
970 while (! GotMessage);
971
972 return WM_QUIT != Info.Msg.message;
973 }
974
975 DWORD
976 STDCALL
977 NtUserMessageCall(
978 DWORD Unknown0,
979 DWORD Unknown1,
980 DWORD Unknown2,
981 DWORD Unknown3,
982 DWORD Unknown4,
983 DWORD Unknown5,
984 DWORD Unknown6)
985 {
986 UNIMPLEMENTED
987
988 return 0;
989 }
990
991 static NTSTATUS FASTCALL
992 CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg, PMSGMEMORY MsgMemoryEntry)
993 {
994 NTSTATUS Status;
995
996 PVOID KernelMem;
997 UINT Size;
998
999 *KernelModeMsg = *UserModeMsg;
1000
1001 /* See if this message type is present in the table */
1002 if (NULL == MsgMemoryEntry)
1003 {
1004 /* Not present, no copying needed */
1005 return STATUS_SUCCESS;
1006 }
1007
1008 /* Determine required size */
1009 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
1010
1011 if (0 != Size)
1012 {
1013 /* Allocate kernel mem */
1014 KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
1015 if (NULL == KernelMem)
1016 {
1017 DPRINT1("Not enough memory to copy message to kernel mem\n");
1018 return STATUS_NO_MEMORY;
1019 }
1020 KernelModeMsg->lParam = (LPARAM) KernelMem;
1021
1022 /* Copy data if required */
1023 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
1024 {
1025 Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
1026 if (! NT_SUCCESS(Status))
1027 {
1028 DPRINT1("Failed to copy message to kernel: invalid usermode buffer\n");
1029 ExFreePool(KernelMem);
1030 return Status;
1031 }
1032 }
1033 else
1034 {
1035 /* Make sure we don't pass any secrets to usermode */
1036 RtlZeroMemory(KernelMem, Size);
1037 }
1038 }
1039 else
1040 {
1041 KernelModeMsg->lParam = 0;
1042 }
1043
1044 return STATUS_SUCCESS;
1045 }
1046
1047 static NTSTATUS FASTCALL
1048 CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
1049 {
1050 NTSTATUS Status;
1051 PMSGMEMORY MsgMemoryEntry;
1052 UINT Size;
1053
1054 /* See if this message type is present in the table */
1055 MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
1056 if (NULL == MsgMemoryEntry)
1057 {
1058 /* Not present, no copying needed */
1059 return STATUS_SUCCESS;
1060 }
1061
1062 /* Determine required size */
1063 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
1064
1065 if (0 != Size)
1066 {
1067 /* Copy data if required */
1068 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
1069 {
1070 Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
1071 if (! NT_SUCCESS(Status))
1072 {
1073 DPRINT1("Failed to copy message from kernel: invalid usermode buffer\n");
1074 ExFreePool((PVOID) KernelModeMsg->lParam);
1075 return Status;
1076 }
1077 }
1078
1079 ExFreePool((PVOID) KernelModeMsg->lParam);
1080 }
1081
1082 return STATUS_SUCCESS;
1083 }
1084
1085 BOOL STDCALL
1086 NtUserPostMessage(HWND Wnd,
1087 UINT Msg,
1088 WPARAM wParam,
1089 LPARAM lParam)
1090 {
1091 PWINDOW_OBJECT Window;
1092 MSG UserModeMsg, KernelModeMsg;
1093 LARGE_INTEGER LargeTickCount;
1094 NTSTATUS Status;
1095 PMSGMEMORY MsgMemoryEntry;
1096
1097 if (WM_QUIT == Msg)
1098 {
1099 MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue, wParam);
1100 }
1101 else if (Wnd == HWND_BROADCAST)
1102 {
1103 HWND *List;
1104 PWINDOW_OBJECT DesktopWindow;
1105 ULONG i;
1106
1107 DesktopWindow = IntGetWindowObject(IntGetDesktopWindow());
1108 List = IntWinListChildren(DesktopWindow);
1109 IntReleaseWindowObject(DesktopWindow);
1110 if (List != NULL)
1111 {
1112 for (i = 0; List[i]; i++)
1113 NtUserPostMessage(List[i], Msg, wParam, lParam);
1114 ExFreePool(List);
1115 }
1116 }
1117 else
1118 {
1119 PSYSTEM_CURSORINFO CurInfo;
1120 Window = IntGetWindowObject(Wnd);
1121 if (NULL == Window)
1122 {
1123 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1124 return FALSE;
1125 }
1126 if(Window->Status & WINDOWSTATUS_DESTROYING)
1127 {
1128 IntReleaseWindowObject(Window);
1129 DPRINT1("Attempted to post message to window 0x%x that is being destroyed!\n", Wnd);
1130 /* FIXME - last error code? */
1131 return FALSE;
1132 }
1133
1134 UserModeMsg.hwnd = Wnd;
1135 UserModeMsg.message = Msg;
1136 UserModeMsg.wParam = wParam;
1137 UserModeMsg.lParam = lParam;
1138 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1139 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1140 if (! NT_SUCCESS(Status))
1141 {
1142 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1143 return FALSE;
1144 }
1145 CurInfo = IntGetSysCursorInfo(PsGetWin32Thread()->Desktop->WindowStation);
1146 KernelModeMsg.pt.x = CurInfo->x;
1147 KernelModeMsg.pt.y = CurInfo->y;
1148 KeQueryTickCount(&LargeTickCount);
1149 KernelModeMsg.time = LargeTickCount.u.LowPart;
1150 MsqPostMessage(Window->MessageQueue, &KernelModeMsg,
1151 NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
1152 QS_POSTMESSAGE);
1153 IntReleaseWindowObject(Window);
1154 }
1155
1156 return TRUE;
1157 }
1158
1159 BOOL STDCALL
1160 NtUserPostThreadMessage(DWORD idThread,
1161 UINT Msg,
1162 WPARAM wParam,
1163 LPARAM lParam)
1164 {
1165 MSG UserModeMsg, KernelModeMsg;
1166 PETHREAD peThread;
1167 PW32THREAD pThread;
1168 NTSTATUS Status;
1169 PMSGMEMORY MsgMemoryEntry;
1170
1171 Status = PsLookupThreadByThreadId((void *)idThread,&peThread);
1172
1173 if( Status == STATUS_SUCCESS ) {
1174 pThread = peThread->Tcb.Win32Thread;
1175 if( !pThread || !pThread->MessageQueue )
1176 {
1177 ObDereferenceObject( peThread );
1178 return FALSE;
1179 }
1180
1181 UserModeMsg.hwnd = NULL;
1182 UserModeMsg.message = Msg;
1183 UserModeMsg.wParam = wParam;
1184 UserModeMsg.lParam = lParam;
1185 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1186 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1187 if (! NT_SUCCESS(Status))
1188 {
1189 ObDereferenceObject( peThread );
1190 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1191 return FALSE;
1192 }
1193 MsqPostMessage(pThread->MessageQueue, &KernelModeMsg,
1194 NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
1195 QS_POSTMESSAGE);
1196 ObDereferenceObject( peThread );
1197 return TRUE;
1198 } else {
1199 SetLastNtError( Status );
1200 return FALSE;
1201 }
1202 }
1203
1204 DWORD STDCALL
1205 NtUserQuerySendMessage(DWORD Unknown0)
1206 {
1207 UNIMPLEMENTED;
1208
1209 return 0;
1210 }
1211
1212 LRESULT FASTCALL
1213 IntSendMessage(HWND hWnd,
1214 UINT Msg,
1215 WPARAM wParam,
1216 LPARAM lParam)
1217 {
1218 ULONG_PTR Result = 0;
1219 if(IntSendMessageTimeout(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
1220 {
1221 return (LRESULT)Result;
1222 }
1223 return 0;
1224 }
1225
1226 static LRESULT FASTCALL
1227 IntSendMessageTimeoutSingle(HWND hWnd,
1228 UINT Msg,
1229 WPARAM wParam,
1230 LPARAM lParam,
1231 UINT uFlags,
1232 UINT uTimeout,
1233 ULONG_PTR *uResult)
1234 {
1235 ULONG_PTR Result;
1236 NTSTATUS Status;
1237 PWINDOW_OBJECT Window;
1238 PMSGMEMORY MsgMemoryEntry;
1239 INT lParamBufferSize;
1240 LPARAM lParamPacked;
1241 PW32THREAD Win32Thread;
1242
1243 /* FIXME: Call hooks. */
1244 Window = IntGetWindowObject(hWnd);
1245 if (!Window)
1246 {
1247 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1248 return FALSE;
1249 }
1250
1251 Win32Thread = PsGetWin32Thread();
1252
1253 if (NULL != Win32Thread &&
1254 Window->MessageQueue == Win32Thread->MessageQueue)
1255 {
1256 if (Win32Thread->IsExiting)
1257 {
1258 /* Never send messages to exiting threads */
1259 IntReleaseWindowObject(Window);
1260 return FALSE;
1261 }
1262
1263 /* See if this message type is present in the table */
1264 MsgMemoryEntry = FindMsgMemory(Msg);
1265 if (NULL == MsgMemoryEntry)
1266 {
1267 lParamBufferSize = -1;
1268 }
1269 else
1270 {
1271 lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1272 }
1273
1274 if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam)))
1275 {
1276 IntReleaseWindowObject(Window);
1277 DPRINT1("Failed to pack message parameters\n");
1278 return FALSE;
1279 }
1280 if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
1281 {
1282 Result = (ULONG_PTR)IntCallWindowProc(Window->WndProcW, FALSE, hWnd, Msg, wParam,
1283 lParamPacked,lParamBufferSize);
1284 }
1285 else
1286 {
1287 Result = (ULONG_PTR)IntCallWindowProc(Window->WndProcA, TRUE, hWnd, Msg, wParam,
1288 lParamPacked,lParamBufferSize);
1289 }
1290
1291 if(uResult)
1292 {
1293 *uResult = Result;
1294 }
1295
1296 if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam)))
1297 {
1298 IntReleaseWindowObject(Window);
1299 DPRINT1("Failed to unpack message parameters\n");
1300 return TRUE;
1301 }
1302
1303 IntReleaseWindowObject(Window);
1304 return TRUE;
1305 }
1306
1307 if(uFlags & SMTO_ABORTIFHUNG && MsqIsHung(Window->MessageQueue))
1308 {
1309 IntReleaseWindowObject(Window);
1310 /* FIXME - Set a LastError? */
1311 return FALSE;
1312 }
1313
1314 if(Window->Status & WINDOWSTATUS_DESTROYING)
1315 {
1316 IntReleaseWindowObject(Window);
1317 /* FIXME - last error? */
1318 DPRINT1("Attempted to send message to window 0x%x that is being destroyed!\n", hWnd);
1319 return FALSE;
1320 }
1321
1322 Status = MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam,
1323 uTimeout, (uFlags & SMTO_BLOCK), uResult);
1324 IntReleaseWindowObject(Window);
1325 if (STATUS_TIMEOUT == Status)
1326 {
1327 /* MSDN says GetLastError() should return 0 after timeout */
1328 SetLastWin32Error(0);
1329 return FALSE;
1330 }
1331 else if (! NT_SUCCESS(Status))
1332 {
1333 SetLastNtError(Status);
1334 return FALSE;
1335 }
1336
1337 return TRUE;
1338 }
1339
1340 LRESULT FASTCALL
1341 IntSendMessageTimeout(HWND hWnd,
1342 UINT Msg,
1343 WPARAM wParam,
1344 LPARAM lParam,
1345 UINT uFlags,
1346 UINT uTimeout,
1347 ULONG_PTR *uResult)
1348 {
1349 PWINDOW_OBJECT DesktopWindow;
1350 HWND *Children;
1351 HWND *Child;
1352
1353 if (HWND_BROADCAST != hWnd)
1354 {
1355 return IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1356 }
1357
1358 DesktopWindow = IntGetWindowObject(IntGetDesktopWindow());
1359 if (NULL == DesktopWindow)
1360 {
1361 SetLastWin32Error(ERROR_INTERNAL_ERROR);
1362 return 0;
1363 }
1364 Children = IntWinListChildren(DesktopWindow);
1365 IntReleaseWindowObject(DesktopWindow);
1366 if (NULL == Children)
1367 {
1368 return 0;
1369 }
1370
1371 for (Child = Children; NULL != *Child; Child++)
1372 {
1373 IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1374 }
1375
1376 ExFreePool(Children);
1377
1378 return (LRESULT) TRUE;
1379 }
1380
1381
1382 /* This function posts a message if the destination's message queue belongs to
1383 another thread, otherwise it sends the message. It does not support broadcast
1384 messages! */
1385 LRESULT FASTCALL
1386 IntPostOrSendMessage(HWND hWnd,
1387 UINT Msg,
1388 WPARAM wParam,
1389 LPARAM lParam)
1390 {
1391 ULONG_PTR Result;
1392 PWINDOW_OBJECT Window;
1393
1394 if(hWnd == HWND_BROADCAST)
1395 {
1396 return 0;
1397 }
1398
1399 Window = IntGetWindowObject(hWnd);
1400 if(!Window)
1401 {
1402 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1403 return 0;
1404 }
1405
1406 if(Window->MessageQueue != PsGetWin32Thread()->MessageQueue)
1407 {
1408 Result = NtUserPostMessage(hWnd, Msg, wParam, lParam);
1409 }
1410 else
1411 {
1412 if(!IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
1413 {
1414 Result = 0;
1415 }
1416 }
1417
1418 IntReleaseWindowObject(Window);
1419
1420 return (LRESULT)Result;
1421 }
1422
1423 LRESULT FASTCALL
1424 IntDoSendMessage(HWND Wnd,
1425 UINT Msg,
1426 WPARAM wParam,
1427 LPARAM lParam,
1428 PDOSENDMESSAGE dsm,
1429 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1430 {
1431 LRESULT Result = TRUE;
1432 NTSTATUS Status;
1433 PWINDOW_OBJECT Window;
1434 NTUSERSENDMESSAGEINFO Info;
1435 MSG UserModeMsg;
1436 MSG KernelModeMsg;
1437 PMSGMEMORY MsgMemoryEntry;
1438
1439 RtlZeroMemory(&Info, sizeof(NTUSERSENDMESSAGEINFO));
1440
1441 /* FIXME: Call hooks. */
1442 if (HWND_BROADCAST != Wnd)
1443 {
1444 Window = IntGetWindowObject(Wnd);
1445 if (NULL == Window)
1446 {
1447 /* Tell usermode to not touch this one */
1448 Info.HandledByKernel = TRUE;
1449 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1450 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
1451 return 0;
1452 }
1453 }
1454
1455 /* FIXME: Check for an exiting window. */
1456
1457 /* See if the current thread can handle the message */
1458 if (HWND_BROADCAST != Wnd && NULL != PsGetWin32Thread() &&
1459 Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
1460 {
1461 /* Gather the information usermode needs to call the window proc directly */
1462 Info.HandledByKernel = FALSE;
1463 if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
1464 {
1465 if (0xFFFF0000 != ((DWORD) Window->WndProcA & 0xFFFF0000))
1466 {
1467 /* Both Unicode and Ansi winprocs are real, see what usermode prefers */
1468 Status = MmCopyFromCaller(&(Info.Ansi), &(UnsafeInfo->Ansi),
1469 sizeof(BOOL));
1470 if (! NT_SUCCESS(Status))
1471 {
1472 Info.Ansi = ! Window->Unicode;
1473 }
1474 Info.Proc = (Info.Ansi ? Window->WndProcA : Window->WndProcW);
1475 }
1476 else
1477 {
1478 /* Real Unicode winproc */
1479 Info.Ansi = FALSE;
1480 Info.Proc = Window->WndProcW;
1481 }
1482 }
1483 else
1484 {
1485 /* Must have real Ansi winproc */
1486 Info.Ansi = TRUE;
1487 Info.Proc = Window->WndProcA;
1488 }
1489 IntReleaseWindowObject(Window);
1490 }
1491 else
1492 {
1493 /* Must be handled by other thread */
1494 if (HWND_BROADCAST != Wnd)
1495 {
1496 IntReleaseWindowObject(Window);
1497 }
1498 Info.HandledByKernel = TRUE;
1499 UserModeMsg.hwnd = Wnd;
1500 UserModeMsg.message = Msg;
1501 UserModeMsg.wParam = wParam;
1502 UserModeMsg.lParam = lParam;
1503 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1504 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1505 if (! NT_SUCCESS(Status))
1506 {
1507 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1508 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1509 return (dsm ? 0 : -1);
1510 }
1511 if(!dsm)
1512 {
1513 Result = IntSendMessage(KernelModeMsg.hwnd, KernelModeMsg.message,
1514 KernelModeMsg.wParam, KernelModeMsg.lParam);
1515 }
1516 else
1517 {
1518 Result = IntSendMessageTimeout(KernelModeMsg.hwnd, KernelModeMsg.message,
1519 KernelModeMsg.wParam, KernelModeMsg.lParam,
1520 dsm->uFlags, dsm->uTimeout, &dsm->Result);
1521 }
1522 Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
1523 if (! NT_SUCCESS(Status))
1524 {
1525 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1526 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1527 return(dsm ? 0 : -1);
1528 }
1529 }
1530
1531 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1532 if (! NT_SUCCESS(Status))
1533 {
1534 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1535 }
1536
1537 return (LRESULT)Result;
1538 }
1539
1540 LRESULT STDCALL
1541 NtUserSendMessageTimeout(HWND hWnd,
1542 UINT Msg,
1543 WPARAM wParam,
1544 LPARAM lParam,
1545 UINT uFlags,
1546 UINT uTimeout,
1547 ULONG_PTR *uResult,
1548 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1549 {
1550 DOSENDMESSAGE dsm;
1551 LRESULT Result;
1552
1553 dsm.uFlags = uFlags;
1554 dsm.uTimeout = uTimeout;
1555 Result = IntDoSendMessage(hWnd, Msg, wParam, lParam, &dsm, UnsafeInfo);
1556 if(uResult != NULL && Result != 0)
1557 {
1558 NTSTATUS Status;
1559
1560 Status = MmCopyToCaller(uResult, &dsm.Result, sizeof(ULONG_PTR));
1561 if(!NT_SUCCESS(Status))
1562 {
1563 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1564 return FALSE;
1565 }
1566 }
1567 return Result;
1568 }
1569
1570 LRESULT STDCALL
1571 NtUserSendMessage(HWND Wnd,
1572 UINT Msg,
1573 WPARAM wParam,
1574 LPARAM lParam,
1575 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1576 {
1577 return IntDoSendMessage(Wnd, Msg, wParam, lParam, NULL, UnsafeInfo);
1578 }
1579
1580 BOOL STDCALL
1581 NtUserSendMessageCallback(HWND hWnd,
1582 UINT Msg,
1583 WPARAM wParam,
1584 LPARAM lParam,
1585 SENDASYNCPROC lpCallBack,
1586 ULONG_PTR dwData)
1587 {
1588 UNIMPLEMENTED;
1589
1590 return 0;
1591 }
1592
1593 BOOL STDCALL
1594 NtUserSendNotifyMessage(HWND hWnd,
1595 UINT Msg,
1596 WPARAM wParam,
1597 LPARAM lParam)
1598 {
1599 UNIMPLEMENTED;
1600
1601 return 0;
1602 }
1603
1604 BOOL STDCALL
1605 NtUserWaitMessage(VOID)
1606 {
1607
1608 return IntWaitMessage(NULL, 0, 0);
1609 }
1610
1611 DWORD STDCALL
1612 NtUserGetQueueStatus(BOOL ClearChanges)
1613 {
1614 PUSER_MESSAGE_QUEUE Queue;
1615 DWORD Result;
1616
1617 Queue = PsGetWin32Thread()->MessageQueue;
1618
1619 IntLockMessageQueue(Queue);
1620
1621 Result = MAKELONG(Queue->QueueBits, Queue->ChangedBits);
1622 if (ClearChanges)
1623 {
1624 Queue->ChangedBits = 0;
1625 }
1626
1627 IntUnLockMessageQueue(Queue);
1628
1629 return Result;
1630 }
1631
1632 BOOL STDCALL
1633 IntInitMessagePumpHook()
1634 {
1635 PsGetCurrentThread()->Tcb.Win32Thread->MessagePumpHookValue++;
1636 return TRUE;
1637 }
1638
1639 BOOL STDCALL
1640 IntUninitMessagePumpHook()
1641 {
1642 if (PsGetCurrentThread()->Tcb.Win32Thread->MessagePumpHookValue <= 0)
1643 {
1644 return FALSE;
1645 }
1646 PsGetCurrentThread()->Tcb.Win32Thread->MessagePumpHookValue--;
1647 return TRUE;
1648 }
1649
1650 /* EOF */