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