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