[CONSRV]
[reactos.git] / reactos / win32ss / user / ntuser / desktop.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Desktops
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserDesktop);
13
14 static NTSTATUS
15 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta);
16
17 static NTSTATUS
18 IntMapDesktopView(IN PDESKTOP pdesk);
19
20 static NTSTATUS
21 IntUnmapDesktopView(IN PDESKTOP pdesk);
22
23 static VOID
24 IntFreeDesktopHeap(IN PDESKTOP pdesk);
25
26 /* GLOBALS *******************************************************************/
27
28 /* Currently active desktop */
29 PDESKTOP gpdeskInputDesktop = NULL;
30 HDC ScreenDeviceContext = NULL;
31 PTHREADINFO gptiDesktopThread;
32 HCURSOR gDesktopCursor = NULL;
33
34 /* OBJECT CALLBACKS **********************************************************/
35
36 NTSTATUS
37 APIENTRY
38 IntDesktopObjectParse(IN PVOID ParseObject,
39 IN PVOID ObjectType,
40 IN OUT PACCESS_STATE AccessState,
41 IN KPROCESSOR_MODE AccessMode,
42 IN ULONG Attributes,
43 IN OUT PUNICODE_STRING CompleteName,
44 IN OUT PUNICODE_STRING RemainingName,
45 IN OUT PVOID Context OPTIONAL,
46 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
47 OUT PVOID *Object)
48 {
49 NTSTATUS Status;
50 PDESKTOP Desktop;
51 OBJECT_ATTRIBUTES ObjectAttributes;
52 PLIST_ENTRY NextEntry, ListHead;
53 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
54 PUNICODE_STRING DesktopName;
55 PBOOLEAN pContext = (PBOOLEAN) Context;
56
57 if(pContext)
58 *pContext = FALSE;
59
60 /* Set the list pointers and loop the window station */
61 ListHead = &WinStaObject->DesktopListHead;
62 NextEntry = ListHead->Flink;
63 while (NextEntry != ListHead)
64 {
65 /* Get the current desktop */
66 Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
67
68 /// @todo Don't mess around with the object headers!
69 /* Get its name */
70 _PRAGMA_WARNING_SUPPRESS(__WARNING_DEREF_NULL_PTR)
71 DesktopName = GET_DESKTOP_NAME(Desktop);
72 if (DesktopName)
73 {
74 /* Compare the name */
75 if (RtlEqualUnicodeString(RemainingName,
76 DesktopName,
77 (Attributes & OBJ_CASE_INSENSITIVE)))
78 {
79 /* We found a match. Did this come from a create? */
80 if (Context)
81 {
82 /* Unless OPEN_IF was given, fail with an error */
83 if (!(Attributes & OBJ_OPENIF))
84 {
85 /* Name collision */
86 return STATUS_OBJECT_NAME_COLLISION;
87 }
88 else
89 {
90 /* Otherwise, return with a warning only */
91 Status = STATUS_OBJECT_NAME_EXISTS;
92 }
93 }
94 else
95 {
96 /* This was a real open, so this is OK */
97 Status = STATUS_SUCCESS;
98 }
99
100 /* Reference the desktop and return it */
101 ObReferenceObject(Desktop);
102 *Object = Desktop;
103 return Status;
104 }
105 }
106
107 /* Go to the next desktop */
108 NextEntry = NextEntry->Flink;
109 }
110
111 /* If we got here but this isn't a create, then fail */
112 if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
113
114 /* Create the desktop object */
115 InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
116 Status = ObCreateObject(KernelMode,
117 ExDesktopObjectType,
118 &ObjectAttributes,
119 KernelMode,
120 NULL,
121 sizeof(DESKTOP),
122 0,
123 0,
124 (PVOID*)&Desktop);
125 if (!NT_SUCCESS(Status)) return Status;
126
127 /* Initialize the desktop */
128 Status = UserInitializeDesktop(Desktop, RemainingName, WinStaObject);
129 if (!NT_SUCCESS(Status))
130 {
131 ObDereferenceObject(Desktop);
132 return Status;
133 }
134
135 /* Set the desktop object and return success */
136 *Object = Desktop;
137 *pContext = TRUE;
138 return STATUS_SUCCESS;
139 }
140
141 VOID APIENTRY
142 IntDesktopObjectDelete(PWIN32_DELETEMETHOD_PARAMETERS Parameters)
143 {
144 PDESKTOP pdesk = (PDESKTOP)Parameters->Object;
145
146 TRACE("Deleting desktop object 0x%p\n", pdesk);
147
148 ASSERT(pdesk->pDeskInfo->spwnd->spwndChild == NULL);
149
150 if (pdesk->pDeskInfo->spwnd)
151 co_UserDestroyWindow(pdesk->pDeskInfo->spwnd);
152
153 if (pdesk->spwndMessage)
154 co_UserDestroyWindow(pdesk->spwndMessage);
155
156 /* Remove the desktop from the window station's list of associcated desktops */
157 RemoveEntryList(&pdesk->ListEntry);
158
159 /* Free the heap */
160 IntFreeDesktopHeap(pdesk);
161 }
162
163 NTSTATUS NTAPI
164 IntDesktopOkToClose(PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS Parameters)
165 {
166 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
167
168 if( pti == NULL)
169 {
170 /* This happens when we leak desktop handles */
171 return STATUS_SUCCESS;
172 }
173
174 /* Do not allow the current desktop or the initial desktop to be closed */
175 if( Parameters->Handle == pti->ppi->hdeskStartup ||
176 Parameters->Handle == pti->hdesk)
177 {
178 return STATUS_ACCESS_DENIED;
179 }
180
181 return STATUS_SUCCESS;
182 }
183
184 NTSTATUS NTAPI IntDesktopObjectOpen(PWIN32_OPENMETHOD_PARAMETERS Parameters)
185 {
186 PPROCESSINFO ppi = PsGetProcessWin32Process(Parameters->Process);
187 if (ppi == NULL)
188 return STATUS_SUCCESS;
189
190 return IntMapDesktopView((PDESKTOP)Parameters->Object);
191 }
192
193 NTSTATUS NTAPI IntDesktopObjectClose(PWIN32_CLOSEMETHOD_PARAMETERS Parameters)
194 {
195 PPROCESSINFO ppi = PsGetProcessWin32Process(Parameters->Process);
196 if (ppi == NULL)
197 {
198 /* This happens when the process leaks desktop handles.
199 * At this point the PPROCESSINFO is already destroyed */
200 return STATUS_SUCCESS;
201 }
202
203 return IntUnmapDesktopView((PDESKTOP)Parameters->Object);
204 }
205
206
207 /* PRIVATE FUNCTIONS **********************************************************/
208
209 INIT_FUNCTION
210 NTSTATUS
211 NTAPI
212 InitDesktopImpl(VOID)
213 {
214 GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
215 DESKTOP_WRITE,
216 DESKTOP_EXECUTE,
217 DESKTOP_ALL_ACCESS};
218
219 /* Set Desktop Object Attributes */
220 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
221 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
222 ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
223 return STATUS_SUCCESS;
224 }
225
226 static int GetSystemVersionString(LPWSTR buffer)
227 {
228 RTL_OSVERSIONINFOEXW versionInfo;
229 int len;
230
231 versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
232
233 if (!NT_SUCCESS(RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo)))
234 return 0;
235
236 if (versionInfo.dwMajorVersion <= 4)
237 len = swprintf(buffer,
238 L"ReactOS Version %lu.%lu %s Build %lu",
239 versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
240 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
241 else
242 len = swprintf(buffer,
243 L"ReactOS %s (Build %lu)",
244 versionInfo.szCSDVersion, versionInfo.dwBuildNumber&0xFFFF);
245
246 return len;
247 }
248
249
250 NTSTATUS FASTCALL
251 IntParseDesktopPath(PEPROCESS Process,
252 PUNICODE_STRING DesktopPath,
253 HWINSTA *hWinSta,
254 HDESK *hDesktop)
255 {
256 OBJECT_ATTRIBUTES ObjectAttributes;
257 UNICODE_STRING ObjectName;
258 NTSTATUS Status;
259 WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL;
260
261 ASSERT(hWinSta);
262 ASSERT(hDesktop);
263 ASSERT(DesktopPath);
264
265 *hWinSta = NULL;
266 *hDesktop = NULL;
267
268 if(DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
269 {
270 /*
271 * Parse the desktop path string which can be in the form "WinSta\Desktop"
272 * or just "Desktop". In latter case WinSta0 will be used.
273 */
274
275 pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\');
276 if(pwstrDesktop != NULL)
277 {
278 *pwstrDesktop = 0;
279 pwstrDesktop++;
280 pwstrWinsta = DesktopPath->Buffer;
281 }
282 else
283 {
284 pwstrDesktop = DesktopPath->Buffer;
285 pwstrWinsta = NULL;
286 }
287
288 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop);
289 }
290
291 #if 0
292 /* Search the process handle table for (inherited) window station
293 handles, use a more appropriate one than WinSta0 if possible. */
294 if (!ObFindHandleForObject(Process,
295 NULL,
296 ExWindowStationObjectType,
297 NULL,
298 (PHANDLE)hWinSta))
299 #endif
300 {
301 /* We had no luck searching for opened handles, use WinSta0 now */
302 if(!pwstrWinsta)
303 pwstrWinsta = L"WinSta0";
304 }
305
306 #if 0
307 /* Search the process handle table for (inherited) desktop
308 handles, use a more appropriate one than Default if possible. */
309 if (!ObFindHandleForObject(Process,
310 NULL,
311 ExDesktopObjectType,
312 NULL,
313 (PHANDLE)hDesktop))
314 #endif
315 {
316 /* We had no luck searching for opened handles, use Desktop now */
317 if(!pwstrDesktop)
318 pwstrDesktop = L"Default";
319 }
320
321 if(*hWinSta == NULL)
322 {
323 swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta);
324 RtlInitUnicodeString( &ObjectName, wstrWinstaFullName);
325
326 TRACE("parsed initial winsta: %wZ\n", &ObjectName);
327
328 /* Open the window station */
329 InitializeObjectAttributes(&ObjectAttributes,
330 &ObjectName,
331 OBJ_CASE_INSENSITIVE,
332 NULL,
333 NULL);
334
335 Status = ObOpenObjectByName(&ObjectAttributes,
336 ExWindowStationObjectType,
337 KernelMode,
338 NULL,
339 WINSTA_ACCESS_ALL,
340 NULL,
341 (HANDLE*)hWinSta);
342
343 if(!NT_SUCCESS(Status))
344 {
345 SetLastNtError(Status);
346 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName );
347 return Status;
348 }
349 }
350
351 if(*hDesktop == NULL)
352 {
353 RtlInitUnicodeString(&ObjectName, pwstrDesktop);
354
355 TRACE("parsed initial desktop: %wZ\n", &ObjectName);
356
357 /* Open the desktop object */
358 InitializeObjectAttributes(&ObjectAttributes,
359 &ObjectName,
360 OBJ_CASE_INSENSITIVE,
361 *hWinSta,
362 NULL);
363
364 Status = ObOpenObjectByName(&ObjectAttributes,
365 ExDesktopObjectType,
366 KernelMode,
367 NULL,
368 DESKTOP_ALL_ACCESS,
369 NULL,
370 (HANDLE*)hDesktop);
371
372 if(!NT_SUCCESS(Status))
373 {
374 *hDesktop = NULL;
375 NtClose(*hWinSta);
376 *hWinSta = NULL;
377 SetLastNtError(Status);
378 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName);
379 return Status;
380 }
381 }
382 return STATUS_SUCCESS;
383 }
384
385 /*
386 * IntValidateDesktopHandle
387 *
388 * Validates the desktop handle.
389 *
390 * Remarks
391 * If the function succeeds, the handle remains referenced. If the
392 * fucntion fails, last error is set.
393 */
394
395 NTSTATUS FASTCALL
396 IntValidateDesktopHandle(
397 HDESK Desktop,
398 KPROCESSOR_MODE AccessMode,
399 ACCESS_MASK DesiredAccess,
400 PDESKTOP *Object)
401 {
402 NTSTATUS Status;
403
404 Status = ObReferenceObjectByHandle(
405 Desktop,
406 DesiredAccess,
407 ExDesktopObjectType,
408 AccessMode,
409 (PVOID*)Object,
410 NULL);
411
412 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
413 Desktop, *Object, DesiredAccess, Status);
414
415 if (!NT_SUCCESS(Status))
416 SetLastNtError(Status);
417
418 return Status;
419 }
420
421 PDESKTOP FASTCALL
422 IntGetActiveDesktop(VOID)
423 {
424 return gpdeskInputDesktop;
425 }
426
427 /*
428 * Returns or creates a handle to the desktop object
429 */
430 HDESK FASTCALL
431 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
432 {
433 NTSTATUS Status;
434 HDESK Ret;
435
436 ASSERT(DesktopObject);
437
438 if (!ObFindHandleForObject(PsGetCurrentProcess(),
439 DesktopObject,
440 ExDesktopObjectType,
441 NULL,
442 (PHANDLE)&Ret))
443 {
444 Status = ObOpenObjectByPointer(DesktopObject,
445 0,
446 NULL,
447 0,
448 ExDesktopObjectType,
449 UserMode,
450 (PHANDLE)&Ret);
451 if(!NT_SUCCESS(Status))
452 {
453 /* Unable to create a handle */
454 ERR("Unable to create a desktop handle\n");
455 return NULL;
456 }
457 }
458 else
459 {
460 ERR("Got handle: %p\n", Ret);
461 }
462
463 return Ret;
464 }
465
466 PUSER_MESSAGE_QUEUE FASTCALL
467 IntGetFocusMessageQueue(VOID)
468 {
469 PDESKTOP pdo = IntGetActiveDesktop();
470 if (!pdo)
471 {
472 TRACE("No active desktop\n");
473 return(NULL);
474 }
475 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
476 }
477
478 VOID FASTCALL
479 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
480 {
481 PUSER_MESSAGE_QUEUE Old;
482 PDESKTOP pdo = IntGetActiveDesktop();
483 if (!pdo)
484 {
485 TRACE("No active desktop\n");
486 return;
487 }
488 if(NewQueue != NULL)
489 {
490 if(NewQueue->Desktop != NULL)
491 {
492 TRACE("Message Queue already attached to another desktop!\n");
493 return;
494 }
495 IntReferenceMessageQueue(NewQueue);
496 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
497 }
498 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
499 if(Old != NULL)
500 {
501 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
502 IntDereferenceMessageQueue(Old);
503 gpqForegroundPrev = Old;
504 }
505 // Only one Q can have active foreground even when there are more than one desktop.
506 if (NewQueue) gpqForeground = pdo->ActiveMessageQueue;
507 else gpqForeground = NULL;
508 }
509
510 PWND FASTCALL
511 IntGetThreadDesktopWindow(PTHREADINFO pti)
512 {
513 if (!pti) pti = PsGetCurrentThreadWin32Thread();
514 if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
515 return NULL;
516 }
517
518 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
519 {
520 if (pWnd->head.rpdesk &&
521 pWnd->head.rpdesk->pDeskInfo)
522 return pWnd->head.rpdesk->pDeskInfo->spwnd;
523 return NULL;
524 }
525
526 HWND FASTCALL IntGetDesktopWindow(VOID)
527 {
528 PDESKTOP pdo = IntGetActiveDesktop();
529 if (!pdo)
530 {
531 TRACE("No active desktop\n");
532 return NULL;
533 }
534 return pdo->DesktopWindow;
535 }
536
537 PWND FASTCALL UserGetDesktopWindow(VOID)
538 {
539 PDESKTOP pdo = IntGetActiveDesktop();
540
541 if (!pdo)
542 {
543 TRACE("No active desktop\n");
544 return NULL;
545 }
546 // return pdo->pDeskInfo->spwnd;
547 return UserGetWindowObject(pdo->DesktopWindow);
548 }
549
550 HWND FASTCALL IntGetMessageWindow(VOID)
551 {
552 PDESKTOP pdo = IntGetActiveDesktop();
553
554 if (!pdo)
555 {
556 TRACE("No active desktop\n");
557 return NULL;
558 }
559 return pdo->spwndMessage->head.h;
560 }
561
562 PWND FASTCALL UserGetMessageWindow(VOID)
563 {
564 PDESKTOP pdo = IntGetActiveDesktop();
565
566 if (!pdo)
567 {
568 TRACE("No active desktop\n");
569 return NULL;
570 }
571 return pdo->spwndMessage;
572 }
573
574 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
575 {
576 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
577 PDESKTOP pdo = pti->rpdesk;
578 if (NULL == pdo)
579 {
580 ERR("Thread doesn't have a desktop\n");
581 return NULL;
582 }
583 return pdo->DesktopWindow;
584 }
585
586 /* PUBLIC FUNCTIONS ***********************************************************/
587
588 BOOL FASTCALL
589 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
590 {
591 PAINTSTRUCT Ps;
592 ULONG Value;
593 //ERR("DesktopWindowProc\n");
594
595 *lResult = 0;
596
597 switch (Msg)
598 {
599 case WM_NCCREATE:
600 if (!Wnd->fnid)
601 {
602 Wnd->fnid = FNID_DESKTOP;
603 }
604 *lResult = (LRESULT)TRUE;
605 return TRUE;
606
607 case WM_CREATE:
608 Value = HandleToULong(PsGetCurrentProcessId());
609 // Save Process ID
610 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
611 Value = HandleToULong(PsGetCurrentThreadId());
612 // Save Thread ID
613 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
614 case WM_CLOSE:
615 return TRUE;
616
617 case WM_DISPLAYCHANGE:
618 co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
619 return TRUE;
620
621 case WM_ERASEBKGND:
622 IntPaintDesktop((HDC)wParam);
623 *lResult = 1;
624 return TRUE;
625
626 case WM_PAINT:
627 {
628 if (IntBeginPaint(Wnd, &Ps))
629 {
630 IntEndPaint(Wnd, &Ps);
631 }
632 return TRUE;
633 }
634 case WM_SYSCOLORCHANGE:
635 co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
636 return TRUE;
637 case WM_SETCURSOR:
638 {
639 PCURICON_OBJECT pcurOld, pcurNew;
640 pcurNew = UserGetCurIconObject(gDesktopCursor);
641 if (!pcurNew)
642 {
643 return TRUE;
644 }
645 pcurOld = UserSetCursor(pcurNew, FALSE);
646 if (pcurOld)
647 {
648 UserDereferenceObject(pcurOld);
649 }
650 return TRUE;
651 }
652
653 case WM_WINDOWPOSCHANGING:
654 {
655 PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
656 if((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
657 {
658 HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop);
659 IntSetThreadDesktop(hdesk, FALSE);
660 }
661 }
662
663 }
664 return TRUE; /* We are done. Do not do any callbacks to user mode */
665 }
666
667 BOOL FASTCALL
668 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
669 {
670 *lResult = 0;
671
672 switch(Msg)
673 {
674 case WM_NCCREATE:
675 pwnd->fnid |= FNID_MESSAGEWND;
676 *lResult = (LRESULT)TRUE;
677 break;
678 case WM_DESTROY:
679 pwnd->fnid |= FNID_DESTROY;
680 break;
681 }
682
683 return TRUE; /* We are done. Do not do any callbacks to user mode */
684 }
685
686 VOID NTAPI DesktopThreadMain()
687 {
688 BOOL Ret;
689 MSG Msg;
690
691 gptiDesktopThread = PsGetCurrentThreadWin32Thread();
692
693 UserEnterExclusive();
694
695 /* Register system classes. This thread does not belong to any desktop so the
696 classes will be allocated from the shared heap */
697 UserRegisterSystemClasses();
698
699 while(TRUE)
700 {
701 Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
702 if (Ret)
703 {
704 IntDispatchMessage(&Msg);
705 }
706 }
707
708 UserLeave();
709 }
710
711 HDC FASTCALL
712 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
713 {
714 PWND DesktopObject = 0;
715 HDC DesktopHDC = 0;
716
717 if (DcType == DC_TYPE_DIRECT)
718 {
719 DesktopObject = UserGetDesktopWindow();
720 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
721 }
722 else
723 {
724 PMONITOR pMonitor = UserGetPrimaryMonitor();
725 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
726 }
727
728 return DesktopHDC;
729 }
730
731 VOID APIENTRY
732 UserRedrawDesktop()
733 {
734 PWND Window = NULL;
735 HRGN hRgn;
736
737 Window = UserGetDesktopWindow();
738 hRgn = IntSysCreateRectRgnIndirect(&Window->rcWindow);
739
740 IntInvalidateWindows( Window,
741 hRgn,
742 RDW_FRAME |
743 RDW_ERASE |
744 RDW_INVALIDATE |
745 RDW_ALLCHILDREN);
746
747 GreDeleteObject(hRgn);
748 }
749
750
751 NTSTATUS FASTCALL
752 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
753 {
754 PWND pwnd = Desktop->pDeskInfo->spwnd;
755 UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
756 ASSERT(pwnd);
757
758 if(!bRedraw)
759 flags |= SWP_NOREDRAW;
760
761 co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
762
763 if(bRedraw)
764 co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
765
766 return STATUS_SUCCESS;
767 }
768
769 NTSTATUS FASTCALL
770 IntHideDesktop(PDESKTOP Desktop)
771 {
772 PWND DesktopWnd;
773
774 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
775 if (! DesktopWnd)
776 {
777 return ERROR_INVALID_WINDOW_HANDLE;
778 }
779 DesktopWnd->style &= ~WS_VISIBLE;
780
781 return STATUS_SUCCESS;
782 }
783
784 static
785 HWND* FASTCALL
786 UserBuildShellHookHwndList(PDESKTOP Desktop)
787 {
788 ULONG entries=0;
789 PSHELL_HOOK_WINDOW Current;
790 HWND* list;
791
792 /* FIXME: If we save nb elements in desktop, we dont have to loop to find nb entries */
793 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
794 entries++;
795
796 if (!entries) return NULL;
797
798 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
799 if (list)
800 {
801 HWND* cursor = list;
802
803 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
804 *cursor++ = Current->hWnd;
805
806 *cursor = NULL; /* Nullterm list */
807 }
808
809 return list;
810 }
811
812 /*
813 * Send the Message to the windows registered for ShellHook
814 * notifications. The lParam contents depend on the Message. See
815 * MSDN for more details (RegisterShellHookWindow)
816 */
817 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
818 {
819 PDESKTOP Desktop = IntGetActiveDesktop();
820 HWND* HwndList;
821
822 if (!gpsi->uiShellMsg)
823 {
824 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
825
826 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
827 if (!gpsi->uiShellMsg)
828 ERR("LastError: %x\n", EngGetLastError());
829 }
830
831 if (!Desktop)
832 {
833 TRACE("IntShellHookNotify: No desktop!\n");
834 return;
835 }
836
837 // FIXME: System Tray Support.
838
839 HwndList = UserBuildShellHookHwndList(Desktop);
840 if (HwndList)
841 {
842 HWND* cursor = HwndList;
843
844 for (; *cursor; cursor++)
845 {
846 TRACE("Sending notify\n");
847 co_IntPostOrSendMessage(*cursor,
848 gpsi->uiShellMsg,
849 Message,
850 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
851 }
852
853 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
854 }
855
856 if (ISITHOOKED(WH_SHELL))
857 {
858 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
859 }
860 }
861
862 /*
863 * Add the window to the ShellHookWindows list. The windows
864 * on that list get notifications that are important to shell
865 * type applications.
866 *
867 * TODO: Validate the window? I'm not sure if sending these messages to
868 * an unsuspecting application that is not your own is a nice thing to do.
869 */
870 BOOL IntRegisterShellHookWindow(HWND hWnd)
871 {
872 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
873 PDESKTOP Desktop = pti->rpdesk;
874 PSHELL_HOOK_WINDOW Entry;
875
876 TRACE("IntRegisterShellHookWindow\n");
877
878 /* First deregister the window, so we can be sure it's never twice in the
879 * list.
880 */
881 IntDeRegisterShellHookWindow(hWnd);
882
883 Entry = ExAllocatePoolWithTag(PagedPool,
884 sizeof(SHELL_HOOK_WINDOW),
885 TAG_WINSTA);
886
887 if (!Entry)
888 return FALSE;
889
890 Entry->hWnd = hWnd;
891
892 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
893
894 return TRUE;
895 }
896
897 /*
898 * Remove the window from the ShellHookWindows list. The windows
899 * on that list get notifications that are important to shell
900 * type applications.
901 */
902 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
903 {
904 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
905 PDESKTOP Desktop = pti->rpdesk;
906 PSHELL_HOOK_WINDOW Current;
907
908 LIST_FOR_EACH(Current, &Desktop->ShellHookWindows, SHELL_HOOK_WINDOW, ListEntry)
909 {
910 if (Current->hWnd == hWnd)
911 {
912 RemoveEntryList(&Current->ListEntry);
913 ExFreePoolWithTag(Current, TAG_WINSTA);
914 return TRUE;
915 }
916 }
917
918 return FALSE;
919 }
920
921 static VOID
922 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
923 {
924 /* FIXME: Disable until unmapping works in mm */
925 #if 0
926 if (Desktop->pheapDesktop != NULL)
927 {
928 MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
929 Desktop->pheapDesktop = NULL;
930 }
931
932 if (Desktop->hsectionDesktop != NULL)
933 {
934 ObDereferenceObject(Desktop->hsectionDesktop);
935 Desktop->hsectionDesktop = NULL;
936 }
937 #endif
938 }
939
940 BOOL FASTCALL
941 IntPaintDesktop(HDC hDC)
942 {
943 RECTL Rect;
944 HBRUSH DesktopBrush, PreviousBrush;
945 HWND hWndDesktop;
946 BOOL doPatBlt = TRUE;
947 PWND WndDesktop;
948 static WCHAR s_wszSafeMode[] = L"Safe Mode";
949 int len;
950 COLORREF color_old;
951 UINT align_old;
952 int mode_old;
953
954 GdiGetClipBox(hDC, &Rect);
955
956 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
957
958 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
959 if (!WndDesktop)
960 {
961 return FALSE;
962 }
963
964 if (!UserGetSystemMetrics(SM_CLEANBOOT))
965 {
966 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
967
968 /*
969 * Paint desktop background
970 */
971 if (gspv.hbmWallpaper != NULL)
972 {
973 SIZE sz;
974 int x, y;
975 HDC hWallpaperDC;
976
977 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
978 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
979
980 if (gspv.WallpaperMode == wmStretch ||
981 gspv.WallpaperMode == wmTile)
982 {
983 x = 0;
984 y = 0;
985 }
986 else
987 {
988 /* Find the upper left corner, can be negative if the bitmap is bigger then the screen */
989 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
990 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
991 }
992
993 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
994 if(hWallpaperDC != NULL)
995 {
996 HBITMAP hOldBitmap;
997
998 /* Fill in the area that the bitmap is not going to cover */
999 if (x > 0 || y > 0)
1000 {
1001 /* FIXME: Clip out the bitmap
1002 can be replaced with "NtGdiPatBlt(hDC, x, y, WinSta->cxWallpaper, WinSta->cyWallpaper, PATCOPY | DSTINVERT);"
1003 once we support DSTINVERT */
1004 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1005 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1006 NtGdiSelectBrush(hDC, PreviousBrush);
1007 }
1008
1009 /* Do not fill the background after it is painted no matter the size of the picture */
1010 doPatBlt = FALSE;
1011
1012 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1013
1014 if (gspv.WallpaperMode == wmStretch)
1015 {
1016 if(Rect.right && Rect.bottom)
1017 NtGdiStretchBlt(hDC,
1018 x,
1019 y,
1020 sz.cx,
1021 sz.cy,
1022 hWallpaperDC,
1023 0,
1024 0,
1025 gspv.cxWallpaper,
1026 gspv.cyWallpaper,
1027 SRCCOPY,
1028 0);
1029
1030 }
1031 else if (gspv.WallpaperMode == wmTile)
1032 {
1033 /* Paint the bitmap across the screen then down */
1034 for(y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1035 {
1036 for(x = 0; x < Rect.right; x += gspv.cxWallpaper)
1037 {
1038 NtGdiBitBlt(hDC,
1039 x,
1040 y,
1041 gspv.cxWallpaper,
1042 gspv.cyWallpaper,
1043 hWallpaperDC,
1044 0,
1045 0,
1046 SRCCOPY,
1047 0,
1048 0);
1049 }
1050 }
1051 }
1052 else
1053 {
1054 NtGdiBitBlt(hDC,
1055 x,
1056 y,
1057 gspv.cxWallpaper,
1058 gspv.cyWallpaper,
1059 hWallpaperDC,
1060 0,
1061 0,
1062 SRCCOPY,
1063 0,
1064 0);
1065 }
1066 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
1067 NtGdiDeleteObjectApp(hWallpaperDC);
1068 }
1069 }
1070 }
1071 else
1072 {
1073 /* Black desktop background in Safe Mode */
1074 DesktopBrush = StockObjects[BLACK_BRUSH];
1075 }
1076 /* Back ground is set to none, clear the screen */
1077 if (doPatBlt)
1078 {
1079 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1080 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1081 NtGdiSelectBrush(hDC, PreviousBrush);
1082 }
1083
1084 /*
1085 * Display system version on the desktop background
1086 */
1087
1088 if (g_PaintDesktopVersion || UserGetSystemMetrics(SM_CLEANBOOT))
1089 {
1090 static WCHAR s_wszVersion[256] = {0};
1091 RECTL rect;
1092
1093 if (*s_wszVersion)
1094 {
1095 len = wcslen(s_wszVersion);
1096 }
1097 else
1098 {
1099 len = GetSystemVersionString(s_wszVersion);
1100 }
1101
1102 if (len)
1103 {
1104 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0))
1105 {
1106 rect.right = UserGetSystemMetrics(SM_CXSCREEN);
1107 rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1108 }
1109
1110 color_old = IntGdiSetTextColor(hDC, RGB(255,255,255));
1111 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1112 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1113
1114 if(!UserGetSystemMetrics(SM_CLEANBOOT))
1115 {
1116 GreExtTextOutW(hDC, rect.right - 16, rect.bottom - 48, 0, NULL, s_wszVersion, len, NULL, 0);
1117 }
1118 else
1119 {
1120 /* Safe Mode */
1121
1122 /* Version information text in top center */
1123 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
1124 GreExtTextOutW(hDC, (rect.right + rect.left)/2, rect.top + 3, 0, NULL, s_wszVersion, len, NULL, 0);
1125
1126 /* Safe Mode text in corners */
1127 len = wcslen(s_wszSafeMode);
1128 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
1129 GreExtTextOutW(hDC, rect.left, rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1130 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
1131 GreExtTextOutW(hDC, rect.right, rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1132 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BASELINE);
1133 GreExtTextOutW(hDC, rect.left, rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1134 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BASELINE);
1135 GreExtTextOutW(hDC, rect.right, rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1136 }
1137
1138 IntGdiSetBkMode(hDC, mode_old);
1139 IntGdiSetTextAlign(hDC, align_old);
1140 IntGdiSetTextColor(hDC, color_old);
1141 }
1142 }
1143 return TRUE;
1144 }
1145
1146 static NTSTATUS
1147 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
1148 {
1149 PVOID DesktopHeapSystemBase = NULL;
1150 ULONG_PTR HeapSize = 400 * 1024;
1151 SIZE_T DesktopInfoSize;
1152 ULONG i;
1153
1154 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
1155
1156 RtlZeroMemory(pdesk, sizeof(DESKTOP));
1157
1158 /* Link the desktop with the parent window station */
1159 pdesk->rpwinstaParent = pwinsta;
1160 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
1161
1162 /* Create the desktop heap */
1163 pdesk->hsectionDesktop = NULL;
1164 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
1165 &DesktopHeapSystemBase,
1166 HeapSize);
1167 if (pdesk->pheapDesktop == NULL)
1168 {
1169 ERR("Failed to create desktop heap!\n");
1170 return STATUS_NO_MEMORY;
1171 }
1172
1173 /* Create DESKTOPINFO */
1174 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
1175 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
1176 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
1177 DesktopInfoSize);
1178 if (pdesk->pDeskInfo == NULL)
1179 {
1180 ERR("Failed to create the DESKTOP structure!\n");
1181 return STATUS_NO_MEMORY;
1182 }
1183
1184 /* Initialize the DESKTOPINFO */
1185 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1186 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1187 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
1188 DesktopName->Buffer,
1189 DesktopName->Length + sizeof(WCHAR));
1190 for (i = 0; i < NB_HOOKS; i++)
1191 {
1192 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
1193 }
1194
1195 InitializeListHead(&pdesk->ShellHookWindows);
1196 InitializeListHead(&pdesk->PtiList);
1197
1198 return STATUS_SUCCESS;
1199 }
1200
1201 /* SYSCALLS *******************************************************************/
1202
1203 /*
1204 * NtUserCreateDesktop
1205 *
1206 * Creates a new desktop.
1207 *
1208 * Parameters
1209 * poaAttribs
1210 * Object Attributes.
1211 *
1212 * lpszDesktopDevice
1213 * Name of the device.
1214 *
1215 * pDeviceMode
1216 * Device Mode.
1217 *
1218 * dwFlags
1219 * Interaction flags.
1220 *
1221 * dwDesiredAccess
1222 * Requested type of access.
1223 *
1224 *
1225 * Return Value
1226 * If the function succeeds, the return value is a handle to the newly
1227 * created desktop. If the specified desktop already exists, the function
1228 * succeeds and returns a handle to the existing desktop. When you are
1229 * finished using the handle, call the CloseDesktop function to close it.
1230 * If the function fails, the return value is NULL.
1231 *
1232 * Status
1233 * @implemented
1234 */
1235
1236 HDESK APIENTRY
1237 NtUserCreateDesktop(
1238 POBJECT_ATTRIBUTES ObjectAttributes,
1239 PUNICODE_STRING lpszDesktopDevice,
1240 LPDEVMODEW lpdmw,
1241 DWORD dwFlags,
1242 ACCESS_MASK dwDesiredAccess)
1243 {
1244 PDESKTOP pdesk = NULL;
1245 NTSTATUS Status = STATUS_SUCCESS;
1246 HDESK hdesk;
1247 BOOLEAN Context;
1248 UNICODE_STRING ClassName;
1249 LARGE_STRING WindowName;
1250 BOOL NoHooks = FALSE;
1251 PWND pWnd = NULL;
1252 CREATESTRUCTW Cs;
1253 PTHREADINFO ptiCurrent;
1254 PCLS pcls;
1255
1256 DECLARE_RETURN(HDESK);
1257
1258 TRACE("Enter NtUserCreateDesktop\n");
1259 UserEnterExclusive();
1260
1261 ptiCurrent = PsGetCurrentThreadWin32Thread();
1262 ASSERT(ptiCurrent);
1263 ASSERT(gptiDesktopThread);
1264
1265 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1266 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1267 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1268 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1269
1270 /*
1271 * Try to open already existing desktop
1272 */
1273 Status = ObOpenObjectByName(
1274 ObjectAttributes,
1275 ExDesktopObjectType,
1276 UserMode,
1277 NULL,
1278 dwDesiredAccess,
1279 (PVOID)&Context,
1280 (HANDLE*)&hdesk);
1281 if (!NT_SUCCESS(Status))
1282 {
1283 ERR("ObOpenObjectByName failed to open/create desktop\n");
1284 SetLastNtError(Status);
1285 RETURN(NULL);
1286 }
1287
1288 /* In case the object was not created (eg if it existed), return now */
1289 if (Context == FALSE)
1290 {
1291 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1292 RETURN( hdesk);
1293 }
1294
1295 /* Reference the desktop */
1296 Status = ObReferenceObjectByHandle(hdesk,
1297 0,
1298 ExDesktopObjectType,
1299 KernelMode,
1300 (PVOID*)&pdesk,
1301 NULL);
1302 if (!NT_SUCCESS(Status))
1303 {
1304 ERR("Failed to reference desktop object\n");
1305 SetLastNtError(Status);
1306 RETURN(NULL);
1307 }
1308
1309 /* Get the desktop window class. The thread desktop does not belong to any desktop
1310 * so the classes created there (including the desktop class) are allocated in the shared heap
1311 * It would cause problems if we used a class that belongs to the caller
1312 */
1313 ClassName.Buffer = WC_DESKTOP;
1314 ClassName.Length = 0;
1315 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1316 if (pcls == NULL)
1317 {
1318 ASSERT(FALSE);
1319 RETURN(NULL);
1320 }
1321
1322 RtlZeroMemory(&WindowName, sizeof(WindowName));
1323 RtlZeroMemory(&Cs, sizeof(Cs));
1324 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
1325 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
1326 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
1327 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
1328 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1329 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1330 Cs.lpszName = (LPCWSTR) &WindowName;
1331 Cs.lpszClass = (LPCWSTR) &ClassName;
1332
1333 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1334 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1335 if (pWnd == NULL)
1336 {
1337 ERR("Failed to create desktop window for the new desktop\n");
1338 RETURN(NULL);
1339 }
1340
1341 pdesk->DesktopWindow = pWnd->head.h;
1342 pdesk->pDeskInfo->spwnd = pWnd;
1343 pWnd->fnid = FNID_DESKTOP;
1344
1345 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
1346 ClassName.Length = 0;
1347 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1348 if (pcls == NULL)
1349 {
1350 ASSERT(FALSE);
1351 RETURN(NULL);
1352 }
1353
1354 RtlZeroMemory(&WindowName, sizeof(WindowName));
1355 RtlZeroMemory(&Cs, sizeof(Cs));
1356 Cs.cx = Cs.cy = 100;
1357 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1358 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1359 Cs.lpszName = (LPCWSTR) &WindowName;
1360 Cs.lpszClass = (LPCWSTR) &ClassName;
1361 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1362 if (pWnd == NULL)
1363 {
1364 ERR("Failed to create message window for the new desktop\n");
1365 RETURN(NULL);
1366 }
1367
1368 pdesk->spwndMessage = pWnd;
1369 pWnd->fnid = FNID_MESSAGEWND;
1370
1371 /* Now,,,
1372 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1373 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1374 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1375 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1376 The rest is same as message window.
1377 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1378 */
1379 RETURN( hdesk);
1380
1381 CLEANUP:
1382 if (pdesk != NULL)
1383 {
1384 ObDereferenceObject(pdesk);
1385 }
1386 if (_ret_ == NULL && hdesk != NULL)
1387 {
1388 ObCloseHandle(hdesk, UserMode);
1389 }
1390 if (!NoHooks)
1391 {
1392 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1393 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1394 }
1395 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1396 UserLeave();
1397 END_CLEANUP;
1398 }
1399
1400 /*
1401 * NtUserOpenDesktop
1402 *
1403 * Opens an existing desktop.
1404 *
1405 * Parameters
1406 * lpszDesktopName
1407 * Name of the existing desktop.
1408 *
1409 * dwFlags
1410 * Interaction flags.
1411 *
1412 * dwDesiredAccess
1413 * Requested type of access.
1414 *
1415 * Return Value
1416 * Handle to the desktop or zero on failure.
1417 *
1418 * Status
1419 * @implemented
1420 */
1421
1422 HDESK APIENTRY
1423 NtUserOpenDesktop(
1424 POBJECT_ATTRIBUTES ObjectAttributes,
1425 DWORD dwFlags,
1426 ACCESS_MASK dwDesiredAccess)
1427 {
1428 NTSTATUS Status;
1429 HDESK Desktop;
1430
1431 Status = ObOpenObjectByName(
1432 ObjectAttributes,
1433 ExDesktopObjectType,
1434 UserMode,
1435 NULL,
1436 dwDesiredAccess,
1437 NULL,
1438 (HANDLE*)&Desktop);
1439
1440 if (!NT_SUCCESS(Status))
1441 {
1442 ERR("Failed to open desktop\n");
1443 SetLastNtError(Status);
1444 return 0;
1445 }
1446
1447 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1448
1449 return Desktop;
1450 }
1451
1452 /*
1453 * NtUserOpenInputDesktop
1454 *
1455 * Opens the input (interactive) desktop.
1456 *
1457 * Parameters
1458 * dwFlags
1459 * Interaction flags.
1460 *
1461 * fInherit
1462 * Inheritance option.
1463 *
1464 * dwDesiredAccess
1465 * Requested type of access.
1466 *
1467 * Return Value
1468 * Handle to the input desktop or zero on failure.
1469 *
1470 * Status
1471 * @implemented
1472 */
1473
1474 HDESK APIENTRY
1475 NtUserOpenInputDesktop(
1476 DWORD dwFlags,
1477 BOOL fInherit,
1478 ACCESS_MASK dwDesiredAccess)
1479 {
1480 NTSTATUS Status;
1481 HDESK hdesk = NULL;
1482 ULONG HandleAttributes = 0;
1483
1484 UserEnterExclusive();
1485 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1486
1487 if(fInherit) HandleAttributes = OBJ_INHERIT;
1488
1489 /* Create a new handle to the object */
1490 Status = ObOpenObjectByPointer(
1491 gpdeskInputDesktop,
1492 HandleAttributes,
1493 NULL,
1494 dwDesiredAccess,
1495 ExDesktopObjectType,
1496 UserMode,
1497 (PHANDLE)&hdesk);
1498
1499 if (!NT_SUCCESS(Status))
1500 {
1501 ERR("Failed to open input desktop object\n");
1502 SetLastNtError(Status);
1503 }
1504
1505 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1506 UserLeave();
1507 return hdesk;
1508 }
1509
1510 /*
1511 * NtUserCloseDesktop
1512 *
1513 * Closes a desktop handle.
1514 *
1515 * Parameters
1516 * hDesktop
1517 * Handle to the desktop.
1518 *
1519 * Return Value
1520 * Status
1521 *
1522 * Remarks
1523 * The desktop handle can be created with NtUserCreateDesktop or
1524 * NtUserOpenDesktop. This function will fail if any thread in the calling
1525 * process is using the specified desktop handle or if the handle refers
1526 * to the initial desktop of the calling process.
1527 *
1528 * Status
1529 * @implemented
1530 */
1531
1532 BOOL APIENTRY
1533 NtUserCloseDesktop(HDESK hDesktop)
1534 {
1535 PDESKTOP pdesk;
1536 NTSTATUS Status;
1537 DECLARE_RETURN(BOOL);
1538
1539 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1540 UserEnterExclusive();
1541
1542 if( hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1543 {
1544 ERR("Attempted to close thread desktop\n");
1545 EngSetLastError(ERROR_BUSY);
1546 RETURN(FALSE);
1547 }
1548
1549 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1550 if (!NT_SUCCESS(Status))
1551 {
1552 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1553 RETURN(FALSE);
1554 }
1555
1556 ObDereferenceObject(pdesk);
1557
1558 Status = ZwClose(hDesktop);
1559 if (!NT_SUCCESS(Status))
1560 {
1561 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1562 SetLastNtError(Status);
1563 RETURN(FALSE);
1564 }
1565
1566 RETURN(TRUE);
1567
1568 CLEANUP:
1569 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1570 UserLeave();
1571 END_CLEANUP;
1572 }
1573
1574 /*
1575 * NtUserPaintDesktop
1576 *
1577 * The NtUserPaintDesktop function fills the clipping region in the
1578 * specified device context with the desktop pattern or wallpaper. The
1579 * function is provided primarily for shell desktops.
1580 *
1581 * Parameters
1582 * hDC
1583 * Handle to the device context.
1584 *
1585 * Status
1586 * @implemented
1587 */
1588
1589 BOOL APIENTRY
1590 NtUserPaintDesktop(HDC hDC)
1591 {
1592 BOOL Ret;
1593 UserEnterExclusive();
1594 TRACE("Enter NtUserPaintDesktop\n");
1595 Ret = IntPaintDesktop(hDC);
1596 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1597 UserLeave();
1598 return Ret;
1599 }
1600
1601 /*
1602 * NtUserSwitchDesktop
1603 *
1604 * Sets the current input (interactive) desktop.
1605 *
1606 * Parameters
1607 * hDesktop
1608 * Handle to desktop.
1609 *
1610 * Return Value
1611 * Status
1612 *
1613 * Status
1614 * @unimplemented
1615 */
1616
1617 BOOL APIENTRY
1618 NtUserSwitchDesktop(HDESK hdesk)
1619 {
1620 PDESKTOP pdesk;
1621 NTSTATUS Status;
1622 BOOL bRedrawDesktop;
1623 DECLARE_RETURN(BOOL);
1624
1625 UserEnterExclusive();
1626 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
1627
1628 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
1629 if (!NT_SUCCESS(Status))
1630 {
1631 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
1632 RETURN(FALSE);
1633 }
1634
1635 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
1636 {
1637 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
1638 RETURN(FALSE);
1639 }
1640
1641 if(pdesk == gpdeskInputDesktop)
1642 {
1643 WARN("NtUserSwitchDesktop called for active desktop\n");
1644 RETURN(TRUE);
1645 }
1646
1647 /*
1648 * Don't allow applications switch the desktop if it's locked, unless the caller
1649 * is the logon application itself
1650 */
1651 if((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
1652 LogonProcess != PsGetCurrentProcessWin32Process())
1653 {
1654 ObDereferenceObject(pdesk);
1655 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
1656 RETURN(FALSE);
1657 }
1658
1659 if(pdesk->rpwinstaParent != InputWindowStation)
1660 {
1661 ObDereferenceObject(pdesk);
1662 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
1663 RETURN(FALSE);
1664 }
1665
1666 /* FIXME: Fail if the process is associated with a secured
1667 desktop such as Winlogon or Screen-Saver */
1668 /* FIXME: Connect to input device */
1669
1670 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
1671
1672 bRedrawDesktop = FALSE;
1673
1674 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
1675 if(gpdeskInputDesktop != NULL)
1676 {
1677 if((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
1678 bRedrawDesktop = TRUE;
1679
1680 /* Hide the previous desktop window */
1681 IntHideDesktop(gpdeskInputDesktop);
1682 }
1683
1684 /* Set the active desktop in the desktop's window station. */
1685 InputWindowStation->ActiveDesktop = pdesk;
1686
1687 /* Set the global state. */
1688 gpdeskInputDesktop = pdesk;
1689
1690 /* Show the new desktop window */
1691 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
1692
1693 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1694 ObDereferenceObject(pdesk);
1695
1696 RETURN(TRUE);
1697
1698 CLEANUP:
1699 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
1700 UserLeave();
1701 END_CLEANUP;
1702 }
1703
1704 /*
1705 * NtUserGetThreadDesktop
1706 *
1707 * Status
1708 * @implemented
1709 */
1710
1711 HDESK APIENTRY
1712 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
1713 {
1714 NTSTATUS Status;
1715 PETHREAD Thread;
1716 PDESKTOP DesktopObject;
1717 HDESK Ret, hThreadDesktop;
1718 OBJECT_HANDLE_INFORMATION HandleInformation;
1719 DECLARE_RETURN(HDESK);
1720
1721 UserEnterExclusive();
1722 TRACE("Enter NtUserGetThreadDesktop\n");
1723
1724 if(!dwThreadId)
1725 {
1726 EngSetLastError(ERROR_INVALID_PARAMETER);
1727 RETURN(0);
1728 }
1729
1730 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
1731 if(!NT_SUCCESS(Status))
1732 {
1733 EngSetLastError(ERROR_INVALID_PARAMETER);
1734 RETURN(0);
1735 }
1736
1737 if(Thread->ThreadsProcess == PsGetCurrentProcess())
1738 {
1739 /* Just return the handle, we queried the desktop handle of a thread running
1740 in the same context */
1741 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
1742 ObDereferenceObject(Thread);
1743 RETURN(Ret);
1744 }
1745
1746 /* Get the desktop handle and the desktop of the thread */
1747 if(!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
1748 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
1749 {
1750 ObDereferenceObject(Thread);
1751 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
1752 RETURN(NULL);
1753 }
1754
1755 /* We could just use DesktopObject instead of looking up the handle, but latter
1756 may be a bit safer (e.g. when the desktop is being destroyed */
1757 /* Switch into the context of the thread we're trying to get the desktop from,
1758 so we can use the handle */
1759 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
1760 Status = ObReferenceObjectByHandle(hThreadDesktop,
1761 GENERIC_ALL,
1762 ExDesktopObjectType,
1763 UserMode,
1764 (PVOID*)&DesktopObject,
1765 &HandleInformation);
1766 KeDetachProcess();
1767
1768 /* The handle couldn't be found, there's nothing to get... */
1769 if(!NT_SUCCESS(Status))
1770 {
1771 ObDereferenceObject(Thread);
1772 RETURN(NULL);
1773 }
1774
1775 /* Lookup our handle table if we can find a handle to the desktop object,
1776 if not, create one */
1777 Ret = IntGetDesktopObjectHandle(DesktopObject);
1778
1779 /* All done, we got a valid handle to the desktop */
1780 ObDereferenceObject(DesktopObject);
1781 ObDereferenceObject(Thread);
1782 RETURN(Ret);
1783
1784 CLEANUP:
1785 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
1786 UserLeave();
1787 END_CLEANUP;
1788 }
1789
1790 static NTSTATUS
1791 IntUnmapDesktopView(IN PDESKTOP pdesk)
1792 {
1793 PPROCESSINFO ppi;
1794 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1795 NTSTATUS Status = STATUS_SUCCESS;
1796
1797 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
1798
1799 ppi = PsGetCurrentProcessWin32Process();
1800 PrevLink = &ppi->HeapMappings.Next;
1801
1802 /* Unmap if we're the last thread using the desktop */
1803 HeapMapping = *PrevLink;
1804 while (HeapMapping != NULL)
1805 {
1806 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1807 {
1808 if (--HeapMapping->Count == 0)
1809 {
1810 *PrevLink = HeapMapping->Next;
1811
1812 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
1813 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
1814 HeapMapping->UserMapping);
1815
1816 ObDereferenceObject(pdesk);
1817
1818 UserHeapFree(HeapMapping);
1819 break;
1820 }
1821 }
1822
1823 PrevLink = &HeapMapping->Next;
1824 HeapMapping = HeapMapping->Next;
1825 }
1826
1827 return Status;
1828 }
1829
1830 static NTSTATUS
1831 IntMapDesktopView(IN PDESKTOP pdesk)
1832 {
1833 PPROCESSINFO ppi;
1834 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
1835 PVOID UserBase = NULL;
1836 SIZE_T ViewSize = 0;
1837 LARGE_INTEGER Offset;
1838 NTSTATUS Status;
1839
1840 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
1841
1842 ppi = PsGetCurrentProcessWin32Process();
1843 PrevLink = &ppi->HeapMappings.Next;
1844
1845 /* Find out if another thread already mapped the desktop heap */
1846 HeapMapping = *PrevLink;
1847 while (HeapMapping != NULL)
1848 {
1849 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
1850 {
1851 HeapMapping->Count++;
1852 return STATUS_SUCCESS;
1853 }
1854
1855 PrevLink = &HeapMapping->Next;
1856 HeapMapping = HeapMapping->Next;
1857 }
1858
1859 /* We're the first, map the heap */
1860 Offset.QuadPart = 0;
1861 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
1862 PsGetCurrentProcess(),
1863 &UserBase,
1864 0,
1865 0,
1866 &Offset,
1867 &ViewSize,
1868 ViewUnmap,
1869 SEC_NO_CHANGE,
1870 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
1871 if (!NT_SUCCESS(Status))
1872 {
1873 ERR("Failed to map desktop\n");
1874 return Status;
1875 }
1876
1877 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
1878
1879 /* Add the mapping */
1880 HeapMapping = UserHeapAlloc(sizeof(W32HEAP_USER_MAPPING));
1881 if (HeapMapping == NULL)
1882 {
1883 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
1884 ERR("UserHeapAlloc() failed!\n");
1885 return STATUS_NO_MEMORY;
1886 }
1887
1888 HeapMapping->Next = NULL;
1889 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
1890 HeapMapping->UserMapping = UserBase;
1891 HeapMapping->Limit = ViewSize;
1892 HeapMapping->Count = 1;
1893 *PrevLink = HeapMapping;
1894
1895 ObReferenceObject(pdesk);
1896
1897 return STATUS_SUCCESS;
1898 }
1899
1900 BOOL
1901 IntSetThreadDesktop(IN HDESK hDesktop,
1902 IN BOOL FreeOnFailure)
1903 {
1904 PDESKTOP pdesk = NULL, pdeskOld;
1905 HDESK hdeskOld;
1906 PTHREADINFO pti;
1907 NTSTATUS Status;
1908 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
1909 PCLIENTINFO pci;
1910
1911 ASSERT(NtCurrentTeb());
1912
1913 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
1914
1915 pti = PsGetCurrentThreadWin32Thread();
1916 pci = pti->pClientInfo;
1917
1918 /* If the caller gave us a desktop, ensure it is valid */
1919 if(hDesktop != NULL)
1920 {
1921 /* Validate the new desktop. */
1922 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1923 if (!NT_SUCCESS(Status))
1924 {
1925 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1926 return FALSE;
1927 }
1928
1929 if (pti->rpdesk == pdesk)
1930 {
1931 /* Nothing to do */
1932 ObDereferenceObject(pdesk);
1933 return TRUE;
1934 }
1935 }
1936
1937 /* Make sure that we don't own any window in the current desktop */
1938 if (!IsListEmpty(&pti->WindowListHead))
1939 {
1940 if(pdesk)
1941 ObDereferenceObject(pdesk);
1942 ERR("Attempted to change thread desktop although the thread has windows!\n");
1943 EngSetLastError(ERROR_BUSY);
1944 return FALSE;
1945 }
1946
1947 /* Desktop is being re-set so clear out foreground. */
1948 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
1949 {
1950 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
1951 IntSetFocusMessageQueue(NULL);
1952 }
1953
1954 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
1955 if(pdesk != NULL)
1956 {
1957 Status = IntMapDesktopView(pdesk);
1958 if (!NT_SUCCESS(Status))
1959 {
1960 ERR("Failed to map desktop heap!\n");
1961 ObDereferenceObject(pdesk);
1962 SetLastNtError(Status);
1963 return FALSE;
1964 }
1965
1966 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
1967 if(pctiNew == NULL)
1968 {
1969 ERR("Failed to allocate new pcti\n");
1970 IntUnmapDesktopView(pdesk);
1971 ObDereferenceObject(pdesk);
1972 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
1973 return FALSE;
1974 }
1975 }
1976
1977 /* free all classes or move them to the shared heap */
1978 if(pti->rpdesk != NULL)
1979 {
1980 if(!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
1981 {
1982 ERR("Failed to move process classes to shared heap!\n");
1983 if(pdesk)
1984 {
1985 DesktopHeapFree(pdesk, pctiNew);
1986 IntUnmapDesktopView(pdesk);
1987 ObDereferenceObject(pdesk);
1988 }
1989 return FALSE;
1990 }
1991 }
1992
1993 pdeskOld = pti->rpdesk;
1994 hdeskOld = pti->hdesk;
1995 if (pti->pcti != &pti->cti)
1996 pctiOld = pti->pcti;
1997 else
1998 pctiOld = NULL;
1999
2000 /* do the switch */
2001 if(pdesk != NULL)
2002 {
2003 pti->rpdesk = pdesk;
2004 pti->hdesk = hDesktop;
2005 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
2006 pti->pcti = pctiNew;
2007
2008 pci->ulClientDelta = DesktopHeapGetUserDelta();
2009 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
2010 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
2011
2012 /* initialize the new pcti */
2013 if(pctiOld != NULL)
2014 {
2015 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
2016 }
2017 else
2018 {
2019 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
2020 pci->fsHooks = pti->fsHooks;
2021 pci->dwTIFlags = pti->TIF_flags;
2022 }
2023 }
2024 else
2025 {
2026 pti->rpdesk = NULL;
2027 pti->hdesk = NULL;
2028 pti->pDeskInfo = NULL;
2029 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
2030 pci->ulClientDelta = 0;
2031 pci->pDeskInfo = NULL;
2032 pci->pClientThreadInfo = NULL;
2033 }
2034
2035 /* clean up the old desktop */
2036 if(pdeskOld != NULL)
2037 {
2038 RemoveEntryList(&pti->PtiLink);
2039 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
2040 IntUnmapDesktopView(pdeskOld);
2041 ObDereferenceObject(pdeskOld);
2042 ZwClose(hdeskOld);
2043 }
2044
2045 if(pdesk)
2046 {
2047 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
2048 }
2049
2050 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
2051
2052 return TRUE;
2053 }
2054
2055 /*
2056 * NtUserSetThreadDesktop
2057 *
2058 * Status
2059 * @implemented
2060 */
2061
2062 BOOL APIENTRY
2063 NtUserSetThreadDesktop(HDESK hDesktop)
2064 {
2065 BOOL ret = FALSE;
2066
2067 UserEnterExclusive();
2068
2069 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2070 // here too and set the NT error level. Q. Is it necessary to have the validation
2071 // in IntSetThreadDesktop? Is it needed there too?
2072 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
2073 ret = IntSetThreadDesktop(hDesktop, FALSE);
2074
2075 UserLeave();
2076
2077 return ret;
2078 }
2079
2080 /* EOF */