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