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