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