- Fix NtUserCallMsgFilter to use SEH. Add support for system commands related to...
[reactos.git] / reactos / subsystems / win32 / 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 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS kernel
22 * PURPOSE: Messages
23 * FILE: subsys/win32k/ntuser/message.c
24 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * REVISION HISTORY:
26 * 06-06-2001 CSH Created
27 */
28
29 /* INCLUDES ******************************************************************/
30
31 #include <w32k.h>
32
33 #define NDEBUG
34 #include <debug.h>
35
36 typedef struct
37 {
38 UINT uFlags;
39 UINT uTimeout;
40 ULONG_PTR Result;
41 }
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 }
71 MSGMEMORY, *PMSGMEMORY;
72
73 static MSGMEMORY MsgMemory[] =
74 {
75 { WM_CREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
76 { WM_DDE_ACK, sizeof(KMDDELPARAM), MMS_FLAG_READ },
77 { WM_DDE_EXECUTE, MMS_SIZE_WPARAM, MMS_FLAG_READ },
78 { WM_GETMINMAXINFO, sizeof(MINMAXINFO), MMS_FLAG_READWRITE },
79 { WM_GETTEXT, MMS_SIZE_WPARAMWCHAR, MMS_FLAG_WRITE },
80 { WM_NCCALCSIZE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
81 { WM_NCCREATE, MMS_SIZE_SPECIAL, MMS_FLAG_READWRITE },
82 { WM_SETTEXT, MMS_SIZE_LPARAMSZ, MMS_FLAG_READ },
83 { WM_STYLECHANGED, sizeof(STYLESTRUCT), MMS_FLAG_READ },
84 { WM_STYLECHANGING, sizeof(STYLESTRUCT), MMS_FLAG_READWRITE },
85 { WM_COPYDATA, MMS_SIZE_SPECIAL, MMS_FLAG_READ },
86 { WM_WINDOWPOSCHANGED, sizeof(WINDOWPOS), MMS_FLAG_READ },
87 { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE },
88 };
89
90 static PMSGMEMORY FASTCALL
91 FindMsgMemory(UINT Msg)
92 {
93 PMSGMEMORY MsgMemoryEntry;
94
95 /* See if this message type is present in the table */
96 for (MsgMemoryEntry = MsgMemory;
97 MsgMemoryEntry < MsgMemory + sizeof(MsgMemory) / sizeof(MSGMEMORY);
98 MsgMemoryEntry++)
99 {
100 if (Msg == MsgMemoryEntry->Message)
101 {
102 return MsgMemoryEntry;
103 }
104 }
105
106 return NULL;
107 }
108
109 static UINT FASTCALL
110 MsgMemorySize(PMSGMEMORY MsgMemoryEntry, WPARAM wParam, LPARAM lParam)
111 {
112 CREATESTRUCTW *Cs;
113 PUNICODE_STRING WindowName;
114 PUNICODE_STRING ClassName;
115 UINT Size = 0;
116
117 _SEH_TRY
118 {
119 if (MMS_SIZE_WPARAM == MsgMemoryEntry->Size)
120 {
121 Size = (UINT)wParam;
122 }
123 else if (MMS_SIZE_WPARAMWCHAR == MsgMemoryEntry->Size)
124 {
125 Size = (UINT) (wParam * sizeof(WCHAR));
126 }
127 else if (MMS_SIZE_LPARAMSZ == MsgMemoryEntry->Size)
128 {
129 Size = (UINT) ((wcslen((PWSTR) lParam) + 1) * sizeof(WCHAR));
130 }
131 else if (MMS_SIZE_SPECIAL == MsgMemoryEntry->Size)
132 {
133 switch(MsgMemoryEntry->Message)
134 {
135 case WM_CREATE:
136 case WM_NCCREATE:
137 Cs = (CREATESTRUCTW *) lParam;
138 WindowName = (PUNICODE_STRING) Cs->lpszName;
139 ClassName = (PUNICODE_STRING) Cs->lpszClass;
140 Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
141 if (IS_ATOM(ClassName->Buffer))
142 {
143 Size += sizeof(WCHAR) + sizeof(ATOM);
144 }
145 else
146 {
147 Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
148 }
149 break;
150
151 case WM_NCCALCSIZE:
152 Size = wParam ? sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS) : sizeof(RECT);
153 break;
154
155 case WM_COPYDATA:
156 Size = sizeof(COPYDATASTRUCT) + ((PCOPYDATASTRUCT)lParam)->cbData;
157 break;
158
159 default:
160 assert(FALSE);
161 Size = 0;
162 break;
163 }
164 }
165 else
166 {
167 Size = MsgMemoryEntry->Size;
168 }
169 }
170 _SEH_HANDLE
171 {
172 DPRINT1("Exception caught in MsgMemorySize()! Status: 0x%x\n", _SEH_GetExceptionCode());
173 Size = 0;
174 }
175 _SEH_END;
176 return Size;
177 }
178
179 static NTSTATUS
180 PackParam(LPARAM *lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
181 {
182 NCCALCSIZE_PARAMS *UnpackedNcCalcsize;
183 NCCALCSIZE_PARAMS *PackedNcCalcsize;
184 CREATESTRUCTW *UnpackedCs;
185 CREATESTRUCTW *PackedCs;
186 PUNICODE_STRING WindowName;
187 PUNICODE_STRING ClassName;
188 UINT Size;
189 PCHAR CsData;
190
191 *lParamPacked = lParam;
192 if (WM_NCCALCSIZE == Msg && wParam)
193 {
194 UnpackedNcCalcsize = (NCCALCSIZE_PARAMS *) lParam;
195 if (UnpackedNcCalcsize->lppos != (PWINDOWPOS) (UnpackedNcCalcsize + 1))
196 {
197 PackedNcCalcsize = ExAllocatePoolWithTag(PagedPool,
198 sizeof(NCCALCSIZE_PARAMS) + sizeof(WINDOWPOS),
199 TAG_MSG);
200 if (NULL == PackedNcCalcsize)
201 {
202 DPRINT1("Not enough memory to pack lParam\n");
203 return STATUS_NO_MEMORY;
204 }
205 RtlCopyMemory(PackedNcCalcsize, UnpackedNcCalcsize, sizeof(NCCALCSIZE_PARAMS));
206 PackedNcCalcsize->lppos = (PWINDOWPOS) (PackedNcCalcsize + 1);
207 RtlCopyMemory(PackedNcCalcsize->lppos, UnpackedNcCalcsize->lppos, sizeof(WINDOWPOS));
208 *lParamPacked = (LPARAM) PackedNcCalcsize;
209 }
210 }
211 else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
212 {
213 UnpackedCs = (CREATESTRUCTW *) lParam;
214 WindowName = (PUNICODE_STRING) UnpackedCs->lpszName;
215 ClassName = (PUNICODE_STRING) UnpackedCs->lpszClass;
216 Size = sizeof(CREATESTRUCTW) + WindowName->Length + sizeof(WCHAR);
217 if (IS_ATOM(ClassName->Buffer))
218 {
219 Size += sizeof(WCHAR) + sizeof(ATOM);
220 }
221 else
222 {
223 Size += sizeof(WCHAR) + ClassName->Length + sizeof(WCHAR);
224 }
225 PackedCs = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
226 if (NULL == PackedCs)
227 {
228 DPRINT1("Not enough memory to pack lParam\n");
229 return STATUS_NO_MEMORY;
230 }
231 RtlCopyMemory(PackedCs, UnpackedCs, sizeof(CREATESTRUCTW));
232 CsData = (PCHAR) (PackedCs + 1);
233 PackedCs->lpszName = (LPCWSTR) (CsData - (PCHAR) PackedCs);
234 RtlCopyMemory(CsData, WindowName->Buffer, WindowName->Length);
235 CsData += WindowName->Length;
236 *((WCHAR *) CsData) = L'\0';
237 CsData += sizeof(WCHAR);
238 PackedCs->lpszClass = (LPCWSTR) (CsData - (PCHAR) PackedCs);
239 if (IS_ATOM(ClassName->Buffer))
240 {
241 *((WCHAR *) CsData) = L'A';
242 CsData += sizeof(WCHAR);
243 *((ATOM *) CsData) = (ATOM)(DWORD_PTR) ClassName->Buffer;
244 CsData += sizeof(ATOM);
245 }
246 else
247 {
248 *((WCHAR *) CsData) = L'S';
249 CsData += sizeof(WCHAR);
250 RtlCopyMemory(CsData, ClassName->Buffer, ClassName->Length);
251 CsData += ClassName->Length;
252 *((WCHAR *) CsData) = L'\0';
253 CsData += sizeof(WCHAR);
254 }
255 ASSERT(CsData == (PCHAR) PackedCs + Size);
256 *lParamPacked = (LPARAM) PackedCs;
257 }
258
259 return STATUS_SUCCESS;
260 }
261
262 static NTSTATUS
263 UnpackParam(LPARAM lParamPacked, UINT Msg, WPARAM wParam, LPARAM lParam)
264 {
265 NCCALCSIZE_PARAMS *UnpackedParams;
266 NCCALCSIZE_PARAMS *PackedParams;
267 PWINDOWPOS UnpackedWindowPos;
268
269 if (lParamPacked == lParam)
270 {
271 return STATUS_SUCCESS;
272 }
273
274 if (WM_NCCALCSIZE == Msg && wParam)
275 {
276 PackedParams = (NCCALCSIZE_PARAMS *) lParamPacked;
277 UnpackedParams = (NCCALCSIZE_PARAMS *) lParam;
278 UnpackedWindowPos = UnpackedParams->lppos;
279 RtlCopyMemory(UnpackedParams, PackedParams, sizeof(NCCALCSIZE_PARAMS));
280 UnpackedParams->lppos = UnpackedWindowPos;
281 RtlCopyMemory(UnpackedWindowPos, PackedParams + 1, sizeof(WINDOWPOS));
282 ExFreePool((PVOID) lParamPacked);
283
284 return STATUS_SUCCESS;
285 }
286 else if (WM_CREATE == Msg || WM_NCCREATE == Msg)
287 {
288 ExFreePool((PVOID) lParamPacked);
289
290 return STATUS_SUCCESS;
291 }
292
293 ASSERT(FALSE);
294
295 return STATUS_INVALID_PARAMETER;
296 }
297
298 BOOL
299 STDCALL
300 NtUserCallMsgFilter(
301 LPMSG msg,
302 INT code)
303 {
304 DECLARE_RETURN(BOOL);
305
306 DPRINT("Enter NtUserCallMsgFilter\n");
307 UserEnterExclusive();
308
309 if ( UserCallNextHookEx( WH_SYSMSGFILTER, code, 0, (LPARAM)msg, FALSE))
310 RETURN( TRUE);
311 RETURN( UserCallNextHookEx( WH_MSGFILTER, code, 0, (LPARAM)msg, FALSE));
312
313 CLEANUP:
314 DPRINT("Leave NtUserCallMsgFilter. ret=%i\n", _ret_);
315 UserLeave();
316 END_CLEANUP;
317 }
318
319 LRESULT STDCALL
320 NtUserDispatchMessage(PNTUSERDISPATCHMESSAGEINFO UnsafeMsgInfo)
321 {
322 NTSTATUS Status;
323 NTUSERDISPATCHMESSAGEINFO MsgInfo;
324 LRESULT Result = TRUE;
325 DECLARE_RETURN(LRESULT);
326
327 DPRINT("Enter NtUserDispatchMessage\n");
328 UserEnterExclusive();
329
330 Status = MmCopyFromCaller(&MsgInfo, UnsafeMsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
331 if (! NT_SUCCESS(Status))
332 {
333 SetLastNtError(Status);
334 RETURN( 0);
335 }
336
337 /* Process timer messages. */
338 if (WM_TIMER == MsgInfo.Msg.message && 0 != MsgInfo.Msg.lParam)
339 {
340 LARGE_INTEGER LargeTickCount;
341 /* FIXME: Call hooks. */
342
343 /* FIXME: Check for continuing validity of timer. */
344
345 MsgInfo.HandledByKernel = FALSE;
346 KeQueryTickCount(&LargeTickCount);
347 MsgInfo.Proc = (WNDPROC) MsgInfo.Msg.lParam;
348 MsgInfo.Msg.lParam = (LPARAM)LargeTickCount.u.LowPart;
349 }
350 else if (NULL == MsgInfo.Msg.hwnd)
351 {
352 MsgInfo.HandledByKernel = TRUE;
353 Result = 0;
354 }
355 else
356 {
357 PWINDOW_OBJECT Window;
358
359 /* Get the window object. */
360 Window = UserGetWindowObject(MsgInfo.Msg.hwnd);
361 if (NULL == Window)
362 {
363 MsgInfo.HandledByKernel = TRUE;
364 Result = 0;
365 }
366 else
367 {
368 if (Window->OwnerThread != PsGetCurrentThread())
369 {
370 DPRINT1("Window doesn't belong to the calling thread!\n");
371 MsgInfo.HandledByKernel = TRUE;
372 Result = 0;
373 }
374 else
375 {
376 /* FIXME: Call hook procedures. */
377
378 MsgInfo.HandledByKernel = FALSE;
379 Result = 0;
380
381 if (Window->Wnd->IsSystem)
382 {
383 MsgInfo.Proc = (!MsgInfo.Ansi ? Window->Wnd->WndProc : Window->Wnd->WndProcExtra);
384 }
385 else
386 {
387 MsgInfo.Ansi = !Window->Wnd->Unicode;
388 MsgInfo.Proc = Window->Wnd->WndProc;
389 }
390 }
391 }
392 }
393 Status = MmCopyToCaller(UnsafeMsgInfo, &MsgInfo, sizeof(NTUSERDISPATCHMESSAGEINFO));
394 if (! NT_SUCCESS(Status))
395 {
396 SetLastNtError(Status);
397 RETURN( 0);
398 }
399
400 RETURN( Result);
401
402 CLEANUP:
403 DPRINT("Leave NtUserDispatchMessage. ret=%i\n", _ret_);
404 UserLeave();
405 END_CLEANUP;
406 }
407
408
409 BOOL STDCALL
410 NtUserTranslateMessage(LPMSG lpMsg,
411 HKL dwhkl)
412 {
413 NTSTATUS Status;
414 MSG SafeMsg;
415 DECLARE_RETURN(BOOL);
416
417 DPRINT("Enter NtUserTranslateMessage\n");
418 UserEnterExclusive();
419
420 Status = MmCopyFromCaller(&SafeMsg, lpMsg, sizeof(MSG));
421 if(!NT_SUCCESS(Status))
422 {
423 SetLastNtError(Status);
424 RETURN( FALSE);
425 }
426
427 RETURN( IntTranslateKbdMessage(&SafeMsg, dwhkl));
428
429 CLEANUP:
430 DPRINT("Leave NtUserTranslateMessage: ret=%i\n",_ret_);
431 UserLeave();
432 END_CLEANUP;
433 }
434
435
436 VOID FASTCALL
437 co_IntSendHitTestMessages(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg)
438 {
439 if(!Msg->hwnd || ThreadQueue->CaptureWindow)
440 {
441 return;
442 }
443
444 switch(Msg->message)
445 {
446 case WM_MOUSEMOVE:
447 {
448 co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
449 break;
450 }
451 case WM_NCMOUSEMOVE:
452 {
453 co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
454 break;
455 }
456 case WM_LBUTTONDOWN:
457 case WM_MBUTTONDOWN:
458 case WM_RBUTTONDOWN:
459 case WM_XBUTTONDOWN:
460 case WM_LBUTTONDBLCLK:
461 case WM_MBUTTONDBLCLK:
462 case WM_RBUTTONDBLCLK:
463 case WM_XBUTTONDBLCLK:
464 {
465 WPARAM wParam;
466 PSYSTEM_CURSORINFO CurInfo;
467
468 if(!IntGetWindowStationObject(InputWindowStation))
469 {
470 break;
471 }
472 CurInfo = IntGetSysCursorInfo(InputWindowStation);
473 wParam = (WPARAM)(CurInfo->ButtonsDown);
474 ObDereferenceObject(InputWindowStation);
475
476 co_IntSendMessage(Msg->hwnd, WM_MOUSEMOVE, wParam, Msg->lParam);
477 co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(HTCLIENT, Msg->message));
478 break;
479 }
480 case WM_NCLBUTTONDOWN:
481 case WM_NCMBUTTONDOWN:
482 case WM_NCRBUTTONDOWN:
483 case WM_NCXBUTTONDOWN:
484 case WM_NCLBUTTONDBLCLK:
485 case WM_NCMBUTTONDBLCLK:
486 case WM_NCRBUTTONDBLCLK:
487 case WM_NCXBUTTONDBLCLK:
488 {
489 co_IntSendMessage(Msg->hwnd, WM_NCMOUSEMOVE, (WPARAM)Msg->wParam, Msg->lParam);
490 co_IntSendMessage(Msg->hwnd, WM_SETCURSOR, (WPARAM)Msg->hwnd, MAKELPARAM(Msg->wParam, Msg->message));
491 break;
492 }
493 }
494 }
495
496 BOOL FASTCALL
497 co_IntActivateWindowMouse(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, PWINDOW_OBJECT MsgWindow,
498 USHORT *HitTest)
499 {
500 ULONG Result;
501 PWINDOW_OBJECT Parent;
502
503 ASSERT_REFS_CO(MsgWindow);
504
505 if(*HitTest == (USHORT)HTTRANSPARENT)
506 {
507 /* eat the message, search again! */
508 return TRUE;
509 }
510
511 Parent = IntGetParent(MsgWindow);//fixme: deref retval?
512 /* fixme: abort if no parent ? */
513 Result = co_IntSendMessage(MsgWindow->hSelf,
514 WM_MOUSEACTIVATE,
515 (WPARAM) (Parent ? Parent->hSelf : NULL),
516 (LPARAM)MAKELONG(*HitTest, Msg->message)
517 );
518
519 switch (Result)
520 {
521 case MA_NOACTIVATEANDEAT:
522 return TRUE;
523 case MA_NOACTIVATE:
524 break;
525 case MA_ACTIVATEANDEAT:
526 co_IntMouseActivateWindow(MsgWindow);
527 return TRUE;
528 default:
529 /* MA_ACTIVATE */
530 co_IntMouseActivateWindow(MsgWindow);
531 break;
532 }
533
534 return FALSE;
535 }
536
537 BOOL FASTCALL
538 co_IntTranslateMouseMessage(PUSER_MESSAGE_QUEUE ThreadQueue, LPMSG Msg, USHORT *HitTest, BOOL Remove)
539 {
540 PWINDOW_OBJECT Window;
541 USER_REFERENCE_ENTRY Ref, DesktopRef;
542
543 if(!(Window = UserGetWindowObject(Msg->hwnd)))
544 {
545 /* let's just eat the message?! */
546 return TRUE;
547 }
548
549 UserRefObjectCo(Window, &Ref);
550
551 if(ThreadQueue == Window->MessageQueue &&
552 ThreadQueue->CaptureWindow != Window->hSelf)
553 {
554 /* only send WM_NCHITTEST messages if we're not capturing the window! */
555 *HitTest = co_IntSendMessage(Window->hSelf, WM_NCHITTEST, 0,
556 MAKELONG(Msg->pt.x, Msg->pt.y));
557
558 if(*HitTest == (USHORT)HTTRANSPARENT)
559 {
560 PWINDOW_OBJECT DesktopWindow;
561 HWND hDesktop = IntGetDesktopWindow();
562
563 if((DesktopWindow = UserGetWindowObject(hDesktop)))
564 {
565 PWINDOW_OBJECT Wnd;
566
567 UserRefObjectCo(DesktopWindow, &DesktopRef);
568
569 co_WinPosWindowFromPoint(DesktopWindow, Window->MessageQueue, &Msg->pt, &Wnd);
570 if(Wnd)
571 {
572 if(Wnd != Window)
573 {
574 /* post the message to the other window */
575 Msg->hwnd = Wnd->hSelf;
576 if(!(Wnd->Status & WINDOWSTATUS_DESTROYING))
577 {
578 MsqPostMessage(Wnd->MessageQueue, Msg, FALSE,
579 Msg->message == WM_MOUSEMOVE ? QS_MOUSEMOVE :
580 QS_MOUSEBUTTON);
581 }
582
583 /* eat the message */
584 UserDereferenceObject(Wnd);
585 UserDerefObjectCo(DesktopWindow);
586 UserDerefObjectCo(Window);
587 return TRUE;
588 }
589 UserDereferenceObject(Wnd);
590 }
591
592 UserDerefObjectCo(DesktopWindow);
593 }
594 }
595 }
596 else
597 {
598 *HitTest = HTCLIENT;
599 }
600
601 if(IS_BTN_MESSAGE(Msg->message, DOWN))
602 {
603 /* generate double click messages, if necessary */
604 if ((((*HitTest) != HTCLIENT) ||
605 (Window->Wnd->Class->Style & CS_DBLCLKS)) &&
606 MsqIsDblClk(Msg, Remove))
607 {
608 Msg->message += WM_LBUTTONDBLCLK - WM_LBUTTONDOWN;
609 }
610 }
611
612 if(Msg->message != WM_MOUSEWHEEL)
613 {
614
615 if ((*HitTest) != HTCLIENT)
616 {
617 Msg->message += WM_NCMOUSEMOVE - WM_MOUSEMOVE;
618 if((Msg->message == WM_NCRBUTTONUP) &&
619 (((*HitTest) == HTCAPTION) || ((*HitTest) == HTSYSMENU)))
620 {
621 Msg->message = WM_CONTEXTMENU;
622 Msg->wParam = (WPARAM)Window->hSelf;
623 }
624 else
625 {
626 Msg->wParam = *HitTest;
627 }
628 Msg->lParam = MAKELONG(Msg->pt.x, Msg->pt.y);
629 }
630 else if(ThreadQueue->MoveSize == NULL &&
631 ThreadQueue->MenuOwner == NULL)
632 {
633 /* NOTE: Msg->pt should remain in screen coordinates. -- FiN */
634 Msg->lParam = MAKELONG(
635 Msg->pt.x - (WORD)Window->Wnd->ClientRect.left,
636 Msg->pt.y - (WORD)Window->Wnd->ClientRect.top);
637 }
638 }
639
640 UserDerefObjectCo(Window);
641 return FALSE;
642 }
643
644
645 /*
646 * Internal version of PeekMessage() doing all the work
647 */
648 BOOL FASTCALL
649 co_IntPeekMessage(PUSER_MESSAGE Msg,
650 HWND hWnd,
651 UINT MsgFilterMin,
652 UINT MsgFilterMax,
653 UINT RemoveMsg)
654 {
655 LARGE_INTEGER LargeTickCount;
656 PUSER_MESSAGE_QUEUE ThreadQueue;
657 PUSER_MESSAGE Message;
658 BOOL Present, RemoveMessages;
659 USER_REFERENCE_ENTRY Ref;
660 USHORT HitTest;
661
662 /* The queues and order in which they are checked are documented in the MSDN
663 article on GetMessage() */
664
665 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetCurrentThreadWin32Thread()->MessageQueue;
666
667 /* Inspect RemoveMsg flags */
668 /* FIXME: The only flag we process is PM_REMOVE - processing of others must still be implemented */
669 RemoveMessages = RemoveMsg & PM_REMOVE;
670
671 CheckMessages:
672
673 Present = FALSE;
674
675 KeQueryTickCount(&LargeTickCount);
676 ThreadQueue->LastMsgRead = LargeTickCount.u.LowPart;
677
678 /* Dispatch sent messages here. */
679 while (co_MsqDispatchOneSentMessage(ThreadQueue))
680 ;
681
682 /* Now look for a quit message. */
683
684 if (ThreadQueue->QuitPosted)
685 {
686 /* According to the PSDK, WM_QUIT messages are always returned, regardless
687 of the filter specified */
688 Msg->Msg.hwnd = NULL;
689 Msg->Msg.message = WM_QUIT;
690 Msg->Msg.wParam = ThreadQueue->QuitExitCode;
691 Msg->Msg.lParam = 0;
692 Msg->FreeLParam = FALSE;
693 if (RemoveMessages)
694 {
695 ThreadQueue->QuitPosted = FALSE;
696 }
697 goto MsgExit;
698 }
699
700 /* Now check for normal messages. */
701 Present = co_MsqFindMessage(ThreadQueue,
702 FALSE,
703 RemoveMessages,
704 hWnd,
705 MsgFilterMin,
706 MsgFilterMax,
707 &Message);
708 if (Present)
709 {
710 RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
711 if (RemoveMessages)
712 {
713 MsqDestroyMessage(Message);
714 }
715 goto MessageFound;
716 }
717
718 /* Check for hardware events. */
719 Present = co_MsqFindMessage(ThreadQueue,
720 TRUE,
721 RemoveMessages,
722 hWnd,
723 MsgFilterMin,
724 MsgFilterMax,
725 &Message);
726 if (Present)
727 {
728 RtlCopyMemory(Msg, Message, sizeof(USER_MESSAGE));
729 if (RemoveMessages)
730 {
731 MsqDestroyMessage(Message);
732 }
733 goto MessageFound;
734 }
735
736 /* Check for sent messages again. */
737 while (co_MsqDispatchOneSentMessage(ThreadQueue))
738 ;
739
740 /* Check for paint messages. */
741 if (IntGetPaintMessage(hWnd, MsgFilterMin, MsgFilterMax, PsGetCurrentThreadWin32Thread(), &Msg->Msg, RemoveMessages))
742 {
743 Msg->FreeLParam = FALSE;
744 goto MsgExit;
745 }
746
747 /* Check for WM_(SYS)TIMER messages */
748 Present = MsqGetTimerMessage(ThreadQueue, hWnd, MsgFilterMin, MsgFilterMax,
749 &Msg->Msg, RemoveMessages);
750 if (Present)
751 {
752 Msg->FreeLParam = FALSE;
753 goto MessageFound;
754 }
755
756 if(Present)
757 {
758 MessageFound:
759
760 if(RemoveMessages)
761 {
762 PWINDOW_OBJECT MsgWindow = NULL;
763
764 if(Msg->Msg.hwnd && (MsgWindow = UserGetWindowObject(Msg->Msg.hwnd)) &&
765 Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST)
766 {
767 USHORT HitTest;
768
769 UserRefObjectCo(MsgWindow, &Ref);
770
771 if(co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, TRUE))
772 /* FIXME - check message filter again, if the message doesn't match anymore,
773 search again */
774 {
775 UserDerefObjectCo(MsgWindow);
776 /* eat the message, search again */
777 goto CheckMessages;
778 }
779
780 if(ThreadQueue->CaptureWindow == NULL)
781 {
782 co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
783 if((Msg->Msg.message != WM_MOUSEMOVE && Msg->Msg.message != WM_NCMOUSEMOVE) &&
784 IS_BTN_MESSAGE(Msg->Msg.message, DOWN) &&
785 co_IntActivateWindowMouse(ThreadQueue, &Msg->Msg, MsgWindow, &HitTest))
786 {
787 UserDerefObjectCo(MsgWindow);
788 /* eat the message, search again */
789 goto CheckMessages;
790 }
791 }
792
793 UserDerefObjectCo(MsgWindow);
794 }
795 else
796 {
797 co_IntSendHitTestMessages(ThreadQueue, &Msg->Msg);
798 }
799
800 // if(MsgWindow)
801 // {
802 // UserDereferenceObject(MsgWindow);
803 // }
804
805 goto MsgExit;
806 }
807
808 if((Msg->Msg.hwnd && Msg->Msg.message >= WM_MOUSEFIRST && Msg->Msg.message <= WM_MOUSELAST) &&
809 co_IntTranslateMouseMessage(ThreadQueue, &Msg->Msg, &HitTest, FALSE))
810 /* FIXME - check message filter again, if the message doesn't match anymore,
811 search again */
812 {
813 /* eat the message, search again */
814 goto CheckMessages;
815 }
816 MsgExit:
817 // The WH_GETMESSAGE hook enables an application to monitor messages about to
818 // be returned by the GetMessage or PeekMessage function.
819 if(ISITHOOKED(WH_GETMESSAGE))
820 {
821 //DPRINT1("Peek WH_GETMESSAGE -> %x\n",&Msg);
822 co_HOOK_CallHooks( WH_GETMESSAGE, HC_ACTION, RemoveMsg & PM_REMOVE, (LPARAM)&Msg->Msg);
823 }
824 return TRUE;
825 }
826
827 return Present;
828 }
829
830 BOOL STDCALL
831 NtUserPeekMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
832 HWND hWnd,
833 UINT MsgFilterMin,
834 UINT MsgFilterMax,
835 UINT RemoveMsg)
836 {
837 NTSTATUS Status;
838 BOOL Present;
839 NTUSERGETMESSAGEINFO Info;
840 PWINDOW_OBJECT Window;
841 PMSGMEMORY MsgMemoryEntry;
842 PVOID UserMem;
843 UINT Size;
844 USER_MESSAGE Msg;
845 DECLARE_RETURN(BOOL);
846
847 DPRINT("Enter NtUserPeekMessage\n");
848 UserEnterExclusive();
849
850 /* Validate input */
851 if (hWnd && hWnd != INVALID_HANDLE_VALUE)
852 {
853 if (!(Window = UserGetWindowObject(hWnd)))
854 {
855 RETURN(-1);
856 }
857 }
858
859 if (MsgFilterMax < MsgFilterMin)
860 {
861 MsgFilterMin = 0;
862 MsgFilterMax = 0;
863 }
864
865 Present = co_IntPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, RemoveMsg);
866
867 if (Present)
868 {
869
870 Info.Msg = Msg.Msg;
871 /* See if this message type is present in the table */
872 MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
873 if (NULL == MsgMemoryEntry)
874 {
875 /* Not present, no copying needed */
876 Info.LParamSize = 0;
877 }
878 else
879 {
880 /* Determine required size */
881 Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
882 Info.Msg.lParam);
883 /* Allocate required amount of user-mode memory */
884 Info.LParamSize = Size;
885 UserMem = NULL;
886 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
887 &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
888 if (! NT_SUCCESS(Status))
889 {
890 SetLastNtError(Status);
891 RETURN( (BOOL) -1);
892 }
893 /* Transfer lParam data to user-mode mem */
894 Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
895 if (! NT_SUCCESS(Status))
896 {
897 ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
898 &Info.LParamSize, MEM_RELEASE);
899 SetLastNtError(Status);
900 RETURN( (BOOL) -1);
901 }
902 Info.Msg.lParam = (LPARAM) UserMem;
903 }
904 if (RemoveMsg && Msg.FreeLParam && 0 != Msg.Msg.lParam)
905 {
906 ExFreePool((void *) Msg.Msg.lParam);
907 }
908 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
909 if (! NT_SUCCESS(Status))
910 {
911 SetLastNtError(Status);
912 RETURN( (BOOL) -1);
913 }
914 }
915
916 RETURN( Present);
917
918 CLEANUP:
919 DPRINT("Leave NtUserPeekMessage, ret=%i\n",_ret_);
920 UserLeave();
921 END_CLEANUP;
922 }
923
924 static BOOL FASTCALL
925 co_IntWaitMessage(HWND Wnd,
926 UINT MsgFilterMin,
927 UINT MsgFilterMax)
928 {
929 PUSER_MESSAGE_QUEUE ThreadQueue;
930 NTSTATUS Status;
931 USER_MESSAGE Msg;
932
933 ThreadQueue = (PUSER_MESSAGE_QUEUE)PsGetCurrentThreadWin32Thread()->MessageQueue;
934
935 do
936 {
937 if (co_IntPeekMessage(&Msg, Wnd, MsgFilterMin, MsgFilterMax, PM_NOREMOVE))
938 {
939 return TRUE;
940 }
941
942 /* Nothing found. Wait for new messages. */
943 Status = co_MsqWaitForNewMessages(ThreadQueue, Wnd, MsgFilterMin, MsgFilterMax);
944 }
945 while ((STATUS_WAIT_0 <= Status && Status <= STATUS_WAIT_63) || STATUS_TIMEOUT == Status);
946
947 SetLastNtError(Status);
948
949 return FALSE;
950 }
951
952 BOOL STDCALL
953 NtUserGetMessage(PNTUSERGETMESSAGEINFO UnsafeInfo,
954 HWND hWnd,
955 UINT MsgFilterMin,
956 UINT MsgFilterMax)
957 /*
958 * FUNCTION: Get a message from the calling thread's message queue.
959 * ARGUMENTS:
960 * UnsafeMsg - Pointer to the structure which receives the returned message.
961 * Wnd - Window whose messages are to be retrieved.
962 * MsgFilterMin - Integer value of the lowest message value to be
963 * retrieved.
964 * MsgFilterMax - Integer value of the highest message value to be
965 * retrieved.
966 */
967 {
968 BOOL GotMessage;
969 NTUSERGETMESSAGEINFO Info;
970 NTSTATUS Status;
971 PWINDOW_OBJECT Window = NULL;
972 PMSGMEMORY MsgMemoryEntry;
973 PVOID UserMem;
974 UINT Size;
975 USER_MESSAGE Msg;
976 DECLARE_RETURN(BOOL);
977 // USER_REFERENCE_ENTRY Ref;
978
979 DPRINT("Enter NtUserGetMessage\n");
980 UserEnterExclusive();
981
982 /* Validate input */
983 if (hWnd && !(Window = UserGetWindowObject(hWnd)))
984 {
985 RETURN(-1);
986 }
987
988 // if (Window) UserRefObjectCo(Window, &Ref);
989
990 if (MsgFilterMax < MsgFilterMin)
991 {
992 MsgFilterMin = 0;
993 MsgFilterMax = 0;
994 }
995
996 do
997 {
998 GotMessage = co_IntPeekMessage(&Msg, hWnd, MsgFilterMin, MsgFilterMax, PM_REMOVE);
999 if (GotMessage)
1000 {
1001 Info.Msg = Msg.Msg;
1002 /* See if this message type is present in the table */
1003 MsgMemoryEntry = FindMsgMemory(Info.Msg.message);
1004 if (NULL == MsgMemoryEntry)
1005 {
1006 /* Not present, no copying needed */
1007 Info.LParamSize = 0;
1008 }
1009 else
1010 {
1011 /* Determine required size */
1012 Size = MsgMemorySize(MsgMemoryEntry, Info.Msg.wParam,
1013 Info.Msg.lParam);
1014 /* Allocate required amount of user-mode memory */
1015 Info.LParamSize = Size;
1016 UserMem = NULL;
1017 Status = ZwAllocateVirtualMemory(NtCurrentProcess(), &UserMem, 0,
1018 &Info.LParamSize, MEM_COMMIT, PAGE_READWRITE);
1019
1020 if (! NT_SUCCESS(Status))
1021 {
1022 SetLastNtError(Status);
1023 RETURN( (BOOL) -1);
1024 }
1025 /* Transfer lParam data to user-mode mem */
1026 Status = MmCopyToCaller(UserMem, (PVOID) Info.Msg.lParam, Size);
1027 if (! NT_SUCCESS(Status))
1028 {
1029 ZwFreeVirtualMemory(NtCurrentProcess(), (PVOID *) &UserMem,
1030 &Info.LParamSize, MEM_DECOMMIT);
1031 SetLastNtError(Status);
1032 RETURN( (BOOL) -1);
1033 }
1034 Info.Msg.lParam = (LPARAM) UserMem;
1035 }
1036 if (Msg.FreeLParam && 0 != Msg.Msg.lParam)
1037 {
1038 ExFreePool((void *) Msg.Msg.lParam);
1039 }
1040 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERGETMESSAGEINFO));
1041 if (! NT_SUCCESS(Status))
1042 {
1043 SetLastNtError(Status);
1044 RETURN( (BOOL) -1);
1045 }
1046 }
1047 else if (! co_IntWaitMessage(hWnd, MsgFilterMin, MsgFilterMax))
1048 {
1049 RETURN( (BOOL) -1);
1050 }
1051 }
1052 while (! GotMessage);
1053
1054 RETURN( WM_QUIT != Info.Msg.message);
1055
1056 CLEANUP:
1057 // if (Window) UserDerefObjectCo(Window);
1058
1059 DPRINT("Leave NtUserGetMessage\n");
1060 UserLeave();
1061 END_CLEANUP;
1062 }
1063
1064
1065 static NTSTATUS FASTCALL
1066 CopyMsgToKernelMem(MSG *KernelModeMsg, MSG *UserModeMsg, PMSGMEMORY MsgMemoryEntry)
1067 {
1068 NTSTATUS Status;
1069
1070 PVOID KernelMem;
1071 UINT Size;
1072
1073 *KernelModeMsg = *UserModeMsg;
1074
1075 /* See if this message type is present in the table */
1076 if (NULL == MsgMemoryEntry)
1077 {
1078 /* Not present, no copying needed */
1079 return STATUS_SUCCESS;
1080 }
1081
1082 /* Determine required size */
1083 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
1084
1085 if (0 != Size)
1086 {
1087 /* Allocate kernel mem */
1088 KernelMem = ExAllocatePoolWithTag(PagedPool, Size, TAG_MSG);
1089 if (NULL == KernelMem)
1090 {
1091 DPRINT1("Not enough memory to copy message to kernel mem\n");
1092 return STATUS_NO_MEMORY;
1093 }
1094 KernelModeMsg->lParam = (LPARAM) KernelMem;
1095
1096 /* Copy data if required */
1097 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_READ))
1098 {
1099 Status = MmCopyFromCaller(KernelMem, (PVOID) UserModeMsg->lParam, Size);
1100 if (! NT_SUCCESS(Status))
1101 {
1102 DPRINT1("Failed to copy message to kernel: invalid usermode buffer\n");
1103 ExFreePool(KernelMem);
1104 return Status;
1105 }
1106 }
1107 else
1108 {
1109 /* Make sure we don't pass any secrets to usermode */
1110 RtlZeroMemory(KernelMem, Size);
1111 }
1112 }
1113 else
1114 {
1115 KernelModeMsg->lParam = 0;
1116 }
1117
1118 return STATUS_SUCCESS;
1119 }
1120
1121 static NTSTATUS FASTCALL
1122 CopyMsgToUserMem(MSG *UserModeMsg, MSG *KernelModeMsg)
1123 {
1124 NTSTATUS Status;
1125 PMSGMEMORY MsgMemoryEntry;
1126 UINT Size;
1127
1128 /* See if this message type is present in the table */
1129 MsgMemoryEntry = FindMsgMemory(UserModeMsg->message);
1130 if (NULL == MsgMemoryEntry)
1131 {
1132 /* Not present, no copying needed */
1133 return STATUS_SUCCESS;
1134 }
1135
1136 /* Determine required size */
1137 Size = MsgMemorySize(MsgMemoryEntry, UserModeMsg->wParam, UserModeMsg->lParam);
1138
1139 if (0 != Size)
1140 {
1141 /* Copy data if required */
1142 if (0 != (MsgMemoryEntry->Flags & MMS_FLAG_WRITE))
1143 {
1144 Status = MmCopyToCaller((PVOID) UserModeMsg->lParam, (PVOID) KernelModeMsg->lParam, Size);
1145 if (! NT_SUCCESS(Status))
1146 {
1147 DPRINT1("Failed to copy message from kernel: invalid usermode buffer\n");
1148 ExFreePool((PVOID) KernelModeMsg->lParam);
1149 return Status;
1150 }
1151 }
1152
1153 ExFreePool((PVOID) KernelModeMsg->lParam);
1154 }
1155
1156 return STATUS_SUCCESS;
1157 }
1158
1159 BOOL FASTCALL
1160 UserPostMessage(HWND Wnd,
1161 UINT Msg,
1162 WPARAM wParam,
1163 LPARAM lParam)
1164 {
1165 MSG UserModeMsg, KernelModeMsg;
1166 LARGE_INTEGER LargeTickCount;
1167 NTSTATUS Status;
1168 PMSGMEMORY MsgMemoryEntry;
1169
1170 if (WM_QUIT == Msg)
1171 {
1172 MsqPostQuitMessage(PsGetCurrentThreadWin32Thread()->MessageQueue, wParam);
1173 }
1174 else if (Wnd == HWND_BROADCAST)
1175 {
1176 HWND *List;
1177 PWINDOW_OBJECT DesktopWindow;
1178 ULONG i;
1179
1180 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
1181 List = IntWinListChildren(DesktopWindow);
1182
1183 if (List != NULL)
1184 {
1185 for (i = 0; List[i]; i++)
1186 UserPostMessage(List[i], Msg, wParam, lParam);
1187 ExFreePool(List);
1188 }
1189 }
1190 else
1191 {
1192 PWINDOW_OBJECT Window;
1193
1194 Window = UserGetWindowObject(Wnd);
1195 if (NULL == Window)
1196 {
1197 return FALSE;
1198 }
1199 if(Window->Status & WINDOWSTATUS_DESTROYING)
1200 {
1201 DPRINT1("Attempted to post message to window 0x%x that is being destroyed!\n", Wnd);
1202 /* FIXME - last error code? */
1203 return FALSE;
1204 }
1205
1206 UserModeMsg.hwnd = Wnd;
1207 UserModeMsg.message = Msg;
1208 UserModeMsg.wParam = wParam;
1209 UserModeMsg.lParam = lParam;
1210 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1211 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1212 if (! NT_SUCCESS(Status))
1213 {
1214 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1215 return FALSE;
1216 }
1217 IntGetCursorLocation(PsGetCurrentThreadWin32Thread()->Desktop->WindowStation,
1218 &KernelModeMsg.pt);
1219 KeQueryTickCount(&LargeTickCount);
1220 KernelModeMsg.time = MsqCalculateMessageTime(&LargeTickCount);
1221 MsqPostMessage(Window->MessageQueue, &KernelModeMsg,
1222 NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
1223 QS_POSTMESSAGE);
1224 }
1225
1226 return TRUE;
1227 }
1228
1229
1230 BOOL STDCALL
1231 NtUserPostMessage(HWND hWnd,
1232 UINT Msg,
1233 WPARAM wParam,
1234 LPARAM lParam)
1235 {
1236 DECLARE_RETURN(BOOL);
1237
1238 DPRINT("Enter NtUserPostMessage\n");
1239 UserEnterExclusive();
1240
1241 RETURN(UserPostMessage(hWnd, Msg, wParam, lParam));
1242
1243 CLEANUP:
1244 DPRINT("Leave NtUserPostMessage, ret=%i\n",_ret_);
1245 UserLeave();
1246 END_CLEANUP;
1247 }
1248
1249
1250
1251 BOOL STDCALL
1252 NtUserPostThreadMessage(DWORD idThread,
1253 UINT Msg,
1254 WPARAM wParam,
1255 LPARAM lParam)
1256 {
1257 MSG UserModeMsg, KernelModeMsg;
1258 PETHREAD peThread;
1259 PW32THREAD pThread;
1260 NTSTATUS Status;
1261 PMSGMEMORY MsgMemoryEntry;
1262 DECLARE_RETURN(BOOL);
1263
1264 DPRINT("Enter NtUserPostThreadMessage\n");
1265 UserEnterExclusive();
1266
1267 Status = PsLookupThreadByThreadId((HANDLE)idThread,&peThread);
1268
1269 if( Status == STATUS_SUCCESS )
1270 {
1271 pThread = (PW32THREAD)peThread->Tcb.Win32Thread;
1272 if( !pThread || !pThread->MessageQueue )
1273 {
1274 ObDereferenceObject( peThread );
1275 RETURN( FALSE);
1276 }
1277
1278 UserModeMsg.hwnd = NULL;
1279 UserModeMsg.message = Msg;
1280 UserModeMsg.wParam = wParam;
1281 UserModeMsg.lParam = lParam;
1282 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1283 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1284 if (! NT_SUCCESS(Status))
1285 {
1286 ObDereferenceObject( peThread );
1287 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1288 RETURN( FALSE);
1289 }
1290 MsqPostMessage(pThread->MessageQueue, &KernelModeMsg,
1291 NULL != MsgMemoryEntry && 0 != KernelModeMsg.lParam,
1292 QS_POSTMESSAGE);
1293 ObDereferenceObject( peThread );
1294 RETURN( TRUE);
1295 }
1296 else
1297 {
1298 SetLastNtError( Status );
1299 RETURN( FALSE);
1300 }
1301
1302 CLEANUP:
1303 DPRINT("Leave NtUserPostThreadMessage, ret=%i\n",_ret_);
1304 UserLeave();
1305 END_CLEANUP;
1306 }
1307
1308 DWORD STDCALL
1309 NtUserQuerySendMessage(DWORD Unknown0)
1310 {
1311 UNIMPLEMENTED;
1312
1313 return 0;
1314 }
1315
1316 LRESULT FASTCALL
1317 co_IntSendMessage(HWND hWnd,
1318 UINT Msg,
1319 WPARAM wParam,
1320 LPARAM lParam)
1321 {
1322 ULONG_PTR Result = 0;
1323 if(co_IntSendMessageTimeout(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
1324 {
1325 return (LRESULT)Result;
1326 }
1327 return 0;
1328 }
1329
1330 static
1331 LRESULT FASTCALL
1332 co_IntSendMessageTimeoutSingle(HWND hWnd,
1333 UINT Msg,
1334 WPARAM wParam,
1335 LPARAM lParam,
1336 UINT uFlags,
1337 UINT uTimeout,
1338 ULONG_PTR *uResult)
1339 {
1340 ULONG_PTR Result;
1341 NTSTATUS Status;
1342 PWINDOW_OBJECT Window = NULL;
1343 PMSGMEMORY MsgMemoryEntry;
1344 INT lParamBufferSize;
1345 LPARAM lParamPacked;
1346 PW32THREAD Win32Thread;
1347 DECLARE_RETURN(LRESULT);
1348 USER_REFERENCE_ENTRY Ref;
1349
1350 /* FIXME: Call hooks. */
1351 if (!(Window = UserGetWindowObject(hWnd)))
1352 {
1353 RETURN( FALSE);
1354 }
1355
1356 UserRefObjectCo(Window, &Ref);
1357
1358 Win32Thread = PsGetCurrentThreadWin32Thread();
1359
1360 if (NULL != Win32Thread &&
1361 Window->MessageQueue == Win32Thread->MessageQueue)
1362 {
1363 if (Win32Thread->IsExiting)
1364 {
1365 /* Never send messages to exiting threads */
1366 RETURN( FALSE);
1367 }
1368
1369 /* See if this message type is present in the table */
1370 MsgMemoryEntry = FindMsgMemory(Msg);
1371 if (NULL == MsgMemoryEntry)
1372 {
1373 lParamBufferSize = -1;
1374 }
1375 else
1376 {
1377 lParamBufferSize = MsgMemorySize(MsgMemoryEntry, wParam, lParam);
1378 }
1379
1380 if (! NT_SUCCESS(PackParam(&lParamPacked, Msg, wParam, lParam)))
1381 {
1382 DPRINT1("Failed to pack message parameters\n");
1383 RETURN( FALSE);
1384 }
1385
1386 Result = (ULONG_PTR)co_IntCallWindowProc(Window->Wnd->WndProc, !Window->Wnd->Unicode, hWnd, Msg, wParam,
1387 lParamPacked,lParamBufferSize);
1388
1389 if(uResult)
1390 {
1391 *uResult = Result;
1392 }
1393
1394 if (! NT_SUCCESS(UnpackParam(lParamPacked, Msg, wParam, lParam)))
1395 {
1396 DPRINT1("Failed to unpack message parameters\n");
1397 RETURN( TRUE);
1398 }
1399
1400 RETURN( TRUE);
1401 }
1402
1403 if(uFlags & SMTO_ABORTIFHUNG && MsqIsHung(Window->MessageQueue))
1404 {
1405 /* FIXME - Set a LastError? */
1406 RETURN( FALSE);
1407 }
1408
1409 if(Window->Status & WINDOWSTATUS_DESTROYING)
1410 {
1411 /* FIXME - last error? */
1412 DPRINT1("Attempted to send message to window 0x%x that is being destroyed!\n", hWnd);
1413 RETURN( FALSE);
1414 }
1415
1416 Status = co_MsqSendMessage(Window->MessageQueue, hWnd, Msg, wParam, lParam,
1417 uTimeout, (uFlags & SMTO_BLOCK), FALSE, uResult);
1418
1419
1420 if (STATUS_TIMEOUT == Status)
1421 {
1422 /* MSDN says GetLastError() should return 0 after timeout */
1423 SetLastWin32Error(0);
1424 RETURN( FALSE);
1425 }
1426 else if (! NT_SUCCESS(Status))
1427 {
1428 SetLastNtError(Status);
1429 RETURN( FALSE);
1430 }
1431
1432 RETURN( TRUE);
1433
1434 CLEANUP:
1435 if (Window) UserDerefObjectCo(Window);
1436 END_CLEANUP;
1437 }
1438
1439 LRESULT FASTCALL
1440 co_IntSendMessageTimeout(HWND hWnd,
1441 UINT Msg,
1442 WPARAM wParam,
1443 LPARAM lParam,
1444 UINT uFlags,
1445 UINT uTimeout,
1446 ULONG_PTR *uResult)
1447 {
1448 PWINDOW_OBJECT DesktopWindow;
1449 HWND *Children;
1450 HWND *Child;
1451
1452 if (HWND_BROADCAST != hWnd)
1453 {
1454 return co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1455 }
1456
1457 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
1458 if (NULL == DesktopWindow)
1459 {
1460 SetLastWin32Error(ERROR_INTERNAL_ERROR);
1461 return 0;
1462 }
1463
1464 Children = IntWinListChildren(DesktopWindow);
1465 if (NULL == Children)
1466 {
1467 return 0;
1468 }
1469
1470 for (Child = Children; NULL != *Child; Child++)
1471 {
1472 co_IntSendMessageTimeoutSingle(*Child, Msg, wParam, lParam, uFlags, uTimeout, uResult);
1473 }
1474
1475 ExFreePool(Children);
1476
1477 return (LRESULT) TRUE;
1478 }
1479
1480
1481 /* This function posts a message if the destination's message queue belongs to
1482 another thread, otherwise it sends the message. It does not support broadcast
1483 messages! */
1484 LRESULT FASTCALL
1485 co_IntPostOrSendMessage(HWND hWnd,
1486 UINT Msg,
1487 WPARAM wParam,
1488 LPARAM lParam)
1489 {
1490 ULONG_PTR Result;
1491 PWINDOW_OBJECT Window;
1492
1493 if(hWnd == HWND_BROADCAST)
1494 {
1495 return 0;
1496 }
1497
1498 if(!(Window = UserGetWindowObject(hWnd)))
1499 {
1500 return 0;
1501 }
1502
1503 if(Window->MessageQueue != PsGetCurrentThreadWin32Thread()->MessageQueue)
1504 {
1505 Result = UserPostMessage(hWnd, Msg, wParam, lParam);
1506 }
1507 else
1508 {
1509 if(!co_IntSendMessageTimeoutSingle(hWnd, Msg, wParam, lParam, SMTO_NORMAL, 0, &Result))
1510 {
1511 Result = 0;
1512 }
1513 }
1514
1515 return (LRESULT)Result;
1516 }
1517
1518 LRESULT FASTCALL
1519 co_IntDoSendMessage(HWND hWnd,
1520 UINT Msg,
1521 WPARAM wParam,
1522 LPARAM lParam,
1523 PDOSENDMESSAGE dsm,
1524 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1525 {
1526 LRESULT Result = TRUE;
1527 NTSTATUS Status;
1528 PWINDOW_OBJECT Window;
1529 NTUSERSENDMESSAGEINFO Info;
1530 MSG UserModeMsg;
1531 MSG KernelModeMsg;
1532 PMSGMEMORY MsgMemoryEntry;
1533
1534 RtlZeroMemory(&Info, sizeof(NTUSERSENDMESSAGEINFO));
1535
1536 /* FIXME: Call hooks. */
1537 if (HWND_BROADCAST != hWnd)
1538 {
1539 Window = UserGetWindowObject(hWnd);
1540 if (NULL == Window)
1541 {
1542 /* Tell usermode to not touch this one */
1543 Info.HandledByKernel = TRUE;
1544 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1545 return 0;
1546 }
1547 if (!Window->Wnd)
1548 return 0;
1549 }
1550
1551 /* FIXME: Check for an exiting window. */
1552
1553 /* See if the current thread can handle the message */
1554 if (HWND_BROADCAST != hWnd && NULL != PsGetCurrentThreadWin32Thread() &&
1555 Window->MessageQueue == PsGetCurrentThreadWin32Thread()->MessageQueue)
1556 {
1557 /* Gather the information usermode needs to call the window proc directly */
1558 Info.HandledByKernel = FALSE;
1559
1560 Status = MmCopyFromCaller(&(Info.Ansi), &(UnsafeInfo->Ansi),
1561 sizeof(BOOL));
1562 if (! NT_SUCCESS(Status))
1563 {
1564 Info.Ansi = ! Window->Wnd->Unicode;
1565 }
1566
1567 if (Window->Wnd->IsSystem)
1568 {
1569 Info.Proc = (!Info.Ansi ? Window->Wnd->WndProc : Window->Wnd->WndProcExtra);
1570 }
1571 else
1572 {
1573 Info.Ansi = !Window->Wnd->Unicode;
1574 Info.Proc = Window->Wnd->WndProc;
1575 }
1576 }
1577 else
1578 {
1579 /* Must be handled by other thread */
1580 // if (HWND_BROADCAST != hWnd)
1581 // {
1582 // UserDereferenceObject(Window);
1583 // }
1584 Info.HandledByKernel = TRUE;
1585 UserModeMsg.hwnd = hWnd;
1586 UserModeMsg.message = Msg;
1587 UserModeMsg.wParam = wParam;
1588 UserModeMsg.lParam = lParam;
1589 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1590 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1591 if (! NT_SUCCESS(Status))
1592 {
1593 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1594 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1595 return (dsm ? 0 : -1);
1596 }
1597 if(!dsm)
1598 {
1599 Result = co_IntSendMessage(KernelModeMsg.hwnd, KernelModeMsg.message,
1600 KernelModeMsg.wParam, KernelModeMsg.lParam);
1601 }
1602 else
1603 {
1604 Result = co_IntSendMessageTimeout(KernelModeMsg.hwnd, KernelModeMsg.message,
1605 KernelModeMsg.wParam, KernelModeMsg.lParam,
1606 dsm->uFlags, dsm->uTimeout, &dsm->Result);
1607 }
1608 Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
1609 if (! NT_SUCCESS(Status))
1610 {
1611 MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1612 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1613 return(dsm ? 0 : -1);
1614 }
1615 }
1616
1617 Status = MmCopyToCaller(UnsafeInfo, &Info, sizeof(NTUSERSENDMESSAGEINFO));
1618 if (! NT_SUCCESS(Status))
1619 {
1620 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1621 }
1622
1623 return (LRESULT)Result;
1624 }
1625
1626 LRESULT STDCALL
1627 NtUserSendMessageTimeout(HWND hWnd,
1628 UINT Msg,
1629 WPARAM wParam,
1630 LPARAM lParam,
1631 UINT uFlags,
1632 UINT uTimeout,
1633 ULONG_PTR *uResult,
1634 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1635 {
1636 DOSENDMESSAGE dsm;
1637 LRESULT Result;
1638 DECLARE_RETURN(BOOL);
1639
1640 DPRINT("Enter NtUserSendMessageTimeout\n");
1641 UserEnterExclusive();
1642
1643 dsm.uFlags = uFlags;
1644 dsm.uTimeout = uTimeout;
1645 Result = co_IntDoSendMessage(hWnd, Msg, wParam, lParam, &dsm, UnsafeInfo);
1646 if(uResult != NULL && Result != 0)
1647 {
1648 NTSTATUS Status;
1649
1650 Status = MmCopyToCaller(uResult, &dsm.Result, sizeof(ULONG_PTR));
1651 if(!NT_SUCCESS(Status))
1652 {
1653 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1654 RETURN( FALSE);
1655 }
1656 }
1657 RETURN( Result);
1658
1659 CLEANUP:
1660 DPRINT("Leave NtUserSendMessageTimeout, ret=%i\n",_ret_);
1661 UserLeave();
1662 END_CLEANUP;
1663 }
1664
1665 LRESULT STDCALL
1666 NtUserSendMessage(HWND Wnd,
1667 UINT Msg,
1668 WPARAM wParam,
1669 LPARAM lParam,
1670 PNTUSERSENDMESSAGEINFO UnsafeInfo)
1671 {
1672 DECLARE_RETURN(BOOL);
1673
1674 DPRINT("Enter NtUserSendMessage\n");
1675 UserEnterExclusive();
1676
1677 RETURN(co_IntDoSendMessage(Wnd, Msg, wParam, lParam, NULL, UnsafeInfo));
1678
1679 CLEANUP:
1680 DPRINT("Leave NtUserSendMessage, ret=%i\n",_ret_);
1681 UserLeave();
1682 END_CLEANUP;
1683 }
1684
1685
1686 BOOL FASTCALL
1687 UserSendNotifyMessage(HWND hWnd,
1688 UINT Msg,
1689 WPARAM wParam,
1690 LPARAM lParam)
1691 {
1692 BOOL Result = TRUE;
1693 // Basicly the same as IntPostOrSendMessage
1694 if (hWnd == HWND_BROADCAST) //Handle Broadcast
1695 {
1696 HWND *List;
1697 PWINDOW_OBJECT DesktopWindow;
1698 ULONG i;
1699
1700 DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
1701 List = IntWinListChildren(DesktopWindow);
1702
1703 if (List != NULL)
1704 {
1705 for (i = 0; List[i]; i++)
1706 {
1707 UserSendNotifyMessage(List[i], Msg, wParam, lParam);
1708 }
1709 ExFreePool(List);
1710 }
1711 }
1712 else
1713 {
1714 ULONG_PTR PResult;
1715 PWINDOW_OBJECT Window;
1716 NTSTATUS Status;
1717 MSG UserModeMsg;
1718 MSG KernelModeMsg;
1719 PMSGMEMORY MsgMemoryEntry;
1720
1721 if(!(Window = UserGetWindowObject(hWnd))) return FALSE;
1722
1723 if(Window->MessageQueue != PsGetCurrentThreadWin32Thread()->MessageQueue)
1724 { // Send message w/o waiting for it.
1725 Result = UserPostMessage(hWnd, Msg, wParam, lParam);
1726 }
1727 else
1728 { // Handle message and callback.
1729 UserModeMsg.hwnd = hWnd;
1730 UserModeMsg.message = Msg;
1731 UserModeMsg.wParam = wParam;
1732 UserModeMsg.lParam = lParam;
1733 MsgMemoryEntry = FindMsgMemory(UserModeMsg.message);
1734 Status = CopyMsgToKernelMem(&KernelModeMsg, &UserModeMsg, MsgMemoryEntry);
1735 if (! NT_SUCCESS(Status))
1736 {
1737 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1738 return FALSE;
1739 }
1740 Result = co_IntSendMessageTimeoutSingle(
1741 KernelModeMsg.hwnd, KernelModeMsg.message,
1742 KernelModeMsg.wParam, KernelModeMsg.lParam,
1743 SMTO_NORMAL, 0, &PResult);
1744
1745 Status = CopyMsgToUserMem(&UserModeMsg, &KernelModeMsg);
1746 if (! NT_SUCCESS(Status))
1747 {
1748 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1749 return FALSE;
1750 }
1751 }
1752 }
1753 return Result;
1754 }
1755
1756
1757 BOOL STDCALL
1758 NtUserSendNotifyMessage(HWND hWnd,
1759 UINT Msg,
1760 WPARAM wParam,
1761 LPARAM lParam)
1762 {
1763 DECLARE_RETURN(BOOL);
1764
1765 DPRINT("EnterNtUserSendNotifyMessage\n");
1766 UserEnterExclusive();
1767
1768 RETURN(UserSendNotifyMessage(hWnd, Msg, wParam, lParam));
1769
1770 CLEANUP:
1771 DPRINT("Leave NtUserSendNotifyMessage, ret=%i\n",_ret_);
1772 UserLeave();
1773 END_CLEANUP;
1774
1775 }
1776
1777
1778 BOOL STDCALL
1779 NtUserWaitMessage(VOID)
1780 {
1781 DECLARE_RETURN(BOOL);
1782
1783 DPRINT("EnterNtUserWaitMessage\n");
1784 UserEnterExclusive();
1785
1786 RETURN(co_IntWaitMessage(NULL, 0, 0));
1787
1788 CLEANUP:
1789 DPRINT("Leave NtUserWaitMessage, ret=%i\n",_ret_);
1790 UserLeave();
1791 END_CLEANUP;
1792 }
1793
1794 DWORD STDCALL
1795 IntGetQueueStatus(BOOL ClearChanges)
1796 {
1797 PUSER_MESSAGE_QUEUE Queue;
1798 DWORD Result;
1799 DECLARE_RETURN(DWORD);
1800
1801 DPRINT("Enter IntGetQueueStatus\n");
1802
1803 Queue = PsGetCurrentThreadWin32Thread()->MessageQueue;
1804
1805 Result = MAKELONG(Queue->QueueBits, Queue->ChangedBits);
1806 if (ClearChanges)
1807 {
1808 Queue->ChangedBits = 0;
1809 }
1810
1811 RETURN(Result);
1812
1813 CLEANUP:
1814 DPRINT("Leave IntGetQueueStatus, ret=%i\n",_ret_);
1815 END_CLEANUP;
1816 }
1817
1818 BOOL STDCALL
1819 IntInitMessagePumpHook()
1820 {
1821 if (((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->ThreadInfo)
1822 {
1823 ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->ThreadInfo->ClientThreadInfo.dwcPumpHook++;
1824 return TRUE;
1825 }
1826 return FALSE;
1827 }
1828
1829 BOOL STDCALL
1830 IntUninitMessagePumpHook()
1831 {
1832 if (((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->ThreadInfo)
1833 {
1834 if (((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->ThreadInfo->ClientThreadInfo.dwcPumpHook <= 0)
1835 {
1836 return FALSE;
1837 }
1838 ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->ThreadInfo->ClientThreadInfo.dwcPumpHook--;
1839 return TRUE;
1840 }
1841 return FALSE;
1842 }
1843
1844
1845 LRESULT STDCALL
1846 NtUserMessageCall(
1847 HWND hWnd,
1848 UINT Msg,
1849 WPARAM wParam,
1850 LPARAM lParam,
1851 ULONG_PTR ResultInfo,
1852 DWORD dwType, // fnID?
1853 BOOL Ansi)
1854 {
1855 LRESULT lResult = 0;
1856 PWINDOW_OBJECT Window = NULL;
1857 USER_REFERENCE_ENTRY Ref;
1858
1859 UserEnterExclusive();
1860
1861 /* Validate input */
1862 if (hWnd && (hWnd != INVALID_HANDLE_VALUE) && !(Window = UserGetWindowObject(hWnd)))
1863 {
1864 return 0;
1865 }
1866 UserRefObjectCo(Window, &Ref);
1867 switch(dwType)
1868 {
1869 case FNID_DEFWINDOWPROC:
1870 lResult = IntDefWindowProc(Window, Msg, wParam, lParam, Ansi);
1871 break;
1872 }
1873 UserDerefObjectCo(Window);
1874 UserLeave();
1875 return lResult;
1876 }
1877
1878 #define INFINITE 0xFFFFFFFF
1879 #define WAIT_FAILED ((DWORD)0xFFFFFFFF)
1880
1881 DWORD
1882 NTAPI
1883 NtUserWaitForInputIdle(
1884 IN HANDLE hProcess,
1885 IN DWORD dwMilliseconds,
1886 IN BOOL Unknown2)
1887 {
1888 PEPROCESS Process;
1889 PW32PROCESS W32Process;
1890 NTSTATUS Status;
1891 HANDLE Handles[2];
1892 LARGE_INTEGER Timeout;
1893 ULONGLONG StartTime, Run, Elapsed = 0;
1894
1895 UserEnterExclusive();
1896
1897 Status = ObReferenceObjectByHandle(hProcess,
1898 PROCESS_QUERY_INFORMATION,
1899 PsProcessType,
1900 UserMode,
1901 (PVOID*)&Process,
1902 NULL);
1903
1904 if (!NT_SUCCESS(Status))
1905 {
1906 UserLeave();
1907 SetLastNtError(Status);
1908 return WAIT_FAILED;
1909 }
1910
1911 W32Process = (PW32PROCESS)Process->Win32Process;
1912 if (!W32Process)
1913 {
1914 ObDereferenceObject(Process);
1915 UserLeave();
1916 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1917 return WAIT_FAILED;
1918 }
1919
1920 EngCreateEvent((PEVENT *)&W32Process->InputIdleEvent);
1921
1922 Handles[0] = Process;
1923 Handles[1] = W32Process->InputIdleEvent;
1924
1925 if (!Handles[1])
1926 {
1927 ObDereferenceObject(Process);
1928 UserLeave();
1929 return STATUS_SUCCESS; /* no event to wait on */
1930 }
1931
1932 StartTime = ((ULONGLONG)SharedUserData->TickCountLowDeprecated *
1933 SharedUserData->TickCountMultiplier / 16777216);
1934
1935 Run = dwMilliseconds;
1936
1937 DPRINT("WFII: waiting for %p\n", Handles[1] );
1938 do
1939 {
1940 Timeout.QuadPart = Run - Elapsed;
1941 UserLeave();
1942 Status = KeWaitForMultipleObjects( 2,
1943 Handles,
1944 WaitAny,
1945 UserRequest,
1946 UserMode,
1947 FALSE,
1948 dwMilliseconds == INFINITE ? NULL : &Timeout,
1949 NULL);
1950 UserEnterExclusive();
1951
1952 if (!NT_SUCCESS(Status))
1953 {
1954 SetLastNtError(Status);
1955 Status = WAIT_FAILED;
1956 goto WaitExit;
1957 }
1958
1959 switch (Status)
1960 {
1961 case STATUS_WAIT_0:
1962 Status = WAIT_FAILED;
1963 goto WaitExit;
1964
1965 case STATUS_WAIT_2:
1966 {
1967 USER_MESSAGE Msg;
1968 co_IntPeekMessage( &Msg, 0, 0, 0, PM_REMOVE | PM_QS_SENDMESSAGE );
1969 break;
1970 }
1971
1972 case STATUS_USER_APC:
1973 case STATUS_ALERTED:
1974 case STATUS_TIMEOUT:
1975 DPRINT1("WFII: timeout\n");
1976 Status = STATUS_TIMEOUT;
1977 goto WaitExit;
1978
1979 default:
1980 DPRINT1("WFII: finished\n");
1981 Status = STATUS_SUCCESS;
1982 goto WaitExit;
1983 }
1984
1985 if (dwMilliseconds != INFINITE)
1986 {
1987 Elapsed = ((ULONGLONG)SharedUserData->TickCountLowDeprecated *
1988 SharedUserData->TickCountMultiplier / 16777216)
1989 - StartTime;
1990
1991 if (Elapsed > Run)
1992 Status = STATUS_TIMEOUT;
1993 break;
1994 }
1995 }
1996 while (1);
1997
1998 WaitExit:
1999 if (W32Process->InputIdleEvent)
2000 {
2001 EngDeleteEvent((PEVENT)W32Process->InputIdleEvent);
2002 W32Process->InputIdleEvent = NULL;
2003 }
2004 ObDereferenceObject(Process);
2005 UserLeave();
2006 return Status;
2007 }
2008
2009 /* EOF */