added pool tags for better debugging
[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.50 2004/02/19 21:12:09 weiden 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 <ddk/ntddk.h>
33 #include <win32k/win32k.h>
34 #include <include/msgqueue.h>
35 #include <include/window.h>
36 #include <include/class.h>
37 #include <include/error.h>
38 #include <include/object.h>
39 #include <include/winsta.h>
40 #include <include/callback.h>
41 #include <include/painting.h>
42 #include <include/input.h>
43 #include <include/desktop.h>
44 #include <include/tags.h>
45 #include <internal/safe.h>
46
47 #define NDEBUG
48 #include <debug.h>
49
50 /* FUNCTIONS *****************************************************************/
51
52 NTSTATUS FASTCALL
53 IntInitMessageImpl(VOID)
54 {
55 return STATUS_SUCCESS;
56 }
57
58 NTSTATUS FASTCALL
59 IntCleanupMessageImpl(VOID)
60 {
61 return STATUS_SUCCESS;
62 }
63
64 LRESULT FASTCALL
65 IntDispatchMessage(MSG* Msg)
66 {
67 LRESULT Result;
68 PWINDOW_OBJECT WindowObject;
69 /* Process timer messages. */
70 if (Msg->message == WM_TIMER)
71 {
72 if (Msg->lParam)
73 {
74 LARGE_INTEGER LargeTickCount;
75 /* FIXME: Call hooks. */
76
77 /* FIXME: Check for continuing validity of timer. */
78
79 KeQueryTickCount(&LargeTickCount);
80 return IntCallWindowProc((WNDPROC)Msg->lParam,
81 FALSE,
82 Msg->hwnd,
83 Msg->message,
84 Msg->wParam,
85 (LPARAM)LargeTickCount.u.LowPart,
86 -1);
87 }
88 }
89
90 if( Msg->hwnd == 0 ) return 0;
91
92 /* Get the window object. */
93 WindowObject = IntGetWindowObject(Msg->hwnd);
94 if(!WindowObject)
95 {
96 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
97 return 0;
98 }
99 if(WindowObject->OwnerThread != PsGetCurrentThread())
100 {
101 IntReleaseWindowObject(WindowObject);
102 DPRINT("Window doesn't belong to the calling thread!\n");
103 return 0;
104 }
105 /* FIXME: Call hook procedures. */
106
107 /* Call the window procedure. */
108 if (0xFFFF0000 != ((DWORD) WindowObject->WndProcW & 0xFFFF0000))
109 {
110 Result = IntCallWindowProc(WindowObject->WndProcW,
111 FALSE,
112 Msg->hwnd,
113 Msg->message,
114 Msg->wParam,
115 Msg->lParam,
116 -1);
117 }
118 else
119 {
120 Result = IntCallWindowProc(WindowObject->WndProcA,
121 TRUE,
122 Msg->hwnd,
123 Msg->message,
124 Msg->wParam,
125 Msg->lParam,
126 -1);
127 }
128
129 IntReleaseWindowObject(WindowObject);
130
131 return Result;
132 }
133
134
135 LRESULT STDCALL
136 NtUserDispatchMessage(CONST MSG* UnsafeMsg)
137 {
138 NTSTATUS Status;
139 MSG Msg;
140
141 Status = MmCopyFromCaller(&Msg, (PVOID) UnsafeMsg, sizeof(MSG));
142 if (! NT_SUCCESS(Status))
143 {
144 SetLastNtError(Status);
145 return 0;
146 }
147
148 return IntDispatchMessage(&Msg);
149 }
150
151
152 BOOL STDCALL
153 NtUserTranslateMessage(LPMSG lpMsg,
154 HKL dwhkl)
155 {
156 NTSTATUS Status;
157 MSG SafeMsg;
158
159 Status = MmCopyFromCaller(&SafeMsg, lpMsg, sizeof(MSG));
160 if(!NT_SUCCESS(Status))
161 {
162 SetLastNtError(Status);
163 return FALSE;
164 }
165
166 return IntTranslateKbdMessage(&SafeMsg, dwhkl);
167 }
168
169
170 VOID FASTCALL
171 IntSendSpecialMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
172 {
173 if(!Msg->hwnd || ThreadQueue->CaptureWindow)
174 {
175 return;
176 }
177
178 switch(Msg->message)
179 {
180 case WM_MOUSEMOVE:
181 {
182 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
183 break;
184 }
185 case WM_NCMOUSEMOVE:
186 {
187 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
188 break;
189 }
190 case WM_LBUTTONDOWN:
191 case WM_MBUTTONDOWN:
192 case WM_RBUTTONDOWN:
193 case WM_XBUTTONDOWN:
194 case WM_LBUTTONDBLCLK:
195 case WM_MBUTTONDBLCLK:
196 case WM_RBUTTONDBLCLK:
197 case WM_XBUTTONDBLCLK:
198 {
199 WPARAM wParam;
200
201 if(!IntGetWindowStationObject(InputWindowStation))
202 {
203 break;
204 }
205 wParam = (WPARAM)InputWindowStation->SystemCursor.ButtonsDown;
206 ObDereferenceObject(InputWindowStation);
207
208 IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam);
209 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
210 break;
211 }
212 case WM_NCLBUTTONDOWN:
213 case WM_NCMBUTTONDOWN:
214 case WM_NCRBUTTONDOWN:
215 case WM_NCXBUTTONDOWN:
216 case WM_NCLBUTTONDBLCLK:
217 case WM_NCMBUTTONDBLCLK:
218 case WM_NCRBUTTONDBLCLK:
219 case WM_NCXBUTTONDBLCLK:
220 {
221 IntSendMessage(Msg->hwnd, WM_NCMOUSEMOVE, (WPARAM)Msg->wParam, Msg->lParam);
222 IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
223 break;
224 }
225 }
226 }
227
228 /*
229 * Internal version of PeekMessage() doing all the work
230 */
231 BOOL STDCALL
232 IntPeekMessage(LPMSG Msg,
233 HWND Wnd,
234 UINT MsgFilterMin,
235 UINT MsgFilterMax,
236 UINT RemoveMsg)
237 {
238 PUSER_MESSAGE_QUEUE ThreadQueue;
239 BOOLEAN Present;
240 PUSER_MESSAGE Message;
241 BOOLEAN RemoveMessages;
242
243 /* The queues and order in which they are checked are documented in the MSDN
244 article on GetMessage() */
245
246 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
247
248 /* Inspect RemoveMsg flags */
249 /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
250 RemoveMessages = RemoveMsg & PM_REMOVE;
251
252 /* Dispatch sent messages here. */
253 while (MsqDispatchOneSentMessage(ThreadQueue))
254 ;
255
256 /* Now look for a quit message. */
257 /* FIXME: WINE checks the message number filter here. */
258 if (ThreadQueue->QuitPosted)
259 {
260 Msg->hwnd = Wnd;
261 Msg->message = WM_QUIT;
262 Msg->wParam = ThreadQueue->QuitExitCode;
263 Msg->lParam = 0;
264 if (RemoveMessages)
265 {
266 ThreadQueue->QuitPosted = FALSE;
267 }
268 return TRUE;
269 }
270
271 /* Now check for normal messages. */
272 Present = MsqFindMessage(ThreadQueue,
273 FALSE,
274 RemoveMessages,
275 Wnd,
276 MsgFilterMin,
277 MsgFilterMax,
278 &Message);
279 if (Present)
280 {
281 RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
282 if (RemoveMessages)
283 {
284 MsqDestroyMessage(Message);
285 IntSendSpecialMessages(ThreadQueue, Msg);
286 }
287 return TRUE;
288 }
289
290 /* Check for hardware events. */
291 Present = MsqFindMessage(ThreadQueue,
292 TRUE,
293 RemoveMessages,
294 Wnd,
295 MsgFilterMin,
296 MsgFilterMax,
297 &Message);
298 if (Present)
299 {
300 RtlCopyMemory(Msg, &Message->Msg, sizeof(MSG));
301 if (RemoveMessages)
302 {
303 MsqDestroyMessage(Message);
304 IntSendSpecialMessages(ThreadQueue, Msg);
305 }
306 return TRUE;
307 }
308
309 /* Check for sent messages again. */
310 while (MsqDispatchOneSentMessage(ThreadQueue))
311 ;
312
313 /* Check for paint messages. */
314 if (IntGetPaintMessage(Wnd, PsGetWin32Thread(), Msg, RemoveMessages))
315 {
316 return TRUE;
317 }
318
319 return FALSE;
320 }
321
322 BOOL STDCALL
323 NtUserPeekMessage(LPMSG UnsafeMsg,
324 HWND Wnd,
325 UINT MsgFilterMin,
326 UINT MsgFilterMax,
327 UINT RemoveMsg)
328 {
329 MSG SafeMsg;
330 NTSTATUS Status;
331 BOOL Present;
332 PWINDOW_OBJECT Window;
333
334 /* Validate input */
335 if (NULL != Wnd)
336 {
337 Window = IntGetWindowObject(Wnd);
338 if(!Window)
339 Wnd = NULL;
340 else
341 IntReleaseWindowObject(Window);
342 }
343 if (MsgFilterMax < MsgFilterMin)
344 {
345 MsgFilterMin = 0;
346 MsgFilterMax = 0;
347 }
348
349 Present = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
350 if (Present)
351 {
352 Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
353 if (! NT_SUCCESS(Status))
354 {
355 /* There is error return documented for PeekMessage().
356 Do the best we can */
357 SetLastNtError(Status);
358 return FALSE;
359 }
360 }
361
362 return Present;
363 }
364
365 static BOOL STDCALL
366 IntWaitMessage(HWND Wnd,
367 UINT MsgFilterMin,
368 UINT MsgFilterMax)
369 {
370 PUSER_MESSAGE_QUEUE ThreadQueue;
371 NTSTATUS Status;
372 MSG Msg;
373
374 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetWin32Thread()->MessageQueue;
375
376 do
377 {
378 if (IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
379 {
380 return TRUE;
381 }
382
383 /* Nothing found. Wait for new messages. */
384 Status = MsqWaitForNewMessages(ThreadQueue);
385 }
386 while (STATUS_WAIT_0 <= STATUS_WAIT_0 && Status <= STATUS_WAIT_63);
387
388 SetLastNtError(Status);
389
390 return FALSE;
391 }
392
393 BOOL STDCALL
394 NtUserGetMessage(LPMSG UnsafeMsg,
395 HWND Wnd,
396 UINT MsgFilterMin,
397 UINT MsgFilterMax)
398 /*
399 * FUNCTION: Get a message from the calling thread's message queue.
400 * ARGUMENTS:
401 * UnsafeMsg - Pointer to the structure which receives the returned message.
402 * Wnd - Window whose messages are to be retrieved.
403 * MsgFilterMin - Integer value of the lowest message value to be
404 * retrieved.
405 * MsgFilterMax - Integer value of the highest message value to be
406 * retrieved.
407 */
408 {
409 BOOL GotMessage;
410 MSG SafeMsg;
411 NTSTATUS Status;
412 PWINDOW_OBJECT Window;
413
414 /* Validate input */
415 if (NULL != Wnd)
416 {
417 Window = IntGetWindowObject(Wnd);
418 if(!Window)
419 Wnd = NULL;
420 else
421 IntReleaseWindowObject(Window);
422 }
423 if (MsgFilterMax < MsgFilterMin)
424 {
425 MsgFilterMin = 0;
426 MsgFilterMax = 0;
427 }
428
429 do
430 {
431 GotMessage = IntPeekMessage(&SafeMsg, Wnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
432 if (GotMessage)
433 {
434 Status = MmCopyToCaller(UnsafeMsg, &SafeMsg, sizeof(MSG));
435 if (! NT_SUCCESS(Status))
436 {
437 SetLastNtError(Status);
438 return (BOOL) -1;
439 }
440 }
441 else
442 {
443 IntWaitMessage(Wnd, MsgFilterMin, MsgFilterMax);
444 }
445 }
446 while (! GotMessage);
447
448 return WM_QUIT != SafeMsg.message;
449 }
450
451 DWORD
452 STDCALL
453 NtUserMessageCall(
454 DWORD Unknown0,
455 DWORD Unknown1,
456 DWORD Unknown2,
457 DWORD Unknown3,
458 DWORD Unknown4,
459 DWORD Unknown5,
460 DWORD Unknown6)
461 {
462 UNIMPLEMENTED
463
464 return 0;
465 }
466
467 BOOL STDCALL
468 NtUserPostMessage(HWND hWnd,
469 UINT Msg,
470 WPARAM wParam,
471 LPARAM lParam)
472 {
473 PWINDOW_OBJECT Window;
474 MSG Mesg;
475 PUSER_MESSAGE Message;
476 LARGE_INTEGER LargeTickCount;
477
478 if (WM_QUIT == Msg)
479 {
480 MsqPostQuitMessage(PsGetWin32Thread()->MessageQueue, wParam);
481 }
482 else if (hWnd == HWND_BROADCAST)
483 {
484 HWND *List;
485 PWINDOW_OBJECT DesktopWindow;
486 ULONG i;
487
488 DesktopWindow = IntGetWindowObject(IntGetDesktopWindow());
489 List = IntWinListChildren(DesktopWindow);
490 IntReleaseWindowObject(DesktopWindow);
491 if (List != NULL)
492 {
493 for (i = 0; List[i]; i++)
494 NtUserPostMessage(List[i], Msg, wParam, lParam);
495 ExFreePool(List);
496 }
497 }
498 else
499 {
500 Window = IntGetWindowObject(hWnd);
501 if (!Window)
502 {
503 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
504 return FALSE;
505 }
506 Mesg.hwnd = hWnd;
507 Mesg.message = Msg;
508 Mesg.wParam = wParam;
509 Mesg.lParam = lParam;
510 Mesg.pt.x = PsGetWin32Process()->WindowStation->SystemCursor.x;
511 Mesg.pt.y = PsGetWin32Process()->WindowStation->SystemCursor.y;
512 KeQueryTickCount(&LargeTickCount);
513 Mesg.time = LargeTickCount.u.LowPart;
514 Message = MsqCreateMessage(&Mesg);
515 MsqPostMessage(Window->MessageQueue, Message);
516 IntReleaseWindowObject(Window);
517 }
518
519 return TRUE;
520 }
521
522 BOOL STDCALL
523 NtUserPostThreadMessage(DWORD idThread,
524 UINT Msg,
525 WPARAM wParam,
526 LPARAM lParam)
527 {
528 MSG Mesg;
529
530 PUSER_MESSAGE Message;
531 PETHREAD peThread;
532 PW32THREAD pThread;
533 NTSTATUS Status;
534
535 Status = PsLookupThreadByThreadId((void *)idThread,&peThread);
536
537 if( Status == STATUS_SUCCESS ) {
538 pThread = peThread->Win32Thread;
539 if( !pThread || !pThread->MessageQueue )
540 {
541 ObDereferenceObject( peThread );
542 return FALSE;
543 }
544 Mesg.hwnd = 0;
545 Mesg.message = Msg;
546 Mesg.wParam = wParam;
547 Mesg.lParam = lParam;
548 Message = MsqCreateMessage(&Mesg);
549 MsqPostMessage(pThread->MessageQueue, Message);
550 ObDereferenceObject( peThread );
551 return TRUE;
552 } else {
553 SetLastNtError( Status );
554 return FALSE;
555 }
556 }
557
558 DWORD STDCALL
559 NtUserQuerySendMessage(DWORD Unknown0)
560 {
561 UNIMPLEMENTED;
562
563 return 0;
564 }
565
566 #define MMS_SIZE_WPARAM -1
567 #define MMS_SIZE_WPARAMWCHAR -2
568 #define MMS_SIZE_LPARAMSZ -3
569 #define MMS_SIZE_SPECIAL -4
570 #define MMS_FLAG_READ 0x01
571 #define MMS_FLAG_WRITE 0x02
572 #define MMS_FLAG_READWRITE (MMS_FLAG_READ | MMS_FLAG_WRITE)
573 typedef struct tagMSGMEMORY
574 {
575 UINT Message;
576 UINT Size;
577 INT Flags;
578 } MSGMEMORY, *PMSGMEMORY;
579
580 static MSGMEMORY MsgMemory[] =
581 {
582 { WM_CREATE, sizeof(CREATESTRUCTW), MMS_FLAG_READWRITE },
583 { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
584 { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
585 { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
586 { WM_NCCREATE, sizeof(CREATESTRUCTW), MMS_FLAG_READWRITE },
587 { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
588 { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
589 { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
590 { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READ },
591 { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
592 };
593
594 static PMSGMEMORY FASTCALL
595 FindMsgMemory(UINT Msg)
596 {
597 PMSGMEMORY MsgMemoryEntry;
598
599 /* See if this message type is present in the table */
600 for (MsgMemoryEntry = MsgMemory;
601 MsgMemoryEntry < MsgMemory + sizeof(MsgMemory) / sizeof(MSGMEMORY);
602 MsgMemoryEntry++)
603 {
604 if (Msg == MsgMemoryEntry->Message)
605 {
606 return MsgMemoryEntry;
607 }
608 }
609
610 return NULL;
611 }
612
613 static UINT FASTCALL
614 MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
615 {
616 if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
617 {
618 return (UINT) wParam;
619 }
620 else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
621 {
622 return (UINT) (wParam * sizeof(WCHAR));
623 }
624 else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
625 {
626 return (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
627 }
628 else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
629 {
630 switch(MsgMemoryEntry->Message)
631 {
632 case WM_NCCALCSIZE:
633 return wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
634 break;
635 default:
636 assert(FALSE);
637 return 0;
638 break;
639 }
640 }
641 else
642 {
643 return MsgMemoryEntry->Size;
644 }
645 }
646
647 static FASTCALL NTSTATUS
648 PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
649 {
650 NCCALCSIZE_PARAMS *UnpackedParams;
651 NCCALCSIZE_PARAMS *PackedParams;
652
653 *lParamPacked = lParam;
654 if (WM_NCCALCSIZE == Msg && wParam)
655 {
656 UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
657 if (UnpackedParams->lppos != (PWINDOWPOS) (UnpackedParams + 1))
658 {
659 PackedParams = ExAllocatePoolWithTag(PagedPool,
660 sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
661 TAG_MSG);
662 if (NULL == PackedParams)
663 {
664 DPRINT1("Not enough memory to pack lParam\n");
665 return STATUS_NO_MEMORY;
666 }
667 RtlCopyMemory(PackedParams, UnpackedParams, sizeof(NCCALCSIZE_PARAMS));
668 PackedParams->lppos = (PWINDOWPOS) (PackedParams + 1);
669 RtlCopyMemory(PackedParams->lppos, UnpackedParams->lppos, sizeof(WINDOWPOS));
670 *lParamPacked = (LPARAM) PackedParams;
671 }
672 }
673
674 return STATUS_SUCCESS;
675 }
676
677 static FASTCALL NTSTATUS
678 UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
679 {
680 NCCALCSIZE_PARAMS *UnpackedParams;
681 NCCALCSIZE_PARAMS *PackedParams;
682 PWINDOWPOS UnpackedWindowPos;
683
684 if (lParamPacked == lParam)
685 {
686 return STATUS_SUCCESS;
687 }
688
689 if (WM_NCCALCSIZE == Msg && wParam)
690 {
691 PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
692 UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
693 UnpackedWindowPos = UnpackedParams->lppos;
694 RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
695 UnpackedParams->lppos = UnpackedWindowPos;
696 RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
697 ExFreePool((PVOID) lParamPacked);
698
699 return STATUS_SUCCESS;
700 }
701
702 assert(FALSE);
703
704 return STATUS_INVALID_PARAMETER;
705 }
706
707 LRESULT STDCALL
708 IntSendMessage(HWND hWnd,
709 UINT Msg,
710 WPARAM wParam,
711 LPARAM lParam)
712 {
713 LRESULT Result;
714 PWINDOW_OBJECT Window;
715 PMSGMEMORY MsgMemoryEntry;
716 INT lParamBufferSize;
717 LPARAM lParamPacked;
718 PW32THREAD Win32Thread;
719
720 /* FIXME: Check for a broadcast or topmost destination. */
721
722 /* FIXME: Call hooks. */
723 Window = IntGetWindowObject(hWnd);
724 if (!Window)
725 {
726 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
727 return 0;
728 }
729
730 Win32Thread = PsGetWin32Thread();
731
732 if (NULL != Win32Thread &&
733 Window->MessageQueue == Win32Thread->MessageQueue)
734 {
735 if (Win32Thread->IsExiting)
736 {
737 /* Never send messages to exiting threads */
738 IntReleaseWindowObject(Window);
739 return 0;
740 }
741
742 /* See if this message type is present in the table */
743 MsgMemoryEntry = FindMsgMemory(Msg);
744 if (NULL == MsgMemoryEntry)
745 {
746 lParamBufferSize = -1;
747 }
748 else
749 {
750 lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
751 }
752
753 if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam)))
754 {
755 IntReleaseWindowObject(Window);
756 DPRINT1("Failed to pack message parameters\n");
757 return -1;
758 }
759 if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
760 {
761 Result = IntCallWindowProc(Window->WndProcW, FALSE, hWnd, Msg, wParam,
762 lParamPacked,lParamBufferSize);
763 }
764 else
765 {
766 Result = IntCallWindowProc(Window->WndProcA, TRUE, hWnd, Msg, wParam,
767 lParamPacked,lParamBufferSize);
768 }
769 if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam)))
770 {
771 IntReleaseWindowObject(Window);
772 DPRINT1("Failed to unpack message parameters\n");
773 return Result;
774 }
775
776 IntReleaseWindowObject(Window);
777 return Result;
778 }
779 else
780 {
781 Result = MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam);
782
783 IntReleaseWindowObject(Window);
784 return Result;
785 }
786 }
787
788 static NTSTATUS FASTCALL
789 CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg)
790 {
791 NTSTATUS Status;
792 PMSGMEMORY MsgMemoryEntry;
793 PVOID KernelMem;
794 UINT Size;
795
796 *KernelModeMsg = *UserModeMsg;
797
798 /* See if this message type is present in the table */
799 MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
800 if (NULL == MsgMemoryEntry)
801 {
802 /* Not present, no copying needed */
803 return STATUS_SUCCESS;
804 }
805
806 /* Determine required size */
807 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
808
809 if (0 != Size)
810 {
811 /* Allocate kernel mem */
812 KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
813 if (NULL == KernelMem)
814 {
815 DPRINT1("Not enough memory to copy message to kernel mem\n");
816 return STATUS_NO_MEMORY;
817 }
818 KernelModeMsg->lParam = (LPARAM) KernelMem;
819
820 /* Copy data if required */
821 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
822 {
823 Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
824 if (! NT_SUCCESS(Status))
825 {
826 DPRINT1("Failed to copy message to kernel: invalid usermode buffer\n");
827 ExFreePool(KernelMem);
828 return Status;
829 }
830 }
831 else
832 {
833 /* Make sure we don't pass any secrets to usermode */
834 RtlZeroMemory(KernelMem, Size);
835 }
836 }
837 else
838 {
839 KernelModeMsg->lParam = 0;
840 }
841
842 return STATUS_SUCCESS;
843 }
844
845 static NTSTATUS FASTCALL
846 CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
847 {
848 NTSTATUS Status;
849 PMSGMEMORY MsgMemoryEntry;
850 UINT Size;
851
852 /* See if this message type is present in the table */
853 MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
854 if (NULL == MsgMemoryEntry)
855 {
856 /* Not present, no copying needed */
857 return STATUS_SUCCESS;
858 }
859
860 /* Determine required size */
861 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
862
863 if (0 != Size)
864 {
865 /* Copy data if required */
866 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
867 {
868 Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
869 if (! NT_SUCCESS(Status))
870 {
871 DPRINT1("Failed to copy message from kernel: invalid usermode buffer\n");
872 ExFreePool((PVOID) KernelModeMsg->lParam);
873 return Status;
874 }
875 }
876
877 ExFreePool((PVOID) KernelModeMsg->lParam);
878 }
879
880 return STATUS_SUCCESS;
881 }
882
883 LRESULT STDCALL
884 NtUserSendMessage(HWND Wnd,
885 UINT Msg,
886 WPARAM wParam,
887 LPARAM lParam,
888 PNTUSERSENDMESSAGEINFO UnsafeInfo)
889 {
890 LRESULT Result;
891 NTSTATUS Status;
892 PWINDOW_OBJECT Window;
893 NTUSERSENDMESSAGEINFO Info;
894 MSG UserModeMsg;
895 MSG KernelModeMsg;
896
897 /* FIXME: Check for a broadcast or topmost destination. */
898
899 RtlZeroMemory(&Info, sizeof(NTUSERSENDMESSAGEINFO));
900
901 /* FIXME: Call hooks. */
902 Window = IntGetWindowObject(Wnd);
903 if (NULL == Window)
904 {
905 /* Tell usermode to not touch this one */
906 Info.HandledByKernel = TRUE;
907 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
908 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
909 return 0;
910 }
911
912 /* FIXME: Check for an exiting window. */
913
914 /* See if the current thread can handle the message */
915 if (NULL != PsGetWin32Thread() &&
916 Window->MessageQueue == PsGetWin32Thread()->MessageQueue)
917 {
918 /* Gather the information usermode needs to call the window proc directly */
919 Info.HandledByKernel = FALSE;
920 if (0xFFFF0000 != ((DWORD) Window->WndProcW & 0xFFFF0000))
921 {
922 if (0xFFFF0000 != ((DWORD) Window->WndProcA & 0xFFFF0000))
923 {
924 /* Both Unicode and Ansi winprocs are real, see what usermode prefers */
925 Status = MmCopyFromCaller(&(Info.Ansi), &(UnsafeInfo->Ansi),
926 sizeof(BOOL));
927 if (! NT_SUCCESS(Status))
928 {
929 Info.Ansi = ! Window->Unicode;
930 }
931 Info.Proc = (Info.Ansi ? Window->WndProcA : Window->WndProcW);
932 }
933 else
934 {
935 /* Real Unicode winproc */
936 Info.Ansi = FALSE;
937 Info.Proc = Window->WndProcW;
938 }
939 }
940 else
941 {
942 /* Must have real Ansi winproc */
943 Info.Ansi = TRUE;
944 Info.Proc = Window->WndProcA;
945 }
946 IntReleaseWindowObject(Window);
947 }
948 else
949 {
950 /* Must be handled by other thread */
951 IntReleaseWindowObject(Window);
952 Info.HandledByKernel = TRUE;
953 UserModeMsg.hwnd = Wnd;
954 UserModeMsg.message = Msg;
955 UserModeMsg.wParam = wParam;
956 UserModeMsg.lParam = lParam;
957 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg);
958 if (! NT_SUCCESS(Status))
959 {
960 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
961 SetLastWin32Error(ERROR_INVALID_PARAMETER);
962 return -1;
963 }
964 Result = IntSendMessage(KernelModeMsg.hwnd, KernelModeMsg.message,
965 KernelModeMsg.wParam, KernelModeMsg.lParam);
966 Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
967 if (! NT_SUCCESS(Status))
968 {
969 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
970 SetLastWin32Error(ERROR_INVALID_PARAMETER);
971 return -1;
972 }
973 }
974
975 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
976 if (! NT_SUCCESS(Status))
977 {
978 SetLastWin32Error(ERROR_INVALID_PARAMETER);
979 }
980
981 return Result;
982 }
983
984 BOOL STDCALL
985 NtUserSendMessageCallback(HWND hWnd,
986 UINT Msg,
987 WPARAM wParam,
988 LPARAM lParam,
989 SENDASYNCPROC lpCallBack,
990 ULONG_PTR dwData)
991 {
992 UNIMPLEMENTED;
993
994 return 0;
995 }
996
997 BOOL STDCALL
998 NtUserSendNotifyMessage(HWND hWnd,
999 UINT Msg,
1000 WPARAM wParam,
1001 LPARAM lParam)
1002 {
1003 UNIMPLEMENTED;
1004
1005 return 0;
1006 }
1007
1008 BOOL STDCALL
1009 NtUserWaitMessage(VOID)
1010 {
1011
1012 return IntWaitMessage(NULL, 0, 0);
1013 }
1014
1015 DWORD STDCALL
1016 NtUserGetQueueStatus(BOOL ClearChanges)
1017 {
1018 PUSER_MESSAGE_QUEUE Queue;
1019 DWORD Result;
1020
1021 Queue = PsGetWin32Thread()->MessageQueue;
1022
1023 ExAcquireFastMutex(&Queue->Lock);
1024
1025 Result = MAKELONG(Queue->ChangedBits, Queue->WakeBits);
1026 if (ClearChanges)
1027 {
1028 Queue->ChangedBits = 0;
1029 }
1030
1031 ExReleaseFastMutex(&Queue->Lock);
1032
1033 return Result;
1034 }
1035
1036 BOOL STDCALL
1037 IntInitMessagePumpHook()
1038 {
1039 PsGetCurrentThread()->Win32Thread->MessagePumpHookValue++;
1040 return TRUE;
1041 }
1042
1043 BOOL STDCALL
1044 IntUninitMessagePumpHook()
1045 {
1046 if (PsGetCurrentThread()->Win32Thread->MessagePumpHookValue <= 0)
1047 {
1048 return FALSE;
1049 }
1050 PsGetCurrentThread()->Win32Thread->MessagePumpHookValue--;
1051 return TRUE;
1052 }
1053
1054 /* EOF */