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