2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
9 /* INCLUDES ******************************************************************/
12 DBG_DEFAULT_CHANNEL(UserDesktop
);
14 #include <reactos/buildno.h>
17 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
);
20 IntMapDesktopView(IN PDESKTOP pdesk
);
23 IntUnmapDesktopView(IN PDESKTOP pdesk
);
26 IntFreeDesktopHeap(IN PDESKTOP pdesk
);
28 /* GLOBALS *******************************************************************/
30 /* These can be changed via CSRSS startup, these are defaults */
31 DWORD gdwDesktopSectionSize
= 512;
32 DWORD gdwNOIOSectionSize
= 128; // A guess, for one or more of the first three system desktops.
34 /* Currently active desktop */
35 PDESKTOP gpdeskInputDesktop
= NULL
;
36 HDC ScreenDeviceContext
= NULL
;
37 PTHREADINFO gptiDesktopThread
= NULL
;
38 HCURSOR gDesktopCursor
= NULL
;
39 PKEVENT gpDesktopThreadStartedEvent
= NULL
;
41 /* OBJECT CALLBACKS **********************************************************/
45 IntDesktopObjectParse(IN PVOID ParseObject
,
47 IN OUT PACCESS_STATE AccessState
,
48 IN KPROCESSOR_MODE AccessMode
,
50 IN OUT PUNICODE_STRING CompleteName
,
51 IN OUT PUNICODE_STRING RemainingName
,
52 IN OUT PVOID Context OPTIONAL
,
53 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
58 OBJECT_ATTRIBUTES ObjectAttributes
;
59 PLIST_ENTRY NextEntry
, ListHead
;
60 PWINSTATION_OBJECT WinStaObject
= (PWINSTATION_OBJECT
)ParseObject
;
61 UNICODE_STRING DesktopName
;
62 PBOOLEAN pContext
= (PBOOLEAN
) Context
;
67 /* Set the list pointers and loop the window station */
68 ListHead
= &WinStaObject
->DesktopListHead
;
69 NextEntry
= ListHead
->Flink
;
70 while (NextEntry
!= ListHead
)
72 /* Get the current desktop */
73 Desktop
= CONTAINING_RECORD(NextEntry
, DESKTOP
, ListEntry
);
75 /* Get the desktop name */
76 ASSERT(Desktop
->pDeskInfo
!= NULL
);
77 RtlInitUnicodeString(&DesktopName
, Desktop
->pDeskInfo
->szDesktopName
);
79 /* Compare the name */
80 if (RtlEqualUnicodeString(RemainingName
,
82 (Attributes
& OBJ_CASE_INSENSITIVE
) != 0))
84 /* We found a match. Did this come from a create? */
87 /* Unless OPEN_IF was given, fail with an error */
88 if (!(Attributes
& OBJ_OPENIF
))
91 return STATUS_OBJECT_NAME_COLLISION
;
95 /* Otherwise, return with a warning only */
96 Status
= STATUS_OBJECT_NAME_EXISTS
;
101 /* This was a real open, so this is OK */
102 Status
= STATUS_SUCCESS
;
105 /* Reference the desktop and return it */
106 ObReferenceObject(Desktop
);
111 /* Go to the next desktop */
112 NextEntry
= NextEntry
->Flink
;
115 /* If we got here but this isn't a create, just fail */
116 if (!Context
) return STATUS_OBJECT_NAME_NOT_FOUND
;
118 /* Create the desktop object */
119 InitializeObjectAttributes(&ObjectAttributes
, RemainingName
, 0, NULL
, NULL
);
120 Status
= ObCreateObject(KernelMode
,
129 if (!NT_SUCCESS(Status
)) return Status
;
131 /* Initialize the desktop */
132 Status
= UserInitializeDesktop(Desktop
, RemainingName
, WinStaObject
);
133 if (!NT_SUCCESS(Status
))
135 ObDereferenceObject(Desktop
);
139 /* Set the desktop object and return success */
142 return STATUS_SUCCESS
;
147 IntDesktopObjectDelete(
148 _In_ PVOID Parameters
)
150 PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters
= Parameters
;
151 PDESKTOP pdesk
= (PDESKTOP
)DeleteParameters
->Object
;
153 TRACE("Deleting desktop object 0x%p\n", pdesk
);
155 if (pdesk
->pDeskInfo
&&
156 pdesk
->pDeskInfo
->spwnd
)
158 ASSERT(pdesk
->pDeskInfo
->spwnd
->spwndChild
== NULL
);
159 co_UserDestroyWindow(pdesk
->pDeskInfo
->spwnd
);
162 if (pdesk
->spwndMessage
)
163 co_UserDestroyWindow(pdesk
->spwndMessage
);
165 /* Remove the desktop from the window station's list of associcated desktops */
166 RemoveEntryList(&pdesk
->ListEntry
);
169 IntFreeDesktopHeap(pdesk
);
171 ObDereferenceObject(pdesk
->rpwinstaParent
);
173 return STATUS_SUCCESS
;
179 _In_ PVOID Parameters
)
181 PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters
= Parameters
;
182 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
186 /* This happens when we leak desktop handles */
187 return STATUS_SUCCESS
;
190 /* Do not allow the current desktop or the initial desktop to be closed */
191 if (OkToCloseParameters
->Handle
== pti
->ppi
->hdeskStartup
||
192 OkToCloseParameters
->Handle
== pti
->hdesk
)
194 return STATUS_ACCESS_DENIED
;
197 return STATUS_SUCCESS
;
202 IntDesktopObjectOpen(
203 _In_ PVOID Parameters
)
205 PWIN32_OPENMETHOD_PARAMETERS OpenParameters
= Parameters
;
206 PPROCESSINFO ppi
= PsGetProcessWin32Process(OpenParameters
->Process
);
208 return STATUS_SUCCESS
;
210 return IntMapDesktopView((PDESKTOP
)OpenParameters
->Object
);
215 IntDesktopObjectClose(
216 _In_ PVOID Parameters
)
218 PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters
= Parameters
;
219 PPROCESSINFO ppi
= PsGetProcessWin32Process(CloseParameters
->Process
);
222 /* This happens when the process leaks desktop handles.
223 * At this point the PPROCESSINFO is already destroyed */
224 return STATUS_SUCCESS
;
227 return IntUnmapDesktopView((PDESKTOP
)CloseParameters
->Object
);
231 /* PRIVATE FUNCTIONS **********************************************************/
236 InitDesktopImpl(VOID
)
238 GENERIC_MAPPING IntDesktopMapping
= { DESKTOP_READ
,
243 /* Set Desktop Object Attributes */
244 ExDesktopObjectType
->TypeInfo
.DefaultNonPagedPoolCharge
= sizeof(DESKTOP
);
245 ExDesktopObjectType
->TypeInfo
.GenericMapping
= IntDesktopMapping
;
246 ExDesktopObjectType
->TypeInfo
.ValidAccessMask
= DESKTOP_ALL_ACCESS
;
248 /* Allocate memory for the event structure */
249 gpDesktopThreadStartedEvent
= ExAllocatePoolWithTag(NonPagedPool
,
252 if (!gpDesktopThreadStartedEvent
)
254 ERR("Failed to allocate event!\n");
255 return STATUS_NO_MEMORY
;
258 /* Initialize the kernel event */
259 KeInitializeEvent(gpDesktopThreadStartedEvent
,
260 SynchronizationEvent
,
263 return STATUS_SUCCESS
;
267 GetSystemVersionString(OUT PWSTR pwszzVersion
,
269 IN BOOLEAN InSafeMode
,
270 IN BOOLEAN AppendNtSystemRoot
)
274 RTL_OSVERSIONINFOEXW VerInfo
;
275 UNICODE_STRING BuildLabString
;
276 UNICODE_STRING CSDVersionString
;
277 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable
[] =
281 RTL_QUERY_REGISTRY_DIRECT
,
288 RTL_QUERY_REGISTRY_DIRECT
,
297 WCHAR BuildLabBuffer
[256];
298 WCHAR VersionBuffer
[256];
301 VerInfo
.dwOSVersionInfoSize
= sizeof(VerInfo
);
304 * This call is uniquely used to retrieve the current CSD numbers.
305 * All the rest (major, minor, ...) is either retrieved from the
306 * SharedUserData structure, or from the registry.
308 RtlGetVersion((PRTL_OSVERSIONINFOW
)&VerInfo
);
311 * - Retrieve the BuildLab string from the registry (set by the kernel).
312 * - In kernel-mode, szCSDVersion is not initialized. Initialize it
313 * and query its value from the registry.
315 RtlZeroMemory(BuildLabBuffer
, sizeof(BuildLabBuffer
));
316 RtlInitEmptyUnicodeString(&BuildLabString
,
318 sizeof(BuildLabBuffer
));
319 RtlZeroMemory(VerInfo
.szCSDVersion
, sizeof(VerInfo
.szCSDVersion
));
320 RtlInitEmptyUnicodeString(&CSDVersionString
,
321 VerInfo
.szCSDVersion
,
322 sizeof(VerInfo
.szCSDVersion
));
323 Status
= RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT
,
325 VersionConfigurationTable
,
328 if (!NT_SUCCESS(Status
))
330 /* Indicate nothing is there */
331 BuildLabString
.Length
= 0;
332 CSDVersionString
.Length
= 0;
334 /* NULL-terminate the strings */
335 BuildLabString
.Buffer
[BuildLabString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
336 CSDVersionString
.Buffer
[CSDVersionString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
338 EndBuffer
= VersionBuffer
;
339 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString
.Length
)
341 /* Print the version string */
342 Status
= RtlStringCbPrintfExW(VersionBuffer
,
343 sizeof(VersionBuffer
),
349 if (!NT_SUCCESS(Status
))
351 /* No version, NULL-terminate the string */
352 *EndBuffer
= UNICODE_NULL
;
357 /* No version, NULL-terminate the string */
358 *EndBuffer
= UNICODE_NULL
;
363 /* String for Safe Mode */
364 Status
= RtlStringCchPrintfW(pwszzVersion
,
366 L
"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
369 SharedUserData
->NtMajorVersion
,
370 SharedUserData
->NtMinorVersion
,
371 (VerInfo
.dwBuildNumber
& 0xFFFF),
374 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
376 Status
= RtlStringCbPrintfW(VersionBuffer
,
377 sizeof(VersionBuffer
),
379 SharedUserData
->NtSystemRoot
);
380 if (NT_SUCCESS(Status
))
382 /* Replace the last newline by a NULL, before concatenating */
383 EndBuffer
= wcsrchr(pwszzVersion
, L
'\n');
384 if (EndBuffer
) *EndBuffer
= UNICODE_NULL
;
386 /* The concatenated string has a terminating newline */
387 Status
= RtlStringCchCatW(pwszzVersion
,
390 if (!NT_SUCCESS(Status
))
392 /* Concatenation failed, put back the newline */
393 if (EndBuffer
) *EndBuffer
= L
'\n';
397 /* Override any failures as the NtSystemRoot string is optional */
398 Status
= STATUS_SUCCESS
;
403 /* Multi-string for Normal Mode */
404 Status
= RtlStringCchPrintfW(pwszzVersion
,
406 L
"ReactOS Version %S\n"
408 L
"Reporting NT %u.%u (Build %u%s)\n",
411 SharedUserData
->NtMajorVersion
,
412 SharedUserData
->NtMinorVersion
,
413 (VerInfo
.dwBuildNumber
& 0xFFFF),
416 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
418 Status
= RtlStringCbPrintfW(VersionBuffer
,
419 sizeof(VersionBuffer
),
421 SharedUserData
->NtSystemRoot
);
422 if (NT_SUCCESS(Status
))
424 Status
= RtlStringCchCatW(pwszzVersion
,
429 /* Override any failures as the NtSystemRoot string is optional */
430 Status
= STATUS_SUCCESS
;
434 if (!NT_SUCCESS(Status
))
436 /* Fall-back string */
437 Status
= RtlStringCchPrintfW(pwszzVersion
,
439 L
"ReactOS Version %S %wZ\n",
442 if (!NT_SUCCESS(Status
))
444 /* General failure, NULL-terminate the string */
445 pwszzVersion
[0] = UNICODE_NULL
;
450 * Convert the string separators (newlines) into NULLs
451 * and NULL-terminate the multi-string.
453 while (*pwszzVersion
)
455 EndBuffer
= wcschr(pwszzVersion
, L
'\n');
456 if (!EndBuffer
) break;
457 pwszzVersion
= EndBuffer
;
459 *pwszzVersion
++ = UNICODE_NULL
;
461 *pwszzVersion
= UNICODE_NULL
;
470 * The IntResolveDesktop function attempts to retrieve valid handles to
471 * a desktop and a window station suitable for the specified process.
472 * The specified desktop path string is used only as a hint for the resolution.
474 * - If the process is already assigned to a window station and a desktop,
475 * handles to these objects are returned directly regardless of the specified
476 * desktop path string. This is what happens when this function is called for
477 * a process that has been already started and connected to the Win32 USER.
479 * - If the process is being connected to the Win32 USER, or is in a state
480 * where a window station is assigned to it but no desktop yet, the desktop
481 * path string is used as a hint for the resolution.
482 * A specified window station (if any, otherwise "WinSta0" is used as default)
483 * is tested for existence and accessibility. If the checks are OK a handle
484 * to it is returned. Otherwise we either fail (the window station does not
485 * exist) or, in case a default window station was used, we attempt to open
486 * or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
487 * typically what happens when a non-interactive process is started while
488 * the WinSta0 window station was used as the default one.
489 * A specified desktop (if any, otherwise "Default" is used as default)
490 * is then tested for existence on the opened window station.
492 * - Rules for the choice of the default window station, when none is specified
493 * in the desktop path:
495 * 1. By default, a SYSTEM process connects to a non-interactive window
496 * station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
497 * or one that has been inherited and that is non-interactive.
498 * Only when the interactive window station WinSta0 is specified that
499 * the process can connect to it (e.g. the case of interactive services).
501 * 2. An interactive process, i.e. a process whose LUID is the same as the
502 * one assigned to WinSta0 by Winlogon on user logon, connects by default
503 * to the WinSta0 window station, unless it has inherited from another
504 * interactive window station (which must be... none other than WinSta0).
506 * 3. A non-interactive (but not SYSTEM) process connects by default to
507 * a non-interactive Service-0xXXXX-YYYY$ window station (whose name
508 * is derived from the process' LUID), or to another non-interactive
509 * window station that has been inherited.
510 * Otherwise it may be able connect to the interactive WinSta0 only if
511 * it has explicit access rights to it.
515 * The user process object.
518 * The desktop path string used as a hint for desktop resolution.
521 * Whether or not the returned handles are inheritable.
524 * Pointer to a window station handle.
527 * Pointer to a desktop handle.
536 IN PEPROCESS Process
,
537 IN PUNICODE_STRING DesktopPath
,
539 OUT HWINSTA
* phWinSta
,
540 OUT HDESK
* phDesktop
)
543 HWINSTA hWinSta
= NULL
, hWinStaDup
= NULL
;
544 HDESK hDesktop
= NULL
, hDesktopDup
= NULL
;
546 HANDLE hProcess
= NULL
;
550 POBJECT_ATTRIBUTES ObjectAttributes
= NULL
;
551 PUNICODE_STRING ObjectName
;
552 UNICODE_STRING WinStaName
, DesktopName
;
553 const UNICODE_STRING WinSta0Name
= RTL_CONSTANT_STRING(L
"WinSta0");
554 PWINSTATION_OBJECT WinStaObject
;
555 HWINSTA hTempWinSta
= NULL
;
556 BOOLEAN bUseDefaultWinSta
= FALSE
;
557 BOOLEAN bInteractive
= FALSE
;
558 BOOLEAN bAccessAllowed
= FALSE
;
567 ppi
= PsGetProcessWin32Process(Process
);
568 /* ppi is typically NULL for console applications that connect to Win32 USER */
569 if (!ppi
) TRACE("IntResolveDesktop: ppi is NULL!\n");
571 if (ppi
&& ppi
->hwinsta
!= NULL
&& ppi
->hdeskStartup
!= NULL
)
574 * If this process is the current one, just return the cached handles.
575 * Otherwise, open the window station and desktop objects.
577 if (Process
== PsGetCurrentProcess())
579 hWinSta
= ppi
->hwinsta
;
580 hDesktop
= ppi
->hdeskStartup
;
584 Status
= ObOpenObjectByPointer(ppi
->prpwinsta
,
588 ExWindowStationObjectType
,
591 if (!NT_SUCCESS(Status
))
593 ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi
->prpwinsta
);
594 SetLastNtError(Status
);
598 Status
= ObOpenObjectByPointer(ppi
->rpdeskStartup
,
605 if (!NT_SUCCESS(Status
))
607 ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi
->rpdeskStartup
);
608 ObCloseHandle(hWinSta
, UserMode
);
609 SetLastNtError(Status
);
615 *phDesktop
= hDesktop
;
616 return STATUS_SUCCESS
;
619 /* We will by default use the default window station and desktop */
620 RtlInitEmptyUnicodeString(&WinStaName
, NULL
, 0);
621 RtlInitEmptyUnicodeString(&DesktopName
, NULL
, 0);
624 * Parse the desktop path string which can be of the form "WinSta\Desktop"
625 * or just "Desktop". In the latter case we use the default window station
626 * on which the process is attached to (or if none, "WinSta0").
628 if (DesktopPath
->Buffer
!= NULL
&& DesktopPath
->Length
> sizeof(WCHAR
))
630 DesktopName
= *DesktopPath
;
632 /* Find the separator */
633 while (DesktopName
.Length
> 0 && *DesktopName
.Buffer
&&
634 *DesktopName
.Buffer
!= OBJ_NAME_PATH_SEPARATOR
)
636 DesktopName
.Buffer
++;
637 DesktopName
.Length
-= sizeof(WCHAR
);
638 DesktopName
.MaximumLength
-= sizeof(WCHAR
);
640 if (DesktopName
.Length
> 0)
642 RtlInitEmptyUnicodeString(&WinStaName
, DesktopPath
->Buffer
,
643 DesktopPath
->Length
- DesktopName
.Length
);
644 // (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
645 WinStaName
.Length
= WinStaName
.MaximumLength
;
647 /* Skip the separator */
648 DesktopName
.Buffer
++;
649 DesktopName
.Length
-= sizeof(WCHAR
);
650 DesktopName
.MaximumLength
-= sizeof(WCHAR
);
654 RtlInitEmptyUnicodeString(&WinStaName
, NULL
, 0);
655 DesktopName
= *DesktopPath
;
659 TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName
, &DesktopName
);
661 /* Retrieve the process LUID */
662 Status
= GetProcessLuid(NULL
, Process
, &ProcessLuid
);
663 if (!NT_SUCCESS(Status
))
665 ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status
);
666 SetLastNtError(Status
);
671 * If this process is not the current one, obtain a temporary handle
672 * to it so that we can perform handles duplication later.
674 if (Process
!= PsGetCurrentProcess())
676 Status
= ObOpenObjectByPointer(Process
,
683 if (!NT_SUCCESS(Status
))
685 ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process
, Status
);
686 SetLastNtError(Status
);
693 * If no window station has been specified, search the process handle table
694 * for inherited window station handles, otherwise use a default one.
696 if (WinStaName
.Buffer
== NULL
)
699 * We want to find a suitable default window station.
700 * For applications that can be interactive, i.e. that have allowed
701 * access to the single interactive window station on the system,
702 * the default window station is 'WinSta0'.
703 * For applications that cannot be interactive, i.e. that do not have
704 * access to 'WinSta0' (e.g. non-interactive services), the default
705 * window station is 'Service-0xXXXX-YYYY$' (created if needed).
706 * Precedence will however be taken by any inherited window station
707 * that possesses the required interactivity property.
709 bUseDefaultWinSta
= TRUE
;
712 * Use the default 'WinSta0' window station. Whether we should
713 * use 'Service-0xXXXX-YYYY$' instead will be determined later.
715 // RtlInitUnicodeString(&WinStaName, L"WinSta0");
716 WinStaName
= WinSta0Name
;
718 if (ObFindHandleForObject(Process
,
720 ExWindowStationObjectType
,
724 TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta
);
729 * If no desktop has been specified, search the process handle table
730 * for inherited desktop handles, otherwise use the Default desktop.
731 * Note that the inherited desktop that we may use, may not belong
732 * to the window station we will connect to.
734 if (DesktopName
.Buffer
== NULL
)
736 /* Use a default desktop name */
737 RtlInitUnicodeString(&DesktopName
, L
"Default");
739 if (ObFindHandleForObject(Process
,
745 TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop
);
751 * We are going to open either a window station or a desktop.
752 * Even if this operation is done from kernel-mode, we should
753 * "emulate" an opening from user-mode (i.e. using an ObjectAttributes
754 * allocated in user-mode, with AccessMode == UserMode) for the
755 * Object Manager to perform proper access validation to the
756 * window station or desktop.
760 * Estimate the maximum size needed for the window station name
761 * and desktop name to be given to ObjectAttributes->ObjectName.
765 /* Window station name */
766 MemSize
= _scwprintf(L
"Service-0x%x-%x$", MAXULONG
, MAXULONG
) * sizeof(WCHAR
);
767 MemSize
= gustrWindowStationsDir
.Length
+ sizeof(OBJ_NAME_PATH_SEPARATOR
)
768 + max(WinStaName
.Length
, MemSize
) + sizeof(UNICODE_NULL
);
769 if (MemSize
> MAXUSHORT
)
771 ERR("IntResolveDesktop: Window station name length is too long.\n");
772 Status
= STATUS_NAME_TOO_LONG
;
775 StrSize
= max(StrSize
, (USHORT
)MemSize
);
778 MemSize
= max(DesktopName
.Length
+ sizeof(UNICODE_NULL
), sizeof(L
"Default"));
779 StrSize
= max(StrSize
, (USHORT
)MemSize
);
781 /* Size for the OBJECT_ATTRIBUTES */
782 MemSize
= ALIGN_UP(sizeof(OBJECT_ATTRIBUTES
), sizeof(PVOID
));
784 /* Add the string size */
785 MemSize
+= ALIGN_UP(sizeof(UNICODE_STRING
), sizeof(PVOID
));
788 /* Allocate the memory in user-mode */
789 Status
= ZwAllocateVirtualMemory(ZwCurrentProcess(),
790 (PVOID
*)&ObjectAttributes
,
795 if (!NT_SUCCESS(Status
))
797 ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status
);
801 ObjectName
= (PUNICODE_STRING
)((ULONG_PTR
)ObjectAttributes
+
802 ALIGN_UP(sizeof(OBJECT_ATTRIBUTES
), sizeof(PVOID
)));
804 RtlInitEmptyUnicodeString(ObjectName
,
805 (PWCHAR
)((ULONG_PTR
)ObjectName
+
806 ALIGN_UP(sizeof(UNICODE_STRING
), sizeof(PVOID
))),
810 /* If we got an inherited window station handle, duplicate and use it */
813 ASSERT(bUseDefaultWinSta
);
815 /* Duplicate the handle if it belongs to another process than the current one */
816 if (Process
!= PsGetCurrentProcess())
819 Status
= ZwDuplicateObject(hProcess
,
822 (PHANDLE
)&hWinStaDup
,
825 DUPLICATE_SAME_ACCESS
);
826 if (!NT_SUCCESS(Status
))
828 ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status
);
829 /* We will use a default window station */
834 hWinSta
= hWinStaDup
;
840 * If we have an inherited window station, check whether
841 * it is interactive and remember that for later.
845 ASSERT(bUseDefaultWinSta
);
847 /* Reference the inherited window station */
848 Status
= ObReferenceObjectByHandle(hWinSta
,
850 ExWindowStationObjectType
,
852 (PVOID
*)&WinStaObject
,
854 if (!NT_SUCCESS(Status
))
856 ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status
);
857 /* We will use a default window station */
860 ASSERT(hWinSta
== hWinStaDup
);
861 ObCloseHandle(hWinStaDup
, UserMode
);
868 ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
869 ProcessLuid
.HighPart
, ProcessLuid
.LowPart
,
870 WinStaObject
->luidUser
.HighPart
, WinStaObject
->luidUser
.LowPart
);
872 /* Check whether this window station is interactive, and remember it for later */
873 bInteractive
= !(WinStaObject
->Flags
& WSS_NOIO
);
875 /* Dereference the window station */
876 ObDereferenceObject(WinStaObject
);
880 /* Build a valid window station name */
881 Status
= RtlStringCbPrintfW(ObjectName
->Buffer
,
882 ObjectName
->MaximumLength
,
884 &gustrWindowStationsDir
,
886 if (!NT_SUCCESS(Status
))
888 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status
);
891 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
893 TRACE("Parsed initial window station: '%wZ'\n", ObjectName
);
895 /* Try to open the window station */
896 InitializeObjectAttributes(ObjectAttributes
,
898 OBJ_CASE_INSENSITIVE
,
902 ObjectAttributes
->Attributes
|= OBJ_INHERIT
;
904 Status
= ObOpenObjectByName(ObjectAttributes
,
905 ExWindowStationObjectType
,
910 (PHANDLE
)&hTempWinSta
);
911 if (!NT_SUCCESS(Status
))
913 ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName
, Status
);
918 // FIXME TODO: Perform a window station access check!!
919 // If we fail AND bUseDefaultWinSta == FALSE we just quit.
923 * Check whether we are opening the (single) interactive
924 * window station, and if so, perform an access check.
926 /* Check whether we are allowed to perform interactions */
927 if (RtlEqualUnicodeString(&WinStaName
, &WinSta0Name
, TRUE
))
929 LUID SystemLuid
= SYSTEM_LUID
;
931 /* Interactive window station: check for user LUID */
932 WinStaObject
= InputWindowStation
;
934 Status
= STATUS_ACCESS_DENIED
;
936 // TODO: Check also that we compare wrt. window station WinSta0
937 // which is the only one that can be interactive on the system.
938 if (((!bUseDefaultWinSta
|| bInherit
) && RtlEqualLuid(&ProcessLuid
, &SystemLuid
)) ||
939 RtlEqualLuid(&ProcessLuid
, &WinStaObject
->luidUser
))
941 /* We are interactive on this window station */
942 bAccessAllowed
= TRUE
;
943 Status
= STATUS_SUCCESS
;
948 /* Non-interactive window station: we have access since we were able to open it */
949 bAccessAllowed
= TRUE
;
950 Status
= STATUS_SUCCESS
;
954 /* If we failed, bail out if we were not trying to open the default window station */
955 if (!NT_SUCCESS(Status
) && !bUseDefaultWinSta
) // if (!bAccessAllowed)
958 if (/* bAccessAllowed && */ bInteractive
|| !bAccessAllowed
)
961 * Close WinSta0 if the inherited window station is interactive so that
962 * we can use it, or we do not have access to the interactive WinSta0.
964 ObCloseHandle(hTempWinSta
, UserMode
);
967 if (bInteractive
== bAccessAllowed
)
969 /* Keep using the inherited window station */
972 else // if (bInteractive != bAccessAllowed)
975 * Close the inherited window station, we will either keep using
976 * the interactive WinSta0, or use Service-0xXXXX-YYYY$.
980 ASSERT(hWinSta
== hWinStaDup
);
981 ObCloseHandle(hWinStaDup
, UserMode
);
984 hWinSta
= hTempWinSta
; // hTempWinSta is NULL in case bAccessAllowed == FALSE
987 if (bUseDefaultWinSta
)
989 if (hWinSta
== NULL
&& !bInteractive
)
991 /* Build a valid window station name from the LUID */
992 Status
= RtlStringCbPrintfW(ObjectName
->Buffer
,
993 ObjectName
->MaximumLength
,
994 L
"%wZ\\Service-0x%x-%x$",
995 &gustrWindowStationsDir
,
996 ProcessLuid
.HighPart
,
997 ProcessLuid
.LowPart
);
998 if (!NT_SUCCESS(Status
))
1000 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status
);
1003 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
1006 * Create or open the non-interactive window station.
1007 * NOTE: The non-interactive window station handle is never inheritable.
1009 // FIXME: Set security!
1010 InitializeObjectAttributes(ObjectAttributes
,
1012 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
1016 Status
= IntCreateWindowStation(&hWinSta
,
1022 if (!NT_SUCCESS(Status
))
1024 ASSERT(hWinSta
== NULL
);
1025 ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
1026 ObjectName
, Status
);
1031 // FIXME: We might not need to always create or open the "Default"
1032 // desktop on the Service-0xXXXX-YYYY$ window station; we may need
1033 // to use another one....
1036 /* Create or open the Default desktop on the window station */
1037 Status
= RtlStringCbCopyW(ObjectName
->Buffer
,
1038 ObjectName
->MaximumLength
,
1040 if (!NT_SUCCESS(Status
))
1042 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status
);
1045 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
1047 /* NOTE: The non-interactive desktop handle is never inheritable. */
1048 // FIXME: Set security!
1049 InitializeObjectAttributes(ObjectAttributes
,
1051 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
1055 Status
= IntCreateDesktop(&hDesktop
,
1062 if (!NT_SUCCESS(Status
))
1064 ASSERT(hDesktop
== NULL
);
1065 ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1066 ObjectName
, hWinSta
, Status
);
1072 if (hWinSta == NULL)
1074 Status = STATUS_UNSUCCESSFUL;
1081 * If we got an inherited desktop handle, duplicate and use it,
1082 * otherwise open a new desktop.
1084 if (hDesktop
!= NULL
)
1086 /* Duplicate the handle if it belongs to another process than the current one */
1087 if (Process
!= PsGetCurrentProcess())
1090 Status
= ZwDuplicateObject(hProcess
,
1093 (PHANDLE
)&hDesktopDup
,
1096 DUPLICATE_SAME_ACCESS
);
1097 if (!NT_SUCCESS(Status
))
1099 ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status
);
1100 /* We will use a default desktop */
1105 hDesktop
= hDesktopDup
;
1110 if ((hWinSta
!= NULL
) && (hDesktop
== NULL
))
1112 Status
= RtlStringCbCopyNW(ObjectName
->Buffer
,
1113 ObjectName
->MaximumLength
,
1115 DesktopName
.Length
);
1116 if (!NT_SUCCESS(Status
))
1118 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status
);
1121 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
1123 TRACE("Parsed initial desktop: '%wZ'\n", ObjectName
);
1125 /* Open the desktop object */
1126 InitializeObjectAttributes(ObjectAttributes
,
1128 OBJ_CASE_INSENSITIVE
,
1132 ObjectAttributes
->Attributes
|= OBJ_INHERIT
;
1134 Status
= ObOpenObjectByName(ObjectAttributes
,
1135 ExDesktopObjectType
,
1140 (PHANDLE
)&hDesktop
);
1141 if (!NT_SUCCESS(Status
))
1143 ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1144 ObjectName
, hWinSta
, Status
);
1150 /* Release the object attributes */
1151 if (ObjectAttributes
)
1154 ZwFreeVirtualMemory(ZwCurrentProcess(),
1155 (PVOID
*)&ObjectAttributes
,
1160 /* Close the temporary process handle */
1161 if (hProcess
) // if (Process != PsGetCurrentProcess())
1162 ObCloseHandle(hProcess
, KernelMode
);
1164 if (NT_SUCCESS(Status
))
1166 *phWinSta
= hWinSta
;
1167 *phDesktop
= hDesktop
;
1168 return STATUS_SUCCESS
;
1172 ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath
, Status
);
1175 ObCloseHandle(hDesktopDup
, UserMode
);
1177 ObCloseHandle(hWinStaDup
, UserMode
);
1180 ObCloseHandle(hDesktop
, UserMode
);
1182 ObCloseHandle(hWinSta
, UserMode
);
1184 SetLastNtError(Status
);
1190 * IntValidateDesktopHandle
1192 * Validates the desktop handle.
1195 * If the function succeeds, the handle remains referenced. If the
1196 * fucntion fails, last error is set.
1200 IntValidateDesktopHandle(
1202 KPROCESSOR_MODE AccessMode
,
1203 ACCESS_MASK DesiredAccess
,
1208 Status
= ObReferenceObjectByHandle(
1211 ExDesktopObjectType
,
1216 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
1217 Desktop
, *Object
, DesiredAccess
, Status
);
1219 if (!NT_SUCCESS(Status
))
1220 SetLastNtError(Status
);
1226 IntGetActiveDesktop(VOID
)
1228 return gpdeskInputDesktop
;
1232 * Returns or creates a handle to the desktop object
1235 IntGetDesktopObjectHandle(PDESKTOP DesktopObject
)
1240 ASSERT(DesktopObject
);
1242 if (!ObFindHandleForObject(PsGetCurrentProcess(),
1244 ExDesktopObjectType
,
1248 Status
= ObOpenObjectByPointer(DesktopObject
,
1252 ExDesktopObjectType
,
1255 if (!NT_SUCCESS(Status
))
1257 /* Unable to create a handle */
1258 ERR("Unable to create a desktop handle\n");
1264 TRACE("Got handle: 0x%p\n", hDesk
);
1270 PUSER_MESSAGE_QUEUE FASTCALL
1271 IntGetFocusMessageQueue(VOID
)
1273 PDESKTOP pdo
= IntGetActiveDesktop();
1276 TRACE("No active desktop\n");
1279 return (PUSER_MESSAGE_QUEUE
)pdo
->ActiveMessageQueue
;
1283 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue
)
1285 PUSER_MESSAGE_QUEUE Old
;
1286 PDESKTOP pdo
= IntGetActiveDesktop();
1289 TRACE("No active desktop\n");
1292 if (NewQueue
!= NULL
)
1294 if (NewQueue
->Desktop
!= NULL
)
1296 TRACE("Message Queue already attached to another desktop!\n");
1299 IntReferenceMessageQueue(NewQueue
);
1300 (void)InterlockedExchangePointer((PVOID
*)&NewQueue
->Desktop
, pdo
);
1302 Old
= (PUSER_MESSAGE_QUEUE
)InterlockedExchangePointer((PVOID
*)&pdo
->ActiveMessageQueue
, NewQueue
);
1305 (void)InterlockedExchangePointer((PVOID
*)&Old
->Desktop
, 0);
1306 gpqForegroundPrev
= Old
;
1307 IntDereferenceMessageQueue(Old
);
1309 // Only one Q can have active foreground even when there are more than one desktop.
1312 gpqForeground
= pdo
->ActiveMessageQueue
;
1316 gpqForeground
= NULL
;
1317 ERR("ptiLastInput is CLEARED!!\n");
1318 ptiLastInput
= NULL
; // ReactOS hacks,,,, should check for process death.
1323 IntGetThreadDesktopWindow(PTHREADINFO pti
)
1325 if (!pti
) pti
= PsGetCurrentThreadWin32Thread();
1326 if (pti
->pDeskInfo
) return pti
->pDeskInfo
->spwnd
;
1330 PWND FASTCALL
co_GetDesktopWindow(PWND pWnd
)
1332 if (pWnd
->head
.rpdesk
&&
1333 pWnd
->head
.rpdesk
->pDeskInfo
)
1334 return pWnd
->head
.rpdesk
->pDeskInfo
->spwnd
;
1338 HWND FASTCALL
IntGetDesktopWindow(VOID
)
1340 PDESKTOP pdo
= IntGetActiveDesktop();
1343 TRACE("No active desktop\n");
1346 return pdo
->DesktopWindow
;
1349 PWND FASTCALL
UserGetDesktopWindow(VOID
)
1351 PDESKTOP pdo
= IntGetActiveDesktop();
1355 TRACE("No active desktop\n");
1358 // return pdo->pDeskInfo->spwnd;
1359 return UserGetWindowObject(pdo
->DesktopWindow
);
1362 HWND FASTCALL
IntGetMessageWindow(VOID
)
1364 PDESKTOP pdo
= IntGetActiveDesktop();
1368 TRACE("No active desktop\n");
1371 return pdo
->spwndMessage
->head
.h
;
1374 PWND FASTCALL
UserGetMessageWindow(VOID
)
1376 PDESKTOP pdo
= IntGetActiveDesktop();
1380 TRACE("No active desktop\n");
1383 return pdo
->spwndMessage
;
1386 HWND FASTCALL
IntGetCurrentThreadDesktopWindow(VOID
)
1388 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1389 PDESKTOP pdo
= pti
->rpdesk
;
1392 ERR("Thread doesn't have a desktop\n");
1395 return pdo
->DesktopWindow
;
1398 /* PUBLIC FUNCTIONS ***********************************************************/
1401 DesktopWindowProc(PWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
1405 //ERR("DesktopWindowProc\n");
1414 Wnd
->fnid
= FNID_DESKTOP
;
1416 *lResult
= (LRESULT
)TRUE
;
1420 Value
= HandleToULong(PsGetCurrentProcessId());
1422 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_PROCESSID
, Value
, FALSE
);
1423 Value
= HandleToULong(PsGetCurrentThreadId());
1425 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_THREADID
, Value
, FALSE
);
1429 case WM_DISPLAYCHANGE
:
1430 co_WinPosSetWindowPos(Wnd
, 0, 0, 0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
| SWP_NOACTIVATE
);
1434 IntPaintDesktop((HDC
)wParam
);
1440 if (IntBeginPaint(Wnd
, &Ps
))
1442 IntEndPaint(Wnd
, &Ps
);
1446 case WM_SYSCOLORCHANGE
:
1447 co_UserRedrawWindow(Wnd
, NULL
, NULL
, RDW_INVALIDATE
|RDW_ERASE
|RDW_ALLCHILDREN
);
1452 PCURICON_OBJECT pcurOld
, pcurNew
;
1453 pcurNew
= UserGetCurIconObject(gDesktopCursor
);
1459 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
1460 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
1463 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
1464 UserDereferenceObject(pcurOld
);
1469 case WM_WINDOWPOSCHANGING
:
1471 PWINDOWPOS pWindowPos
= (PWINDOWPOS
)lParam
;
1472 if ((pWindowPos
->flags
& SWP_SHOWWINDOW
) != 0)
1474 HDESK hdesk
= UserOpenInputDesktop(0, FALSE
, DESKTOP_ALL_ACCESS
);
1475 IntSetThreadDesktop(hdesk
, FALSE
);
1480 TRACE("DWP calling IDWP Msg %d\n",Msg
);
1481 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
1483 return TRUE
; /* We are done. Do not do any callbacks to user mode */
1487 UserMessageWindowProc(PWND pwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
1494 pwnd
->fnid
|= FNID_MESSAGEWND
;
1495 *lResult
= (LRESULT
)TRUE
;
1498 pwnd
->fnid
|= FNID_DESTROY
;
1501 ERR("UMWP calling IDWP\n");
1502 *lResult
= IntDefWindowProc(pwnd
, Msg
, wParam
, lParam
, FALSE
);
1505 return TRUE
; /* We are done. Do not do any callbacks to user mode */
1508 VOID NTAPI
DesktopThreadMain(VOID
)
1513 gptiDesktopThread
= PsGetCurrentThreadWin32Thread();
1515 UserEnterExclusive();
1517 /* Register system classes. This thread does not belong to any desktop so the
1518 classes will be allocated from the shared heap */
1519 UserRegisterSystemClasses();
1521 KeSetEvent(gpDesktopThreadStartedEvent
, IO_NO_INCREMENT
, FALSE
);
1525 Ret
= co_IntGetPeekMessage(&Msg
, 0, 0, 0, PM_REMOVE
, TRUE
);
1528 IntDispatchMessage(&Msg
);
1536 UserGetDesktopDC(ULONG DcType
, BOOL bAltDc
, BOOL ValidatehWnd
)
1538 PWND DesktopObject
= 0;
1541 /* This can be called from GDI/DX, so acquire the USER lock */
1542 UserEnterExclusive();
1544 if (DcType
== DC_TYPE_DIRECT
)
1546 DesktopObject
= UserGetDesktopWindow();
1547 DesktopHDC
= (HDC
)UserGetWindowDC(DesktopObject
);
1551 PMONITOR pMonitor
= UserGetPrimaryMonitor();
1552 DesktopHDC
= IntGdiCreateDisplayDC(pMonitor
->hDev
, DcType
, bAltDc
);
1561 UserRedrawDesktop(VOID
)
1566 Window
= UserGetDesktopWindow();
1567 Rgn
= IntSysCreateRectpRgnIndirect(&Window
->rcWindow
);
1569 IntInvalidateWindows( Window
,
1581 co_IntShowDesktop(PDESKTOP Desktop
, ULONG Width
, ULONG Height
, BOOL bRedraw
)
1583 PWND pwnd
= Desktop
->pDeskInfo
->spwnd
;
1584 UINT flags
= SWP_NOACTIVATE
|SWP_NOZORDER
|SWP_SHOWWINDOW
;
1588 flags
|= SWP_NOREDRAW
;
1590 co_WinPosSetWindowPos(pwnd
, NULL
, 0, 0, Width
, Height
, flags
);
1593 co_UserRedrawWindow( pwnd
, NULL
, 0, RDW_UPDATENOW
| RDW_ALLCHILDREN
| RDW_INVALIDATE
);
1595 return STATUS_SUCCESS
;
1599 IntHideDesktop(PDESKTOP Desktop
)
1603 DesktopWnd
= IntGetWindowObject(Desktop
->DesktopWindow
);
1606 return ERROR_INVALID_WINDOW_HANDLE
;
1608 DesktopWnd
->style
&= ~WS_VISIBLE
;
1610 return STATUS_SUCCESS
;
1615 UserBuildShellHookHwndList(PDESKTOP Desktop
)
1618 PLIST_ENTRY ListEntry
;
1619 PSHELL_HOOK_WINDOW Current
;
1622 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1623 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1624 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1626 ListEntry
= ListEntry
->Flink
;
1630 if (!entries
) return NULL
;
1632 list
= ExAllocatePoolWithTag(PagedPool
, sizeof(HWND
) * (entries
+ 1), USERTAG_WINDOWLIST
); /* alloc one extra for nullterm */
1635 HWND
* cursor
= list
;
1637 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1638 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1640 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1641 ListEntry
= ListEntry
->Flink
;
1642 *cursor
++ = Current
->hWnd
;
1645 *cursor
= NULL
; /* Nullterm list */
1652 * Send the Message to the windows registered for ShellHook
1653 * notifications. The lParam contents depend on the Message. See
1654 * MSDN for more details (RegisterShellHookWindow)
1656 VOID
co_IntShellHookNotify(WPARAM Message
, WPARAM wParam
, LPARAM lParam
)
1658 PDESKTOP Desktop
= IntGetActiveDesktop();
1661 if (!gpsi
->uiShellMsg
)
1663 gpsi
->uiShellMsg
= IntAddAtom(L
"SHELLHOOK");
1665 TRACE("MsgType = %x\n", gpsi
->uiShellMsg
);
1666 if (!gpsi
->uiShellMsg
)
1667 ERR("LastError: %x\n", EngGetLastError());
1672 TRACE("IntShellHookNotify: No desktop!\n");
1676 // Allow other devices have a shot at foreground.
1677 if (Message
== HSHELL_APPCOMMAND
) ptiLastInput
= NULL
;
1679 // FIXME: System Tray Support.
1681 HwndList
= UserBuildShellHookHwndList(Desktop
);
1684 HWND
* cursor
= HwndList
;
1686 for (; *cursor
; cursor
++)
1688 TRACE("Sending notify\n");
1689 UserPostMessage(*cursor
,
1692 (Message
== HSHELL_LANGUAGE
? lParam
: (LPARAM
)wParam
) );
1693 /* co_IntPostOrSendMessage(*cursor,
1696 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1699 ExFreePoolWithTag(HwndList
, USERTAG_WINDOWLIST
);
1702 if (ISITHOOKED(WH_SHELL
))
1704 co_HOOK_CallHooks(WH_SHELL
, Message
, wParam
, lParam
);
1709 * Add the window to the ShellHookWindows list. The windows
1710 * on that list get notifications that are important to shell
1711 * type applications.
1713 * TODO: Validate the window? I'm not sure if sending these messages to
1714 * an unsuspecting application that is not your own is a nice thing to do.
1716 BOOL
IntRegisterShellHookWindow(HWND hWnd
)
1718 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1719 PDESKTOP Desktop
= pti
->rpdesk
;
1720 PSHELL_HOOK_WINDOW Entry
;
1722 TRACE("IntRegisterShellHookWindow\n");
1724 /* First deregister the window, so we can be sure it's never twice in the
1727 IntDeRegisterShellHookWindow(hWnd
);
1729 Entry
= ExAllocatePoolWithTag(PagedPool
,
1730 sizeof(SHELL_HOOK_WINDOW
),
1738 InsertTailList(&Desktop
->ShellHookWindows
, &Entry
->ListEntry
);
1744 * Remove the window from the ShellHookWindows list. The windows
1745 * on that list get notifications that are important to shell
1746 * type applications.
1748 BOOL
IntDeRegisterShellHookWindow(HWND hWnd
)
1750 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1751 PDESKTOP Desktop
= pti
->rpdesk
;
1752 PLIST_ENTRY ListEntry
;
1753 PSHELL_HOOK_WINDOW Current
;
1755 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1756 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1758 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1759 ListEntry
= ListEntry
->Flink
;
1760 if (Current
->hWnd
== hWnd
)
1762 RemoveEntryList(&Current
->ListEntry
);
1763 ExFreePoolWithTag(Current
, TAG_WINSTA
);
1772 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop
)
1774 /* FIXME: Disable until unmapping works in mm */
1776 if (Desktop
->pheapDesktop
!= NULL
)
1778 MmUnmapViewInSessionSpace(Desktop
->pheapDesktop
);
1779 Desktop
->pheapDesktop
= NULL
;
1782 if (Desktop
->hsectionDesktop
!= NULL
)
1784 ObDereferenceObject(Desktop
->hsectionDesktop
);
1785 Desktop
->hsectionDesktop
= NULL
;
1791 IntPaintDesktop(HDC hDC
)
1793 static WCHAR s_wszSafeMode
[] = L
"Safe Mode"; // FIXME: Localize!
1796 HBRUSH DesktopBrush
, PreviousBrush
;
1798 BOOL doPatBlt
= TRUE
;
1802 if (GdiGetClipBox(hDC
, &Rect
) == ERROR
)
1805 hWndDesktop
= IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1807 WndDesktop
= UserGetWindowObject(hWndDesktop
); // rpdesk->pDeskInfo->spwnd;
1811 /* Retrieve the current SafeMode state */
1812 InSafeMode
= (UserGetSystemMetrics(SM_CLEANBOOT
) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1816 DesktopBrush
= (HBRUSH
)WndDesktop
->pcls
->hbrBackground
;
1819 * Paint desktop background
1821 if (gspv
.hbmWallpaper
!= NULL
)
1825 int scaledWidth
, scaledHeight
;
1826 int wallpaperX
, wallpaperY
, wallpaperWidth
, wallpaperHeight
;
1829 sz
.cx
= WndDesktop
->rcWindow
.right
- WndDesktop
->rcWindow
.left
;
1830 sz
.cy
= WndDesktop
->rcWindow
.bottom
- WndDesktop
->rcWindow
.top
;
1832 if (gspv
.WallpaperMode
== wmFit
||
1833 gspv
.WallpaperMode
== wmFill
)
1835 int scaleNum
, scaleDen
;
1837 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
1838 if ((sz
.cx
* gspv
.cyWallpaper
) > (sz
.cy
* gspv
.cxWallpaper
))
1840 if (gspv
.WallpaperMode
== wmFit
)
1843 scaleDen
= gspv
.cyWallpaper
;
1848 scaleDen
= gspv
.cxWallpaper
;
1853 if (gspv
.WallpaperMode
== wmFit
)
1856 scaleDen
= gspv
.cxWallpaper
;
1861 scaleDen
= gspv
.cyWallpaper
;
1865 scaledWidth
= EngMulDiv(gspv
.cxWallpaper
, scaleNum
, scaleDen
);
1866 scaledHeight
= EngMulDiv(gspv
.cyWallpaper
, scaleNum
, scaleDen
);
1868 if (gspv
.WallpaperMode
== wmFill
)
1870 wallpaperX
= (((scaledWidth
- sz
.cx
) * gspv
.cxWallpaper
) / (2 * scaledWidth
));
1871 wallpaperY
= (((scaledHeight
- sz
.cy
) * gspv
.cyWallpaper
) / (2 * scaledHeight
));
1873 wallpaperWidth
= (sz
.cx
* gspv
.cxWallpaper
) / scaledWidth
;
1874 wallpaperHeight
= (sz
.cy
* gspv
.cyWallpaper
) / scaledHeight
;
1878 if (gspv
.WallpaperMode
== wmStretch
||
1879 gspv
.WallpaperMode
== wmTile
||
1880 gspv
.WallpaperMode
== wmFill
)
1885 else if (gspv
.WallpaperMode
== wmFit
)
1887 x
= (sz
.cx
- scaledWidth
) / 2;
1888 y
= (sz
.cy
- scaledHeight
) / 2;
1892 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1893 x
= (sz
.cx
/ 2) - (gspv
.cxWallpaper
/ 2);
1894 y
= (sz
.cy
/ 2) - (gspv
.cyWallpaper
/ 2);
1897 hWallpaperDC
= NtGdiCreateCompatibleDC(hDC
);
1898 if (hWallpaperDC
!= NULL
)
1902 /* Fill in the area that the bitmap is not going to cover */
1905 /* FIXME: Clip out the bitmap
1906 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1907 once we support DSTINVERT */
1908 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1909 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1910 NtGdiSelectBrush(hDC
, PreviousBrush
);
1913 /* Do not fill the background after it is painted no matter the size of the picture */
1916 hOldBitmap
= NtGdiSelectBitmap(hWallpaperDC
, gspv
.hbmWallpaper
);
1918 if (gspv
.WallpaperMode
== wmStretch
)
1920 if (Rect
.right
&& Rect
.bottom
)
1921 NtGdiStretchBlt(hDC
,
1934 else if (gspv
.WallpaperMode
== wmTile
)
1936 /* Paint the bitmap across the screen then down */
1937 for (y
= 0; y
< Rect
.bottom
; y
+= gspv
.cyWallpaper
)
1939 for (x
= 0; x
< Rect
.right
; x
+= gspv
.cxWallpaper
)
1955 else if (gspv
.WallpaperMode
== wmFit
)
1957 if (Rect
.right
&& Rect
.bottom
)
1959 NtGdiStretchBlt(hDC
,
1973 else if (gspv
.WallpaperMode
== wmFill
)
1975 if (Rect
.right
&& Rect
.bottom
)
1977 NtGdiStretchBlt(hDC
,
2005 NtGdiSelectBitmap(hWallpaperDC
, hOldBitmap
);
2006 NtGdiDeleteObjectApp(hWallpaperDC
);
2012 /* Black desktop background in Safe Mode */
2013 DesktopBrush
= StockObjects
[BLACK_BRUSH
];
2016 /* Background is set to none, clear the screen */
2019 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
2020 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
2021 NtGdiSelectBrush(hDC
, PreviousBrush
);
2025 * Display the system version on the desktop background
2027 if (InSafeMode
|| g_AlwaysDisplayVersion
|| g_PaintDesktopVersion
)
2030 static WCHAR wszzVersion
[1024] = L
"\0";
2032 /* Only used in normal mode */
2033 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
2034 static POLYTEXTW VerStrs
[4] = {{0},{0},{0},{0}};
2038 HFONT hFont1
= NULL
, hFont2
= NULL
, hOldFont
= NULL
;
2039 COLORREF crText
, color_old
;
2044 if (!UserSystemParametersInfo(SPI_GETWORKAREA
, 0, &Rect
, 0))
2046 Rect
.left
= Rect
.top
= 0;
2047 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
2048 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
2052 RECTL_vOffsetRect(&Rect
, -Rect
.left
, -Rect
.top
);
2056 * Set up the fonts (otherwise use default ones)
2059 /* Font for the principal version string */
2060 hFont1
= GreCreateFontIndirectW(&gspv
.ncm
.lfCaptionFont
);
2061 /* Font for the secondary version strings */
2062 hFont2
= GreCreateFontIndirectW(&gspv
.ncm
.lfMenuFont
);
2065 hOldFont
= NtGdiSelectFont(hDC
, hFont1
);
2067 if (gspv
.hbmWallpaper
== NULL
)
2069 /* Retrieve the brush fill colour */
2070 // TODO: The following code constitutes "GreGetBrushColor".
2071 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
2072 pdc
= DC_LockDc(hDC
);
2075 crText
= pdc
->eboFill
.ulRGBColor
;
2080 crText
= RGB(0, 0, 0);
2082 NtGdiSelectBrush(hDC
, PreviousBrush
);
2084 /* Adjust text colour according to the brush */
2085 if (GetRValue(crText
) + GetGValue(crText
) + GetBValue(crText
) > 128 * 3)
2086 crText
= RGB(0, 0, 0);
2088 crText
= RGB(255, 255, 255);
2092 /* Always use white when the text is displayed on top of a wallpaper */
2093 crText
= RGB(255, 255, 255);
2096 color_old
= IntGdiSetTextColor(hDC
, crText
);
2097 align_old
= IntGdiSetTextAlign(hDC
, TA_RIGHT
);
2098 mode_old
= IntGdiSetBkMode(hDC
, TRANSPARENT
);
2100 /* Display the system version information */
2103 Status
= GetSystemVersionString(wszzVersion
,
2104 ARRAYSIZE(wszzVersion
),
2106 g_AlwaysDisplayVersion
);
2107 if (!InSafeMode
&& NT_SUCCESS(Status
) && *wszzVersion
)
2109 PWCHAR pstr
= wszzVersion
;
2110 for (i
= 0; (i
< ARRAYSIZE(VerStrs
)) && *pstr
; ++i
)
2112 VerStrs
[i
].n
= lstrlenW(pstr
);
2113 VerStrs
[i
].lpstr
= pstr
;
2114 pstr
+= (VerStrs
[i
].n
+ 1);
2120 Status
= STATUS_SUCCESS
;
2122 if (NT_SUCCESS(Status
) && *wszzVersion
)
2127 LONG TotalHeight
= 0;
2129 /* Normal Mode: multiple version information text separated by newlines */
2130 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
2132 /* Compute the heights of the strings */
2133 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2134 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
2136 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
2139 GreGetTextExtentW(hDC
, VerStrs
[i
].lpstr
, VerStrs
[i
].n
, &Size
, 1);
2140 VerStrs
[i
].y
= Size
.cy
; // Store the string height
2141 TotalHeight
+= Size
.cy
;
2143 /* While the first string was using hFont1, all the others use hFont2 */
2144 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
2146 /* The total height must not exceed the screen height */
2147 TotalHeight
= min(TotalHeight
, Rect
.bottom
);
2149 /* Display the strings */
2150 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2151 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
2153 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
2156 TotalHeight
-= VerStrs
[i
].y
;
2159 Rect
.bottom
- TotalHeight
- 5,
2165 /* While the first string was using hFont1, all the others use hFont2 */
2166 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
2171 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2173 /* Safe Mode: single version information text in top center */
2174 len
= wcslen(wszzVersion
);
2176 IntGdiSetTextAlign(hDC
, TA_CENTER
| TA_TOP
);
2177 GreExtTextOutW(hDC
, (Rect
.right
+ Rect
.left
)/2, Rect
.top
+ 3, 0, NULL
, wszzVersion
, len
, NULL
, 0);
2183 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2185 /* Print Safe Mode text in corners */
2186 len
= wcslen(s_wszSafeMode
);
2188 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_TOP
);
2189 GreExtTextOutW(hDC
, Rect
.left
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2190 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_TOP
);
2191 GreExtTextOutW(hDC
, Rect
.right
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2192 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_BOTTOM
);
2193 GreExtTextOutW(hDC
, Rect
.left
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2194 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
2195 GreExtTextOutW(hDC
, Rect
.right
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2198 IntGdiSetBkMode(hDC
, mode_old
);
2199 IntGdiSetTextAlign(hDC
, align_old
);
2200 IntGdiSetTextColor(hDC
, color_old
);
2203 GreDeleteObject(hFont2
);
2207 NtGdiSelectFont(hDC
, hOldFont
);
2208 GreDeleteObject(hFont1
);
2216 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
)
2218 PVOID DesktopHeapSystemBase
= NULL
;
2219 ULONG_PTR HeapSize
= gdwDesktopSectionSize
* 1024;
2220 SIZE_T DesktopInfoSize
;
2223 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk
, DesktopName
);
2225 RtlZeroMemory(pdesk
, sizeof(DESKTOP
));
2227 /* Link the desktop with the parent window station */
2228 ObReferenceObject(pwinsta
);
2229 pdesk
->rpwinstaParent
= pwinsta
;
2230 InsertTailList(&pwinsta
->DesktopListHead
, &pdesk
->ListEntry
);
2232 /* Create the desktop heap */
2233 pdesk
->hsectionDesktop
= NULL
;
2234 pdesk
->pheapDesktop
= UserCreateHeap(&pdesk
->hsectionDesktop
,
2235 &DesktopHeapSystemBase
,
2237 if (pdesk
->pheapDesktop
== NULL
)
2239 ERR("Failed to create desktop heap!\n");
2240 return STATUS_NO_MEMORY
;
2243 /* Create DESKTOPINFO */
2244 DesktopInfoSize
= sizeof(DESKTOPINFO
) + DesktopName
->Length
+ sizeof(WCHAR
);
2245 pdesk
->pDeskInfo
= RtlAllocateHeap(pdesk
->pheapDesktop
,
2246 HEAP_NO_SERIALIZE
| HEAP_ZERO_MEMORY
,
2248 if (pdesk
->pDeskInfo
== NULL
)
2250 ERR("Failed to create the DESKTOP structure!\n");
2251 return STATUS_NO_MEMORY
;
2254 /* Initialize the DESKTOPINFO */
2255 pdesk
->pDeskInfo
->pvDesktopBase
= DesktopHeapSystemBase
;
2256 pdesk
->pDeskInfo
->pvDesktopLimit
= (PVOID
)((ULONG_PTR
)DesktopHeapSystemBase
+ HeapSize
);
2257 RtlCopyMemory(pdesk
->pDeskInfo
->szDesktopName
,
2258 DesktopName
->Buffer
,
2259 DesktopName
->Length
+ sizeof(WCHAR
));
2260 for (i
= 0; i
< NB_HOOKS
; i
++)
2262 InitializeListHead(&pdesk
->pDeskInfo
->aphkStart
[i
]);
2265 InitializeListHead(&pdesk
->ShellHookWindows
);
2266 InitializeListHead(&pdesk
->PtiList
);
2268 return STATUS_SUCCESS
;
2271 /* SYSCALLS *******************************************************************/
2274 * NtUserCreateDesktop
2276 * Creates a new desktop.
2280 * Object Attributes.
2283 * Name of the device.
2289 * Interaction flags.
2292 * Requested type of access.
2296 * If the function succeeds, the return value is a handle to the newly
2297 * created desktop. If the specified desktop already exists, the function
2298 * succeeds and returns a handle to the existing desktop. When you are
2299 * finished using the handle, call the CloseDesktop function to close it.
2300 * If the function fails, the return value is NULL.
2309 OUT HDESK
* phDesktop
,
2310 IN POBJECT_ATTRIBUTES ObjectAttributes
,
2311 IN KPROCESSOR_MODE AccessMode
,
2312 IN PUNICODE_STRING lpszDesktopDevice OPTIONAL
,
2313 IN LPDEVMODEW lpdmw OPTIONAL
,
2315 IN ACCESS_MASK dwDesiredAccess
)
2318 PDESKTOP pdesk
= NULL
;
2320 BOOLEAN Context
= FALSE
;
2321 UNICODE_STRING ClassName
;
2322 LARGE_STRING WindowName
;
2323 BOOL NoHooks
= FALSE
;
2326 PTHREADINFO ptiCurrent
;
2329 TRACE("Enter IntCreateDesktop\n");
2334 ptiCurrent
= PsGetCurrentThreadWin32Thread();
2336 ASSERT(gptiDesktopThread
);
2338 /* Turn off hooks when calling any CreateWindowEx from inside win32k */
2339 NoHooks
= (ptiCurrent
->TIF_flags
& TIF_DISABLEHOOKS
);
2340 ptiCurrent
->TIF_flags
|= TIF_DISABLEHOOKS
;
2341 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
2344 * Try to open already existing desktop
2346 Status
= ObOpenObjectByName(ObjectAttributes
,
2347 ExDesktopObjectType
,
2353 if (!NT_SUCCESS(Status
))
2355 ERR("ObOpenObjectByName failed to open/create desktop\n");
2359 /* In case the object was not created (eg if it existed), return now */
2360 if (Context
== FALSE
)
2362 TRACE("IntCreateDesktop opened desktop '%wZ'\n", ObjectAttributes
->ObjectName
);
2363 Status
= STATUS_SUCCESS
;
2367 /* Reference the desktop */
2368 Status
= ObReferenceObjectByHandle(hDesk
,
2370 ExDesktopObjectType
,
2374 if (!NT_SUCCESS(Status
))
2376 ERR("Failed to reference desktop object\n");
2380 /* Get the desktop window class. The thread desktop does not belong to any desktop
2381 * so the classes created there (including the desktop class) are allocated in the shared heap
2382 * It would cause problems if we used a class that belongs to the caller
2384 ClassName
.Buffer
= WC_DESKTOP
;
2385 ClassName
.Length
= 0;
2386 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
2390 Status
= STATUS_UNSUCCESSFUL
;
2394 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2395 RtlZeroMemory(&Cs
, sizeof(Cs
));
2396 Cs
.x
= UserGetSystemMetrics(SM_XVIRTUALSCREEN
),
2397 Cs
.y
= UserGetSystemMetrics(SM_YVIRTUALSCREEN
),
2398 Cs
.cx
= UserGetSystemMetrics(SM_CXVIRTUALSCREEN
),
2399 Cs
.cy
= UserGetSystemMetrics(SM_CYVIRTUALSCREEN
),
2400 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
2401 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2402 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
2403 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
2405 /* Use IntCreateWindow instead of co_UserCreateWindowEx because the later expects a thread with a desktop */
2406 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
, WINVER
);
2409 ERR("Failed to create desktop window for the new desktop\n");
2410 Status
= STATUS_UNSUCCESSFUL
;
2414 pdesk
->dwSessionId
= PsGetCurrentProcessSessionId();
2415 pdesk
->DesktopWindow
= pWnd
->head
.h
;
2416 pdesk
->pDeskInfo
->spwnd
= pWnd
;
2417 pWnd
->fnid
= FNID_DESKTOP
;
2419 ClassName
.Buffer
= MAKEINTATOM(gpsi
->atomSysClass
[ICLS_HWNDMESSAGE
]);
2420 ClassName
.Length
= 0;
2421 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
2425 Status
= STATUS_UNSUCCESSFUL
;
2429 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2430 RtlZeroMemory(&Cs
, sizeof(Cs
));
2431 Cs
.cx
= Cs
.cy
= 100;
2432 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
2433 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2434 Cs
.lpszName
= (LPCWSTR
)&WindowName
;
2435 Cs
.lpszClass
= (LPCWSTR
)&ClassName
;
2436 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
, WINVER
);
2439 ERR("Failed to create message window for the new desktop\n");
2440 Status
= STATUS_UNSUCCESSFUL
;
2444 pdesk
->spwndMessage
= pWnd
;
2445 pWnd
->fnid
= FNID_MESSAGEWND
;
2448 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
2449 Create Tooltip. Saved in DesktopObject->spwndTooltip.
2450 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
2451 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
2452 The rest is same as message window.
2453 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
2455 Status
= STATUS_SUCCESS
;
2460 ObDereferenceObject(pdesk
);
2462 if (!NT_SUCCESS(Status
) && hDesk
!= NULL
)
2464 ObCloseHandle(hDesk
, AccessMode
);
2469 ptiCurrent
->TIF_flags
&= ~TIF_DISABLEHOOKS
;
2470 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
2473 TRACE("Leave IntCreateDesktop, Status 0x%08lx\n", Status
);
2475 if (NT_SUCCESS(Status
))
2478 SetLastNtError(Status
);
2483 NtUserCreateDesktop(
2484 POBJECT_ATTRIBUTES ObjectAttributes
,
2485 PUNICODE_STRING lpszDesktopDevice
,
2488 ACCESS_MASK dwDesiredAccess
)
2493 DECLARE_RETURN(HDESK
);
2495 TRACE("Enter NtUserCreateDesktop\n");
2496 UserEnterExclusive();
2498 Status
= IntCreateDesktop(&hDesk
,
2505 if (!NT_SUCCESS(Status
))
2507 ERR("IntCreateDesktop failed, Status 0x%08lx\n", Status
);
2508 // SetLastNtError(Status);
2515 TRACE("Leave NtUserCreateDesktop, ret=0x%p\n", _ret_
);
2523 * Opens an existing desktop.
2527 * Name of the existing desktop.
2530 * Interaction flags.
2533 * Requested type of access.
2536 * Handle to the desktop or zero on failure.
2544 POBJECT_ATTRIBUTES ObjectAttributes
,
2546 ACCESS_MASK dwDesiredAccess
)
2551 Status
= ObOpenObjectByName(
2553 ExDesktopObjectType
,
2560 if (!NT_SUCCESS(Status
))
2562 ERR("Failed to open desktop\n");
2563 SetLastNtError(Status
);
2567 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes
->ObjectName
->Buffer
, Desktop
);
2572 HDESK
UserOpenInputDesktop(DWORD dwFlags
,
2574 ACCESS_MASK dwDesiredAccess
)
2576 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
2578 ULONG HandleAttributes
= 0;
2581 if (!gpdeskInputDesktop
)
2586 if (pti
->ppi
->prpwinsta
!= InputWindowStation
)
2588 ERR("Tried to open input desktop from non interactive winsta!\n");
2589 EngSetLastError(ERROR_INVALID_FUNCTION
);
2593 if (fInherit
) HandleAttributes
= OBJ_INHERIT
;
2595 /* Create a new handle to the object */
2596 Status
= ObOpenObjectByPointer(
2601 ExDesktopObjectType
,
2605 if (!NT_SUCCESS(Status
))
2607 ERR("Failed to open input desktop object\n");
2608 SetLastNtError(Status
);
2615 * NtUserOpenInputDesktop
2617 * Opens the input (interactive) desktop.
2621 * Interaction flags.
2624 * Inheritance option.
2627 * Requested type of access.
2630 * Handle to the input desktop or zero on failure.
2637 NtUserOpenInputDesktop(
2640 ACCESS_MASK dwDesiredAccess
)
2644 UserEnterExclusive();
2645 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2647 hdesk
= UserOpenInputDesktop(dwFlags
, fInherit
, dwDesiredAccess
);
2649 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk
);
2655 * NtUserCloseDesktop
2657 * Closes a desktop handle.
2661 * Handle to the desktop.
2667 * The desktop handle can be created with NtUserCreateDesktop or
2668 * NtUserOpenDesktop. This function will fail if any thread in the calling
2669 * process is using the specified desktop handle or if the handle refers
2670 * to the initial desktop of the calling process.
2677 NtUserCloseDesktop(HDESK hDesktop
)
2681 DECLARE_RETURN(BOOL
);
2683 TRACE("NtUserCloseDesktop(0x%p) called\n", hDesktop
);
2684 UserEnterExclusive();
2686 if (hDesktop
== gptiCurrent
->hdesk
|| hDesktop
== gptiCurrent
->ppi
->hdeskStartup
)
2688 ERR("Attempted to close thread desktop\n");
2689 EngSetLastError(ERROR_BUSY
);
2693 Status
= IntValidateDesktopHandle(hDesktop
, UserMode
, 0, &pdesk
);
2694 if (!NT_SUCCESS(Status
))
2696 ERR("Validation of desktop handle 0x%p failed\n", hDesktop
);
2700 ObDereferenceObject(pdesk
);
2702 Status
= ObCloseHandle(hDesktop
, UserMode
);
2703 if (!NT_SUCCESS(Status
))
2705 ERR("Failed to close desktop handle 0x%p\n", hDesktop
);
2706 SetLastNtError(Status
);
2713 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_
);
2719 * NtUserPaintDesktop
2721 * The NtUserPaintDesktop function fills the clipping region in the
2722 * specified device context with the desktop pattern or wallpaper. The
2723 * function is provided primarily for shell desktops.
2727 * Handle to the device context.
2734 NtUserPaintDesktop(HDC hDC
)
2737 UserEnterExclusive();
2738 TRACE("Enter NtUserPaintDesktop\n");
2739 Ret
= IntPaintDesktop(hDC
);
2740 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret
);
2746 * NtUserResolveDesktop
2748 * The NtUserResolveDesktop function attempts to retrieve valid handles to
2749 * a desktop and a window station suitable for the specified process.
2750 * The specified desktop path string is used only as a hint for the resolution.
2752 * See the description of IntResolveDesktop for more details.
2756 * Handle to a user process.
2759 * The desktop path string used as a hint for desktop resolution.
2762 * Whether or not the returned handles are inheritable.
2765 * Pointer to a window station handle.
2768 * Handle to the desktop (direct return value) and
2769 * handle to the associated window station (by pointer).
2770 * NULL in case of failure.
2773 * Callable by CSRSS only.
2781 NtUserResolveDesktop(
2782 IN HANDLE ProcessHandle
,
2783 IN PUNICODE_STRING DesktopPath
,
2785 OUT HWINSTA
* phWinSta
)
2789 HWINSTA hWinSta
= NULL
;
2790 HDESK hDesktop
= NULL
;
2791 UNICODE_STRING CapturedDesktopPath
;
2793 /* Allow only the Console Server to perform this operation (via CSRSS) */
2794 if (PsGetCurrentProcess() != gpepCSRSS
)
2797 /* Get the process object the user handle was referencing */
2798 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2799 PROCESS_QUERY_INFORMATION
,
2804 if (!NT_SUCCESS(Status
))
2807 // UserEnterShared();
2811 /* Probe the handle pointer */
2812 // ProbeForWriteHandle
2813 ProbeForWrite(phWinSta
, sizeof(HWINSTA
), sizeof(HWINSTA
));
2815 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2817 Status
= _SEH2_GetExceptionCode();
2818 _SEH2_YIELD(goto Quit
);
2822 /* Capture the user desktop path string */
2823 Status
= ProbeAndCaptureUnicodeString(&CapturedDesktopPath
,
2826 if (!NT_SUCCESS(Status
))
2829 /* Call the internal function */
2830 Status
= IntResolveDesktop(Process
,
2831 &CapturedDesktopPath
,
2835 if (!NT_SUCCESS(Status
))
2837 ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status
);
2844 /* Return the window station handle */
2845 *phWinSta
= hWinSta
;
2847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2849 Status
= _SEH2_GetExceptionCode();
2851 /* We failed, close the opened desktop and window station */
2852 if (hDesktop
) ObCloseHandle(hDesktop
, UserMode
);
2854 if (hWinSta
) ObCloseHandle(hWinSta
, UserMode
);
2858 /* Free the captured string */
2859 ReleaseCapturedUnicodeString(&CapturedDesktopPath
, UserMode
);
2864 /* Dereference the process object */
2865 ObDereferenceObject(Process
);
2867 /* Return the desktop handle */
2872 * NtUserSwitchDesktop
2874 * Sets the current input (interactive) desktop.
2878 * Handle to desktop.
2888 NtUserSwitchDesktop(HDESK hdesk
)
2892 BOOL bRedrawDesktop
;
2893 DECLARE_RETURN(BOOL
);
2895 UserEnterExclusive();
2896 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk
);
2898 Status
= IntValidateDesktopHandle(hdesk
, UserMode
, 0, &pdesk
);
2899 if (!NT_SUCCESS(Status
))
2901 ERR("Validation of desktop handle 0x%p failed\n", hdesk
);
2905 if (PsGetCurrentProcessSessionId() != pdesk
->rpwinstaParent
->dwSessionId
)
2907 ObDereferenceObject(pdesk
);
2908 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2912 if (pdesk
== gpdeskInputDesktop
)
2914 ObDereferenceObject(pdesk
);
2915 WARN("NtUserSwitchDesktop called for active desktop\n");
2920 * Don't allow applications switch the desktop if it's locked, unless the caller
2921 * is the logon application itself
2923 if ((pdesk
->rpwinstaParent
->Flags
& WSS_LOCKED
) &&
2924 gpidLogon
!= PsGetCurrentProcessId())
2926 ObDereferenceObject(pdesk
);
2927 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk
);
2931 if (pdesk
->rpwinstaParent
!= InputWindowStation
)
2933 ObDereferenceObject(pdesk
);
2934 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk
);
2938 /* FIXME: Fail if the process is associated with a secured
2939 desktop such as Winlogon or Screen-Saver */
2940 /* FIXME: Connect to input device */
2942 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop
, pdesk
);
2944 bRedrawDesktop
= FALSE
;
2946 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2947 if (gpdeskInputDesktop
!= NULL
)
2949 if ((gpdeskInputDesktop
->pDeskInfo
->spwnd
->style
& WS_VISIBLE
) == WS_VISIBLE
)
2950 bRedrawDesktop
= TRUE
;
2952 /* Hide the previous desktop window */
2953 IntHideDesktop(gpdeskInputDesktop
);
2956 /* Set the active desktop in the desktop's window station. */
2957 InputWindowStation
->ActiveDesktop
= pdesk
;
2959 /* Set the global state. */
2960 gpdeskInputDesktop
= pdesk
;
2962 /* Show the new desktop window */
2963 co_IntShowDesktop(pdesk
, UserGetSystemMetrics(SM_CXSCREEN
), UserGetSystemMetrics(SM_CYSCREEN
), bRedrawDesktop
);
2965 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2966 ObDereferenceObject(pdesk
);
2971 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_
);
2977 * NtUserGetThreadDesktop
2984 NtUserGetThreadDesktop(DWORD dwThreadId
, HDESK hConsoleDesktop
)
2990 PDESKTOP DesktopObject
;
2991 OBJECT_HANDLE_INFORMATION HandleInformation
;
2993 UserEnterExclusive();
2994 TRACE("Enter NtUserGetThreadDesktop\n");
2998 EngSetLastError(ERROR_INVALID_PARAMETER
);
3003 /* Validate the Win32 thread and retrieve its information */
3004 pti
= IntTID2PTI(UlongToHandle(dwThreadId
));
3007 /* Get the desktop handle of the thread */
3009 Process
= pti
->ppi
->peProcess
;
3011 else if (hConsoleDesktop
)
3014 * The thread may belong to a console, so attempt to use the provided
3015 * console desktop handle as a fallback. Otherwise this means that the
3016 * thread is either not Win32 or invalid.
3018 hDesk
= hConsoleDesktop
;
3019 Process
= gpepCSRSS
;
3023 EngSetLastError(ERROR_INVALID_PARAMETER
);
3030 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId
);
3034 if (Process
== PsGetCurrentProcess())
3037 * Just return the handle, since we queried the desktop handle
3038 * of a thread running in the same context.
3044 * We could just use the cached rpdesk instead of looking up the handle,
3045 * but it may actually be safer to validate the desktop and get a temporary
3046 * reference to it so that it does not disappear under us (e.g. when the
3047 * desktop is being destroyed) during the operation.
3050 * Switch into the context of the thread we are trying to get
3051 * the desktop from, so we can use the handle.
3053 KeAttachProcess(&Process
->Pcb
);
3054 Status
= ObReferenceObjectByHandle(hDesk
,
3056 ExDesktopObjectType
,
3058 (PVOID
*)&DesktopObject
,
3059 &HandleInformation
);
3062 if (NT_SUCCESS(Status
))
3065 * Lookup our handle table if we can find a handle to the desktop object.
3066 * If not, create one.
3067 * QUESTION: Do we really need to create a handle in case it doesn't exist??
3069 hDesk
= IntGetDesktopObjectHandle(DesktopObject
);
3071 /* All done, we got a valid handle to the desktop */
3072 ObDereferenceObject(DesktopObject
);
3076 /* The handle could not be found, there is nothing to get... */
3082 ERR("Could not retrieve or access desktop for thread 0x%x\n", dwThreadId
);
3083 EngSetLastError(ERROR_ACCESS_DENIED
);
3087 TRACE("Leave NtUserGetThreadDesktop, hDesk = 0x%p\n", hDesk
);
3093 IntUnmapDesktopView(IN PDESKTOP pdesk
)
3096 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
3097 NTSTATUS Status
= STATUS_SUCCESS
;
3099 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk
);
3101 ppi
= PsGetCurrentProcessWin32Process();
3104 * Unmap if we're the last thread using the desktop.
3105 * Start the search at the next mapping: skip the first entry
3106 * as it must be the global user heap mapping.
3108 PrevLink
= &ppi
->HeapMappings
.Next
;
3109 HeapMapping
= *PrevLink
;
3110 while (HeapMapping
!= NULL
)
3112 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
3114 if (--HeapMapping
->Count
== 0)
3116 *PrevLink
= HeapMapping
->Next
;
3118 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi
, pdesk
);
3119 Status
= MmUnmapViewOfSection(PsGetCurrentProcess(),
3120 HeapMapping
->UserMapping
);
3122 ObDereferenceObject(pdesk
);
3124 UserHeapFree(HeapMapping
);
3129 PrevLink
= &HeapMapping
->Next
;
3130 HeapMapping
= HeapMapping
->Next
;
3137 IntMapDesktopView(IN PDESKTOP pdesk
)
3140 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
3141 PVOID UserBase
= NULL
;
3142 SIZE_T ViewSize
= 0;
3143 LARGE_INTEGER Offset
;
3146 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk
);
3148 ppi
= PsGetCurrentProcessWin32Process();
3151 * Find out if another thread already mapped the desktop heap.
3152 * Start the search at the next mapping: skip the first entry
3153 * as it must be the global user heap mapping.
3155 PrevLink
= &ppi
->HeapMappings
.Next
;
3156 HeapMapping
= *PrevLink
;
3157 while (HeapMapping
!= NULL
)
3159 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
3161 HeapMapping
->Count
++;
3162 return STATUS_SUCCESS
;
3165 PrevLink
= &HeapMapping
->Next
;
3166 HeapMapping
= HeapMapping
->Next
;
3169 /* We're the first, map the heap */
3170 Offset
.QuadPart
= 0;
3171 Status
= MmMapViewOfSection(pdesk
->hsectionDesktop
,
3172 PsGetCurrentProcess(),
3180 PAGE_EXECUTE_READ
); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
3181 if (!NT_SUCCESS(Status
))
3183 ERR("Failed to map desktop\n");
3187 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi
, pdesk
);
3189 /* Add the mapping */
3190 HeapMapping
= UserHeapAlloc(sizeof(*HeapMapping
));
3191 if (HeapMapping
== NULL
)
3193 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase
);
3194 ERR("UserHeapAlloc() failed!\n");
3195 return STATUS_NO_MEMORY
;
3198 HeapMapping
->Next
= NULL
;
3199 HeapMapping
->KernelMapping
= (PVOID
)pdesk
->pheapDesktop
;
3200 HeapMapping
->UserMapping
= UserBase
;
3201 HeapMapping
->Limit
= ViewSize
;
3202 HeapMapping
->Count
= 1;
3203 *PrevLink
= HeapMapping
;
3205 ObReferenceObject(pdesk
);
3207 return STATUS_SUCCESS
;
3211 IntSetThreadDesktop(IN HDESK hDesktop
,
3212 IN BOOL FreeOnFailure
)
3214 PDESKTOP pdesk
= NULL
, pdeskOld
;
3217 PCLIENTTHREADINFO pctiOld
, pctiNew
= NULL
;
3220 ASSERT(NtCurrentTeb());
3222 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop
, FreeOnFailure
);
3224 pti
= PsGetCurrentThreadWin32Thread();
3225 pci
= pti
->pClientInfo
;
3227 /* If the caller gave us a desktop, ensure it is valid */
3228 if (hDesktop
!= NULL
)
3230 /* Validate the new desktop. */
3231 Status
= IntValidateDesktopHandle(hDesktop
, UserMode
, 0, &pdesk
);
3232 if (!NT_SUCCESS(Status
))
3234 ERR("Validation of desktop handle 0x%p failed\n", hDesktop
);
3238 if (pti
->rpdesk
== pdesk
)
3241 ObDereferenceObject(pdesk
);
3246 /* Make sure that we don't own any window in the current desktop */
3247 if (!IsListEmpty(&pti
->WindowListHead
))
3250 ObDereferenceObject(pdesk
);
3251 ERR("Attempted to change thread desktop although the thread has windows!\n");
3252 EngSetLastError(ERROR_BUSY
);
3256 /* Desktop is being re-set so clear out foreground. */
3257 if (pti
->rpdesk
!= pdesk
&& pti
->MessageQueue
== gpqForeground
)
3259 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
3260 IntSetFocusMessageQueue(NULL
);
3263 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
3266 Status
= IntMapDesktopView(pdesk
);
3267 if (!NT_SUCCESS(Status
))
3269 ERR("Failed to map desktop heap!\n");
3270 ObDereferenceObject(pdesk
);
3271 SetLastNtError(Status
);
3275 pctiNew
= DesktopHeapAlloc(pdesk
, sizeof(CLIENTTHREADINFO
));
3276 if (pctiNew
== NULL
)
3278 ERR("Failed to allocate new pcti\n");
3279 IntUnmapDesktopView(pdesk
);
3280 ObDereferenceObject(pdesk
);
3281 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3287 * Processes, in particular Winlogon.exe, that manage window stations
3288 * (especially the interactive WinSta0 window station) and desktops,
3289 * may not be able to connect at startup to a window station and have
3290 * an associated desktop as well, if none exists on the system already.
3291 * Because creating a new window station does not affect the window station
3292 * associated to the process, and because neither by associating a window
3293 * station to the process nor creating a new desktop on it does associate
3294 * a startup desktop to that process, the process has to actually assigns
3295 * one of its threads to a desktop so that it gets automatically an assigned
3298 * This is what actually happens for Winlogon.exe, which is started without
3299 * any window station and desktop. By creating the first (and therefore
3300 * interactive) WinSta0 window station, then assigning WinSta0 to itself
3301 * and creating the Default desktop on it, and then assigning this desktop
3302 * to its main thread, Winlogon.exe basically does the similar steps that
3303 * would have been done automatically at its startup if there were already
3304 * an existing WinSta0 window station and Default desktop.
3306 * Of course all this must not be done if we are a SYSTEM or CSRSS thread.
3308 // if (pti->ppi->peProcess != gpepCSRSS)
3309 if (!(pti
->TIF_flags
& (TIF_SYSTEMTHREAD
| TIF_CSRSSTHREAD
)) &&
3310 pti
->ppi
->rpdeskStartup
== NULL
&& hDesktop
!= NULL
)
3312 ERR("The process 0x%p '%s' didn't have an assigned startup desktop before, assigning it now!\n",
3313 pti
->ppi
->peProcess
, pti
->ppi
->peProcess
->ImageFileName
);
3315 pti
->ppi
->hdeskStartup
= hDesktop
;
3316 pti
->ppi
->rpdeskStartup
= pdesk
;
3319 /* free all classes or move them to the shared heap */
3320 if (pti
->rpdesk
!= NULL
)
3322 if (!IntCheckProcessDesktopClasses(pti
->rpdesk
, FreeOnFailure
))
3324 ERR("Failed to move process classes to shared heap!\n");
3327 DesktopHeapFree(pdesk
, pctiNew
);
3328 IntUnmapDesktopView(pdesk
);
3329 ObDereferenceObject(pdesk
);
3335 pdeskOld
= pti
->rpdesk
;
3336 if (pti
->pcti
!= &pti
->cti
)
3337 pctiOld
= pti
->pcti
;
3344 pti
->rpdesk
= pdesk
;
3345 pti
->hdesk
= hDesktop
;
3346 pti
->pDeskInfo
= pti
->rpdesk
->pDeskInfo
;
3347 pti
->pcti
= pctiNew
;
3349 pci
->ulClientDelta
= DesktopHeapGetUserDelta();
3350 pci
->pDeskInfo
= (PVOID
)((ULONG_PTR
)pti
->pDeskInfo
- pci
->ulClientDelta
);
3351 pci
->pClientThreadInfo
= (PVOID
)((ULONG_PTR
)pti
->pcti
- pci
->ulClientDelta
);
3353 /* initialize the new pcti */
3354 if (pctiOld
!= NULL
)
3356 RtlCopyMemory(pctiNew
, pctiOld
, sizeof(CLIENTTHREADINFO
));
3360 RtlZeroMemory(pctiNew
, sizeof(CLIENTTHREADINFO
));
3361 pci
->fsHooks
= pti
->fsHooks
;
3362 pci
->dwTIFlags
= pti
->TIF_flags
;
3369 pti
->pDeskInfo
= NULL
;
3370 pti
->pcti
= &pti
->cti
; // Always point inside so there will be no crash when posting or sending msg's!
3371 pci
->ulClientDelta
= 0;
3372 pci
->pDeskInfo
= NULL
;
3373 pci
->pClientThreadInfo
= NULL
;
3376 /* clean up the old desktop */
3377 if (pdeskOld
!= NULL
)
3379 RemoveEntryList(&pti
->PtiLink
);
3380 if (pctiOld
) DesktopHeapFree(pdeskOld
, pctiOld
);
3381 IntUnmapDesktopView(pdeskOld
);
3382 ObDereferenceObject(pdeskOld
);
3387 InsertTailList(&pdesk
->PtiList
, &pti
->PtiLink
);
3390 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti
, pti
->ppi
, pdeskOld
, pdesk
);
3396 * NtUserSetThreadDesktop
3403 NtUserSetThreadDesktop(HDESK hDesktop
)
3407 UserEnterExclusive();
3409 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
3410 // here too and set the NT error level. Q. Is it necessary to have the validation
3411 // in IntSetThreadDesktop? Is it needed there too?
3412 if (hDesktop
|| (!hDesktop
&& PsGetCurrentProcess() == gpepCSRSS
))
3413 ret
= IntSetThreadDesktop(hDesktop
, FALSE
);