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