xref: /reactos/win32ss/user/ntuser/desktop.c (revision 6ca6088f)
1 /*
2  *  COPYRIGHT:        See COPYING in the top level directory
3  *  PROJECT:          ReactOS Win32k subsystem
4  *  PURPOSE:          Desktops
5  *  FILE:             subsystems/win32/win32k/ntuser/desktop.c
6  *  PROGRAMMER:       Casper S. Hornstrup (chorns@users.sourceforge.net)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserDesktop);
13 
14 #include <reactos/buildno.h>
15 
16 static NTSTATUS
17 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta);
18 
19 static NTSTATUS
20 IntMapDesktopView(IN PDESKTOP pdesk);
21 
22 static NTSTATUS
23 IntUnmapDesktopView(IN PDESKTOP pdesk);
24 
25 static VOID
26 IntFreeDesktopHeap(IN PDESKTOP pdesk);
27 
28 /* GLOBALS *******************************************************************/
29 
30 /* These can be changed via registry settings.
31  * Default values (interactive desktop / non-interactive desktop):
32  * Windows 2003 x86: 3 MB / 512 KB (Disconnect: 64 KB, Winlogon: 128 KB)
33  * Windows 7 x86: 12 MB / 512 KB
34  * Windows 7 x64: 20 MB / 768 KB
35  * Windows 10 x64: 20 MB / 4 MB
36  * See:
37  * - https://dbmentors.blogspot.com/2011/09/desktop-heap-overview.html
38  * - https://www.ibm.com/support/pages/using-microsoft-desktop-heap-monitor-dheapmon-determine-desktop-heap-space-and-troubleshoot-filenet-p8-and-image-services-issues
39  * - https://www.betaarchive.com/wiki/index.php?title=Microsoft_KB_Archive/184802
40  * - https://kb.firedaemon.com/support/solutions/articles/4000086192-windows-service-quota-limits-and-desktop-heap-exhaustion
41  * - https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/desktop-heap-limitation-out-of-memory
42  */
43 #ifdef _WIN64
44 DWORD gdwDesktopSectionSize = 20 * 1024; // 20 MB (Windows 7 style)
45 #else
46 DWORD gdwDesktopSectionSize = 3 * 1024; // 3 MB (Windows 2003 style)
47 #endif
48 DWORD gdwNOIOSectionSize    = 128;
49 DWORD gdwWinlogonSectionSize = 128;
50 
51 /* Currently active desktop */
52 PDESKTOP gpdeskInputDesktop = NULL;
53 HDC ScreenDeviceContext = NULL;
54 PTHREADINFO gptiDesktopThread = NULL;
55 HCURSOR gDesktopCursor = NULL;
56 PKEVENT gpDesktopThreadStartedEvent = NULL;
57 
58 /* OBJECT CALLBACKS **********************************************************/
59 
60 NTSTATUS
61 APIENTRY
62 IntDesktopObjectParse(IN PVOID ParseObject,
63                       IN PVOID ObjectType,
64                       IN OUT PACCESS_STATE AccessState,
65                       IN KPROCESSOR_MODE AccessMode,
66                       IN ULONG Attributes,
67                       IN OUT PUNICODE_STRING CompleteName,
68                       IN OUT PUNICODE_STRING RemainingName,
69                       IN OUT PVOID Context OPTIONAL,
70                       IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
71                       OUT PVOID *Object)
72 {
73     NTSTATUS Status;
74     PDESKTOP Desktop;
75     OBJECT_ATTRIBUTES ObjectAttributes;
76     PLIST_ENTRY NextEntry, ListHead;
77     PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
78     UNICODE_STRING DesktopName;
79     PBOOLEAN pContext = (PBOOLEAN) Context;
80 
81     if (pContext)
82         *pContext = FALSE;
83 
84     /* Set the list pointers and loop the window station */
85     ListHead = &WinStaObject->DesktopListHead;
86     NextEntry = ListHead->Flink;
87     while (NextEntry != ListHead)
88     {
89         /* Get the current desktop */
90         Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
91 
92         /* Get the desktop name */
93         ASSERT(Desktop->pDeskInfo != NULL);
94         RtlInitUnicodeString(&DesktopName, Desktop->pDeskInfo->szDesktopName);
95 
96         /* Compare the name */
97         if (RtlEqualUnicodeString(RemainingName,
98                                   &DesktopName,
99                                   (Attributes & OBJ_CASE_INSENSITIVE) != 0))
100         {
101             /* We found a match. Did this come from a create? */
102             if (Context)
103             {
104                 /* Unless OPEN_IF was given, fail with an error */
105                 if (!(Attributes & OBJ_OPENIF))
106                 {
107                     /* Name collision */
108                     return STATUS_OBJECT_NAME_COLLISION;
109                 }
110                 else
111                 {
112                     /* Otherwise, return with a warning only */
113                     Status = STATUS_OBJECT_NAME_EXISTS;
114                 }
115             }
116             else
117             {
118                 /* This was a real open, so this is OK */
119                 Status = STATUS_SUCCESS;
120             }
121 
122             /* Reference the desktop and return it */
123             ObReferenceObject(Desktop);
124             *Object = Desktop;
125             return Status;
126         }
127 
128         /* Go to the next desktop */
129         NextEntry = NextEntry->Flink;
130     }
131 
132     /* If we got here but this isn't a create, just fail */
133     if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
134 
135     /* Create the desktop object */
136     InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
137     Status = ObCreateObject(KernelMode,
138                             ExDesktopObjectType,
139                             &ObjectAttributes,
140                             KernelMode,
141                             NULL,
142                             sizeof(DESKTOP),
143                             0,
144                             0,
145                             (PVOID*)&Desktop);
146     if (!NT_SUCCESS(Status)) return Status;
147 
148     /* Assign security to the desktop we have created */
149     Status = IntAssignDesktopSecurityOnParse(WinStaObject, Desktop, AccessState);
150     if (!NT_SUCCESS(Status))
151     {
152         ObDereferenceObject(Desktop);
153         return Status;
154     }
155 
156     /* Initialize the desktop */
157     Status = UserInitializeDesktop(Desktop, RemainingName, WinStaObject);
158     if (!NT_SUCCESS(Status))
159     {
160         ObDereferenceObject(Desktop);
161         return Status;
162     }
163 
164     /* Set the desktop object and return success */
165     *Object = Desktop;
166     *pContext = TRUE;
167     return STATUS_SUCCESS;
168 }
169 
170 NTSTATUS
171 NTAPI
172 IntDesktopObjectDelete(
173     _In_ PVOID Parameters)
174 {
175     PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
176     PDESKTOP pdesk = (PDESKTOP)DeleteParameters->Object;
177 
178     TRACE("Deleting desktop object 0x%p\n", pdesk);
179 
180     if (pdesk->pDeskInfo &&
181         pdesk->pDeskInfo->spwnd)
182     {
183         ASSERT(pdesk->pDeskInfo->spwnd->spwndChild == NULL);
184         co_UserDestroyWindow(pdesk->pDeskInfo->spwnd);
185     }
186 
187     if (pdesk->spwndMessage)
188         co_UserDestroyWindow(pdesk->spwndMessage);
189 
190     /* Remove the desktop from the window station's list of associated desktops */
191     RemoveEntryList(&pdesk->ListEntry);
192 
193     /* Free the heap */
194     IntFreeDesktopHeap(pdesk);
195 
196     ObDereferenceObject(pdesk->rpwinstaParent);
197 
198     return STATUS_SUCCESS;
199 }
200 
201 NTSTATUS
202 NTAPI
203 IntDesktopOkToClose(
204     _In_ PVOID Parameters)
205 {
206     PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
207     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
208 
209     if (pti == NULL)
210     {
211         /* This happens when we leak desktop handles */
212         return STATUS_SUCCESS;
213     }
214 
215     /* Do not allow the current desktop or the initial desktop to be closed */
216     if (OkToCloseParameters->Handle == pti->ppi->hdeskStartup ||
217         OkToCloseParameters->Handle == pti->hdesk)
218     {
219         return STATUS_ACCESS_DENIED;
220     }
221 
222     return STATUS_SUCCESS;
223 }
224 
225 NTSTATUS
226 NTAPI
227 IntDesktopObjectOpen(
228     _In_ PVOID Parameters)
229 {
230     NTSTATUS Ret;
231     PWIN32_OPENMETHOD_PARAMETERS OpenParameters = Parameters;
232     PPROCESSINFO ppi = PsGetProcessWin32Process(OpenParameters->Process);
233     if (ppi == NULL)
234         return STATUS_SUCCESS;
235 
236     UserEnterExclusive();
237     Ret = IntMapDesktopView((PDESKTOP)OpenParameters->Object);
238     UserLeave();
239     return Ret;
240 }
241 
242 NTSTATUS
243 NTAPI
244 IntDesktopObjectClose(
245     _In_ PVOID Parameters)
246 {
247     NTSTATUS Ret;
248     PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters = Parameters;
249     PPROCESSINFO ppi = PsGetProcessWin32Process(CloseParameters->Process);
250     if (ppi == NULL)
251     {
252         /* This happens when the process leaks desktop handles.
253          * At this point the PPROCESSINFO is already destroyed */
254         return STATUS_SUCCESS;
255     }
256 
257     UserEnterExclusive();
258     Ret = IntUnmapDesktopView((PDESKTOP)CloseParameters->Object);
259     UserLeave();
260     return Ret;
261 }
262 
263 
264 /* PRIVATE FUNCTIONS **********************************************************/
265 
266 CODE_SEG("INIT")
267 NTSTATUS
268 NTAPI
269 InitDesktopImpl(VOID)
270 {
271     GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
272                                           DESKTOP_WRITE,
273                                           DESKTOP_EXECUTE,
274                                           DESKTOP_ALL_ACCESS};
275 
276     /* Set Desktop Object Attributes */
277     ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
278     ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
279     ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
280 
281     /* Allocate memory for the event structure */
282     gpDesktopThreadStartedEvent = ExAllocatePoolWithTag(NonPagedPool,
283                                                         sizeof(KEVENT),
284                                                         USERTAG_EVENT);
285     if (!gpDesktopThreadStartedEvent)
286     {
287         ERR("Failed to allocate event!\n");
288         return STATUS_NO_MEMORY;
289     }
290 
291     /* Initialize the kernel event */
292     KeInitializeEvent(gpDesktopThreadStartedEvent,
293                       SynchronizationEvent,
294                       FALSE);
295 
296     return STATUS_SUCCESS;
297 }
298 
299 static NTSTATUS
300 GetSystemVersionString(OUT PWSTR pwszzVersion,
301                        IN SIZE_T cchDest,
302                        IN BOOLEAN InSafeMode,
303                        IN BOOLEAN AppendNtSystemRoot)
304 {
305     NTSTATUS Status;
306 
307     RTL_OSVERSIONINFOEXW VerInfo;
308     UNICODE_STRING BuildLabString;
309     UNICODE_STRING CSDVersionString;
310     RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable[] =
311     {
312         {
313             NULL,
314             RTL_QUERY_REGISTRY_DIRECT,
315             L"BuildLab",
316             &BuildLabString,
317             REG_NONE, NULL, 0
318         },
319         {
320             NULL,
321             RTL_QUERY_REGISTRY_DIRECT,
322             L"CSDVersion",
323             &CSDVersionString,
324             REG_NONE, NULL, 0
325         },
326 
327         {0}
328     };
329 
330     WCHAR BuildLabBuffer[256];
331     WCHAR VersionBuffer[256];
332     PWCHAR EndBuffer;
333 
334     VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
335 
336     /*
337      * This call is uniquely used to retrieve the current CSD numbers.
338      * All the rest (major, minor, ...) is either retrieved from the
339      * SharedUserData structure, or from the registry.
340      */
341     RtlGetVersion((PRTL_OSVERSIONINFOW)&VerInfo);
342 
343     /*
344      * - Retrieve the BuildLab string from the registry (set by the kernel).
345      * - In kernel-mode, szCSDVersion is not initialized. Initialize it
346      *   and query its value from the registry.
347      */
348     RtlZeroMemory(BuildLabBuffer, sizeof(BuildLabBuffer));
349     RtlInitEmptyUnicodeString(&BuildLabString,
350                               BuildLabBuffer,
351                               sizeof(BuildLabBuffer));
352     RtlZeroMemory(VerInfo.szCSDVersion, sizeof(VerInfo.szCSDVersion));
353     RtlInitEmptyUnicodeString(&CSDVersionString,
354                               VerInfo.szCSDVersion,
355                               sizeof(VerInfo.szCSDVersion));
356     Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
357                                     L"",
358                                     VersionConfigurationTable,
359                                     NULL,
360                                     NULL);
361     if (!NT_SUCCESS(Status))
362     {
363         /* Indicate nothing is there */
364         BuildLabString.Length = 0;
365         CSDVersionString.Length = 0;
366     }
367     /* NULL-terminate the strings */
368     BuildLabString.Buffer[BuildLabString.Length / sizeof(WCHAR)] = UNICODE_NULL;
369     CSDVersionString.Buffer[CSDVersionString.Length / sizeof(WCHAR)] = UNICODE_NULL;
370 
371     EndBuffer = VersionBuffer;
372     if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString.Length)
373     {
374         /* Print the version string */
375         Status = RtlStringCbPrintfExW(VersionBuffer,
376                                       sizeof(VersionBuffer),
377                                       &EndBuffer,
378                                       NULL,
379                                       0,
380                                       L": %wZ",
381                                       &CSDVersionString);
382         if (!NT_SUCCESS(Status))
383         {
384             /* No version, NULL-terminate the string */
385             *EndBuffer = UNICODE_NULL;
386         }
387     }
388     else
389     {
390         /* No version, NULL-terminate the string */
391         *EndBuffer = UNICODE_NULL;
392     }
393 
394     if (InSafeMode)
395     {
396         /* String for Safe Mode */
397         Status = RtlStringCchPrintfW(pwszzVersion,
398                                      cchDest,
399                                      L"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
400                                      KERNEL_VERSION_STR,
401                                      &BuildLabString,
402                                      SharedUserData->NtMajorVersion,
403                                      SharedUserData->NtMinorVersion,
404                                      (VerInfo.dwBuildNumber & 0xFFFF),
405                                      VersionBuffer);
406 
407         if (AppendNtSystemRoot && NT_SUCCESS(Status))
408         {
409             Status = RtlStringCbPrintfW(VersionBuffer,
410                                         sizeof(VersionBuffer),
411                                         L" - %s\n",
412                                         SharedUserData->NtSystemRoot);
413             if (NT_SUCCESS(Status))
414             {
415                 /* Replace the last newline by a NULL, before concatenating */
416                 EndBuffer = wcsrchr(pwszzVersion, L'\n');
417                 if (EndBuffer) *EndBuffer = UNICODE_NULL;
418 
419                 /* The concatenated string has a terminating newline */
420                 Status = RtlStringCchCatW(pwszzVersion,
421                                           cchDest,
422                                           VersionBuffer);
423                 if (!NT_SUCCESS(Status))
424                 {
425                     /* Concatenation failed, put back the newline */
426                     if (EndBuffer) *EndBuffer = L'\n';
427                 }
428             }
429 
430             /* Override any failures as the NtSystemRoot string is optional */
431             Status = STATUS_SUCCESS;
432         }
433     }
434     else
435     {
436         /* Multi-string for Normal Mode */
437         Status = RtlStringCchPrintfW(pwszzVersion,
438                                      cchDest,
439                                      L"ReactOS Version %S\n"
440                                      L"Build %wZ\n"
441                                      L"Reporting NT %u.%u (Build %u%s)\n",
442                                      KERNEL_VERSION_STR,
443                                      &BuildLabString,
444                                      SharedUserData->NtMajorVersion,
445                                      SharedUserData->NtMinorVersion,
446                                      (VerInfo.dwBuildNumber & 0xFFFF),
447                                      VersionBuffer);
448 
449         if (AppendNtSystemRoot && NT_SUCCESS(Status))
450         {
451             Status = RtlStringCbPrintfW(VersionBuffer,
452                                         sizeof(VersionBuffer),
453                                         L"%s\n",
454                                         SharedUserData->NtSystemRoot);
455             if (NT_SUCCESS(Status))
456             {
457                 Status = RtlStringCchCatW(pwszzVersion,
458                                           cchDest,
459                                           VersionBuffer);
460             }
461 
462             /* Override any failures as the NtSystemRoot string is optional */
463             Status = STATUS_SUCCESS;
464         }
465     }
466 
467     if (!NT_SUCCESS(Status))
468     {
469         /* Fall-back string */
470         Status = RtlStringCchPrintfW(pwszzVersion,
471                                      cchDest,
472                                      L"ReactOS Version %S %wZ\n",
473                                      KERNEL_VERSION_STR,
474                                      &BuildLabString);
475         if (!NT_SUCCESS(Status))
476         {
477             /* General failure, NULL-terminate the string */
478             pwszzVersion[0] = UNICODE_NULL;
479         }
480     }
481 
482     /*
483      * Convert the string separators (newlines) into NULLs
484      * and NULL-terminate the multi-string.
485      */
486     while (*pwszzVersion)
487     {
488         EndBuffer = wcschr(pwszzVersion, L'\n');
489         if (!EndBuffer) break;
490         pwszzVersion = EndBuffer;
491 
492         *pwszzVersion++ = UNICODE_NULL;
493     }
494     *pwszzVersion = UNICODE_NULL;
495 
496     return Status;
497 }
498 
499 
500 /*
501  * IntResolveDesktop
502  *
503  * The IntResolveDesktop function attempts to retrieve valid handles to
504  * a desktop and a window station suitable for the specified process.
505  * The specified desktop path string is used only as a hint for the resolution.
506  *
507  * - If the process is already assigned to a window station and a desktop,
508  *   handles to these objects are returned directly regardless of the specified
509  *   desktop path string. This is what happens when this function is called for
510  *   a process that has been already started and connected to the Win32 USER.
511  *
512  * - If the process is being connected to the Win32 USER, or is in a state
513  *   where a window station is assigned to it but no desktop yet, the desktop
514  *   path string is used as a hint for the resolution.
515  *   A specified window station (if any, otherwise "WinSta0" is used as default)
516  *   is tested for existence and accessibility. If the checks are OK a handle
517  *   to it is returned. Otherwise we either fail (the window station does not
518  *   exist) or, in case a default window station was used, we attempt to open
519  *   or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
520  *   typically what happens when a non-interactive process is started while
521  *   the WinSta0 window station was used as the default one.
522  *   A specified desktop (if any, otherwise "Default" is used as default)
523  *   is then tested for existence on the opened window station.
524  *
525  * - Rules for the choice of the default window station, when none is specified
526  *   in the desktop path:
527  *
528  *   1. By default, a SYSTEM process connects to a non-interactive window
529  *      station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
530  *      or one that has been inherited and that is non-interactive.
531  *      Only when the interactive window station WinSta0 is specified that
532  *      the process can connect to it (e.g. the case of interactive services).
533  *
534  *   2. An interactive process, i.e. a process whose LUID is the same as the
535  *      one assigned to WinSta0 by Winlogon on user logon, connects by default
536  *      to the WinSta0 window station, unless it has inherited from another
537  *      interactive window station (which must be... none other than WinSta0).
538  *
539  *   3. A non-interactive (but not SYSTEM) process connects by default to
540  *      a non-interactive Service-0xXXXX-YYYY$ window station (whose name
541  *      is derived from the process' LUID), or to another non-interactive
542  *      window station that has been inherited.
543  *      Otherwise it may be able connect to the interactive WinSta0 only if
544  *      it has explicit access rights to it.
545  *
546  * Parameters
547  *    Process
548  *       The user process object.
549  *
550  *    DesktopPath
551  *       The desktop path string used as a hint for desktop resolution.
552  *
553  *    bInherit
554  *       Whether or not the returned handles are inheritable.
555  *
556  *    phWinSta
557  *       Pointer to a window station handle.
558  *
559  *    phDesktop
560  *       Pointer to a desktop handle.
561  *
562  * Return Value
563  *    Status code.
564  */
565 
566 NTSTATUS
567 FASTCALL
568 IntResolveDesktop(
569     IN PEPROCESS Process,
570     IN PUNICODE_STRING DesktopPath,
571     IN BOOL bInherit,
572     OUT HWINSTA* phWinSta,
573     OUT HDESK* phDesktop)
574 {
575     NTSTATUS Status;
576     HWINSTA hWinSta = NULL, hWinStaDup = NULL;
577     HDESK hDesktop = NULL, hDesktopDup = NULL;
578     PPROCESSINFO ppi;
579     HANDLE hProcess = NULL;
580     LUID ProcessLuid;
581     USHORT StrSize;
582     SIZE_T MemSize;
583     PSECURITY_DESCRIPTOR ServiceSD;
584     POBJECT_ATTRIBUTES ObjectAttributes = NULL;
585     PUNICODE_STRING ObjectName;
586     UNICODE_STRING WinStaName, DesktopName;
587     const UNICODE_STRING WinSta0Name = RTL_CONSTANT_STRING(L"WinSta0");
588     PWINSTATION_OBJECT WinStaObject;
589     HWINSTA hTempWinSta = NULL;
590     BOOLEAN bUseDefaultWinSta = FALSE;
591     BOOLEAN bInteractive = FALSE;
592     BOOLEAN bAccessAllowed = FALSE;
593 
594     ASSERT(UserIsEnteredExclusive());
595 
596     ASSERT(phWinSta);
597     ASSERT(phDesktop);
598     ASSERT(DesktopPath);
599 
600     *phWinSta  = NULL;
601     *phDesktop = NULL;
602 
603     ppi = PsGetProcessWin32Process(Process);
604     /* ppi is typically NULL for console applications that connect to Win32 USER */
605     if (!ppi) TRACE("IntResolveDesktop: ppi is NULL!\n");
606 
607     if (ppi && ppi->hwinsta != NULL && ppi->hdeskStartup != NULL)
608     {
609         /*
610          * If this process is the current one, just return the cached handles.
611          * Otherwise, open the window station and desktop objects.
612          */
613         if (Process == PsGetCurrentProcess())
614         {
615             hWinSta  = ppi->hwinsta;
616             hDesktop = ppi->hdeskStartup;
617         }
618         else
619         {
620             Status = ObOpenObjectByPointer(ppi->prpwinsta,
621                                            0,
622                                            NULL,
623                                            MAXIMUM_ALLOWED,
624                                            ExWindowStationObjectType,
625                                            UserMode,
626                                            (PHANDLE)&hWinSta);
627             if (!NT_SUCCESS(Status))
628             {
629                 ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi->prpwinsta);
630                 SetLastNtError(Status);
631                 return Status;
632             }
633 
634             Status = ObOpenObjectByPointer(ppi->rpdeskStartup,
635                                            0,
636                                            NULL,
637                                            MAXIMUM_ALLOWED,
638                                            ExDesktopObjectType,
639                                            UserMode,
640                                            (PHANDLE)&hDesktop);
641             if (!NT_SUCCESS(Status))
642             {
643                 ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi->rpdeskStartup);
644                 ObCloseHandle(hWinSta, UserMode);
645                 SetLastNtError(Status);
646                 return Status;
647             }
648         }
649 
650         *phWinSta  = hWinSta;
651         *phDesktop = hDesktop;
652         return STATUS_SUCCESS;
653     }
654 
655     /* We will by default use the default window station and desktop */
656     RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
657     RtlInitEmptyUnicodeString(&DesktopName, NULL, 0);
658 
659     /*
660      * Parse the desktop path string which can be of the form "WinSta\Desktop"
661      * or just "Desktop". In the latter case we use the default window station
662      * on which the process is attached to (or if none, "WinSta0").
663      */
664     if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
665     {
666         DesktopName = *DesktopPath;
667 
668         /* Find the separator */
669         while (DesktopName.Length > 0 && *DesktopName.Buffer &&
670                *DesktopName.Buffer != OBJ_NAME_PATH_SEPARATOR)
671         {
672             DesktopName.Buffer++;
673             DesktopName.Length -= sizeof(WCHAR);
674             DesktopName.MaximumLength -= sizeof(WCHAR);
675         }
676         if (DesktopName.Length > 0)
677         {
678             RtlInitEmptyUnicodeString(&WinStaName, DesktopPath->Buffer,
679                                       DesktopPath->Length - DesktopName.Length);
680             // (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
681             WinStaName.Length = WinStaName.MaximumLength;
682 
683             /* Skip the separator */
684             DesktopName.Buffer++;
685             DesktopName.Length -= sizeof(WCHAR);
686             DesktopName.MaximumLength -= sizeof(WCHAR);
687         }
688         else
689         {
690             RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
691             DesktopName = *DesktopPath;
692         }
693     }
694 
695     TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName, &DesktopName);
696 
697     /* Retrieve the process LUID */
698     Status = GetProcessLuid(NULL, Process, &ProcessLuid);
699     if (!NT_SUCCESS(Status))
700     {
701         ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status);
702         SetLastNtError(Status);
703         return Status;
704     }
705 
706     /*
707      * If this process is not the current one, obtain a temporary handle
708      * to it so that we can perform handles duplication later.
709      */
710     if (Process != PsGetCurrentProcess())
711     {
712         Status = ObOpenObjectByPointer(Process,
713                                        OBJ_KERNEL_HANDLE,
714                                        NULL,
715                                        0,
716                                        *PsProcessType,
717                                        KernelMode,
718                                        &hProcess);
719         if (!NT_SUCCESS(Status))
720         {
721             ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process, Status);
722             SetLastNtError(Status);
723             return Status;
724         }
725         ASSERT(hProcess);
726     }
727 
728     /*
729      * If no window station has been specified, search the process handle table
730      * for inherited window station handles, otherwise use a default one.
731      */
732     if (WinStaName.Buffer == NULL)
733     {
734         /*
735          * We want to find a suitable default window station.
736          * For applications that can be interactive, i.e. that have allowed
737          * access to the single interactive window station on the system,
738          * the default window station is 'WinSta0'.
739          * For applications that cannot be interactive, i.e. that do not have
740          * access to 'WinSta0' (e.g. non-interactive services), the default
741          * window station is 'Service-0xXXXX-YYYY$' (created if needed).
742          * Precedence will however be taken by any inherited window station
743          * that possesses the required interactivity property.
744          */
745         bUseDefaultWinSta = TRUE;
746 
747         /*
748          * Use the default 'WinSta0' window station. Whether we should
749          * use 'Service-0xXXXX-YYYY$' instead will be determined later.
750          */
751         // RtlInitUnicodeString(&WinStaName, L"WinSta0");
752         WinStaName = WinSta0Name;
753 
754         if (ObFindHandleForObject(Process,
755                                   NULL,
756                                   ExWindowStationObjectType,
757                                   NULL,
758                                   (PHANDLE)&hWinSta))
759         {
760             TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta);
761         }
762     }
763 
764     /*
765      * If no desktop has been specified, search the process handle table
766      * for inherited desktop handles, otherwise use the Default desktop.
767      * Note that the inherited desktop that we may use, may not belong
768      * to the window station we will connect to.
769      */
770     if (DesktopName.Buffer == NULL)
771     {
772         /* Use a default desktop name */
773         RtlInitUnicodeString(&DesktopName, L"Default");
774 
775         if (ObFindHandleForObject(Process,
776                                   NULL,
777                                   ExDesktopObjectType,
778                                   NULL,
779                                   (PHANDLE)&hDesktop))
780         {
781             TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop);
782         }
783     }
784 
785 
786     /*
787      * We are going to open either a window station or a desktop.
788      * Even if this operation is done from kernel-mode, we should
789      * "emulate" an opening from user-mode (i.e. using an ObjectAttributes
790      * allocated in user-mode, with AccessMode == UserMode) for the
791      * Object Manager to perform proper access validation to the
792      * window station or desktop.
793      */
794 
795     /*
796      * Estimate the maximum size needed for the window station name
797      * and desktop name to be given to ObjectAttributes->ObjectName.
798      */
799     StrSize = 0;
800 
801     /* Window station name */
802     MemSize = _scwprintf(L"Service-0x%x-%x$", MAXULONG, MAXULONG) * sizeof(WCHAR);
803     MemSize = gustrWindowStationsDir.Length + sizeof(OBJ_NAME_PATH_SEPARATOR)
804               + max(WinStaName.Length, MemSize) + sizeof(UNICODE_NULL);
805     if (MemSize > MAXUSHORT)
806     {
807         ERR("IntResolveDesktop: Window station name length is too long.\n");
808         Status = STATUS_NAME_TOO_LONG;
809         goto Quit;
810     }
811     StrSize = max(StrSize, (USHORT)MemSize);
812 
813     /* Desktop name */
814     MemSize = max(DesktopName.Length + sizeof(UNICODE_NULL), sizeof(L"Default"));
815     StrSize = max(StrSize, (USHORT)MemSize);
816 
817     /* Size for the OBJECT_ATTRIBUTES */
818     MemSize = ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID));
819 
820     /* Add the string size */
821     MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
822     MemSize += StrSize;
823 
824     /* Allocate the memory in user-mode */
825     Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
826                                      (PVOID*)&ObjectAttributes,
827                                      0,
828                                      &MemSize,
829                                      MEM_COMMIT,
830                                      PAGE_READWRITE);
831     if (!NT_SUCCESS(Status))
832     {
833         ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
834         goto Quit;
835     }
836 
837     ObjectName = (PUNICODE_STRING)((ULONG_PTR)ObjectAttributes +
838                      ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID)));
839 
840     RtlInitEmptyUnicodeString(ObjectName,
841                               (PWCHAR)((ULONG_PTR)ObjectName +
842                                   ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
843                               StrSize);
844 
845 
846     /* If we got an inherited window station handle, duplicate and use it */
847     if (hWinSta)
848     {
849         ASSERT(bUseDefaultWinSta);
850 
851         /* Duplicate the handle if it belongs to another process than the current one */
852         if (Process != PsGetCurrentProcess())
853         {
854             ASSERT(hProcess);
855             Status = ZwDuplicateObject(hProcess,
856                                        hWinSta,
857                                        ZwCurrentProcess(),
858                                        (PHANDLE)&hWinStaDup,
859                                        0,
860                                        0,
861                                        DUPLICATE_SAME_ACCESS);
862             if (!NT_SUCCESS(Status))
863             {
864                 ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
865                 /* We will use a default window station */
866                 hWinSta = NULL;
867             }
868             else
869             {
870                 hWinSta = hWinStaDup;
871             }
872         }
873     }
874 
875     /*
876      * If we have an inherited window station, check whether
877      * it is interactive and remember that for later.
878      */
879     if (hWinSta)
880     {
881         ASSERT(bUseDefaultWinSta);
882 
883         /* Reference the inherited window station */
884         Status = ObReferenceObjectByHandle(hWinSta,
885                                            0,
886                                            ExWindowStationObjectType,
887                                            KernelMode,
888                                            (PVOID*)&WinStaObject,
889                                            NULL);
890         if (!NT_SUCCESS(Status))
891         {
892             ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
893             /* We will use a default window station */
894             if (hWinStaDup)
895             {
896                 ASSERT(hWinSta == hWinStaDup);
897                 ObCloseHandle(hWinStaDup, UserMode);
898                 hWinStaDup = NULL;
899             }
900             hWinSta = NULL;
901         }
902         else
903         {
904             ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
905                 ProcessLuid.HighPart, ProcessLuid.LowPart,
906                 WinStaObject->luidUser.HighPart, WinStaObject->luidUser.LowPart);
907 
908             /* Check whether this window station is interactive, and remember it for later */
909             bInteractive = !(WinStaObject->Flags & WSS_NOIO);
910 
911             /* Dereference the window station */
912             ObDereferenceObject(WinStaObject);
913         }
914     }
915 
916     /* Build a valid window station name */
917     Status = RtlStringCbPrintfW(ObjectName->Buffer,
918                                 ObjectName->MaximumLength,
919                                 L"%wZ\\%wZ",
920                                 &gustrWindowStationsDir,
921                                 &WinStaName);
922     if (!NT_SUCCESS(Status))
923     {
924         ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
925         goto Quit;
926     }
927     ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
928 
929     TRACE("Parsed initial window station: '%wZ'\n", ObjectName);
930 
931     /* Try to open the window station */
932     InitializeObjectAttributes(ObjectAttributes,
933                                ObjectName,
934                                OBJ_CASE_INSENSITIVE,
935                                NULL,
936                                NULL);
937     if (bInherit)
938         ObjectAttributes->Attributes |= OBJ_INHERIT;
939 
940     Status = ObOpenObjectByName(ObjectAttributes,
941                                 ExWindowStationObjectType,
942                                 UserMode,
943                                 NULL,
944                                 WINSTA_ACCESS_ALL,
945                                 NULL,
946                                 (PHANDLE)&hTempWinSta);
947     if (!NT_SUCCESS(Status))
948     {
949         ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName, Status);
950     }
951     else
952     {
953         //
954         // FIXME TODO: Perform a window station access check!!
955         // If we fail AND bUseDefaultWinSta == FALSE we just quit.
956         //
957 
958         /*
959          * Check whether we are opening the (single) interactive
960          * window station, and if so, perform an access check.
961          */
962         /* Check whether we are allowed to perform interactions */
963         if (RtlEqualUnicodeString(&WinStaName, &WinSta0Name, TRUE))
964         {
965             LUID SystemLuid = SYSTEM_LUID;
966 
967             /* Interactive window station: check for user LUID */
968             WinStaObject = InputWindowStation;
969 
970             Status = STATUS_ACCESS_DENIED;
971 
972             // TODO: Check also that we compare wrt. window station WinSta0
973             // which is the only one that can be interactive on the system.
974             if (((!bUseDefaultWinSta || bInherit) && RtlEqualLuid(&ProcessLuid, &SystemLuid)) ||
975                  RtlEqualLuid(&ProcessLuid, &WinStaObject->luidUser))
976             {
977                 /* We are interactive on this window station */
978                 bAccessAllowed = TRUE;
979                 Status = STATUS_SUCCESS;
980             }
981         }
982         else
983         {
984             /* Non-interactive window station: we have access since we were able to open it */
985             bAccessAllowed = TRUE;
986             Status = STATUS_SUCCESS;
987         }
988     }
989 
990     /* If we failed, bail out if we were not trying to open the default window station */
991     if (!NT_SUCCESS(Status) && !bUseDefaultWinSta) // if (!bAccessAllowed)
992         goto Quit;
993 
994     if (/* bAccessAllowed && */ bInteractive || !bAccessAllowed)
995     {
996         /*
997          * Close WinSta0 if the inherited window station is interactive so that
998          * we can use it, or we do not have access to the interactive WinSta0.
999          */
1000         ObCloseHandle(hTempWinSta, UserMode);
1001         hTempWinSta = NULL;
1002     }
1003     if (bInteractive == bAccessAllowed)
1004     {
1005         /* Keep using the inherited window station */
1006         NOTHING;
1007     }
1008     else // if (bInteractive != bAccessAllowed)
1009     {
1010         /*
1011          * Close the inherited window station, we will either keep using
1012          * the interactive WinSta0, or use Service-0xXXXX-YYYY$.
1013          */
1014         if (hWinStaDup)
1015         {
1016             ASSERT(hWinSta == hWinStaDup);
1017             ObCloseHandle(hWinStaDup, UserMode);
1018             hWinStaDup = NULL;
1019         }
1020         hWinSta = hTempWinSta; // hTempWinSta is NULL in case bAccessAllowed == FALSE
1021     }
1022 
1023     if (bUseDefaultWinSta)
1024     {
1025         if (hWinSta == NULL && !bInteractive)
1026         {
1027             /* Build a valid window station name from the LUID */
1028             Status = RtlStringCbPrintfW(ObjectName->Buffer,
1029                                         ObjectName->MaximumLength,
1030                                         L"%wZ\\Service-0x%x-%x$",
1031                                         &gustrWindowStationsDir,
1032                                         ProcessLuid.HighPart,
1033                                         ProcessLuid.LowPart);
1034             if (!NT_SUCCESS(Status))
1035             {
1036                 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
1037                 goto Quit;
1038             }
1039             ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1040 
1041             /*
1042              * Set up a security descriptor for the new service's window station.
1043              * A service has an associated window station and desktop. The newly
1044              * created window station and desktop will get this security descriptor
1045              * if such objects weren't created before.
1046              */
1047             Status = IntCreateServiceSecurity(&ServiceSD);
1048             if (!NT_SUCCESS(Status))
1049             {
1050                 ERR("Failed to create a security descriptor for service window station, Status 0x%08lx\n", Status);
1051                 goto Quit;
1052             }
1053 
1054             /*
1055              * Create or open the non-interactive window station.
1056              * NOTE: The non-interactive window station handle is never inheritable.
1057              */
1058             InitializeObjectAttributes(ObjectAttributes,
1059                                        ObjectName,
1060                                        OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
1061                                        NULL,
1062                                        ServiceSD);
1063 
1064             Status = IntCreateWindowStation(&hWinSta,
1065                                             ObjectAttributes,
1066                                             UserMode,
1067                                             KernelMode,
1068                                             MAXIMUM_ALLOWED,
1069                                             0, 0, 0, 0, 0);
1070 
1071             IntFreeSecurityBuffer(ServiceSD);
1072 
1073             if (!NT_SUCCESS(Status))
1074             {
1075                 ASSERT(hWinSta == NULL);
1076                 ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
1077                     ObjectName, Status);
1078                 goto Quit;
1079             }
1080 
1081             //
1082             // FIXME: We might not need to always create or open the "Default"
1083             // desktop on the Service-0xXXXX-YYYY$ window station; we may need
1084             // to use another one....
1085             //
1086 
1087             /* Create or open the Default desktop on the window station */
1088             Status = RtlStringCbCopyW(ObjectName->Buffer,
1089                                       ObjectName->MaximumLength,
1090                                       L"Default");
1091             if (!NT_SUCCESS(Status))
1092             {
1093                 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
1094                 goto Quit;
1095             }
1096             ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1097 
1098             /*
1099              * NOTE: The non-interactive desktop handle is never inheritable.
1100              * The security descriptor is inherited from the newly created
1101              * window station for the desktop.
1102              */
1103             InitializeObjectAttributes(ObjectAttributes,
1104                                        ObjectName,
1105                                        OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
1106                                        hWinSta,
1107                                        NULL);
1108 
1109             Status = IntCreateDesktop(&hDesktop,
1110                                       ObjectAttributes,
1111                                       UserMode,
1112                                       NULL,
1113                                       NULL,
1114                                       0,
1115                                       MAXIMUM_ALLOWED);
1116             if (!NT_SUCCESS(Status))
1117             {
1118                 ASSERT(hDesktop == NULL);
1119                 ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1120                     ObjectName, hWinSta, Status);
1121             }
1122 
1123             goto Quit;
1124         }
1125 /*
1126         if (hWinSta == NULL)
1127         {
1128             Status = STATUS_UNSUCCESSFUL;
1129             goto Quit;
1130         }
1131 */
1132     }
1133 
1134     /*
1135      * If we got an inherited desktop handle, duplicate and use it,
1136      * otherwise open a new desktop.
1137      */
1138     if (hDesktop != NULL)
1139     {
1140         /* Duplicate the handle if it belongs to another process than the current one */
1141         if (Process != PsGetCurrentProcess())
1142         {
1143             ASSERT(hProcess);
1144             Status = ZwDuplicateObject(hProcess,
1145                                        hDesktop,
1146                                        ZwCurrentProcess(),
1147                                        (PHANDLE)&hDesktopDup,
1148                                        0,
1149                                        0,
1150                                        DUPLICATE_SAME_ACCESS);
1151             if (!NT_SUCCESS(Status))
1152             {
1153                 ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status);
1154                 /* We will use a default desktop */
1155                 hDesktop = NULL;
1156             }
1157             else
1158             {
1159                 hDesktop = hDesktopDup;
1160             }
1161         }
1162     }
1163 
1164     if ((hWinSta != NULL) && (hDesktop == NULL))
1165     {
1166         Status = RtlStringCbCopyNW(ObjectName->Buffer,
1167                                    ObjectName->MaximumLength,
1168                                    DesktopName.Buffer,
1169                                    DesktopName.Length);
1170         if (!NT_SUCCESS(Status))
1171         {
1172             ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
1173             goto Quit;
1174         }
1175         ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1176 
1177         TRACE("Parsed initial desktop: '%wZ'\n", ObjectName);
1178 
1179         /* Open the desktop object */
1180         InitializeObjectAttributes(ObjectAttributes,
1181                                    ObjectName,
1182                                    OBJ_CASE_INSENSITIVE,
1183                                    hWinSta,
1184                                    NULL);
1185         if (bInherit)
1186             ObjectAttributes->Attributes |= OBJ_INHERIT;
1187 
1188         Status = ObOpenObjectByName(ObjectAttributes,
1189                                     ExDesktopObjectType,
1190                                     UserMode,
1191                                     NULL,
1192                                     DESKTOP_ALL_ACCESS,
1193                                     NULL,
1194                                     (PHANDLE)&hDesktop);
1195         if (!NT_SUCCESS(Status))
1196         {
1197             ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1198                 ObjectName, hWinSta, Status);
1199             goto Quit;
1200         }
1201     }
1202 
1203 Quit:
1204     /* Release the object attributes */
1205     if (ObjectAttributes)
1206     {
1207         MemSize = 0;
1208         ZwFreeVirtualMemory(ZwCurrentProcess(),
1209                             (PVOID*)&ObjectAttributes,
1210                             &MemSize,
1211                             MEM_RELEASE);
1212     }
1213 
1214     /* Close the temporary process handle */
1215     if (hProcess) // if (Process != PsGetCurrentProcess())
1216         ObCloseHandle(hProcess, KernelMode);
1217 
1218     if (NT_SUCCESS(Status))
1219     {
1220         *phWinSta  = hWinSta;
1221         *phDesktop = hDesktop;
1222         return STATUS_SUCCESS;
1223     }
1224     else
1225     {
1226         ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath, Status);
1227 
1228         if (hDesktopDup)
1229             ObCloseHandle(hDesktopDup, UserMode);
1230         if (hWinStaDup)
1231             ObCloseHandle(hWinStaDup, UserMode);
1232 
1233         if (hDesktop)
1234             ObCloseHandle(hDesktop, UserMode);
1235         if (hWinSta)
1236             ObCloseHandle(hWinSta, UserMode);
1237 
1238         SetLastNtError(Status);
1239         return Status;
1240     }
1241 }
1242 
1243 /*
1244  * IntValidateDesktopHandle
1245  *
1246  * Validates the desktop handle.
1247  *
1248  * Remarks
1249  *    If the function succeeds, the handle remains referenced. If the
1250  *    fucntion fails, last error is set.
1251  */
1252 
1253 NTSTATUS FASTCALL
1254 IntValidateDesktopHandle(
1255     HDESK Desktop,
1256     KPROCESSOR_MODE AccessMode,
1257     ACCESS_MASK DesiredAccess,
1258     PDESKTOP *Object)
1259 {
1260     NTSTATUS Status;
1261 
1262     Status = ObReferenceObjectByHandle(Desktop,
1263                                        DesiredAccess,
1264                                        ExDesktopObjectType,
1265                                        AccessMode,
1266                                        (PVOID*)Object,
1267                                        NULL);
1268 
1269     TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
1270           Desktop, *Object, DesiredAccess, Status);
1271 
1272     if (!NT_SUCCESS(Status))
1273         SetLastNtError(Status);
1274 
1275     return Status;
1276 }
1277 
1278 PDESKTOP FASTCALL
1279 IntGetActiveDesktop(VOID)
1280 {
1281     return gpdeskInputDesktop;
1282 }
1283 
1284 /*
1285  * Returns or creates a handle to the desktop object
1286  */
1287 HDESK FASTCALL
1288 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
1289 {
1290     NTSTATUS Status;
1291     HDESK hDesk;
1292 
1293     ASSERT(DesktopObject);
1294 
1295     if (!ObFindHandleForObject(PsGetCurrentProcess(),
1296                                DesktopObject,
1297                                ExDesktopObjectType,
1298                                NULL,
1299                                (PHANDLE)&hDesk))
1300     {
1301         Status = ObOpenObjectByPointer(DesktopObject,
1302                                        0,
1303                                        NULL,
1304                                        0,
1305                                        ExDesktopObjectType,
1306                                        UserMode,
1307                                        (PHANDLE)&hDesk);
1308         if (!NT_SUCCESS(Status))
1309         {
1310             /* Unable to create a handle */
1311             ERR("Unable to create a desktop handle\n");
1312             return NULL;
1313         }
1314     }
1315     else
1316     {
1317         TRACE("Got handle: 0x%p\n", hDesk);
1318     }
1319 
1320     return hDesk;
1321 }
1322 
1323 PUSER_MESSAGE_QUEUE FASTCALL
1324 IntGetFocusMessageQueue(VOID)
1325 {
1326     PDESKTOP pdo = IntGetActiveDesktop();
1327     if (!pdo)
1328     {
1329         TRACE("No active desktop\n");
1330         return(NULL);
1331     }
1332     return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
1333 }
1334 
1335 VOID FASTCALL
1336 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
1337 {
1338     PUSER_MESSAGE_QUEUE Old;
1339     PDESKTOP pdo = IntGetActiveDesktop();
1340     if (!pdo)
1341     {
1342         TRACE("No active desktop\n");
1343         return;
1344     }
1345     if (NewQueue != NULL)
1346     {
1347         if (NewQueue->Desktop != NULL)
1348         {
1349             TRACE("Message Queue already attached to another desktop!\n");
1350             return;
1351         }
1352         IntReferenceMessageQueue(NewQueue);
1353         (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
1354     }
1355     Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
1356     if (Old != NULL)
1357     {
1358         (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
1359         gpqForegroundPrev = Old;
1360         IntDereferenceMessageQueue(Old);
1361     }
1362     // Only one Q can have active foreground even when there are more than one desktop.
1363     if (NewQueue)
1364     {
1365         gpqForeground = pdo->ActiveMessageQueue;
1366     }
1367     else
1368     {
1369         gpqForeground = NULL;
1370         ERR("ptiLastInput is CLEARED!!\n");
1371         ptiLastInput = NULL; // ReactOS hacks... should check for process death.
1372     }
1373 }
1374 
1375 PWND FASTCALL
1376 IntGetThreadDesktopWindow(PTHREADINFO pti)
1377 {
1378     if (!pti) pti = PsGetCurrentThreadWin32Thread();
1379     if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
1380     return NULL;
1381 }
1382 
1383 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
1384 {
1385     if (pWnd->head.rpdesk &&
1386         pWnd->head.rpdesk->pDeskInfo)
1387         return pWnd->head.rpdesk->pDeskInfo->spwnd;
1388     return NULL;
1389 }
1390 
1391 HWND FASTCALL IntGetDesktopWindow(VOID)
1392 {
1393     PDESKTOP pdo = IntGetActiveDesktop();
1394     if (!pdo)
1395     {
1396         TRACE("No active desktop\n");
1397         return NULL;
1398     }
1399     return pdo->DesktopWindow;
1400 }
1401 
1402 // Win: _GetDesktopWindow
1403 PWND FASTCALL UserGetDesktopWindow(VOID)
1404 {
1405     PDESKTOP pdo = IntGetActiveDesktop();
1406 
1407     if (!pdo)
1408     {
1409         TRACE("No active desktop\n");
1410         return NULL;
1411     }
1412     // return pdo->pDeskInfo->spwnd;
1413     return UserGetWindowObject(pdo->DesktopWindow);
1414 }
1415 
1416 HWND FASTCALL IntGetMessageWindow(VOID)
1417 {
1418     PDESKTOP pdo = IntGetActiveDesktop();
1419 
1420     if (!pdo)
1421     {
1422         TRACE("No active desktop\n");
1423         return NULL;
1424     }
1425     return UserHMGetHandle(pdo->spwndMessage);
1426 }
1427 
1428 // Win: _GetMessageWindow
1429 PWND FASTCALL UserGetMessageWindow(VOID)
1430 {
1431     PDESKTOP pdo = IntGetActiveDesktop();
1432 
1433     if (!pdo)
1434     {
1435         TRACE("No active desktop\n");
1436         return NULL;
1437     }
1438     return pdo->spwndMessage;
1439 }
1440 
1441 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
1442 {
1443     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1444     PDESKTOP pdo = pti->rpdesk;
1445     if (NULL == pdo)
1446     {
1447         ERR("Thread doesn't have a desktop\n");
1448         return NULL;
1449     }
1450     return pdo->DesktopWindow;
1451 }
1452 
1453 /* PUBLIC FUNCTIONS ***********************************************************/
1454 
1455 BOOL FASTCALL
1456 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
1457 {
1458     PAINTSTRUCT Ps;
1459     ULONG Value;
1460     //ERR("DesktopWindowProc\n");
1461 
1462     *lResult = 0;
1463 
1464     switch (Msg)
1465     {
1466         case WM_NCCREATE:
1467             if (!Wnd->fnid)
1468             {
1469                 Wnd->fnid = FNID_DESKTOP;
1470             }
1471             *lResult = (LRESULT)TRUE;
1472             return TRUE;
1473 
1474         case WM_CREATE:
1475             Value = HandleToULong(PsGetCurrentProcessId());
1476             // Save Process ID
1477             co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
1478             Value = HandleToULong(PsGetCurrentThreadId());
1479             // Save Thread ID
1480             co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
1481         case WM_CLOSE:
1482             return TRUE;
1483 
1484         case WM_DISPLAYCHANGE:
1485             co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
1486             return TRUE;
1487 
1488         case WM_ERASEBKGND:
1489             IntPaintDesktop((HDC)wParam);
1490             *lResult = 1;
1491             return TRUE;
1492 
1493         case WM_PAINT:
1494         {
1495             if (IntBeginPaint(Wnd, &Ps))
1496             {
1497                 IntEndPaint(Wnd, &Ps);
1498             }
1499             return TRUE;
1500         }
1501         case WM_SYSCOLORCHANGE:
1502             co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1503             return TRUE;
1504 
1505         case WM_SETCURSOR:
1506         {
1507             PCURICON_OBJECT pcurOld, pcurNew;
1508             pcurNew = UserGetCurIconObject(gDesktopCursor);
1509             if (!pcurNew)
1510             {
1511                 return TRUE;
1512             }
1513 
1514             pcurNew->CURSORF_flags |= CURSORF_CURRENT;
1515             pcurOld = UserSetCursor(pcurNew, FALSE);
1516             if (pcurOld)
1517             {
1518                 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
1519                 UserDereferenceObject(pcurOld);
1520             }
1521             return TRUE;
1522         }
1523 
1524         case WM_WINDOWPOSCHANGING:
1525         {
1526             PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
1527             if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
1528             {
1529                 HDESK hdesk = UserOpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
1530                 IntSetThreadDesktop(hdesk, FALSE);
1531             }
1532             break;
1533         }
1534         default:
1535             TRACE("DWP calling IDWP Msg %d\n",Msg);
1536             //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
1537     }
1538     return TRUE; /* We are done. Do not do any callbacks to user mode */
1539 }
1540 
1541 BOOL FASTCALL
1542 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
1543 {
1544     *lResult = 0;
1545 
1546     switch(Msg)
1547     {
1548     case WM_NCCREATE:
1549         pwnd->fnid |= FNID_MESSAGEWND;
1550         *lResult = (LRESULT)TRUE;
1551         break;
1552     case WM_DESTROY:
1553         pwnd->fnid |= FNID_DESTROY;
1554         break;
1555     default:
1556         ERR("UMWP calling IDWP\n");
1557         *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE);
1558     }
1559 
1560     return TRUE; /* We are done. Do not do any callbacks to user mode */
1561 }
1562 
1563 VOID NTAPI DesktopThreadMain(VOID)
1564 {
1565     BOOL Ret;
1566     MSG Msg;
1567 
1568     gptiDesktopThread = PsGetCurrentThreadWin32Thread();
1569 
1570     UserEnterExclusive();
1571 
1572     /* Register system classes. This thread does not belong to any desktop so the
1573        classes will be allocated from the shared heap */
1574     UserRegisterSystemClasses();
1575 
1576     KeSetEvent(gpDesktopThreadStartedEvent, IO_NO_INCREMENT, FALSE);
1577 
1578     while (TRUE)
1579     {
1580         Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
1581         if (Ret)
1582         {
1583             IntDispatchMessage(&Msg);
1584         }
1585     }
1586 
1587     UserLeave();
1588 }
1589 
1590 HDC FASTCALL
1591 UserGetDesktopDC(ULONG DcType, BOOL bAltDc, BOOL ValidatehWnd)
1592 {
1593     PWND DesktopObject = 0;
1594     HDC DesktopHDC = 0;
1595 
1596     /* This can be called from GDI/DX, so acquire the USER lock */
1597     UserEnterExclusive();
1598 
1599     if (DcType == DCTYPE_DIRECT)
1600     {
1601         DesktopObject = UserGetDesktopWindow();
1602         DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
1603     }
1604     else
1605     {
1606         PMONITOR pMonitor = UserGetPrimaryMonitor();
1607         DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, bAltDc);
1608     }
1609 
1610     UserLeave();
1611 
1612     return DesktopHDC;
1613 }
1614 
1615 VOID APIENTRY
1616 UserRedrawDesktop(VOID)
1617 {
1618     PWND Window = NULL;
1619     PREGION Rgn;
1620 
1621     Window = UserGetDesktopWindow();
1622     Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
1623 
1624     IntInvalidateWindows(Window,
1625                          Rgn,
1626                          RDW_FRAME | RDW_ERASE |
1627                          RDW_INVALIDATE | RDW_ALLCHILDREN);
1628 
1629     REGION_Delete(Rgn);
1630 }
1631 
1632 
1633 NTSTATUS FASTCALL
1634 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
1635 {
1636     PWND pwnd = Desktop->pDeskInfo->spwnd;
1637     UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
1638     ASSERT(pwnd);
1639 
1640     if (!bRedraw)
1641         flags |= SWP_NOREDRAW;
1642 
1643     co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
1644 
1645     if (bRedraw)
1646         co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
1647 
1648     return STATUS_SUCCESS;
1649 }
1650 
1651 NTSTATUS FASTCALL
1652 IntHideDesktop(PDESKTOP Desktop)
1653 {
1654     PWND DesktopWnd;
1655 
1656     DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
1657     if (! DesktopWnd)
1658     {
1659         return ERROR_INVALID_WINDOW_HANDLE;
1660     }
1661     DesktopWnd->style &= ~WS_VISIBLE;
1662 
1663     return STATUS_SUCCESS;
1664 }
1665 
1666 static
1667 HWND* FASTCALL
1668 UserBuildShellHookHwndList(PDESKTOP Desktop)
1669 {
1670     ULONG entries=0;
1671     PLIST_ENTRY ListEntry;
1672     PSHELL_HOOK_WINDOW Current;
1673     HWND* list;
1674 
1675     /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1676     ListEntry = Desktop->ShellHookWindows.Flink;
1677     while (ListEntry != &Desktop->ShellHookWindows)
1678     {
1679         ListEntry = ListEntry->Flink;
1680         entries++;
1681     }
1682 
1683     if (!entries) return NULL;
1684 
1685     list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
1686     if (list)
1687     {
1688         HWND* cursor = list;
1689 
1690         ListEntry = Desktop->ShellHookWindows.Flink;
1691         while (ListEntry != &Desktop->ShellHookWindows)
1692         {
1693             Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1694             ListEntry = ListEntry->Flink;
1695             *cursor++ = Current->hWnd;
1696         }
1697 
1698         *cursor = NULL; /* Nullterm list */
1699     }
1700 
1701     return list;
1702 }
1703 
1704 /*
1705  * Send the Message to the windows registered for ShellHook
1706  * notifications. The lParam contents depend on the Message. See
1707  * MSDN for more details (RegisterShellHookWindow)
1708  */
1709 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
1710 {
1711     PDESKTOP Desktop = IntGetActiveDesktop();
1712     HWND* HwndList;
1713 
1714     if (!gpsi->uiShellMsg)
1715     {
1716         gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
1717 
1718         TRACE("MsgType = %x\n", gpsi->uiShellMsg);
1719         if (!gpsi->uiShellMsg)
1720             ERR("LastError: %x\n", EngGetLastError());
1721     }
1722 
1723     if (!Desktop)
1724     {
1725         TRACE("IntShellHookNotify: No desktop!\n");
1726         return;
1727     }
1728 
1729     // Allow other devices have a shot at foreground.
1730     if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
1731 
1732     // FIXME: System Tray Support.
1733 
1734     HwndList = UserBuildShellHookHwndList(Desktop);
1735     if (HwndList)
1736     {
1737         HWND* cursor = HwndList;
1738 
1739         for (; *cursor; cursor++)
1740         {
1741             TRACE("Sending notify\n");
1742             UserPostMessage(*cursor,
1743                             gpsi->uiShellMsg,
1744                             Message,
1745                             (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
1746 /*            co_IntPostOrSendMessage(*cursor,
1747                                     gpsi->uiShellMsg,
1748                                     Message,
1749                                     (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1750         }
1751 
1752         ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
1753     }
1754 
1755     if (ISITHOOKED(WH_SHELL))
1756     {
1757         co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
1758     }
1759 }
1760 
1761 /*
1762  * Add the window to the ShellHookWindows list. The windows
1763  * on that list get notifications that are important to shell
1764  * type applications.
1765  *
1766  * TODO: Validate the window? I'm not sure if sending these messages to
1767  * an unsuspecting application that is not your own is a nice thing to do.
1768  */
1769 BOOL IntRegisterShellHookWindow(HWND hWnd)
1770 {
1771     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1772     PDESKTOP Desktop = pti->rpdesk;
1773     PSHELL_HOOK_WINDOW Entry;
1774 
1775     TRACE("IntRegisterShellHookWindow\n");
1776 
1777     /* First deregister the window, so we can be sure it's never twice in the
1778      * list.
1779      */
1780     IntDeRegisterShellHookWindow(hWnd);
1781 
1782     Entry = ExAllocatePoolWithTag(PagedPool,
1783                                   sizeof(SHELL_HOOK_WINDOW),
1784                                   TAG_WINSTA);
1785 
1786     if (!Entry)
1787         return FALSE;
1788 
1789     Entry->hWnd = hWnd;
1790 
1791     InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
1792 
1793     return TRUE;
1794 }
1795 
1796 /*
1797  * Remove the window from the ShellHookWindows list. The windows
1798  * on that list get notifications that are important to shell
1799  * type applications.
1800  */
1801 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
1802 {
1803     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1804     PDESKTOP Desktop = pti->rpdesk;
1805     PLIST_ENTRY ListEntry;
1806     PSHELL_HOOK_WINDOW Current;
1807 
1808     // FIXME: This probably shouldn't happen, but it does
1809     if (Desktop == NULL)
1810     {
1811         Desktop = IntGetActiveDesktop();
1812         if (Desktop == NULL)
1813             return FALSE;
1814     }
1815 
1816     ListEntry = Desktop->ShellHookWindows.Flink;
1817     while (ListEntry != &Desktop->ShellHookWindows)
1818     {
1819         Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1820         ListEntry = ListEntry->Flink;
1821         if (Current->hWnd == hWnd)
1822         {
1823             RemoveEntryList(&Current->ListEntry);
1824             ExFreePoolWithTag(Current, TAG_WINSTA);
1825             return TRUE;
1826         }
1827     }
1828 
1829     return FALSE;
1830 }
1831 
1832 static VOID
1833 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
1834 {
1835     /* FIXME: Disable until unmapping works in mm */
1836 #if 0
1837     if (Desktop->pheapDesktop != NULL)
1838     {
1839         MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
1840         Desktop->pheapDesktop = NULL;
1841     }
1842 
1843     if (Desktop->hsectionDesktop != NULL)
1844     {
1845         ObDereferenceObject(Desktop->hsectionDesktop);
1846         Desktop->hsectionDesktop = NULL;
1847     }
1848 #endif
1849 }
1850 
1851 BOOL FASTCALL
1852 IntPaintDesktop(HDC hDC)
1853 {
1854     static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize!
1855 
1856     RECTL Rect;
1857     HBRUSH DesktopBrush, PreviousBrush;
1858     HWND hWndDesktop;
1859     BOOL doPatBlt = TRUE;
1860     PWND WndDesktop;
1861     BOOLEAN InSafeMode;
1862 
1863     if (GdiGetClipBox(hDC, &Rect) == ERROR)
1864         return FALSE;
1865 
1866     hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1867 
1868     WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
1869     if (!WndDesktop)
1870         return FALSE;
1871 
1872     /* Retrieve the current SafeMode state */
1873     InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1874 
1875     if (!InSafeMode)
1876     {
1877         DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1878 
1879         /*
1880          * Paint desktop background
1881          */
1882         if (gspv.hbmWallpaper != NULL)
1883         {
1884             SIZE sz;
1885             int x, y;
1886             int scaledWidth, scaledHeight;
1887             int wallpaperX, wallpaperY, wallpaperWidth, wallpaperHeight;
1888             HDC hWallpaperDC;
1889 
1890             sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
1891             sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
1892 
1893             if (gspv.WallpaperMode == wmFit ||
1894                 gspv.WallpaperMode == wmFill)
1895             {
1896                 int scaleNum, scaleDen;
1897 
1898                 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
1899                 if ((sz.cx * gspv.cyWallpaper) > (sz.cy * gspv.cxWallpaper))
1900                 {
1901                     if (gspv.WallpaperMode == wmFit)
1902                     {
1903                         scaleNum = sz.cy;
1904                         scaleDen = gspv.cyWallpaper;
1905                     }
1906                     else
1907                     {
1908                         scaleNum = sz.cx;
1909                         scaleDen = gspv.cxWallpaper;
1910                     }
1911                 }
1912                 else
1913                 {
1914                     if (gspv.WallpaperMode == wmFit)
1915                     {
1916                         scaleNum = sz.cx;
1917                         scaleDen = gspv.cxWallpaper;
1918                     }
1919                     else
1920                     {
1921                         scaleNum = sz.cy;
1922                         scaleDen = gspv.cyWallpaper;
1923                     }
1924                 }
1925 
1926                 scaledWidth = EngMulDiv(gspv.cxWallpaper, scaleNum, scaleDen);
1927                 scaledHeight = EngMulDiv(gspv.cyWallpaper, scaleNum, scaleDen);
1928 
1929                 if (gspv.WallpaperMode == wmFill)
1930                 {
1931                     wallpaperX = (((scaledWidth - sz.cx) * gspv.cxWallpaper) / (2 * scaledWidth));
1932                     wallpaperY = (((scaledHeight - sz.cy) * gspv.cyWallpaper) / (2 * scaledHeight));
1933 
1934                     wallpaperWidth = (sz.cx * gspv.cxWallpaper) / scaledWidth;
1935                     wallpaperHeight = (sz.cy * gspv.cyWallpaper) / scaledHeight;
1936                 }
1937             }
1938 
1939             if (gspv.WallpaperMode == wmStretch ||
1940                 gspv.WallpaperMode == wmTile ||
1941                 gspv.WallpaperMode == wmFill)
1942             {
1943                 x = 0;
1944                 y = 0;
1945             }
1946             else if (gspv.WallpaperMode == wmFit)
1947             {
1948                 x = (sz.cx - scaledWidth) / 2;
1949                 y = (sz.cy - scaledHeight) / 2;
1950             }
1951             else
1952             {
1953                 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1954                 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
1955                 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
1956             }
1957 
1958             hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1959             if (hWallpaperDC != NULL)
1960             {
1961                 HBITMAP hOldBitmap;
1962 
1963                 /* Fill in the area that the bitmap is not going to cover */
1964                 if (x > 0 || y > 0)
1965                 {
1966                     /* FIXME: Clip out the bitmap
1967                        can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1968                        once we support DSTINVERT */
1969                     PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1970                     NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1971                     NtGdiSelectBrush(hDC, PreviousBrush);
1972                 }
1973 
1974                 /* Do not fill the background after it is painted no matter the size of the picture */
1975                 doPatBlt = FALSE;
1976 
1977                 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1978 
1979                 if (gspv.WallpaperMode == wmStretch)
1980                 {
1981                     if (Rect.right && Rect.bottom)
1982                         NtGdiStretchBlt(hDC,
1983                                         x,
1984                                         y,
1985                                         sz.cx,
1986                                         sz.cy,
1987                                         hWallpaperDC,
1988                                         0,
1989                                         0,
1990                                         gspv.cxWallpaper,
1991                                         gspv.cyWallpaper,
1992                                         SRCCOPY,
1993                                         0);
1994                 }
1995                 else if (gspv.WallpaperMode == wmTile)
1996                 {
1997                     /* Paint the bitmap across the screen then down */
1998                     for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1999                     {
2000                         for (x = 0; x < Rect.right; x += gspv.cxWallpaper)
2001                         {
2002                             NtGdiBitBlt(hDC,
2003                                         x,
2004                                         y,
2005                                         gspv.cxWallpaper,
2006                                         gspv.cyWallpaper,
2007                                         hWallpaperDC,
2008                                         0,
2009                                         0,
2010                                         SRCCOPY,
2011                                         0,
2012                                         0);
2013                         }
2014                     }
2015                 }
2016                 else if (gspv.WallpaperMode == wmFit)
2017                 {
2018                     if (Rect.right && Rect.bottom)
2019                     {
2020                         NtGdiStretchBlt(hDC,
2021                                         x,
2022                                         y,
2023                                         scaledWidth,
2024                                         scaledHeight,
2025                                         hWallpaperDC,
2026                                         0,
2027                                         0,
2028                                         gspv.cxWallpaper,
2029                                         gspv.cyWallpaper,
2030                                         SRCCOPY,
2031                                         0);
2032                     }
2033                 }
2034                 else if (gspv.WallpaperMode == wmFill)
2035                 {
2036                     if (Rect.right && Rect.bottom)
2037                     {
2038                         NtGdiStretchBlt(hDC,
2039                                         x,
2040                                         y,
2041                                         sz.cx,
2042                                         sz.cy,
2043                                         hWallpaperDC,
2044                                         wallpaperX,
2045                                         wallpaperY,
2046                                         wallpaperWidth,
2047                                         wallpaperHeight,
2048                                         SRCCOPY,
2049                                         0);
2050                     }
2051                 }
2052                 else
2053                 {
2054                     NtGdiBitBlt(hDC,
2055                                 x,
2056                                 y,
2057                                 gspv.cxWallpaper,
2058                                 gspv.cyWallpaper,
2059                                 hWallpaperDC,
2060                                 0,
2061                                 0,
2062                                 SRCCOPY,
2063                                 0,
2064                                 0);
2065                 }
2066                 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
2067                 NtGdiDeleteObjectApp(hWallpaperDC);
2068             }
2069         }
2070     }
2071     else
2072     {
2073         /* Black desktop background in Safe Mode */
2074         DesktopBrush = StockObjects[BLACK_BRUSH];
2075     }
2076 
2077     /* Background is set to none, clear the screen */
2078     if (doPatBlt)
2079     {
2080         PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
2081         NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
2082         NtGdiSelectBrush(hDC, PreviousBrush);
2083     }
2084 
2085     /*
2086      * Display the system version on the desktop background
2087      */
2088     if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion)
2089     {
2090         NTSTATUS Status;
2091         static WCHAR wszzVersion[1024] = L"\0";
2092 
2093         /* Only used in normal mode */
2094         // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
2095         static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}};
2096         INT i = 0;
2097         SIZE_T len;
2098 
2099         HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL;
2100         COLORREF crText, color_old;
2101         UINT align_old;
2102         INT mode_old;
2103         PDC pdc;
2104 
2105         if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0))
2106         {
2107             Rect.left = Rect.top = 0;
2108             Rect.right  = UserGetSystemMetrics(SM_CXSCREEN);
2109             Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
2110         }
2111         else
2112         {
2113             RECTL_vOffsetRect(&Rect, -Rect.left, -Rect.top);
2114         }
2115 
2116         /*
2117          * Set up the fonts (otherwise use default ones)
2118          */
2119 
2120         /* Font for the principal version string */
2121         hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
2122         /* Font for the secondary version strings */
2123         hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
2124 
2125         if (hFont1)
2126             hOldFont = NtGdiSelectFont(hDC, hFont1);
2127 
2128         if (gspv.hbmWallpaper == NULL)
2129         {
2130             /* Retrieve the brush fill colour */
2131             // TODO: The following code constitutes "GreGetBrushColor".
2132             PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
2133             pdc = DC_LockDc(hDC);
2134             if (pdc)
2135             {
2136                 crText = pdc->eboFill.ulRGBColor;
2137                 DC_UnlockDc(pdc);
2138             }
2139             else
2140             {
2141                 crText = RGB(0, 0, 0);
2142             }
2143             NtGdiSelectBrush(hDC, PreviousBrush);
2144 
2145             /* Adjust text colour according to the brush */
2146             if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
2147                 crText = RGB(0, 0, 0);
2148             else
2149                 crText = RGB(255, 255, 255);
2150         }
2151         else
2152         {
2153             /* Always use white when the text is displayed on top of a wallpaper */
2154             crText = RGB(255, 255, 255);
2155         }
2156 
2157         color_old = IntGdiSetTextColor(hDC, crText);
2158         align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
2159         mode_old  = IntGdiSetBkMode(hDC, TRANSPARENT);
2160 
2161         /* Display the system version information */
2162         if (!*wszzVersion)
2163         {
2164             Status = GetSystemVersionString(wszzVersion,
2165                                             ARRAYSIZE(wszzVersion),
2166                                             InSafeMode,
2167                                             g_AlwaysDisplayVersion);
2168             if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
2169             {
2170                 PWCHAR pstr = wszzVersion;
2171                 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
2172                 {
2173                     VerStrs[i].n = lstrlenW(pstr);
2174                     VerStrs[i].lpstr = pstr;
2175                     pstr += (VerStrs[i].n + 1);
2176                 }
2177             }
2178         }
2179         else
2180         {
2181             Status = STATUS_SUCCESS;
2182         }
2183         if (NT_SUCCESS(Status) && *wszzVersion)
2184         {
2185             if (!InSafeMode)
2186             {
2187                 SIZE Size = {0, 0};
2188                 LONG TotalHeight = 0;
2189 
2190                 /* Normal Mode: multiple version information text separated by newlines */
2191                 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
2192 
2193                 /* Compute the heights of the strings */
2194                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2195                 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
2196                 {
2197                     if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
2198                         break;
2199 
2200                     GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
2201                     VerStrs[i].y = Size.cy; // Store the string height
2202                     TotalHeight += Size.cy;
2203 
2204                     /* While the first string was using hFont1, all the others use hFont2 */
2205                     if (hFont2) NtGdiSelectFont(hDC, hFont2);
2206                 }
2207                 /* The total height must not exceed the screen height */
2208                 TotalHeight = min(TotalHeight, Rect.bottom);
2209 
2210                 /* Display the strings */
2211                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2212                 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
2213                 {
2214                     if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
2215                         break;
2216 
2217                     TotalHeight -= VerStrs[i].y;
2218                     GreExtTextOutW(hDC,
2219                                    Rect.right - 5,
2220                                    Rect.bottom - TotalHeight - 5,
2221                                    0, NULL,
2222                                    VerStrs[i].lpstr,
2223                                    VerStrs[i].n,
2224                                    NULL, 0);
2225 
2226                     /* While the first string was using hFont1, all the others use hFont2 */
2227                     if (hFont2) NtGdiSelectFont(hDC, hFont2);
2228                 }
2229             }
2230             else
2231             {
2232                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2233 
2234                 /* Safe Mode: single version information text in top center */
2235                 len = wcslen(wszzVersion);
2236 
2237                 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
2238                 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
2239             }
2240         }
2241 
2242         if (InSafeMode)
2243         {
2244             if (hFont1) NtGdiSelectFont(hDC, hFont1);
2245 
2246             /* Print Safe Mode text in corners */
2247             len = wcslen(s_wszSafeMode);
2248 
2249             IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
2250             GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
2251             IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
2252             GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
2253             IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
2254             GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
2255             IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
2256             GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
2257         }
2258 
2259         IntGdiSetBkMode(hDC, mode_old);
2260         IntGdiSetTextAlign(hDC, align_old);
2261         IntGdiSetTextColor(hDC, color_old);
2262 
2263         if (hFont2)
2264             GreDeleteObject(hFont2);
2265 
2266         if (hFont1)
2267         {
2268             NtGdiSelectFont(hDC, hOldFont);
2269             GreDeleteObject(hFont1);
2270         }
2271     }
2272 
2273     return TRUE;
2274 }
2275 
2276 static NTSTATUS
2277 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
2278 {
2279     static const UNICODE_STRING WinlogonDesktop = RTL_CONSTANT_STRING(L"Winlogon");
2280     PVOID DesktopHeapSystemBase = NULL;
2281     ULONG_PTR HeapSize;
2282     SIZE_T DesktopInfoSize;
2283     ULONG i;
2284 
2285     TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
2286 
2287     RtlZeroMemory(pdesk, sizeof(DESKTOP));
2288 
2289     /* Set desktop size, based on whether the WinSta is interactive or not */
2290     if (pwinsta == InputWindowStation)
2291     {
2292         /* Check if the Desktop is named "Winlogon" */
2293         if (RtlEqualUnicodeString(DesktopName, &WinlogonDesktop, TRUE))
2294         {
2295             HeapSize = gdwWinlogonSectionSize * 1024;
2296         }
2297         else
2298         {
2299             HeapSize = gdwDesktopSectionSize * 1024;
2300         }
2301     }
2302     else
2303     {
2304         HeapSize = gdwNOIOSectionSize * 1024;
2305     }
2306 
2307     /* Link the desktop with the parent window station */
2308     ObReferenceObject(pwinsta);
2309     pdesk->rpwinstaParent = pwinsta;
2310     InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
2311 
2312     /* Create the desktop heap */
2313     pdesk->hsectionDesktop = NULL;
2314     pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
2315                                          &DesktopHeapSystemBase,
2316                                          HeapSize);
2317     if (pdesk->pheapDesktop == NULL)
2318     {
2319         ERR("Failed to create desktop heap!\n");
2320         return STATUS_NO_MEMORY;
2321     }
2322 
2323     /* Create DESKTOPINFO */
2324     DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
2325     pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
2326                                        HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
2327                                        DesktopInfoSize);
2328     if (pdesk->pDeskInfo == NULL)
2329     {
2330         ERR("Failed to create the DESKTOP structure!\n");
2331         return STATUS_NO_MEMORY;
2332     }
2333 
2334     /* Initialize the DESKTOPINFO */
2335     pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
2336     pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
2337     RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
2338                   DesktopName->Buffer,
2339                   DesktopName->Length + sizeof(WCHAR));
2340     for (i = 0; i < NB_HOOKS; i++)
2341     {
2342         InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
2343     }
2344 
2345     InitializeListHead(&pdesk->ShellHookWindows);
2346     InitializeListHead(&pdesk->PtiList);
2347 
2348     return STATUS_SUCCESS;
2349 }
2350 
2351 /* SYSCALLS *******************************************************************/
2352 
2353 /*
2354  * NtUserCreateDesktop
2355  *
2356  * Creates a new desktop.
2357  *
2358  * Parameters
2359  *    poaAttribs
2360  *       Object Attributes.
2361  *
2362  *    lpszDesktopDevice
2363  *       Name of the device.
2364  *
2365  *    pDeviceMode
2366  *       Device Mode.
2367  *
2368  *    dwFlags
2369  *       Interaction flags.
2370  *
2371  *    dwDesiredAccess
2372  *       Requested type of access.
2373  *
2374  *
2375  * Return Value
2376  *    If the function succeeds, the return value is a handle to the newly
2377  *    created desktop. If the specified desktop already exists, the function
2378  *    succeeds and returns a handle to the existing desktop. When you are
2379  *    finished using the handle, call the CloseDesktop function to close it.
2380  *    If the function fails, the return value is NULL.
2381  *
2382  * Status
2383  *    @implemented
2384  */
2385 
2386 NTSTATUS
2387 FASTCALL
2388 IntCreateDesktop(
2389     OUT HDESK* phDesktop,
2390     IN POBJECT_ATTRIBUTES ObjectAttributes,
2391     IN KPROCESSOR_MODE AccessMode,
2392     IN PUNICODE_STRING lpszDesktopDevice OPTIONAL,
2393     IN LPDEVMODEW lpdmw OPTIONAL,
2394     IN DWORD dwFlags,
2395     IN ACCESS_MASK dwDesiredAccess)
2396 {
2397     NTSTATUS Status;
2398     PDESKTOP pdesk = NULL;
2399     HDESK hDesk;
2400     BOOLEAN Context = FALSE;
2401     UNICODE_STRING ClassName;
2402     LARGE_STRING WindowName;
2403     BOOL NoHooks = FALSE;
2404     PWND pWnd = NULL;
2405     CREATESTRUCTW Cs;
2406     PTHREADINFO ptiCurrent;
2407     PCLS pcls;
2408 
2409     TRACE("Enter IntCreateDesktop\n");
2410 
2411     ASSERT(UserIsEnteredExclusive());
2412 
2413     ASSERT(phDesktop);
2414     *phDesktop = NULL;
2415 
2416     ptiCurrent = PsGetCurrentThreadWin32Thread();
2417     ASSERT(ptiCurrent);
2418     ASSERT(gptiDesktopThread);
2419 
2420     /* Turn off hooks when calling any CreateWindowEx from inside win32k */
2421     NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
2422     ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
2423     ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
2424 
2425     /*
2426      * Try to open already existing desktop
2427      */
2428     Status = ObOpenObjectByName(ObjectAttributes,
2429                                 ExDesktopObjectType,
2430                                 AccessMode,
2431                                 NULL,
2432                                 dwDesiredAccess,
2433                                 (PVOID)&Context,
2434                                 (PHANDLE)&hDesk);
2435     if (!NT_SUCCESS(Status))
2436     {
2437         ERR("ObOpenObjectByName failed to open/create desktop\n");
2438         goto Quit;
2439     }
2440 
2441     /* In case the object was not created (eg if it existed), return now */
2442     if (Context == FALSE)
2443     {
2444         TRACE("IntCreateDesktop opened desktop '%wZ'\n", ObjectAttributes->ObjectName);
2445         Status = STATUS_SUCCESS;
2446         goto Quit;
2447     }
2448 
2449     /* Reference the desktop */
2450     Status = ObReferenceObjectByHandle(hDesk,
2451                                        0,
2452                                        ExDesktopObjectType,
2453                                        KernelMode,
2454                                        (PVOID*)&pdesk,
2455                                        NULL);
2456     if (!NT_SUCCESS(Status))
2457     {
2458         ERR("Failed to reference desktop object\n");
2459         goto Quit;
2460     }
2461 
2462     /* Get the desktop window class. The thread desktop does not belong to any desktop
2463      * so the classes created there (including the desktop class) are allocated in the shared heap
2464      * It would cause problems if we used a class that belongs to the caller
2465      */
2466     ClassName.Buffer = WC_DESKTOP;
2467     ClassName.Length = 0;
2468     pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
2469     if (pcls == NULL)
2470     {
2471         ASSERT(FALSE);
2472         Status = STATUS_UNSUCCESSFUL;
2473         goto Quit;
2474     }
2475 
2476     RtlZeroMemory(&WindowName, sizeof(WindowName));
2477     RtlZeroMemory(&Cs, sizeof(Cs));
2478     Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
2479     Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
2480     Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
2481     Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
2482     Cs.style = WS_POPUP|WS_CLIPCHILDREN;
2483     Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2484     Cs.lpszName = (LPCWSTR) &WindowName;
2485     Cs.lpszClass = (LPCWSTR) &ClassName;
2486 
2487     /* Use IntCreateWindow instead of co_UserCreateWindowEx because the later expects a thread with a desktop */
2488     pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
2489     if (pWnd == NULL)
2490     {
2491         ERR("Failed to create desktop window for the new desktop\n");
2492         Status = STATUS_UNSUCCESSFUL;
2493         goto Quit;
2494     }
2495 
2496     pdesk->dwSessionId = PsGetCurrentProcessSessionId();
2497     pdesk->DesktopWindow = UserHMGetHandle(pWnd);
2498     pdesk->pDeskInfo->spwnd = pWnd;
2499     pWnd->fnid = FNID_DESKTOP;
2500 
2501     ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
2502     ClassName.Length = 0;
2503     pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
2504     if (pcls == NULL)
2505     {
2506         ASSERT(FALSE);
2507         Status = STATUS_UNSUCCESSFUL;
2508         goto Quit;
2509     }
2510 
2511     RtlZeroMemory(&WindowName, sizeof(WindowName));
2512     RtlZeroMemory(&Cs, sizeof(Cs));
2513     Cs.cx = Cs.cy = 100;
2514     Cs.style = WS_POPUP|WS_CLIPCHILDREN;
2515     Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2516     Cs.lpszName = (LPCWSTR)&WindowName;
2517     Cs.lpszClass = (LPCWSTR)&ClassName;
2518     pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
2519     if (pWnd == NULL)
2520     {
2521         ERR("Failed to create message window for the new desktop\n");
2522         Status = STATUS_UNSUCCESSFUL;
2523         goto Quit;
2524     }
2525 
2526     pdesk->spwndMessage = pWnd;
2527     pWnd->fnid = FNID_MESSAGEWND;
2528 
2529     /* Now...
2530        if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
2531        Create Tooltip. Saved in DesktopObject->spwndTooltip.
2532        Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
2533        hWndParent are spwndMessage. Use hModuleWin for server side winproc!
2534        The rest is same as message window.
2535        http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
2536     */
2537     Status = STATUS_SUCCESS;
2538 
2539 Quit:
2540     if (pdesk != NULL)
2541     {
2542         ObDereferenceObject(pdesk);
2543     }
2544     if (!NT_SUCCESS(Status) && hDesk != NULL)
2545     {
2546         ObCloseHandle(hDesk, AccessMode);
2547         hDesk = NULL;
2548     }
2549     if (!NoHooks)
2550     {
2551         ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
2552         ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
2553     }
2554 
2555     TRACE("Leave IntCreateDesktop, Status 0x%08lx\n", Status);
2556 
2557     if (NT_SUCCESS(Status))
2558         *phDesktop = hDesk;
2559     else
2560         SetLastNtError(Status);
2561     return Status;
2562 }
2563 
2564 HDESK APIENTRY
2565 NtUserCreateDesktop(
2566     POBJECT_ATTRIBUTES ObjectAttributes,
2567     PUNICODE_STRING lpszDesktopDevice,
2568     LPDEVMODEW lpdmw,
2569     DWORD dwFlags,
2570     ACCESS_MASK dwDesiredAccess)
2571 {
2572     NTSTATUS Status;
2573     HDESK hDesk;
2574     HDESK Ret = NULL;
2575 
2576     TRACE("Enter NtUserCreateDesktop\n");
2577     UserEnterExclusive();
2578 
2579     Status = IntCreateDesktop(&hDesk,
2580                               ObjectAttributes,
2581                               UserMode,
2582                               lpszDesktopDevice,
2583                               lpdmw,
2584                               dwFlags,
2585                               dwDesiredAccess);
2586     if (!NT_SUCCESS(Status))
2587     {
2588         ERR("IntCreateDesktop failed, Status 0x%08lx\n", Status);
2589         // SetLastNtError(Status);
2590         goto Exit; // Return NULL
2591     }
2592 
2593     Ret = hDesk;
2594 
2595 Exit:
2596     TRACE("Leave NtUserCreateDesktop, ret=0x%p\n", Ret);
2597     UserLeave();
2598     return Ret;
2599 }
2600 
2601 /*
2602  * NtUserOpenDesktop
2603  *
2604  * Opens an existing desktop.
2605  *
2606  * Parameters
2607  *    lpszDesktopName
2608  *       Name of the existing desktop.
2609  *
2610  *    dwFlags
2611  *       Interaction flags.
2612  *
2613  *    dwDesiredAccess
2614  *       Requested type of access.
2615  *
2616  * Return Value
2617  *    Handle to the desktop or zero on failure.
2618  *
2619  * Status
2620  *    @implemented
2621  */
2622 
2623 HDESK APIENTRY
2624 NtUserOpenDesktop(
2625     POBJECT_ATTRIBUTES ObjectAttributes,
2626     DWORD dwFlags,
2627     ACCESS_MASK dwDesiredAccess)
2628 {
2629     NTSTATUS Status;
2630     HDESK Desktop;
2631 
2632     Status = ObOpenObjectByName(
2633                  ObjectAttributes,
2634                  ExDesktopObjectType,
2635                  UserMode,
2636                  NULL,
2637                  dwDesiredAccess,
2638                  NULL,
2639                  (HANDLE*)&Desktop);
2640 
2641     if (!NT_SUCCESS(Status))
2642     {
2643         ERR("Failed to open desktop\n");
2644         SetLastNtError(Status);
2645         return NULL;
2646     }
2647 
2648     TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
2649 
2650     return Desktop;
2651 }
2652 
2653 HDESK UserOpenInputDesktop(DWORD dwFlags,
2654                            BOOL fInherit,
2655                            ACCESS_MASK dwDesiredAccess)
2656 {
2657     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2658     NTSTATUS Status;
2659     ULONG HandleAttributes = 0;
2660     HDESK hdesk = NULL;
2661 
2662     if (!gpdeskInputDesktop)
2663     {
2664         return NULL;
2665     }
2666 
2667     if (pti->ppi->prpwinsta != InputWindowStation)
2668     {
2669         ERR("Tried to open input desktop from non interactive winsta!\n");
2670         EngSetLastError(ERROR_INVALID_FUNCTION);
2671         return NULL;
2672     }
2673 
2674     if (fInherit) HandleAttributes = OBJ_INHERIT;
2675 
2676     /* Create a new handle to the object */
2677     Status = ObOpenObjectByPointer(
2678                  gpdeskInputDesktop,
2679                  HandleAttributes,
2680                  NULL,
2681                  dwDesiredAccess,
2682                  ExDesktopObjectType,
2683                  UserMode,
2684                  (PHANDLE)&hdesk);
2685 
2686     if (!NT_SUCCESS(Status))
2687     {
2688         ERR("Failed to open input desktop object\n");
2689         SetLastNtError(Status);
2690     }
2691 
2692     return hdesk;
2693 }
2694 
2695 /*
2696  * NtUserOpenInputDesktop
2697  *
2698  * Opens the input (interactive) desktop.
2699  *
2700  * Parameters
2701  *    dwFlags
2702  *       Interaction flags.
2703  *
2704  *    fInherit
2705  *       Inheritance option.
2706  *
2707  *    dwDesiredAccess
2708  *       Requested type of access.
2709  *
2710  * Return Value
2711  *    Handle to the input desktop or zero on failure.
2712  *
2713  * Status
2714  *    @implemented
2715  */
2716 
2717 HDESK APIENTRY
2718 NtUserOpenInputDesktop(
2719     DWORD dwFlags,
2720     BOOL fInherit,
2721     ACCESS_MASK dwDesiredAccess)
2722 {
2723     HDESK hdesk;
2724 
2725     UserEnterExclusive();
2726     TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n", gpdeskInputDesktop);
2727 
2728     hdesk = UserOpenInputDesktop(dwFlags, fInherit, dwDesiredAccess);
2729 
2730     TRACE("NtUserOpenInputDesktop returning 0x%p\n", hdesk);
2731     UserLeave();
2732     return hdesk;
2733 }
2734 
2735 /*
2736  * NtUserCloseDesktop
2737  *
2738  * Closes a desktop handle.
2739  *
2740  * Parameters
2741  *    hDesktop
2742  *       Handle to the desktop.
2743  *
2744  * Return Value
2745  *   Status
2746  *
2747  * Remarks
2748  *   The desktop handle can be created with NtUserCreateDesktop or
2749  *   NtUserOpenDesktop. This function will fail if any thread in the calling
2750  *   process is using the specified desktop handle or if the handle refers
2751  *   to the initial desktop of the calling process.
2752  *
2753  * Status
2754  *    @implemented
2755  */
2756 
2757 BOOL APIENTRY
2758 NtUserCloseDesktop(HDESK hDesktop)
2759 {
2760     PDESKTOP pdesk;
2761     NTSTATUS Status;
2762     BOOL Ret = FALSE;
2763 
2764     TRACE("NtUserCloseDesktop(0x%p) called\n", hDesktop);
2765     UserEnterExclusive();
2766 
2767     if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
2768     {
2769         ERR("Attempted to close thread desktop\n");
2770         EngSetLastError(ERROR_BUSY);
2771         goto Exit; // Return FALSE
2772     }
2773 
2774     Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
2775     if (!NT_SUCCESS(Status))
2776     {
2777         ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
2778         goto Exit; // Return FALSE
2779     }
2780 
2781     ObDereferenceObject(pdesk);
2782 
2783     Status = ObCloseHandle(hDesktop, UserMode);
2784     if (!NT_SUCCESS(Status))
2785     {
2786         ERR("Failed to close desktop handle 0x%p\n", hDesktop);
2787         SetLastNtError(Status);
2788         goto Exit; // Return FALSE
2789     }
2790 
2791     Ret = TRUE;
2792 
2793 Exit:
2794     TRACE("Leave NtUserCloseDesktop, ret=%i\n", Ret);
2795     UserLeave();
2796     return Ret;
2797 }
2798 
2799 /*
2800  * NtUserPaintDesktop
2801  *
2802  * The NtUserPaintDesktop function fills the clipping region in the
2803  * specified device context with the desktop pattern or wallpaper. The
2804  * function is provided primarily for shell desktops.
2805  *
2806  * Parameters
2807  *    hDC
2808  *       Handle to the device context.
2809  *
2810  * Status
2811  *    @implemented
2812  */
2813 
2814 BOOL APIENTRY
2815 NtUserPaintDesktop(HDC hDC)
2816 {
2817     BOOL Ret;
2818 
2819     UserEnterExclusive();
2820     TRACE("Enter NtUserPaintDesktop\n");
2821 
2822     Ret = IntPaintDesktop(hDC);
2823 
2824     TRACE("Leave NtUserPaintDesktop, ret=%i\n", Ret);
2825     UserLeave();
2826     return Ret;
2827 }
2828 
2829 /*
2830  * NtUserResolveDesktop
2831  *
2832  * The NtUserResolveDesktop function attempts to retrieve valid handles to
2833  * a desktop and a window station suitable for the specified process.
2834  * The specified desktop path string is used only as a hint for the resolution.
2835  *
2836  * See the description of IntResolveDesktop for more details.
2837  *
2838  * Parameters
2839  *    ProcessHandle
2840  *       Handle to a user process.
2841  *
2842  *    DesktopPath
2843  *       The desktop path string used as a hint for desktop resolution.
2844  *
2845  *    bInherit
2846  *       Whether or not the returned handles are inheritable.
2847  *
2848  *    phWinSta
2849  *       Pointer to a window station handle.
2850  *
2851  * Return Value
2852  *    Handle to the desktop (direct return value) and
2853  *    handle to the associated window station (by pointer).
2854  *    NULL in case of failure.
2855  *
2856  * Remarks
2857  *    Callable by CSRSS only.
2858  *
2859  * Status
2860  *    @implemented
2861  */
2862 
2863 HDESK
2864 NTAPI
2865 NtUserResolveDesktop(
2866     IN HANDLE ProcessHandle,
2867     IN PUNICODE_STRING DesktopPath,
2868     IN BOOL bInherit,
2869     OUT HWINSTA* phWinSta)
2870 {
2871     NTSTATUS Status;
2872     PEPROCESS Process;
2873     HWINSTA hWinSta = NULL;
2874     HDESK hDesktop  = NULL;
2875     UNICODE_STRING CapturedDesktopPath;
2876 
2877     /* Allow only the Console Server to perform this operation (via CSRSS) */
2878     if (PsGetCurrentProcess() != gpepCSRSS)
2879         return NULL;
2880 
2881     /* Get the process object the user handle was referencing */
2882     Status = ObReferenceObjectByHandle(ProcessHandle,
2883                                        PROCESS_QUERY_INFORMATION,
2884                                        *PsProcessType,
2885                                        UserMode,
2886                                        (PVOID*)&Process,
2887                                        NULL);
2888     if (!NT_SUCCESS(Status))
2889         return NULL;
2890 
2891     UserEnterExclusive();
2892 
2893     _SEH2_TRY
2894     {
2895         /* Probe the handle pointer */
2896         // ProbeForWriteHandle
2897         ProbeForWrite(phWinSta, sizeof(HWINSTA), sizeof(HWINSTA));
2898     }
2899     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2900     {
2901         Status = _SEH2_GetExceptionCode();
2902         _SEH2_YIELD(goto Quit);
2903     }
2904     _SEH2_END;
2905 
2906     /* Capture the user desktop path string */
2907     Status = ProbeAndCaptureUnicodeString(&CapturedDesktopPath,
2908                                           UserMode,
2909                                           DesktopPath);
2910     if (!NT_SUCCESS(Status))
2911         goto Quit;
2912 
2913     /* Call the internal function */
2914     Status = IntResolveDesktop(Process,
2915                                &CapturedDesktopPath,
2916                                bInherit,
2917                                &hWinSta,
2918                                &hDesktop);
2919     if (!NT_SUCCESS(Status))
2920     {
2921         ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status);
2922         hWinSta  = NULL;
2923         hDesktop = NULL;
2924     }
2925 
2926     _SEH2_TRY
2927     {
2928         /* Return the window station handle */
2929         *phWinSta = hWinSta;
2930     }
2931     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2932     {
2933         Status = _SEH2_GetExceptionCode();
2934 
2935         /* We failed, close the opened desktop and window station */
2936         if (hDesktop) ObCloseHandle(hDesktop, UserMode);
2937         hDesktop = NULL;
2938         if (hWinSta) ObCloseHandle(hWinSta, UserMode);
2939     }
2940     _SEH2_END;
2941 
2942     /* Free the captured string */
2943     ReleaseCapturedUnicodeString(&CapturedDesktopPath, UserMode);
2944 
2945 Quit:
2946     UserLeave();
2947 
2948     /* Dereference the process object */
2949     ObDereferenceObject(Process);
2950 
2951     /* Return the desktop handle */
2952     return hDesktop;
2953 }
2954 
2955 /*
2956  * NtUserSwitchDesktop
2957  *
2958  * Sets the current input (interactive) desktop.
2959  *
2960  * Parameters
2961  *    hDesktop
2962  *       Handle to desktop.
2963  *
2964  * Return Value
2965  *    Status
2966  *
2967  * Status
2968  *    @unimplemented
2969  */
2970 
2971 BOOL APIENTRY
2972 NtUserSwitchDesktop(HDESK hdesk)
2973 {
2974     PDESKTOP pdesk;
2975     NTSTATUS Status;
2976     BOOL bRedrawDesktop;
2977     BOOL Ret = FALSE;
2978 
2979     UserEnterExclusive();
2980     TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
2981 
2982     Status = IntValidateDesktopHandle(hdesk, UserMode, 0, &pdesk);
2983     if (!NT_SUCCESS(Status))
2984     {
2985         ERR("Validation of desktop handle 0x%p failed\n", hdesk);
2986         goto Exit; // Return FALSE
2987     }
2988 
2989     if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
2990     {
2991         ObDereferenceObject(pdesk);
2992         ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2993         goto Exit; // Return FALSE
2994     }
2995 
2996     if (pdesk == gpdeskInputDesktop)
2997     {
2998         ObDereferenceObject(pdesk);
2999         WARN("NtUserSwitchDesktop called for active desktop\n");
3000         Ret = TRUE;
3001         goto Exit;
3002     }
3003 
3004     /*
3005      * Don't allow applications switch the desktop if it's locked, unless the caller
3006      * is the logon application itself
3007      */
3008     if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
3009         gpidLogon != PsGetCurrentProcessId())
3010     {
3011         ObDereferenceObject(pdesk);
3012         ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
3013         goto Exit; // Return FALSE
3014     }
3015 
3016     if (pdesk->rpwinstaParent != InputWindowStation)
3017     {
3018         ObDereferenceObject(pdesk);
3019         ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
3020         goto Exit; // Return FALSE
3021     }
3022 
3023     /* FIXME: Fail if the process is associated with a secured
3024               desktop such as Winlogon or Screen-Saver */
3025     /* FIXME: Connect to input device */
3026 
3027     TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
3028 
3029     bRedrawDesktop = FALSE;
3030 
3031     /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
3032     if (gpdeskInputDesktop != NULL)
3033     {
3034         if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
3035             bRedrawDesktop = TRUE;
3036 
3037         /* Hide the previous desktop window */
3038         IntHideDesktop(gpdeskInputDesktop);
3039     }
3040 
3041     /* Set the active desktop in the desktop's window station. */
3042     InputWindowStation->ActiveDesktop = pdesk;
3043 
3044     /* Set the global state. */
3045     gpdeskInputDesktop = pdesk;
3046 
3047     /* Show the new desktop window */
3048     co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
3049 
3050     TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n", gpdeskInputDesktop);
3051     ObDereferenceObject(pdesk);
3052 
3053     Ret = TRUE;
3054 
3055 Exit:
3056     TRACE("Leave NtUserSwitchDesktop, ret=%i\n", Ret);
3057     UserLeave();
3058     return Ret;
3059 }
3060 
3061 /*
3062  * NtUserGetThreadDesktop
3063  *
3064  * Status
3065  *    @implemented
3066  */
3067 
3068 HDESK APIENTRY
3069 NtUserGetThreadDesktop(DWORD dwThreadId, HDESK hConsoleDesktop)
3070 {
3071     HDESK hDesk;
3072     NTSTATUS Status;
3073     PTHREADINFO pti;
3074     PEPROCESS Process;
3075     PDESKTOP DesktopObject;
3076     OBJECT_HANDLE_INFORMATION HandleInformation;
3077 
3078     UserEnterExclusive();
3079     TRACE("Enter NtUserGetThreadDesktop\n");
3080 
3081     if (!dwThreadId)
3082     {
3083         EngSetLastError(ERROR_INVALID_PARAMETER);
3084         hDesk = NULL;
3085         goto Quit;
3086     }
3087 
3088     /* Validate the Win32 thread and retrieve its information */
3089     pti = IntTID2PTI(UlongToHandle(dwThreadId));
3090     if (pti)
3091     {
3092         /* Get the desktop handle of the thread */
3093         hDesk = pti->hdesk;
3094         Process = pti->ppi->peProcess;
3095     }
3096     else if (hConsoleDesktop)
3097     {
3098         /*
3099          * The thread may belong to a console, so attempt to use the provided
3100          * console desktop handle as a fallback. Otherwise this means that the
3101          * thread is either not Win32 or invalid.
3102          */
3103         hDesk = hConsoleDesktop;
3104         Process = gpepCSRSS;
3105     }
3106     else
3107     {
3108         EngSetLastError(ERROR_INVALID_PARAMETER);
3109         hDesk = NULL;
3110         goto Quit;
3111     }
3112 
3113     if (!hDesk)
3114     {
3115         ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
3116         goto Quit;
3117     }
3118 
3119     if (Process == PsGetCurrentProcess())
3120     {
3121         /*
3122          * Just return the handle, since we queried the desktop handle
3123          * of a thread running in the same context.
3124          */
3125         goto Quit;
3126     }
3127 
3128     /*
3129      * We could just use the cached rpdesk instead of looking up the handle,
3130      * but it may actually be safer to validate the desktop and get a temporary
3131      * reference to it so that it does not disappear under us (e.g. when the
3132      * desktop is being destroyed) during the operation.
3133      */
3134     /*
3135      * Switch into the context of the thread we are trying to get
3136      * the desktop from, so we can use the handle.
3137      */
3138     KeAttachProcess(&Process->Pcb);
3139     Status = ObReferenceObjectByHandle(hDesk,
3140                                        0,
3141                                        ExDesktopObjectType,
3142                                        UserMode,
3143                                        (PVOID*)&DesktopObject,
3144                                        &HandleInformation);
3145     KeDetachProcess();
3146 
3147     if (NT_SUCCESS(Status))
3148     {
3149         /*
3150          * Lookup our handle table if we can find a handle to the desktop object.
3151          * If not, create one.
3152          * QUESTION: Do we really need to create a handle in case it doesn't exist??
3153          */
3154         hDesk = IntGetDesktopObjectHandle(DesktopObject);
3155 
3156         /* All done, we got a valid handle to the desktop */
3157         ObDereferenceObject(DesktopObject);
3158     }
3159     else
3160     {
3161         /* The handle could not be found, there is nothing to get... */
3162         hDesk = NULL;
3163     }
3164 
3165     if (!hDesk)
3166     {
3167         ERR("Could not retrieve or access desktop for thread 0x%x\n", dwThreadId);
3168         EngSetLastError(ERROR_ACCESS_DENIED);
3169     }
3170 
3171 Quit:
3172     TRACE("Leave NtUserGetThreadDesktop, hDesk = 0x%p\n", hDesk);
3173     UserLeave();
3174     return hDesk;
3175 }
3176 
3177 static NTSTATUS
3178 IntUnmapDesktopView(IN PDESKTOP pdesk)
3179 {
3180     PPROCESSINFO ppi;
3181     PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
3182     NTSTATUS Status = STATUS_SUCCESS;
3183 
3184     TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
3185 
3186     ppi = PsGetCurrentProcessWin32Process();
3187 
3188     /*
3189      * Unmap if we're the last thread using the desktop.
3190      * Start the search at the next mapping: skip the first entry
3191      * as it must be the global user heap mapping.
3192      */
3193     PrevLink = &ppi->HeapMappings.Next;
3194     HeapMapping = *PrevLink;
3195     while (HeapMapping != NULL)
3196     {
3197         if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
3198         {
3199             if (--HeapMapping->Count == 0)
3200             {
3201                 *PrevLink = HeapMapping->Next;
3202 
3203                 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
3204                 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
3205                                               HeapMapping->UserMapping);
3206 
3207                 ObDereferenceObject(pdesk);
3208 
3209                 UserHeapFree(HeapMapping);
3210                 break;
3211             }
3212         }
3213 
3214         PrevLink = &HeapMapping->Next;
3215         HeapMapping = HeapMapping->Next;
3216     }
3217 
3218     return Status;
3219 }
3220 
3221 static NTSTATUS
3222 IntMapDesktopView(IN PDESKTOP pdesk)
3223 {
3224     PPROCESSINFO ppi;
3225     PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
3226     PVOID UserBase = NULL;
3227     SIZE_T ViewSize = 0;
3228     LARGE_INTEGER Offset;
3229     NTSTATUS Status;
3230 
3231     TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
3232 
3233     ppi = PsGetCurrentProcessWin32Process();
3234 
3235     /*
3236      * Find out if another thread already mapped the desktop heap.
3237      * Start the search at the next mapping: skip the first entry
3238      * as it must be the global user heap mapping.
3239      */
3240     PrevLink = &ppi->HeapMappings.Next;
3241     HeapMapping = *PrevLink;
3242     while (HeapMapping != NULL)
3243     {
3244         if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
3245         {
3246             HeapMapping->Count++;
3247             return STATUS_SUCCESS;
3248         }
3249 
3250         PrevLink = &HeapMapping->Next;
3251         HeapMapping = HeapMapping->Next;
3252     }
3253 
3254     /* We're the first, map the heap */
3255     Offset.QuadPart = 0;
3256     Status = MmMapViewOfSection(pdesk->hsectionDesktop,
3257                                 PsGetCurrentProcess(),
3258                                 &UserBase,
3259                                 0,
3260                                 0,
3261                                 &Offset,
3262                                 &ViewSize,
3263                                 ViewUnmap,
3264                                 SEC_NO_CHANGE,
3265                                 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
3266     if (!NT_SUCCESS(Status))
3267     {
3268         ERR("Failed to map desktop\n");
3269         return Status;
3270     }
3271 
3272     TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
3273 
3274     /* Add the mapping */
3275     HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
3276     if (HeapMapping == NULL)
3277     {
3278         MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
3279         ERR("UserHeapAlloc() failed!\n");
3280         return STATUS_NO_MEMORY;
3281     }
3282 
3283     HeapMapping->Next = NULL;
3284     HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
3285     HeapMapping->UserMapping = UserBase;
3286     HeapMapping->Limit = ViewSize;
3287     HeapMapping->Count = 1;
3288     *PrevLink = HeapMapping;
3289 
3290     ObReferenceObject(pdesk);
3291 
3292     return STATUS_SUCCESS;
3293 }
3294 
3295 BOOL
3296 IntSetThreadDesktop(IN HDESK hDesktop,
3297                     IN BOOL FreeOnFailure)
3298 {
3299     PDESKTOP pdesk = NULL, pdeskOld;
3300     PTHREADINFO pti;
3301     NTSTATUS Status;
3302     PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
3303     PCLIENTINFO pci;
3304 
3305     ASSERT(NtCurrentTeb());
3306 
3307     TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
3308 
3309     pti = PsGetCurrentThreadWin32Thread();
3310     pci = pti->pClientInfo;
3311 
3312     /* If the caller gave us a desktop, ensure it is valid */
3313     if (hDesktop != NULL)
3314     {
3315         /* Validate the new desktop. */
3316         Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
3317         if (!NT_SUCCESS(Status))
3318         {
3319             ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
3320             return FALSE;
3321         }
3322 
3323         if (pti->rpdesk == pdesk)
3324         {
3325             /* Nothing to do */
3326             ObDereferenceObject(pdesk);
3327             return TRUE;
3328         }
3329     }
3330 
3331     /* Make sure that we don't own any window in the current desktop */
3332     if (!IsListEmpty(&pti->WindowListHead))
3333     {
3334         if (pdesk)
3335             ObDereferenceObject(pdesk);
3336         ERR("Attempted to change thread desktop although the thread has windows!\n");
3337         EngSetLastError(ERROR_BUSY);
3338         return FALSE;
3339     }
3340 
3341     /* Desktop is being re-set so clear out foreground. */
3342     if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
3343     {
3344         // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
3345         IntSetFocusMessageQueue(NULL);
3346     }
3347 
3348     /* Before doing the switch, map the new desktop heap and allocate the new pcti */
3349     if (pdesk != NULL)
3350     {
3351         Status = IntMapDesktopView(pdesk);
3352         if (!NT_SUCCESS(Status))
3353         {
3354             ERR("Failed to map desktop heap!\n");
3355             ObDereferenceObject(pdesk);
3356             SetLastNtError(Status);
3357             return FALSE;
3358         }
3359 
3360         pctiNew = DesktopHeapAlloc(pdesk, sizeof(CLIENTTHREADINFO));
3361         if (pctiNew == NULL)
3362         {
3363             ERR("Failed to allocate new pcti\n");
3364             IntUnmapDesktopView(pdesk);
3365             ObDereferenceObject(pdesk);
3366             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3367             return FALSE;
3368         }
3369     }
3370 
3371     /*
3372      * Processes, in particular Winlogon.exe, that manage window stations
3373      * (especially the interactive WinSta0 window station) and desktops,
3374      * may not be able to connect at startup to a window station and have
3375      * an associated desktop as well, if none exists on the system already.
3376      * Because creating a new window station does not affect the window station
3377      * associated to the process, and because neither by associating a window
3378      * station to the process nor creating a new desktop on it does associate
3379      * a startup desktop to that process, the process has to actually assigns
3380      * one of its threads to a desktop so that it gets automatically an assigned
3381      * startup desktop.
3382      *
3383      * This is what actually happens for Winlogon.exe, which is started without
3384      * any window station and desktop. By creating the first (and therefore
3385      * interactive) WinSta0 window station, then assigning WinSta0 to itself
3386      * and creating the Default desktop on it, and then assigning this desktop
3387      * to its main thread, Winlogon.exe basically does the similar steps that
3388      * would have been done automatically at its startup if there were already
3389      * an existing WinSta0 window station and Default desktop.
3390      *
3391      * Of course all this must not be done if we are a SYSTEM or CSRSS thread.
3392      */
3393     // if (pti->ppi->peProcess != gpepCSRSS)
3394     if (!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
3395         pti->ppi->rpdeskStartup == NULL && hDesktop != NULL)
3396     {
3397         ERR("The process 0x%p '%s' didn't have an assigned startup desktop before, assigning it now!\n",
3398             pti->ppi->peProcess, pti->ppi->peProcess->ImageFileName);
3399 
3400         pti->ppi->hdeskStartup = hDesktop;
3401         pti->ppi->rpdeskStartup = pdesk;
3402     }
3403 
3404     /* free all classes or move them to the shared heap */
3405     if (pti->rpdesk != NULL)
3406     {
3407         if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
3408         {
3409             ERR("Failed to move process classes to shared heap!\n");
3410             if (pdesk)
3411             {
3412                 DesktopHeapFree(pdesk, pctiNew);
3413                 IntUnmapDesktopView(pdesk);
3414                 ObDereferenceObject(pdesk);
3415             }
3416             return FALSE;
3417         }
3418     }
3419 
3420     pdeskOld = pti->rpdesk;
3421     if (pti->pcti != &pti->cti)
3422         pctiOld = pti->pcti;
3423     else
3424         pctiOld = NULL;
3425 
3426     /* do the switch */
3427     if (pdesk != NULL)
3428     {
3429         pti->rpdesk = pdesk;
3430         pti->hdesk = hDesktop;
3431         pti->pDeskInfo = pti->rpdesk->pDeskInfo;
3432         pti->pcti = pctiNew;
3433 
3434         pci->ulClientDelta = DesktopHeapGetUserDelta();
3435         pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
3436         pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
3437 
3438         /* initialize the new pcti */
3439         if (pctiOld != NULL)
3440         {
3441             RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
3442         }
3443         else
3444         {
3445             RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
3446             pci->fsHooks = pti->fsHooks;
3447             pci->dwTIFlags = pti->TIF_flags;
3448         }
3449     }
3450     else
3451     {
3452         pti->rpdesk = NULL;
3453         pti->hdesk = NULL;
3454         pti->pDeskInfo = NULL;
3455         pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
3456         pci->ulClientDelta = 0;
3457         pci->pDeskInfo = NULL;
3458         pci->pClientThreadInfo = NULL;
3459     }
3460 
3461     /* clean up the old desktop */
3462     if (pdeskOld != NULL)
3463     {
3464         RemoveEntryList(&pti->PtiLink);
3465         if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
3466         IntUnmapDesktopView(pdeskOld);
3467         ObDereferenceObject(pdeskOld);
3468     }
3469 
3470     if (pdesk)
3471     {
3472         InsertTailList(&pdesk->PtiList, &pti->PtiLink);
3473     }
3474 
3475     TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
3476 
3477     return TRUE;
3478 }
3479 
3480 /*
3481  * NtUserSetThreadDesktop
3482  *
3483  * Status
3484  *    @implemented
3485  */
3486 
3487 BOOL APIENTRY
3488 NtUserSetThreadDesktop(HDESK hDesktop)
3489 {
3490     BOOL ret = FALSE;
3491 
3492     UserEnterExclusive();
3493 
3494     // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
3495     // here too and set the NT error level. Q. Is it necessary to have the validation
3496     // in IntSetThreadDesktop? Is it needed there too?
3497     if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
3498         ret = IntSetThreadDesktop(hDesktop, FALSE);
3499 
3500     UserLeave();
3501 
3502     return ret;
3503 }
3504 
3505 /* EOF */
3506