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
;
40 /* OBJECT CALLBACKS **********************************************************/
44 IntDesktopObjectParse(IN PVOID ParseObject
,
46 IN OUT PACCESS_STATE AccessState
,
47 IN KPROCESSOR_MODE AccessMode
,
49 IN OUT PUNICODE_STRING CompleteName
,
50 IN OUT PUNICODE_STRING RemainingName
,
51 IN OUT PVOID Context OPTIONAL
,
52 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL
,
57 OBJECT_ATTRIBUTES ObjectAttributes
;
58 PLIST_ENTRY NextEntry
, ListHead
;
59 PWINSTATION_OBJECT WinStaObject
= (PWINSTATION_OBJECT
)ParseObject
;
60 UNICODE_STRING DesktopName
;
61 PBOOLEAN pContext
= (PBOOLEAN
) Context
;
66 /* Set the list pointers and loop the window station */
67 ListHead
= &WinStaObject
->DesktopListHead
;
68 NextEntry
= ListHead
->Flink
;
69 while (NextEntry
!= ListHead
)
71 /* Get the current desktop */
72 Desktop
= CONTAINING_RECORD(NextEntry
, DESKTOP
, ListEntry
);
74 /* Get the desktop name */
75 ASSERT(Desktop
->pDeskInfo
!= NULL
);
76 RtlInitUnicodeString(&DesktopName
, Desktop
->pDeskInfo
->szDesktopName
);
78 /* Compare the name */
79 if (RtlEqualUnicodeString(RemainingName
,
81 (Attributes
& OBJ_CASE_INSENSITIVE
) != 0))
83 /* We found a match. Did this come from a create? */
86 /* Unless OPEN_IF was given, fail with an error */
87 if (!(Attributes
& OBJ_OPENIF
))
90 return STATUS_OBJECT_NAME_COLLISION
;
94 /* Otherwise, return with a warning only */
95 Status
= STATUS_OBJECT_NAME_EXISTS
;
100 /* This was a real open, so this is OK */
101 Status
= STATUS_SUCCESS
;
104 /* Reference the desktop and return it */
105 ObReferenceObject(Desktop
);
110 /* Go to the next desktop */
111 NextEntry
= NextEntry
->Flink
;
114 /* If we got here but this isn't a create, just fail */
115 if (!Context
) return STATUS_OBJECT_NAME_NOT_FOUND
;
117 /* Create the desktop object */
118 InitializeObjectAttributes(&ObjectAttributes
, RemainingName
, 0, NULL
, NULL
);
119 Status
= ObCreateObject(KernelMode
,
128 if (!NT_SUCCESS(Status
)) return Status
;
130 /* Initialize the desktop */
131 Status
= UserInitializeDesktop(Desktop
, RemainingName
, WinStaObject
);
132 if (!NT_SUCCESS(Status
))
134 ObDereferenceObject(Desktop
);
138 /* Set the desktop object and return success */
141 return STATUS_SUCCESS
;
146 IntDesktopObjectDelete(
147 _In_ PVOID Parameters
)
149 PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters
= Parameters
;
150 PDESKTOP pdesk
= (PDESKTOP
)DeleteParameters
->Object
;
152 TRACE("Deleting desktop object 0x%p\n", pdesk
);
154 if (pdesk
->pDeskInfo
&&
155 pdesk
->pDeskInfo
->spwnd
)
157 ASSERT(pdesk
->pDeskInfo
->spwnd
->spwndChild
== NULL
);
158 co_UserDestroyWindow(pdesk
->pDeskInfo
->spwnd
);
161 if (pdesk
->spwndMessage
)
162 co_UserDestroyWindow(pdesk
->spwndMessage
);
164 /* Remove the desktop from the window station's list of associcated desktops */
165 RemoveEntryList(&pdesk
->ListEntry
);
168 IntFreeDesktopHeap(pdesk
);
170 ObDereferenceObject(pdesk
->rpwinstaParent
);
172 return STATUS_SUCCESS
;
178 _In_ PVOID Parameters
)
180 PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters
= Parameters
;
181 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
185 /* This happens when we leak desktop handles */
186 return STATUS_SUCCESS
;
189 /* Do not allow the current desktop or the initial desktop to be closed */
190 if (OkToCloseParameters
->Handle
== pti
->ppi
->hdeskStartup
||
191 OkToCloseParameters
->Handle
== pti
->hdesk
)
193 return STATUS_ACCESS_DENIED
;
196 return STATUS_SUCCESS
;
201 IntDesktopObjectOpen(
202 _In_ PVOID Parameters
)
204 PWIN32_OPENMETHOD_PARAMETERS OpenParameters
= Parameters
;
205 PPROCESSINFO ppi
= PsGetProcessWin32Process(OpenParameters
->Process
);
207 return STATUS_SUCCESS
;
209 return IntMapDesktopView((PDESKTOP
)OpenParameters
->Object
);
214 IntDesktopObjectClose(
215 _In_ PVOID Parameters
)
217 PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters
= Parameters
;
218 PPROCESSINFO ppi
= PsGetProcessWin32Process(CloseParameters
->Process
);
221 /* This happens when the process leaks desktop handles.
222 * At this point the PPROCESSINFO is already destroyed */
223 return STATUS_SUCCESS
;
226 return IntUnmapDesktopView((PDESKTOP
)CloseParameters
->Object
);
230 /* PRIVATE FUNCTIONS **********************************************************/
235 InitDesktopImpl(VOID
)
237 GENERIC_MAPPING IntDesktopMapping
= { DESKTOP_READ
,
242 /* Set Desktop Object Attributes */
243 ExDesktopObjectType
->TypeInfo
.DefaultNonPagedPoolCharge
= sizeof(DESKTOP
);
244 ExDesktopObjectType
->TypeInfo
.GenericMapping
= IntDesktopMapping
;
245 ExDesktopObjectType
->TypeInfo
.ValidAccessMask
= DESKTOP_ALL_ACCESS
;
246 return STATUS_SUCCESS
;
250 GetSystemVersionString(OUT PWSTR pwszzVersion
,
252 IN BOOLEAN InSafeMode
,
253 IN BOOLEAN AppendNtSystemRoot
)
257 RTL_OSVERSIONINFOEXW VerInfo
;
258 UNICODE_STRING BuildLabString
;
259 UNICODE_STRING CSDVersionString
;
260 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable
[] =
264 RTL_QUERY_REGISTRY_DIRECT
,
271 RTL_QUERY_REGISTRY_DIRECT
,
280 WCHAR BuildLabBuffer
[256];
281 WCHAR VersionBuffer
[256];
284 VerInfo
.dwOSVersionInfoSize
= sizeof(VerInfo
);
287 * This call is uniquely used to retrieve the current CSD numbers.
288 * All the rest (major, minor, ...) is either retrieved from the
289 * SharedUserData structure, or from the registry.
291 RtlGetVersion((PRTL_OSVERSIONINFOW
)&VerInfo
);
294 * - Retrieve the BuildLab string from the registry (set by the kernel).
295 * - In kernel-mode, szCSDVersion is not initialized. Initialize it
296 * and query its value from the registry.
298 RtlZeroMemory(BuildLabBuffer
, sizeof(BuildLabBuffer
));
299 RtlInitEmptyUnicodeString(&BuildLabString
,
301 sizeof(BuildLabBuffer
));
302 RtlZeroMemory(VerInfo
.szCSDVersion
, sizeof(VerInfo
.szCSDVersion
));
303 RtlInitEmptyUnicodeString(&CSDVersionString
,
304 VerInfo
.szCSDVersion
,
305 sizeof(VerInfo
.szCSDVersion
));
306 Status
= RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT
,
308 VersionConfigurationTable
,
311 if (!NT_SUCCESS(Status
))
313 /* Indicate nothing is there */
314 BuildLabString
.Length
= 0;
315 CSDVersionString
.Length
= 0;
317 /* NULL-terminate the strings */
318 BuildLabString
.Buffer
[BuildLabString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
319 CSDVersionString
.Buffer
[CSDVersionString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
321 EndBuffer
= VersionBuffer
;
322 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString
.Length
)
324 /* Print the version string */
325 Status
= RtlStringCbPrintfExW(VersionBuffer
,
326 sizeof(VersionBuffer
),
332 if (!NT_SUCCESS(Status
))
334 /* No version, NULL-terminate the string */
335 *EndBuffer
= UNICODE_NULL
;
340 /* No version, NULL-terminate the string */
341 *EndBuffer
= UNICODE_NULL
;
346 /* String for Safe Mode */
347 Status
= RtlStringCchPrintfW(pwszzVersion
,
349 L
"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
352 SharedUserData
->NtMajorVersion
,
353 SharedUserData
->NtMinorVersion
,
354 (VerInfo
.dwBuildNumber
& 0xFFFF),
357 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
359 Status
= RtlStringCbPrintfW(VersionBuffer
,
360 sizeof(VersionBuffer
),
362 SharedUserData
->NtSystemRoot
);
363 if (NT_SUCCESS(Status
))
365 /* Replace the last newline by a NULL, before concatenating */
366 EndBuffer
= wcsrchr(pwszzVersion
, L
'\n');
367 if (EndBuffer
) *EndBuffer
= UNICODE_NULL
;
369 /* The concatenated string has a terminating newline */
370 Status
= RtlStringCchCatW(pwszzVersion
,
373 if (!NT_SUCCESS(Status
))
375 /* Concatenation failed, put back the newline */
376 if (EndBuffer
) *EndBuffer
= L
'\n';
380 /* Override any failures as the NtSystemRoot string is optional */
381 Status
= STATUS_SUCCESS
;
386 /* Multi-string for Normal Mode */
387 Status
= RtlStringCchPrintfW(pwszzVersion
,
389 L
"ReactOS Version %S\n"
391 L
"Reporting NT %u.%u (Build %u%s)\n",
394 SharedUserData
->NtMajorVersion
,
395 SharedUserData
->NtMinorVersion
,
396 (VerInfo
.dwBuildNumber
& 0xFFFF),
399 if (AppendNtSystemRoot
&& NT_SUCCESS(Status
))
401 Status
= RtlStringCbPrintfW(VersionBuffer
,
402 sizeof(VersionBuffer
),
404 SharedUserData
->NtSystemRoot
);
405 if (NT_SUCCESS(Status
))
407 Status
= RtlStringCchCatW(pwszzVersion
,
412 /* Override any failures as the NtSystemRoot string is optional */
413 Status
= STATUS_SUCCESS
;
417 if (!NT_SUCCESS(Status
))
419 /* Fall-back string */
420 Status
= RtlStringCchPrintfW(pwszzVersion
,
422 L
"ReactOS Version %S %wZ\n",
425 if (!NT_SUCCESS(Status
))
427 /* General failure, NULL-terminate the string */
428 pwszzVersion
[0] = UNICODE_NULL
;
433 * Convert the string separators (newlines) into NULLs
434 * and NULL-terminate the multi-string.
436 while (*pwszzVersion
)
438 EndBuffer
= wcschr(pwszzVersion
, L
'\n');
439 if (!EndBuffer
) break;
440 pwszzVersion
= EndBuffer
;
442 *pwszzVersion
++ = UNICODE_NULL
;
444 *pwszzVersion
= UNICODE_NULL
;
453 * The IntResolveDesktop function attempts to retrieve valid handles to
454 * a desktop and a window station suitable for the specified process.
455 * The specified desktop path string is used only as a hint for the resolution.
457 * - If the process is already assigned to a window station and a desktop,
458 * handles to these objects are returned directly regardless of the specified
459 * desktop path string. This is what happens when this function is called for
460 * a process that has been already started and connected to the Win32 USER.
462 * - If the process is being connected to the Win32 USER, or is in a state
463 * where a window station is assigned to it but no desktop yet, the desktop
464 * path string is used as a hint for the resolution.
465 * A specified window station (if any, otherwise "WinSta0" is used as default)
466 * is tested for existence and accessibility. If the checks are OK a handle
467 * to it is returned. Otherwise we either fail (the window station does not
468 * exist) or, in case a default window station was used, we attempt to open
469 * or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
470 * typically what happens when a non-interactive process is started while
471 * the WinSta0 window station was used as the default one.
472 * A specified desktop (if any, otherwise "Default" is used as default)
473 * is then tested for existence on the opened window station.
475 * - Rules for the choice of the default window station, when none is specified
476 * in the desktop path:
478 * 1. By default, a SYSTEM process connects to a non-interactive window
479 * station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
480 * or one that has been inherited and that is non-interactive.
481 * Only when the interactive window station WinSta0 is specified that
482 * the process can connect to it (e.g. the case of interactive services).
484 * 2. An interactive process, i.e. a process whose LUID is the same as the
485 * one assigned to WinSta0 by Winlogon on user logon, connects by default
486 * to the WinSta0 window station, unless it has inherited from another
487 * interactive window station (which must be... none other than WinSta0).
489 * 3. A non-interactive (but not SYSTEM) process connects by default to
490 * a non-interactive Service-0xXXXX-YYYY$ window station (whose name
491 * is derived from the process' LUID), or to another non-interactive
492 * window station that has been inherited.
493 * Otherwise it may be able connect to the interactive WinSta0 only if
494 * it has explicit access rights to it.
498 * The user process object.
501 * The desktop path string used as a hint for desktop resolution.
504 * Whether or not the returned handles are inheritable.
507 * Pointer to a window station handle.
510 * Pointer to a desktop handle.
519 IN PEPROCESS Process
,
520 IN PUNICODE_STRING DesktopPath
,
522 OUT HWINSTA
* phWinSta
,
523 OUT HDESK
* phDesktop
)
526 HWINSTA hWinSta
= NULL
, hWinStaDup
= NULL
;
527 HDESK hDesktop
= NULL
, hDesktopDup
= NULL
;
529 HANDLE hProcess
= NULL
;
533 POBJECT_ATTRIBUTES ObjectAttributes
= NULL
;
534 PUNICODE_STRING ObjectName
;
535 UNICODE_STRING WinStaName
, DesktopName
;
536 const UNICODE_STRING WinSta0Name
= RTL_CONSTANT_STRING(L
"WinSta0");
537 PWINSTATION_OBJECT WinStaObject
;
538 HWINSTA hTempWinSta
= NULL
;
539 BOOLEAN bUseDefaultWinSta
= FALSE
;
540 BOOLEAN bInteractive
= FALSE
;
541 BOOLEAN bAccessAllowed
= FALSE
;
550 ppi
= PsGetProcessWin32Process(Process
);
551 /* ppi is typically NULL for console applications that connect to Win32 USER */
552 if (!ppi
) TRACE("IntResolveDesktop: ppi is NULL!\n");
554 if (ppi
&& ppi
->hwinsta
!= NULL
&& ppi
->hdeskStartup
!= NULL
)
557 * If this process is the current one, just return the cached handles.
558 * Otherwise, open the window station and desktop objects.
560 if (Process
== PsGetCurrentProcess())
562 hWinSta
= ppi
->hwinsta
;
563 hDesktop
= ppi
->hdeskStartup
;
567 Status
= ObOpenObjectByPointer(ppi
->prpwinsta
,
571 ExWindowStationObjectType
,
574 if (!NT_SUCCESS(Status
))
576 ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi
->prpwinsta
);
577 SetLastNtError(Status
);
581 Status
= ObOpenObjectByPointer(ppi
->rpdeskStartup
,
588 if (!NT_SUCCESS(Status
))
590 ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi
->rpdeskStartup
);
591 ObCloseHandle(hWinSta
, UserMode
);
592 SetLastNtError(Status
);
598 *phDesktop
= hDesktop
;
599 return STATUS_SUCCESS
;
602 /* We will by default use the default window station and desktop */
603 RtlInitEmptyUnicodeString(&WinStaName
, NULL
, 0);
604 RtlInitEmptyUnicodeString(&DesktopName
, NULL
, 0);
607 * Parse the desktop path string which can be of the form "WinSta\Desktop"
608 * or just "Desktop". In the latter case we use the default window station
609 * on which the process is attached to (or if none, "WinSta0").
611 if (DesktopPath
->Buffer
!= NULL
&& DesktopPath
->Length
> sizeof(WCHAR
))
613 DesktopName
= *DesktopPath
;
615 /* Find the separator */
616 while (DesktopName
.Length
> 0 && *DesktopName
.Buffer
&&
617 *DesktopName
.Buffer
!= OBJ_NAME_PATH_SEPARATOR
)
619 DesktopName
.Buffer
++;
620 DesktopName
.Length
-= sizeof(WCHAR
);
621 DesktopName
.MaximumLength
-= sizeof(WCHAR
);
623 if (DesktopName
.Length
> 0)
625 RtlInitEmptyUnicodeString(&WinStaName
, DesktopPath
->Buffer
,
626 DesktopPath
->Length
- DesktopName
.Length
);
627 // (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
628 WinStaName
.Length
= WinStaName
.MaximumLength
;
630 /* Skip the separator */
631 DesktopName
.Buffer
++;
632 DesktopName
.Length
-= sizeof(WCHAR
);
633 DesktopName
.MaximumLength
-= sizeof(WCHAR
);
637 RtlInitEmptyUnicodeString(&WinStaName
, NULL
, 0);
638 DesktopName
= *DesktopPath
;
642 TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName
, &DesktopName
);
644 /* Retrieve the process LUID */
645 Status
= GetProcessLuid(NULL
, Process
, &ProcessLuid
);
646 if (!NT_SUCCESS(Status
))
648 ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status
);
649 SetLastNtError(Status
);
654 * If this process is not the current one, obtain a temporary handle
655 * to it so that we can perform handles duplication later.
657 if (Process
!= PsGetCurrentProcess())
659 Status
= ObOpenObjectByPointer(Process
,
666 if (!NT_SUCCESS(Status
))
668 ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process
, Status
);
669 SetLastNtError(Status
);
676 * If no window station has been specified, search the process handle table
677 * for inherited window station handles, otherwise use a default one.
679 if (WinStaName
.Buffer
== NULL
)
682 * We want to find a suitable default window station.
683 * For applications that can be interactive, i.e. that have allowed
684 * access to the single interactive window station on the system,
685 * the default window station is 'WinSta0'.
686 * For applications that cannot be interactive, i.e. that do not have
687 * access to 'WinSta0' (e.g. non-interactive services), the default
688 * window station is 'Service-0xXXXX-YYYY$' (created if needed).
689 * Precedence will however be taken by any inherited window station
690 * that possesses the required interactivity property.
692 bUseDefaultWinSta
= TRUE
;
695 * Use the default 'WinSta0' window station. Whether we should
696 * use 'Service-0xXXXX-YYYY$' instead will be determined later.
698 // RtlInitUnicodeString(&WinStaName, L"WinSta0");
699 WinStaName
= WinSta0Name
;
701 if (ObFindHandleForObject(Process
,
703 ExWindowStationObjectType
,
707 TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta
);
712 * If no desktop has been specified, search the process handle table
713 * for inherited desktop handles, otherwise use the Default desktop.
714 * Note that the inherited desktop that we may use, may not belong
715 * to the window station we will connect to.
717 if (DesktopName
.Buffer
== NULL
)
719 /* Use a default desktop name */
720 RtlInitUnicodeString(&DesktopName
, L
"Default");
722 if (ObFindHandleForObject(Process
,
728 TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop
);
734 * We are going to open either a window station or a desktop.
735 * Even if this operation is done from kernel-mode, we should
736 * "emulate" an opening from user-mode (i.e. using an ObjectAttributes
737 * allocated in user-mode, with AccessMode == UserMode) for the
738 * Object Manager to perform proper access validation to the
739 * window station or desktop.
743 * Estimate the maximum size needed for the window station name
744 * and desktop name to be given to ObjectAttributes->ObjectName.
748 /* Window station name */
749 MemSize
= _scwprintf(L
"Service-0x%x-%x$", MAXULONG
, MAXULONG
) * sizeof(WCHAR
);
750 MemSize
= gustrWindowStationsDir
.Length
+ sizeof(OBJ_NAME_PATH_SEPARATOR
)
751 + max(WinStaName
.Length
, MemSize
) + sizeof(UNICODE_NULL
);
752 if (MemSize
> MAXUSHORT
)
754 ERR("IntResolveDesktop: Window station name length is too long.\n");
755 Status
= STATUS_NAME_TOO_LONG
;
758 StrSize
= max(StrSize
, (USHORT
)MemSize
);
761 MemSize
= max(DesktopName
.Length
+ sizeof(UNICODE_NULL
), sizeof(L
"Default"));
762 StrSize
= max(StrSize
, (USHORT
)MemSize
);
764 /* Size for the OBJECT_ATTRIBUTES */
765 MemSize
= ALIGN_UP(sizeof(OBJECT_ATTRIBUTES
), sizeof(PVOID
));
767 /* Add the string size */
768 MemSize
+= ALIGN_UP(sizeof(UNICODE_STRING
), sizeof(PVOID
));
771 /* Allocate the memory in user-mode */
772 Status
= ZwAllocateVirtualMemory(ZwCurrentProcess(),
773 (PVOID
*)&ObjectAttributes
,
778 if (!NT_SUCCESS(Status
))
780 ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status
);
784 ObjectName
= (PUNICODE_STRING
)((ULONG_PTR
)ObjectAttributes
+
785 ALIGN_UP(sizeof(OBJECT_ATTRIBUTES
), sizeof(PVOID
)));
787 RtlInitEmptyUnicodeString(ObjectName
,
788 (PWCHAR
)((ULONG_PTR
)ObjectName
+
789 ALIGN_UP(sizeof(UNICODE_STRING
), sizeof(PVOID
))),
793 /* If we got an inherited window station handle, duplicate and use it */
796 ASSERT(bUseDefaultWinSta
);
798 /* Duplicate the handle if it belongs to another process than the current one */
799 if (Process
!= PsGetCurrentProcess())
802 Status
= ZwDuplicateObject(hProcess
,
805 (PHANDLE
)&hWinStaDup
,
808 DUPLICATE_SAME_ACCESS
);
809 if (!NT_SUCCESS(Status
))
811 ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status
);
812 /* We will use a default window station */
817 hWinSta
= hWinStaDup
;
823 * If we have an inherited window station, check whether
824 * it is interactive and remember that for later.
828 ASSERT(bUseDefaultWinSta
);
830 /* Reference the inherited window station */
831 Status
= ObReferenceObjectByHandle(hWinSta
,
833 ExWindowStationObjectType
,
835 (PVOID
*)&WinStaObject
,
837 if (!NT_SUCCESS(Status
))
839 ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status
);
840 /* We will use a default window station */
843 ASSERT(hWinSta
== hWinStaDup
);
844 ObCloseHandle(hWinStaDup
, UserMode
);
851 ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
852 ProcessLuid
.HighPart
, ProcessLuid
.LowPart
,
853 WinStaObject
->luidUser
.HighPart
, WinStaObject
->luidUser
.LowPart
);
855 /* Check whether this window station is interactive, and remember it for later */
856 bInteractive
= !(WinStaObject
->Flags
& WSS_NOIO
);
858 /* Dereference the window station */
859 ObDereferenceObject(WinStaObject
);
863 /* Build a valid window station name */
864 Status
= RtlStringCbPrintfW(ObjectName
->Buffer
,
865 ObjectName
->MaximumLength
,
867 &gustrWindowStationsDir
,
869 if (!NT_SUCCESS(Status
))
871 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status
);
874 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
876 TRACE("Parsed initial window station: '%wZ'\n", ObjectName
);
878 /* Try to open the window station */
879 InitializeObjectAttributes(ObjectAttributes
,
881 OBJ_CASE_INSENSITIVE
,
885 ObjectAttributes
->Attributes
|= OBJ_INHERIT
;
887 Status
= ObOpenObjectByName(ObjectAttributes
,
888 ExWindowStationObjectType
,
893 (PHANDLE
)&hTempWinSta
);
894 if (!NT_SUCCESS(Status
))
896 ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName
, Status
);
901 // FIXME TODO: Perform a window station access check!!
902 // If we fail AND bUseDefaultWinSta == FALSE we just quit.
906 * Check whether we are opening the (single) interactive
907 * window station, and if so, perform an access check.
909 /* Check whether we are allowed to perform interactions */
910 if (RtlEqualUnicodeString(&WinStaName
, &WinSta0Name
, TRUE
))
912 LUID SystemLuid
= SYSTEM_LUID
;
914 /* Interactive window station: check for user LUID */
915 WinStaObject
= InputWindowStation
;
917 Status
= STATUS_ACCESS_DENIED
;
919 // TODO: Check also that we compare wrt. window station WinSta0
920 // which is the only one that can be interactive on the system.
921 if (((!bUseDefaultWinSta
|| bInherit
) && RtlEqualLuid(&ProcessLuid
, &SystemLuid
)) ||
922 RtlEqualLuid(&ProcessLuid
, &WinStaObject
->luidUser
))
924 /* We are interactive on this window station */
925 bAccessAllowed
= TRUE
;
926 Status
= STATUS_SUCCESS
;
931 /* Non-interactive window station: we have access since we were able to open it */
932 bAccessAllowed
= TRUE
;
933 Status
= STATUS_SUCCESS
;
937 /* If we failed, bail out if we were not trying to open the default window station */
938 if (!NT_SUCCESS(Status
) && !bUseDefaultWinSta
) // if (!bAccessAllowed)
941 if (/* bAccessAllowed && */ bInteractive
|| !bAccessAllowed
)
944 * Close WinSta0 if the inherited window station is interactive so that
945 * we can use it, or we do not have access to the interactive WinSta0.
947 ObCloseHandle(hTempWinSta
, UserMode
);
950 if (bInteractive
== bAccessAllowed
)
952 /* Keep using the inherited window station */
955 else // if (bInteractive != bAccessAllowed)
958 * Close the inherited window station, we will either keep using
959 * the interactive WinSta0, or use Service-0xXXXX-YYYY$.
963 ASSERT(hWinSta
== hWinStaDup
);
964 ObCloseHandle(hWinStaDup
, UserMode
);
967 hWinSta
= hTempWinSta
; // hTempWinSta is NULL in case bAccessAllowed == FALSE
970 if (bUseDefaultWinSta
)
972 if (hWinSta
== NULL
&& !bInteractive
)
974 /* Build a valid window station name from the LUID */
975 Status
= RtlStringCbPrintfW(ObjectName
->Buffer
,
976 ObjectName
->MaximumLength
,
977 L
"%wZ\\Service-0x%x-%x$",
978 &gustrWindowStationsDir
,
979 ProcessLuid
.HighPart
,
980 ProcessLuid
.LowPart
);
981 if (!NT_SUCCESS(Status
))
983 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status
);
986 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
989 * Create or open the non-interactive window station.
990 * NOTE: The non-interactive window station handle is never inheritable.
992 // FIXME: Set security!
993 InitializeObjectAttributes(ObjectAttributes
,
995 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
999 Status
= IntCreateWindowStation(&hWinSta
,
1005 if (!NT_SUCCESS(Status
))
1007 ASSERT(hWinSta
== NULL
);
1008 ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
1009 ObjectName
, Status
);
1014 // FIXME: We might not need to always create or open the "Default"
1015 // desktop on the Service-0xXXXX-YYYY$ window station; we may need
1016 // to use another one....
1019 /* Create or open the Default desktop on the window station */
1020 Status
= RtlStringCbCopyW(ObjectName
->Buffer
,
1021 ObjectName
->MaximumLength
,
1023 if (!NT_SUCCESS(Status
))
1025 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status
);
1028 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
1030 /* NOTE: The non-interactive desktop handle is never inheritable. */
1031 // FIXME: Set security!
1032 InitializeObjectAttributes(ObjectAttributes
,
1034 OBJ_CASE_INSENSITIVE
| OBJ_OPENIF
,
1038 Status
= IntCreateDesktop(&hDesktop
,
1045 if (!NT_SUCCESS(Status
))
1047 ASSERT(hDesktop
== NULL
);
1048 ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1049 ObjectName
, hWinSta
, Status
);
1055 if (hWinSta == NULL)
1057 Status = STATUS_UNSUCCESSFUL;
1064 * If we got an inherited desktop handle, duplicate and use it,
1065 * otherwise open a new desktop.
1067 if (hDesktop
!= NULL
)
1069 /* Duplicate the handle if it belongs to another process than the current one */
1070 if (Process
!= PsGetCurrentProcess())
1073 Status
= ZwDuplicateObject(hProcess
,
1076 (PHANDLE
)&hDesktopDup
,
1079 DUPLICATE_SAME_ACCESS
);
1080 if (!NT_SUCCESS(Status
))
1082 ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status
);
1083 /* We will use a default desktop */
1088 hDesktop
= hDesktopDup
;
1093 if ((hWinSta
!= NULL
) && (hDesktop
== NULL
))
1095 Status
= RtlStringCbCopyNW(ObjectName
->Buffer
,
1096 ObjectName
->MaximumLength
,
1098 DesktopName
.Length
);
1099 if (!NT_SUCCESS(Status
))
1101 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status
);
1104 ObjectName
->Length
= (USHORT
)(wcslen(ObjectName
->Buffer
) * sizeof(WCHAR
));
1106 TRACE("Parsed initial desktop: '%wZ'\n", ObjectName
);
1108 /* Open the desktop object */
1109 InitializeObjectAttributes(ObjectAttributes
,
1111 OBJ_CASE_INSENSITIVE
,
1115 ObjectAttributes
->Attributes
|= OBJ_INHERIT
;
1117 Status
= ObOpenObjectByName(ObjectAttributes
,
1118 ExDesktopObjectType
,
1123 (PHANDLE
)&hDesktop
);
1124 if (!NT_SUCCESS(Status
))
1126 ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1127 ObjectName
, hWinSta
, Status
);
1133 /* Release the object attributes */
1134 if (ObjectAttributes
)
1137 ZwFreeVirtualMemory(ZwCurrentProcess(),
1138 (PVOID
*)&ObjectAttributes
,
1143 /* Close the temporary process handle */
1144 if (hProcess
) // if (Process != PsGetCurrentProcess())
1145 ObCloseHandle(hProcess
, KernelMode
);
1147 if (NT_SUCCESS(Status
))
1149 *phWinSta
= hWinSta
;
1150 *phDesktop
= hDesktop
;
1151 return STATUS_SUCCESS
;
1155 ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath
, Status
);
1158 ObCloseHandle(hDesktopDup
, UserMode
);
1160 ObCloseHandle(hWinStaDup
, UserMode
);
1163 ObCloseHandle(hDesktop
, UserMode
);
1165 ObCloseHandle(hWinSta
, UserMode
);
1167 SetLastNtError(Status
);
1173 * IntValidateDesktopHandle
1175 * Validates the desktop handle.
1178 * If the function succeeds, the handle remains referenced. If the
1179 * fucntion fails, last error is set.
1183 IntValidateDesktopHandle(
1185 KPROCESSOR_MODE AccessMode
,
1186 ACCESS_MASK DesiredAccess
,
1191 Status
= ObReferenceObjectByHandle(
1194 ExDesktopObjectType
,
1199 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
1200 Desktop
, *Object
, DesiredAccess
, Status
);
1202 if (!NT_SUCCESS(Status
))
1203 SetLastNtError(Status
);
1209 IntGetActiveDesktop(VOID
)
1211 return gpdeskInputDesktop
;
1215 * Returns or creates a handle to the desktop object
1218 IntGetDesktopObjectHandle(PDESKTOP DesktopObject
)
1223 ASSERT(DesktopObject
);
1225 if (!ObFindHandleForObject(PsGetCurrentProcess(),
1227 ExDesktopObjectType
,
1231 Status
= ObOpenObjectByPointer(DesktopObject
,
1235 ExDesktopObjectType
,
1238 if (!NT_SUCCESS(Status
))
1240 /* Unable to create a handle */
1241 ERR("Unable to create a desktop handle\n");
1247 TRACE("Got handle: 0x%p\n", hDesk
);
1253 PUSER_MESSAGE_QUEUE FASTCALL
1254 IntGetFocusMessageQueue(VOID
)
1256 PDESKTOP pdo
= IntGetActiveDesktop();
1259 TRACE("No active desktop\n");
1262 return (PUSER_MESSAGE_QUEUE
)pdo
->ActiveMessageQueue
;
1266 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue
)
1268 PUSER_MESSAGE_QUEUE Old
;
1269 PDESKTOP pdo
= IntGetActiveDesktop();
1272 TRACE("No active desktop\n");
1275 if (NewQueue
!= NULL
)
1277 if (NewQueue
->Desktop
!= NULL
)
1279 TRACE("Message Queue already attached to another desktop!\n");
1282 IntReferenceMessageQueue(NewQueue
);
1283 (void)InterlockedExchangePointer((PVOID
*)&NewQueue
->Desktop
, pdo
);
1285 Old
= (PUSER_MESSAGE_QUEUE
)InterlockedExchangePointer((PVOID
*)&pdo
->ActiveMessageQueue
, NewQueue
);
1288 (void)InterlockedExchangePointer((PVOID
*)&Old
->Desktop
, 0);
1289 gpqForegroundPrev
= Old
;
1290 IntDereferenceMessageQueue(Old
);
1292 // Only one Q can have active foreground even when there are more than one desktop.
1295 gpqForeground
= pdo
->ActiveMessageQueue
;
1299 gpqForeground
= NULL
;
1300 ERR("ptiLastInput is CLEARED!!\n");
1301 ptiLastInput
= NULL
; // ReactOS hacks,,,, should check for process death.
1306 IntGetThreadDesktopWindow(PTHREADINFO pti
)
1308 if (!pti
) pti
= PsGetCurrentThreadWin32Thread();
1309 if (pti
->pDeskInfo
) return pti
->pDeskInfo
->spwnd
;
1313 PWND FASTCALL
co_GetDesktopWindow(PWND pWnd
)
1315 if (pWnd
->head
.rpdesk
&&
1316 pWnd
->head
.rpdesk
->pDeskInfo
)
1317 return pWnd
->head
.rpdesk
->pDeskInfo
->spwnd
;
1321 HWND FASTCALL
IntGetDesktopWindow(VOID
)
1323 PDESKTOP pdo
= IntGetActiveDesktop();
1326 TRACE("No active desktop\n");
1329 return pdo
->DesktopWindow
;
1332 PWND FASTCALL
UserGetDesktopWindow(VOID
)
1334 PDESKTOP pdo
= IntGetActiveDesktop();
1338 TRACE("No active desktop\n");
1341 // return pdo->pDeskInfo->spwnd;
1342 return UserGetWindowObject(pdo
->DesktopWindow
);
1345 HWND FASTCALL
IntGetMessageWindow(VOID
)
1347 PDESKTOP pdo
= IntGetActiveDesktop();
1351 TRACE("No active desktop\n");
1354 return pdo
->spwndMessage
->head
.h
;
1357 PWND FASTCALL
UserGetMessageWindow(VOID
)
1359 PDESKTOP pdo
= IntGetActiveDesktop();
1363 TRACE("No active desktop\n");
1366 return pdo
->spwndMessage
;
1369 HWND FASTCALL
IntGetCurrentThreadDesktopWindow(VOID
)
1371 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1372 PDESKTOP pdo
= pti
->rpdesk
;
1375 ERR("Thread doesn't have a desktop\n");
1378 return pdo
->DesktopWindow
;
1381 /* PUBLIC FUNCTIONS ***********************************************************/
1384 DesktopWindowProc(PWND Wnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
1388 //ERR("DesktopWindowProc\n");
1397 Wnd
->fnid
= FNID_DESKTOP
;
1399 *lResult
= (LRESULT
)TRUE
;
1403 Value
= HandleToULong(PsGetCurrentProcessId());
1405 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_PROCESSID
, Value
, FALSE
);
1406 Value
= HandleToULong(PsGetCurrentThreadId());
1408 co_UserSetWindowLong(UserHMGetHandle(Wnd
), DT_GWL_THREADID
, Value
, FALSE
);
1412 case WM_DISPLAYCHANGE
:
1413 co_WinPosSetWindowPos(Wnd
, 0, 0, 0, LOWORD(lParam
), HIWORD(lParam
), SWP_NOZORDER
| SWP_NOACTIVATE
);
1417 IntPaintDesktop((HDC
)wParam
);
1423 if (IntBeginPaint(Wnd
, &Ps
))
1425 IntEndPaint(Wnd
, &Ps
);
1429 case WM_SYSCOLORCHANGE
:
1430 co_UserRedrawWindow(Wnd
, NULL
, NULL
, RDW_INVALIDATE
|RDW_ERASE
|RDW_ALLCHILDREN
);
1435 PCURICON_OBJECT pcurOld
, pcurNew
;
1436 pcurNew
= UserGetCurIconObject(gDesktopCursor
);
1442 pcurNew
->CURSORF_flags
|= CURSORF_CURRENT
;
1443 pcurOld
= UserSetCursor(pcurNew
, FALSE
);
1446 pcurOld
->CURSORF_flags
&= ~CURSORF_CURRENT
;
1447 UserDereferenceObject(pcurOld
);
1452 case WM_WINDOWPOSCHANGING
:
1454 PWINDOWPOS pWindowPos
= (PWINDOWPOS
)lParam
;
1455 if ((pWindowPos
->flags
& SWP_SHOWWINDOW
) != 0)
1457 HDESK hdesk
= IntGetDesktopObjectHandle(gpdeskInputDesktop
);
1458 IntSetThreadDesktop(hdesk
, FALSE
);
1463 TRACE("DWP calling IDWP Msg %d\n",Msg
);
1464 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
1466 return TRUE
; /* We are done. Do not do any callbacks to user mode */
1470 UserMessageWindowProc(PWND pwnd
, UINT Msg
, WPARAM wParam
, LPARAM lParam
, LRESULT
*lResult
)
1477 pwnd
->fnid
|= FNID_MESSAGEWND
;
1478 *lResult
= (LRESULT
)TRUE
;
1481 pwnd
->fnid
|= FNID_DESTROY
;
1484 ERR("UMWP calling IDWP\n");
1485 *lResult
= IntDefWindowProc(pwnd
, Msg
, wParam
, lParam
, FALSE
);
1488 return TRUE
; /* We are done. Do not do any callbacks to user mode */
1491 VOID NTAPI
DesktopThreadMain(VOID
)
1496 gptiDesktopThread
= PsGetCurrentThreadWin32Thread();
1498 UserEnterExclusive();
1500 /* Register system classes. This thread does not belong to any desktop so the
1501 classes will be allocated from the shared heap */
1502 UserRegisterSystemClasses();
1506 Ret
= co_IntGetPeekMessage(&Msg
, 0, 0, 0, PM_REMOVE
, TRUE
);
1509 IntDispatchMessage(&Msg
);
1517 UserGetDesktopDC(ULONG DcType
, BOOL EmptyDC
, BOOL ValidatehWnd
)
1519 PWND DesktopObject
= 0;
1522 /* This can be called from GDI/DX, so acquire the USER lock */
1523 UserEnterExclusive();
1525 if (DcType
== DC_TYPE_DIRECT
)
1527 DesktopObject
= UserGetDesktopWindow();
1528 DesktopHDC
= (HDC
)UserGetWindowDC(DesktopObject
);
1532 PMONITOR pMonitor
= UserGetPrimaryMonitor();
1533 DesktopHDC
= IntGdiCreateDisplayDC(pMonitor
->hDev
, DcType
, EmptyDC
);
1542 UserRedrawDesktop(VOID
)
1547 Window
= UserGetDesktopWindow();
1548 Rgn
= IntSysCreateRectpRgnIndirect(&Window
->rcWindow
);
1550 IntInvalidateWindows( Window
,
1562 co_IntShowDesktop(PDESKTOP Desktop
, ULONG Width
, ULONG Height
, BOOL bRedraw
)
1564 PWND pwnd
= Desktop
->pDeskInfo
->spwnd
;
1565 UINT flags
= SWP_NOACTIVATE
|SWP_NOZORDER
|SWP_SHOWWINDOW
;
1569 flags
|= SWP_NOREDRAW
;
1571 co_WinPosSetWindowPos(pwnd
, NULL
, 0, 0, Width
, Height
, flags
);
1574 co_UserRedrawWindow( pwnd
, NULL
, 0, RDW_UPDATENOW
| RDW_ALLCHILDREN
| RDW_INVALIDATE
);
1576 return STATUS_SUCCESS
;
1580 IntHideDesktop(PDESKTOP Desktop
)
1584 DesktopWnd
= IntGetWindowObject(Desktop
->DesktopWindow
);
1587 return ERROR_INVALID_WINDOW_HANDLE
;
1589 DesktopWnd
->style
&= ~WS_VISIBLE
;
1591 return STATUS_SUCCESS
;
1596 UserBuildShellHookHwndList(PDESKTOP Desktop
)
1599 PLIST_ENTRY ListEntry
;
1600 PSHELL_HOOK_WINDOW Current
;
1603 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1604 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1605 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1607 ListEntry
= ListEntry
->Flink
;
1611 if (!entries
) return NULL
;
1613 list
= ExAllocatePoolWithTag(PagedPool
, sizeof(HWND
) * (entries
+ 1), USERTAG_WINDOWLIST
); /* alloc one extra for nullterm */
1616 HWND
* cursor
= list
;
1618 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1619 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1621 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1622 ListEntry
= ListEntry
->Flink
;
1623 *cursor
++ = Current
->hWnd
;
1626 *cursor
= NULL
; /* Nullterm list */
1633 * Send the Message to the windows registered for ShellHook
1634 * notifications. The lParam contents depend on the Message. See
1635 * MSDN for more details (RegisterShellHookWindow)
1637 VOID
co_IntShellHookNotify(WPARAM Message
, WPARAM wParam
, LPARAM lParam
)
1639 PDESKTOP Desktop
= IntGetActiveDesktop();
1642 if (!gpsi
->uiShellMsg
)
1644 gpsi
->uiShellMsg
= IntAddAtom(L
"SHELLHOOK");
1646 TRACE("MsgType = %x\n", gpsi
->uiShellMsg
);
1647 if (!gpsi
->uiShellMsg
)
1648 ERR("LastError: %x\n", EngGetLastError());
1653 TRACE("IntShellHookNotify: No desktop!\n");
1657 // Allow other devices have a shot at foreground.
1658 if (Message
== HSHELL_APPCOMMAND
) ptiLastInput
= NULL
;
1660 // FIXME: System Tray Support.
1662 HwndList
= UserBuildShellHookHwndList(Desktop
);
1665 HWND
* cursor
= HwndList
;
1667 for (; *cursor
; cursor
++)
1669 TRACE("Sending notify\n");
1670 UserPostMessage(*cursor
,
1673 (Message
== HSHELL_LANGUAGE
? lParam
: (LPARAM
)wParam
) );
1674 /* co_IntPostOrSendMessage(*cursor,
1677 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1680 ExFreePoolWithTag(HwndList
, USERTAG_WINDOWLIST
);
1683 if (ISITHOOKED(WH_SHELL
))
1685 co_HOOK_CallHooks(WH_SHELL
, Message
, wParam
, lParam
);
1690 * Add the window to the ShellHookWindows list. The windows
1691 * on that list get notifications that are important to shell
1692 * type applications.
1694 * TODO: Validate the window? I'm not sure if sending these messages to
1695 * an unsuspecting application that is not your own is a nice thing to do.
1697 BOOL
IntRegisterShellHookWindow(HWND hWnd
)
1699 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1700 PDESKTOP Desktop
= pti
->rpdesk
;
1701 PSHELL_HOOK_WINDOW Entry
;
1703 TRACE("IntRegisterShellHookWindow\n");
1705 /* First deregister the window, so we can be sure it's never twice in the
1708 IntDeRegisterShellHookWindow(hWnd
);
1710 Entry
= ExAllocatePoolWithTag(PagedPool
,
1711 sizeof(SHELL_HOOK_WINDOW
),
1719 InsertTailList(&Desktop
->ShellHookWindows
, &Entry
->ListEntry
);
1725 * Remove the window from the ShellHookWindows list. The windows
1726 * on that list get notifications that are important to shell
1727 * type applications.
1729 BOOL
IntDeRegisterShellHookWindow(HWND hWnd
)
1731 PTHREADINFO pti
= PsGetCurrentThreadWin32Thread();
1732 PDESKTOP Desktop
= pti
->rpdesk
;
1733 PLIST_ENTRY ListEntry
;
1734 PSHELL_HOOK_WINDOW Current
;
1736 ListEntry
= Desktop
->ShellHookWindows
.Flink
;
1737 while (ListEntry
!= &Desktop
->ShellHookWindows
)
1739 Current
= CONTAINING_RECORD(ListEntry
, SHELL_HOOK_WINDOW
, ListEntry
);
1740 ListEntry
= ListEntry
->Flink
;
1741 if (Current
->hWnd
== hWnd
)
1743 RemoveEntryList(&Current
->ListEntry
);
1744 ExFreePoolWithTag(Current
, TAG_WINSTA
);
1753 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop
)
1755 /* FIXME: Disable until unmapping works in mm */
1757 if (Desktop
->pheapDesktop
!= NULL
)
1759 MmUnmapViewInSessionSpace(Desktop
->pheapDesktop
);
1760 Desktop
->pheapDesktop
= NULL
;
1763 if (Desktop
->hsectionDesktop
!= NULL
)
1765 ObDereferenceObject(Desktop
->hsectionDesktop
);
1766 Desktop
->hsectionDesktop
= NULL
;
1772 IntPaintDesktop(HDC hDC
)
1774 static WCHAR s_wszSafeMode
[] = L
"Safe Mode"; // FIXME: Localize!
1777 HBRUSH DesktopBrush
, PreviousBrush
;
1779 BOOL doPatBlt
= TRUE
;
1783 if (GdiGetClipBox(hDC
, &Rect
) == ERROR
)
1786 hWndDesktop
= IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1788 WndDesktop
= UserGetWindowObject(hWndDesktop
); // rpdesk->pDeskInfo->spwnd;
1792 /* Retrieve the current SafeMode state */
1793 InSafeMode
= (UserGetSystemMetrics(SM_CLEANBOOT
) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1797 DesktopBrush
= (HBRUSH
)WndDesktop
->pcls
->hbrBackground
;
1800 * Paint desktop background
1802 if (gspv
.hbmWallpaper
!= NULL
)
1806 int scaledWidth
, scaledHeight
;
1807 int wallpaperX
, wallpaperY
, wallpaperWidth
, wallpaperHeight
;
1810 sz
.cx
= WndDesktop
->rcWindow
.right
- WndDesktop
->rcWindow
.left
;
1811 sz
.cy
= WndDesktop
->rcWindow
.bottom
- WndDesktop
->rcWindow
.top
;
1813 if (gspv
.WallpaperMode
== wmFit
||
1814 gspv
.WallpaperMode
== wmFill
)
1816 int scaleNum
, scaleDen
;
1818 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
1819 if ((sz
.cx
* gspv
.cyWallpaper
) > (sz
.cy
* gspv
.cxWallpaper
))
1821 if (gspv
.WallpaperMode
== wmFit
)
1824 scaleDen
= gspv
.cyWallpaper
;
1829 scaleDen
= gspv
.cxWallpaper
;
1834 if (gspv
.WallpaperMode
== wmFit
)
1837 scaleDen
= gspv
.cxWallpaper
;
1842 scaleDen
= gspv
.cyWallpaper
;
1846 scaledWidth
= EngMulDiv(gspv
.cxWallpaper
, scaleNum
, scaleDen
);
1847 scaledHeight
= EngMulDiv(gspv
.cyWallpaper
, scaleNum
, scaleDen
);
1849 if (gspv
.WallpaperMode
== wmFill
)
1851 wallpaperX
= (((scaledWidth
- sz
.cx
) * gspv
.cxWallpaper
) / (2 * scaledWidth
));
1852 wallpaperY
= (((scaledHeight
- sz
.cy
) * gspv
.cyWallpaper
) / (2 * scaledHeight
));
1854 wallpaperWidth
= (sz
.cx
* gspv
.cxWallpaper
) / scaledWidth
;
1855 wallpaperHeight
= (sz
.cy
* gspv
.cyWallpaper
) / scaledHeight
;
1859 if (gspv
.WallpaperMode
== wmStretch
||
1860 gspv
.WallpaperMode
== wmTile
||
1861 gspv
.WallpaperMode
== wmFill
)
1866 else if (gspv
.WallpaperMode
== wmFit
)
1868 x
= (sz
.cx
- scaledWidth
) / 2;
1869 y
= (sz
.cy
- scaledHeight
) / 2;
1873 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1874 x
= (sz
.cx
/ 2) - (gspv
.cxWallpaper
/ 2);
1875 y
= (sz
.cy
/ 2) - (gspv
.cyWallpaper
/ 2);
1878 hWallpaperDC
= NtGdiCreateCompatibleDC(hDC
);
1879 if (hWallpaperDC
!= NULL
)
1883 /* Fill in the area that the bitmap is not going to cover */
1886 /* FIXME: Clip out the bitmap
1887 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1888 once we support DSTINVERT */
1889 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
1890 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
1891 NtGdiSelectBrush(hDC
, PreviousBrush
);
1894 /* Do not fill the background after it is painted no matter the size of the picture */
1897 hOldBitmap
= NtGdiSelectBitmap(hWallpaperDC
, gspv
.hbmWallpaper
);
1899 if (gspv
.WallpaperMode
== wmStretch
)
1901 if (Rect
.right
&& Rect
.bottom
)
1902 NtGdiStretchBlt(hDC
,
1915 else if (gspv
.WallpaperMode
== wmTile
)
1917 /* Paint the bitmap across the screen then down */
1918 for (y
= 0; y
< Rect
.bottom
; y
+= gspv
.cyWallpaper
)
1920 for (x
= 0; x
< Rect
.right
; x
+= gspv
.cxWallpaper
)
1936 else if (gspv
.WallpaperMode
== wmFit
)
1938 if (Rect
.right
&& Rect
.bottom
)
1940 NtGdiStretchBlt(hDC
,
1954 else if (gspv
.WallpaperMode
== wmFill
)
1956 if (Rect
.right
&& Rect
.bottom
)
1958 NtGdiStretchBlt(hDC
,
1986 NtGdiSelectBitmap(hWallpaperDC
, hOldBitmap
);
1987 NtGdiDeleteObjectApp(hWallpaperDC
);
1993 /* Black desktop background in Safe Mode */
1994 DesktopBrush
= StockObjects
[BLACK_BRUSH
];
1997 /* Background is set to none, clear the screen */
2000 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
2001 NtGdiPatBlt(hDC
, Rect
.left
, Rect
.top
, Rect
.right
, Rect
.bottom
, PATCOPY
);
2002 NtGdiSelectBrush(hDC
, PreviousBrush
);
2006 * Display the system version on the desktop background
2008 if (InSafeMode
|| g_AlwaysDisplayVersion
|| g_PaintDesktopVersion
)
2011 static WCHAR wszzVersion
[1024] = L
"\0";
2013 /* Only used in normal mode */
2014 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
2015 static POLYTEXTW VerStrs
[4] = {{0},{0},{0},{0}};
2019 HFONT hFont1
= NULL
, hFont2
= NULL
, hOldFont
= NULL
;
2020 COLORREF crText
, color_old
;
2025 if (!UserSystemParametersInfo(SPI_GETWORKAREA
, 0, &Rect
, 0))
2027 Rect
.left
= Rect
.top
= 0;
2028 Rect
.right
= UserGetSystemMetrics(SM_CXSCREEN
);
2029 Rect
.bottom
= UserGetSystemMetrics(SM_CYSCREEN
);
2033 RECTL_vOffsetRect(&Rect
, -Rect
.left
, -Rect
.top
);
2037 * Set up the fonts (otherwise use default ones)
2040 /* Font for the principal version string */
2041 hFont1
= GreCreateFontIndirectW(&gspv
.ncm
.lfCaptionFont
);
2042 /* Font for the secondary version strings */
2043 hFont2
= GreCreateFontIndirectW(&gspv
.ncm
.lfMenuFont
);
2046 hOldFont
= NtGdiSelectFont(hDC
, hFont1
);
2048 if (gspv
.hbmWallpaper
== NULL
)
2050 /* Retrieve the brush fill colour */
2051 // TODO: The following code constitutes "GreGetBrushColor".
2052 PreviousBrush
= NtGdiSelectBrush(hDC
, DesktopBrush
);
2053 pdc
= DC_LockDc(hDC
);
2056 crText
= pdc
->eboFill
.ulRGBColor
;
2061 crText
= RGB(0, 0, 0);
2063 NtGdiSelectBrush(hDC
, PreviousBrush
);
2065 /* Adjust text colour according to the brush */
2066 if (GetRValue(crText
) + GetGValue(crText
) + GetBValue(crText
) > 128 * 3)
2067 crText
= RGB(0, 0, 0);
2069 crText
= RGB(255, 255, 255);
2073 /* Always use white when the text is displayed on top of a wallpaper */
2074 crText
= RGB(255, 255, 255);
2077 color_old
= IntGdiSetTextColor(hDC
, crText
);
2078 align_old
= IntGdiSetTextAlign(hDC
, TA_RIGHT
);
2079 mode_old
= IntGdiSetBkMode(hDC
, TRANSPARENT
);
2081 /* Display the system version information */
2084 Status
= GetSystemVersionString(wszzVersion
,
2085 ARRAYSIZE(wszzVersion
),
2087 g_AlwaysDisplayVersion
);
2088 if (!InSafeMode
&& NT_SUCCESS(Status
) && *wszzVersion
)
2090 PWCHAR pstr
= wszzVersion
;
2091 for (i
= 0; (i
< ARRAYSIZE(VerStrs
)) && *pstr
; ++i
)
2093 VerStrs
[i
].n
= lstrlenW(pstr
);
2094 VerStrs
[i
].lpstr
= pstr
;
2095 pstr
+= (VerStrs
[i
].n
+ 1);
2101 Status
= STATUS_SUCCESS
;
2103 if (NT_SUCCESS(Status
) && *wszzVersion
)
2108 LONG TotalHeight
= 0;
2110 /* Normal Mode: multiple version information text separated by newlines */
2111 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
2113 /* Compute the heights of the strings */
2114 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2115 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
2117 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
2120 GreGetTextExtentW(hDC
, VerStrs
[i
].lpstr
, VerStrs
[i
].n
, &Size
, 1);
2121 VerStrs
[i
].y
= Size
.cy
; // Store the string height
2122 TotalHeight
+= Size
.cy
;
2124 /* While the first string was using hFont1, all the others use hFont2 */
2125 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
2127 /* The total height must not exceed the screen height */
2128 TotalHeight
= min(TotalHeight
, Rect
.bottom
);
2130 /* Display the strings */
2131 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2132 for (i
= 0; i
< ARRAYSIZE(VerStrs
); ++i
)
2134 if (!VerStrs
[i
].lpstr
|| !*VerStrs
[i
].lpstr
|| (VerStrs
[i
].n
== 0))
2137 TotalHeight
-= VerStrs
[i
].y
;
2140 Rect
.bottom
- TotalHeight
- 5,
2146 /* While the first string was using hFont1, all the others use hFont2 */
2147 if (hFont2
) NtGdiSelectFont(hDC
, hFont2
);
2152 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2154 /* Safe Mode: single version information text in top center */
2155 len
= wcslen(wszzVersion
);
2157 IntGdiSetTextAlign(hDC
, TA_CENTER
| TA_TOP
);
2158 GreExtTextOutW(hDC
, (Rect
.right
+ Rect
.left
)/2, Rect
.top
+ 3, 0, NULL
, wszzVersion
, len
, NULL
, 0);
2164 if (hFont1
) NtGdiSelectFont(hDC
, hFont1
);
2166 /* Print Safe Mode text in corners */
2167 len
= wcslen(s_wszSafeMode
);
2169 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_TOP
);
2170 GreExtTextOutW(hDC
, Rect
.left
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2171 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_TOP
);
2172 GreExtTextOutW(hDC
, Rect
.right
, Rect
.top
+ 3, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2173 IntGdiSetTextAlign(hDC
, TA_LEFT
| TA_BOTTOM
);
2174 GreExtTextOutW(hDC
, Rect
.left
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2175 IntGdiSetTextAlign(hDC
, TA_RIGHT
| TA_BOTTOM
);
2176 GreExtTextOutW(hDC
, Rect
.right
, Rect
.bottom
- 5, 0, NULL
, s_wszSafeMode
, len
, NULL
, 0);
2179 IntGdiSetBkMode(hDC
, mode_old
);
2180 IntGdiSetTextAlign(hDC
, align_old
);
2181 IntGdiSetTextColor(hDC
, color_old
);
2184 GreDeleteObject(hFont2
);
2188 NtGdiSelectFont(hDC
, hOldFont
);
2189 GreDeleteObject(hFont1
);
2197 UserInitializeDesktop(PDESKTOP pdesk
, PUNICODE_STRING DesktopName
, PWINSTATION_OBJECT pwinsta
)
2199 PVOID DesktopHeapSystemBase
= NULL
;
2200 ULONG_PTR HeapSize
= gdwDesktopSectionSize
* 1024;
2201 SIZE_T DesktopInfoSize
;
2204 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk
, DesktopName
);
2206 RtlZeroMemory(pdesk
, sizeof(DESKTOP
));
2208 /* Link the desktop with the parent window station */
2209 ObReferenceObject(pwinsta
);
2210 pdesk
->rpwinstaParent
= pwinsta
;
2211 InsertTailList(&pwinsta
->DesktopListHead
, &pdesk
->ListEntry
);
2213 /* Create the desktop heap */
2214 pdesk
->hsectionDesktop
= NULL
;
2215 pdesk
->pheapDesktop
= UserCreateHeap(&pdesk
->hsectionDesktop
,
2216 &DesktopHeapSystemBase
,
2218 if (pdesk
->pheapDesktop
== NULL
)
2220 ERR("Failed to create desktop heap!\n");
2221 return STATUS_NO_MEMORY
;
2224 /* Create DESKTOPINFO */
2225 DesktopInfoSize
= sizeof(DESKTOPINFO
) + DesktopName
->Length
+ sizeof(WCHAR
);
2226 pdesk
->pDeskInfo
= RtlAllocateHeap(pdesk
->pheapDesktop
,
2227 HEAP_NO_SERIALIZE
| HEAP_ZERO_MEMORY
,
2229 if (pdesk
->pDeskInfo
== NULL
)
2231 ERR("Failed to create the DESKTOP structure!\n");
2232 return STATUS_NO_MEMORY
;
2235 /* Initialize the DESKTOPINFO */
2236 pdesk
->pDeskInfo
->pvDesktopBase
= DesktopHeapSystemBase
;
2237 pdesk
->pDeskInfo
->pvDesktopLimit
= (PVOID
)((ULONG_PTR
)DesktopHeapSystemBase
+ HeapSize
);
2238 RtlCopyMemory(pdesk
->pDeskInfo
->szDesktopName
,
2239 DesktopName
->Buffer
,
2240 DesktopName
->Length
+ sizeof(WCHAR
));
2241 for (i
= 0; i
< NB_HOOKS
; i
++)
2243 InitializeListHead(&pdesk
->pDeskInfo
->aphkStart
[i
]);
2246 InitializeListHead(&pdesk
->ShellHookWindows
);
2247 InitializeListHead(&pdesk
->PtiList
);
2249 return STATUS_SUCCESS
;
2252 /* SYSCALLS *******************************************************************/
2255 * NtUserCreateDesktop
2257 * Creates a new desktop.
2261 * Object Attributes.
2264 * Name of the device.
2270 * Interaction flags.
2273 * Requested type of access.
2277 * If the function succeeds, the return value is a handle to the newly
2278 * created desktop. If the specified desktop already exists, the function
2279 * succeeds and returns a handle to the existing desktop. When you are
2280 * finished using the handle, call the CloseDesktop function to close it.
2281 * If the function fails, the return value is NULL.
2290 OUT HDESK
* phDesktop
,
2291 IN POBJECT_ATTRIBUTES ObjectAttributes
,
2292 IN KPROCESSOR_MODE AccessMode
,
2293 IN PUNICODE_STRING lpszDesktopDevice OPTIONAL
,
2294 IN LPDEVMODEW lpdmw OPTIONAL
,
2296 IN ACCESS_MASK dwDesiredAccess
)
2299 PDESKTOP pdesk
= NULL
;
2301 BOOLEAN Context
= FALSE
;
2302 UNICODE_STRING ClassName
;
2303 LARGE_STRING WindowName
;
2304 BOOL NoHooks
= FALSE
;
2307 PTHREADINFO ptiCurrent
;
2310 TRACE("Enter IntCreateDesktop\n");
2315 ptiCurrent
= PsGetCurrentThreadWin32Thread();
2317 ASSERT(gptiDesktopThread
);
2319 /* Turn off hooks when calling any CreateWindowEx from inside win32k */
2320 NoHooks
= (ptiCurrent
->TIF_flags
& TIF_DISABLEHOOKS
);
2321 ptiCurrent
->TIF_flags
|= TIF_DISABLEHOOKS
;
2322 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
2325 * Try to open already existing desktop
2327 Status
= ObOpenObjectByName(ObjectAttributes
,
2328 ExDesktopObjectType
,
2334 if (!NT_SUCCESS(Status
))
2336 ERR("ObOpenObjectByName failed to open/create desktop\n");
2340 /* In case the object was not created (eg if it existed), return now */
2341 if (Context
== FALSE
)
2343 TRACE("IntCreateDesktop opened desktop '%wZ'\n", ObjectAttributes
->ObjectName
);
2344 Status
= STATUS_SUCCESS
;
2348 /* Reference the desktop */
2349 Status
= ObReferenceObjectByHandle(hDesk
,
2351 ExDesktopObjectType
,
2355 if (!NT_SUCCESS(Status
))
2357 ERR("Failed to reference desktop object\n");
2361 /* Get the desktop window class. The thread desktop does not belong to any desktop
2362 * so the classes created there (including the desktop class) are allocated in the shared heap
2363 * It would cause problems if we used a class that belongs to the caller
2365 ClassName
.Buffer
= WC_DESKTOP
;
2366 ClassName
.Length
= 0;
2367 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
2371 Status
= STATUS_UNSUCCESSFUL
;
2375 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2376 RtlZeroMemory(&Cs
, sizeof(Cs
));
2377 Cs
.x
= UserGetSystemMetrics(SM_XVIRTUALSCREEN
),
2378 Cs
.y
= UserGetSystemMetrics(SM_YVIRTUALSCREEN
),
2379 Cs
.cx
= UserGetSystemMetrics(SM_CXVIRTUALSCREEN
),
2380 Cs
.cy
= UserGetSystemMetrics(SM_CYVIRTUALSCREEN
),
2381 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
2382 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2383 Cs
.lpszName
= (LPCWSTR
) &WindowName
;
2384 Cs
.lpszClass
= (LPCWSTR
) &ClassName
;
2386 /* Use IntCreateWindow instead of co_UserCreateWindowEx because the later expects a thread with a desktop */
2387 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
2390 ERR("Failed to create desktop window for the new desktop\n");
2391 Status
= STATUS_UNSUCCESSFUL
;
2395 pdesk
->dwSessionId
= PsGetCurrentProcessSessionId();
2396 pdesk
->DesktopWindow
= pWnd
->head
.h
;
2397 pdesk
->pDeskInfo
->spwnd
= pWnd
;
2398 pWnd
->fnid
= FNID_DESKTOP
;
2400 ClassName
.Buffer
= MAKEINTATOM(gpsi
->atomSysClass
[ICLS_HWNDMESSAGE
]);
2401 ClassName
.Length
= 0;
2402 pcls
= IntGetAndReferenceClass(&ClassName
, 0, TRUE
);
2406 Status
= STATUS_UNSUCCESSFUL
;
2410 RtlZeroMemory(&WindowName
, sizeof(WindowName
));
2411 RtlZeroMemory(&Cs
, sizeof(Cs
));
2412 Cs
.cx
= Cs
.cy
= 100;
2413 Cs
.style
= WS_POPUP
|WS_CLIPCHILDREN
;
2414 Cs
.hInstance
= hModClient
; // hModuleWin; // Server side winproc!
2415 Cs
.lpszName
= (LPCWSTR
)&WindowName
;
2416 Cs
.lpszClass
= (LPCWSTR
)&ClassName
;
2417 pWnd
= IntCreateWindow(&Cs
, &WindowName
, pcls
, NULL
, NULL
, NULL
, pdesk
);
2420 ERR("Failed to create message window for the new desktop\n");
2421 Status
= STATUS_UNSUCCESSFUL
;
2425 pdesk
->spwndMessage
= pWnd
;
2426 pWnd
->fnid
= FNID_MESSAGEWND
;
2429 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
2430 Create Tooltip. Saved in DesktopObject->spwndTooltip.
2431 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
2432 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
2433 The rest is same as message window.
2434 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
2436 Status
= STATUS_SUCCESS
;
2441 ObDereferenceObject(pdesk
);
2443 if (!NT_SUCCESS(Status
) && hDesk
!= NULL
)
2445 ObCloseHandle(hDesk
, AccessMode
);
2450 ptiCurrent
->TIF_flags
&= ~TIF_DISABLEHOOKS
;
2451 ptiCurrent
->pClientInfo
->dwTIFlags
= ptiCurrent
->TIF_flags
;
2454 TRACE("Leave IntCreateDesktop, Status 0x%08lx\n", Status
);
2456 if (NT_SUCCESS(Status
))
2459 SetLastNtError(Status
);
2464 NtUserCreateDesktop(
2465 POBJECT_ATTRIBUTES ObjectAttributes
,
2466 PUNICODE_STRING lpszDesktopDevice
,
2469 ACCESS_MASK dwDesiredAccess
)
2474 DECLARE_RETURN(HDESK
);
2476 TRACE("Enter NtUserCreateDesktop\n");
2477 UserEnterExclusive();
2479 Status
= IntCreateDesktop(&hDesk
,
2486 if (!NT_SUCCESS(Status
))
2488 ERR("IntCreateDesktop failed, Status 0x%08lx\n", Status
);
2489 // SetLastNtError(Status);
2496 TRACE("Leave NtUserCreateDesktop, ret=0x%p\n", _ret_
);
2504 * Opens an existing desktop.
2508 * Name of the existing desktop.
2511 * Interaction flags.
2514 * Requested type of access.
2517 * Handle to the desktop or zero on failure.
2525 POBJECT_ATTRIBUTES ObjectAttributes
,
2527 ACCESS_MASK dwDesiredAccess
)
2532 Status
= ObOpenObjectByName(
2534 ExDesktopObjectType
,
2541 if (!NT_SUCCESS(Status
))
2543 ERR("Failed to open desktop\n");
2544 SetLastNtError(Status
);
2548 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes
->ObjectName
->Buffer
, Desktop
);
2554 * NtUserOpenInputDesktop
2556 * Opens the input (interactive) desktop.
2560 * Interaction flags.
2563 * Inheritance option.
2566 * Requested type of access.
2569 * Handle to the input desktop or zero on failure.
2576 NtUserOpenInputDesktop(
2579 ACCESS_MASK dwDesiredAccess
)
2583 ULONG HandleAttributes
= 0;
2585 UserEnterExclusive();
2586 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2588 if (fInherit
) HandleAttributes
= OBJ_INHERIT
;
2590 /* Create a new handle to the object */
2591 Status
= ObOpenObjectByPointer(
2596 ExDesktopObjectType
,
2600 if (!NT_SUCCESS(Status
))
2602 ERR("Failed to open input desktop object\n");
2603 SetLastNtError(Status
);
2606 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk
);
2612 * NtUserCloseDesktop
2614 * Closes a desktop handle.
2618 * Handle to the desktop.
2624 * The desktop handle can be created with NtUserCreateDesktop or
2625 * NtUserOpenDesktop. This function will fail if any thread in the calling
2626 * process is using the specified desktop handle or if the handle refers
2627 * to the initial desktop of the calling process.
2634 NtUserCloseDesktop(HDESK hDesktop
)
2638 DECLARE_RETURN(BOOL
);
2640 TRACE("NtUserCloseDesktop(0x%p) called\n", hDesktop
);
2641 UserEnterExclusive();
2643 if (hDesktop
== gptiCurrent
->hdesk
|| hDesktop
== gptiCurrent
->ppi
->hdeskStartup
)
2645 ERR("Attempted to close thread desktop\n");
2646 EngSetLastError(ERROR_BUSY
);
2650 Status
= IntValidateDesktopHandle(hDesktop
, UserMode
, 0, &pdesk
);
2651 if (!NT_SUCCESS(Status
))
2653 ERR("Validation of desktop handle 0x%p failed\n", hDesktop
);
2657 ObDereferenceObject(pdesk
);
2659 Status
= ZwClose(hDesktop
);
2660 if (!NT_SUCCESS(Status
))
2662 ERR("Failed to close desktop handle 0x%p\n", hDesktop
);
2663 SetLastNtError(Status
);
2670 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_
);
2676 * NtUserPaintDesktop
2678 * The NtUserPaintDesktop function fills the clipping region in the
2679 * specified device context with the desktop pattern or wallpaper. The
2680 * function is provided primarily for shell desktops.
2684 * Handle to the device context.
2691 NtUserPaintDesktop(HDC hDC
)
2694 UserEnterExclusive();
2695 TRACE("Enter NtUserPaintDesktop\n");
2696 Ret
= IntPaintDesktop(hDC
);
2697 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret
);
2703 * NtUserResolveDesktop
2705 * The NtUserResolveDesktop function attempts to retrieve valid handles to
2706 * a desktop and a window station suitable for the specified process.
2707 * The specified desktop path string is used only as a hint for the resolution.
2709 * See the description of IntResolveDesktop for more details.
2713 * Handle to a user process.
2716 * The desktop path string used as a hint for desktop resolution.
2719 * Whether or not the returned handles are inheritable.
2722 * Pointer to a window station handle.
2725 * Handle to the desktop (direct return value) and
2726 * handle to the associated window station (by pointer).
2727 * NULL in case of failure.
2730 * Callable by CSRSS only.
2738 NtUserResolveDesktop(
2739 IN HANDLE ProcessHandle
,
2740 IN PUNICODE_STRING DesktopPath
,
2742 OUT HWINSTA
* phWinSta
)
2746 HWINSTA hWinSta
= NULL
;
2747 HDESK hDesktop
= NULL
;
2748 UNICODE_STRING CapturedDesktopPath
;
2750 /* Allow only the Console Server to perform this operation (via CSRSS) */
2751 if (PsGetCurrentProcess() != gpepCSRSS
)
2754 /* Get the process object the user handle was referencing */
2755 Status
= ObReferenceObjectByHandle(ProcessHandle
,
2756 PROCESS_QUERY_INFORMATION
,
2761 if (!NT_SUCCESS(Status
))
2764 // UserEnterShared();
2768 /* Probe the handle pointer */
2769 // ProbeForWriteHandle
2770 ProbeForWrite(phWinSta
, sizeof(HWINSTA
), sizeof(HWINSTA
));
2772 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2774 Status
= _SEH2_GetExceptionCode();
2775 _SEH2_YIELD(goto Quit
);
2779 /* Capture the user desktop path string */
2780 Status
= ProbeAndCaptureUnicodeString(&CapturedDesktopPath
,
2783 if (!NT_SUCCESS(Status
))
2786 /* Call the internal function */
2787 Status
= IntResolveDesktop(Process
,
2788 &CapturedDesktopPath
,
2792 if (!NT_SUCCESS(Status
))
2794 ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status
);
2801 /* Return the window station handle */
2802 *phWinSta
= hWinSta
;
2804 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
2806 Status
= _SEH2_GetExceptionCode();
2808 /* We failed, close the opened desktop and window station */
2809 if (hDesktop
) ObCloseHandle(hDesktop
, UserMode
);
2811 if (hWinSta
) ObCloseHandle(hWinSta
, UserMode
);
2815 /* Free the captured string */
2816 ReleaseCapturedUnicodeString(&CapturedDesktopPath
, UserMode
);
2821 /* Dereference the process object */
2822 ObDereferenceObject(Process
);
2824 /* Return the desktop handle */
2829 * NtUserSwitchDesktop
2831 * Sets the current input (interactive) desktop.
2835 * Handle to desktop.
2845 NtUserSwitchDesktop(HDESK hdesk
)
2849 BOOL bRedrawDesktop
;
2850 DECLARE_RETURN(BOOL
);
2852 UserEnterExclusive();
2853 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk
);
2855 Status
= IntValidateDesktopHandle(hdesk
, UserMode
, 0, &pdesk
);
2856 if (!NT_SUCCESS(Status
))
2858 ERR("Validation of desktop handle 0x%p failed\n", hdesk
);
2862 if (PsGetCurrentProcessSessionId() != pdesk
->rpwinstaParent
->dwSessionId
)
2864 ObDereferenceObject(pdesk
);
2865 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2869 if (pdesk
== gpdeskInputDesktop
)
2871 ObDereferenceObject(pdesk
);
2872 WARN("NtUserSwitchDesktop called for active desktop\n");
2877 * Don't allow applications switch the desktop if it's locked, unless the caller
2878 * is the logon application itself
2880 if ((pdesk
->rpwinstaParent
->Flags
& WSS_LOCKED
) &&
2881 gpidLogon
!= PsGetCurrentProcessId())
2883 ObDereferenceObject(pdesk
);
2884 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk
);
2888 if (pdesk
->rpwinstaParent
!= InputWindowStation
)
2890 ObDereferenceObject(pdesk
);
2891 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk
);
2895 /* FIXME: Fail if the process is associated with a secured
2896 desktop such as Winlogon or Screen-Saver */
2897 /* FIXME: Connect to input device */
2899 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop
, pdesk
);
2901 bRedrawDesktop
= FALSE
;
2903 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2904 if (gpdeskInputDesktop
!= NULL
)
2906 if ((gpdeskInputDesktop
->pDeskInfo
->spwnd
->style
& WS_VISIBLE
) == WS_VISIBLE
)
2907 bRedrawDesktop
= TRUE
;
2909 /* Hide the previous desktop window */
2910 IntHideDesktop(gpdeskInputDesktop
);
2913 /* Set the active desktop in the desktop's window station. */
2914 InputWindowStation
->ActiveDesktop
= pdesk
;
2916 /* Set the global state. */
2917 gpdeskInputDesktop
= pdesk
;
2919 /* Show the new desktop window */
2920 co_IntShowDesktop(pdesk
, UserGetSystemMetrics(SM_CXSCREEN
), UserGetSystemMetrics(SM_CYSCREEN
), bRedrawDesktop
);
2922 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop
);
2923 ObDereferenceObject(pdesk
);
2928 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_
);
2934 * NtUserGetThreadDesktop
2941 NtUserGetThreadDesktop(DWORD dwThreadId
, DWORD Unknown1
)
2945 PDESKTOP DesktopObject
;
2946 HDESK hDesk
, hThreadDesktop
;
2947 OBJECT_HANDLE_INFORMATION HandleInformation
;
2948 DECLARE_RETURN(HDESK
);
2950 UserEnterExclusive();
2951 TRACE("Enter NtUserGetThreadDesktop\n");
2955 EngSetLastError(ERROR_INVALID_PARAMETER
);
2959 Status
= PsLookupThreadByThreadId((HANDLE
)(DWORD_PTR
)dwThreadId
, &Thread
);
2960 if (!NT_SUCCESS(Status
))
2962 EngSetLastError(ERROR_INVALID_PARAMETER
);
2966 if (Thread
->ThreadsProcess
== PsGetCurrentProcess())
2968 /* Just return the handle, we queried the desktop handle of a thread running
2969 in the same context */
2970 hDesk
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
;
2971 ObDereferenceObject(Thread
);
2975 /* Get the desktop handle and the desktop of the thread */
2976 if (!(hThreadDesktop
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->hdesk
) ||
2977 !(DesktopObject
= ((PTHREADINFO
)Thread
->Tcb
.Win32Thread
)->rpdesk
))
2979 ObDereferenceObject(Thread
);
2980 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId
);
2984 /* We could just use DesktopObject instead of looking up the handle, but latter
2985 may be a bit safer (e.g. when the desktop is being destroyed */
2986 /* Switch into the context of the thread we're trying to get the desktop from,
2987 so we can use the handle */
2988 KeAttachProcess(&Thread
->ThreadsProcess
->Pcb
);
2989 Status
= ObReferenceObjectByHandle(hThreadDesktop
,
2991 ExDesktopObjectType
,
2993 (PVOID
*)&DesktopObject
,
2994 &HandleInformation
);
2997 /* The handle couldn't be found, there's nothing to get... */
2998 if (!NT_SUCCESS(Status
))
3000 ObDereferenceObject(Thread
);
3004 /* Lookup our handle table if we can find a handle to the desktop object,
3005 if not, create one */
3006 hDesk
= IntGetDesktopObjectHandle(DesktopObject
);
3008 /* All done, we got a valid handle to the desktop */
3009 ObDereferenceObject(DesktopObject
);
3010 ObDereferenceObject(Thread
);
3014 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_
);
3020 IntUnmapDesktopView(IN PDESKTOP pdesk
)
3023 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
3024 NTSTATUS Status
= STATUS_SUCCESS
;
3026 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk
);
3028 ppi
= PsGetCurrentProcessWin32Process();
3031 * Unmap if we're the last thread using the desktop.
3032 * Start the search at the next mapping: skip the first entry
3033 * as it must be the global user heap mapping.
3035 PrevLink
= &ppi
->HeapMappings
.Next
;
3036 HeapMapping
= *PrevLink
;
3037 while (HeapMapping
!= NULL
)
3039 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
3041 if (--HeapMapping
->Count
== 0)
3043 *PrevLink
= HeapMapping
->Next
;
3045 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi
, pdesk
);
3046 Status
= MmUnmapViewOfSection(PsGetCurrentProcess(),
3047 HeapMapping
->UserMapping
);
3049 ObDereferenceObject(pdesk
);
3051 UserHeapFree(HeapMapping
);
3056 PrevLink
= &HeapMapping
->Next
;
3057 HeapMapping
= HeapMapping
->Next
;
3064 IntMapDesktopView(IN PDESKTOP pdesk
)
3067 PW32HEAP_USER_MAPPING HeapMapping
, *PrevLink
;
3068 PVOID UserBase
= NULL
;
3069 SIZE_T ViewSize
= 0;
3070 LARGE_INTEGER Offset
;
3073 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk
);
3075 ppi
= PsGetCurrentProcessWin32Process();
3078 * Find out if another thread already mapped the desktop heap.
3079 * Start the search at the next mapping: skip the first entry
3080 * as it must be the global user heap mapping.
3082 PrevLink
= &ppi
->HeapMappings
.Next
;
3083 HeapMapping
= *PrevLink
;
3084 while (HeapMapping
!= NULL
)
3086 if (HeapMapping
->KernelMapping
== (PVOID
)pdesk
->pheapDesktop
)
3088 HeapMapping
->Count
++;
3089 return STATUS_SUCCESS
;
3092 PrevLink
= &HeapMapping
->Next
;
3093 HeapMapping
= HeapMapping
->Next
;
3096 /* We're the first, map the heap */
3097 Offset
.QuadPart
= 0;
3098 Status
= MmMapViewOfSection(pdesk
->hsectionDesktop
,
3099 PsGetCurrentProcess(),
3107 PAGE_EXECUTE_READ
); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
3108 if (!NT_SUCCESS(Status
))
3110 ERR("Failed to map desktop\n");
3114 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi
, pdesk
);
3116 /* Add the mapping */
3117 HeapMapping
= UserHeapAlloc(sizeof(*HeapMapping
));
3118 if (HeapMapping
== NULL
)
3120 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase
);
3121 ERR("UserHeapAlloc() failed!\n");
3122 return STATUS_NO_MEMORY
;
3125 HeapMapping
->Next
= NULL
;
3126 HeapMapping
->KernelMapping
= (PVOID
)pdesk
->pheapDesktop
;
3127 HeapMapping
->UserMapping
= UserBase
;
3128 HeapMapping
->Limit
= ViewSize
;
3129 HeapMapping
->Count
= 1;
3130 *PrevLink
= HeapMapping
;
3132 ObReferenceObject(pdesk
);
3134 return STATUS_SUCCESS
;
3138 IntSetThreadDesktop(IN HDESK hDesktop
,
3139 IN BOOL FreeOnFailure
)
3141 PDESKTOP pdesk
= NULL
, pdeskOld
;
3144 PCLIENTTHREADINFO pctiOld
, pctiNew
= NULL
;
3147 ASSERT(NtCurrentTeb());
3149 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop
, FreeOnFailure
);
3151 pti
= PsGetCurrentThreadWin32Thread();
3152 pci
= pti
->pClientInfo
;
3154 /* If the caller gave us a desktop, ensure it is valid */
3155 if (hDesktop
!= NULL
)
3157 /* Validate the new desktop. */
3158 Status
= IntValidateDesktopHandle(hDesktop
, UserMode
, 0, &pdesk
);
3159 if (!NT_SUCCESS(Status
))
3161 ERR("Validation of desktop handle 0x%p failed\n", hDesktop
);
3165 if (pti
->rpdesk
== pdesk
)
3168 ObDereferenceObject(pdesk
);
3173 /* Make sure that we don't own any window in the current desktop */
3174 if (!IsListEmpty(&pti
->WindowListHead
))
3177 ObDereferenceObject(pdesk
);
3178 ERR("Attempted to change thread desktop although the thread has windows!\n");
3179 EngSetLastError(ERROR_BUSY
);
3183 /* Desktop is being re-set so clear out foreground. */
3184 if (pti
->rpdesk
!= pdesk
&& pti
->MessageQueue
== gpqForeground
)
3186 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
3187 IntSetFocusMessageQueue(NULL
);
3190 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
3193 Status
= IntMapDesktopView(pdesk
);
3194 if (!NT_SUCCESS(Status
))
3196 ERR("Failed to map desktop heap!\n");
3197 ObDereferenceObject(pdesk
);
3198 SetLastNtError(Status
);
3202 pctiNew
= DesktopHeapAlloc(pdesk
, sizeof(CLIENTTHREADINFO
));
3203 if (pctiNew
== NULL
)
3205 ERR("Failed to allocate new pcti\n");
3206 IntUnmapDesktopView(pdesk
);
3207 ObDereferenceObject(pdesk
);
3208 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY
);
3213 /* free all classes or move them to the shared heap */
3214 if (pti
->rpdesk
!= NULL
)
3216 if (!IntCheckProcessDesktopClasses(pti
->rpdesk
, FreeOnFailure
))
3218 ERR("Failed to move process classes to shared heap!\n");
3221 DesktopHeapFree(pdesk
, pctiNew
);
3222 IntUnmapDesktopView(pdesk
);
3223 ObDereferenceObject(pdesk
);
3229 pdeskOld
= pti
->rpdesk
;
3230 if (pti
->pcti
!= &pti
->cti
)
3231 pctiOld
= pti
->pcti
;
3238 pti
->rpdesk
= pdesk
;
3239 pti
->hdesk
= hDesktop
;
3240 pti
->pDeskInfo
= pti
->rpdesk
->pDeskInfo
;
3241 pti
->pcti
= pctiNew
;
3243 pci
->ulClientDelta
= DesktopHeapGetUserDelta();
3244 pci
->pDeskInfo
= (PVOID
)((ULONG_PTR
)pti
->pDeskInfo
- pci
->ulClientDelta
);
3245 pci
->pClientThreadInfo
= (PVOID
)((ULONG_PTR
)pti
->pcti
- pci
->ulClientDelta
);
3247 /* initialize the new pcti */
3248 if (pctiOld
!= NULL
)
3250 RtlCopyMemory(pctiNew
, pctiOld
, sizeof(CLIENTTHREADINFO
));
3254 RtlZeroMemory(pctiNew
, sizeof(CLIENTTHREADINFO
));
3255 pci
->fsHooks
= pti
->fsHooks
;
3256 pci
->dwTIFlags
= pti
->TIF_flags
;
3263 pti
->pDeskInfo
= NULL
;
3264 pti
->pcti
= &pti
->cti
; // Always point inside so there will be no crash when posting or sending msg's!
3265 pci
->ulClientDelta
= 0;
3266 pci
->pDeskInfo
= NULL
;
3267 pci
->pClientThreadInfo
= NULL
;
3270 /* clean up the old desktop */
3271 if (pdeskOld
!= NULL
)
3273 RemoveEntryList(&pti
->PtiLink
);
3274 if (pctiOld
) DesktopHeapFree(pdeskOld
, pctiOld
);
3275 IntUnmapDesktopView(pdeskOld
);
3276 ObDereferenceObject(pdeskOld
);
3281 InsertTailList(&pdesk
->PtiList
, &pti
->PtiLink
);
3284 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti
, pti
->ppi
, pdeskOld
, pdesk
);
3290 * NtUserSetThreadDesktop
3297 NtUserSetThreadDesktop(HDESK hDesktop
)
3301 UserEnterExclusive();
3303 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
3304 // here too and set the NT error level. Q. Is it necessary to have the validation
3305 // in IntSetThreadDesktop? Is it needed there too?
3306 if (hDesktop
|| (!hDesktop
&& PsGetCurrentProcess() == gpepCSRSS
))
3307 ret
= IntSetThreadDesktop(hDesktop
, FALSE
);