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