xref: /reactos/win32ss/user/ntuser/desktop.c (revision 0622ce17)
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 NTSTATUS FASTCALL
451 IntParseDesktopPath(PEPROCESS Process,
452                     PUNICODE_STRING DesktopPath,
453                     HWINSTA *hWinSta,
454                     HDESK *hDesktop)
455 {
456     OBJECT_ATTRIBUTES ObjectAttributes;
457     UNICODE_STRING ObjectName;
458     NTSTATUS Status;
459     WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL;
460 
461     ASSERT(hWinSta);
462     ASSERT(hDesktop);
463     ASSERT(DesktopPath);
464 
465     *hWinSta = NULL;
466     *hDesktop = NULL;
467 
468     if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
469     {
470         /*
471          * Parse the desktop path string which can be in the form "WinSta\Desktop"
472          * or just "Desktop". In latter case WinSta0 will be used.
473          */
474 
475         pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\');
476         if (pwstrDesktop != NULL)
477         {
478             *pwstrDesktop = 0;
479             pwstrDesktop++;
480             pwstrWinsta = DesktopPath->Buffer;
481         }
482         else
483         {
484             pwstrDesktop = DesktopPath->Buffer;
485             pwstrWinsta = NULL;
486         }
487 
488         TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop);
489     }
490 
491 #if 0
492     /* Search the process handle table for (inherited) window station
493        handles, use a more appropriate one than WinSta0 if possible. */
494     if (!ObFindHandleForObject(Process,
495                                NULL,
496                                ExWindowStationObjectType,
497                                NULL,
498                                (PHANDLE)hWinSta))
499 #endif
500     {
501         /* We had no luck searching for opened handles, use WinSta0 now */
502         if (!pwstrWinsta)
503             pwstrWinsta = L"WinSta0";
504     }
505 
506 #if 0
507     /* Search the process handle table for (inherited) desktop
508        handles, use a more appropriate one than Default if possible. */
509     if (!ObFindHandleForObject(Process,
510                                NULL,
511                                ExDesktopObjectType,
512                                NULL,
513                                (PHANDLE)hDesktop))
514 #endif
515     {
516         /* We had no luck searching for opened handles, use Desktop now */
517         if (!pwstrDesktop)
518             pwstrDesktop = L"Default";
519     }
520 
521     if (*hWinSta == NULL)
522     {
523         swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta);
524         RtlInitUnicodeString( &ObjectName, wstrWinstaFullName);
525 
526         TRACE("parsed initial winsta: %wZ\n", &ObjectName);
527 
528         /* Open the window station */
529         InitializeObjectAttributes(&ObjectAttributes,
530                                    &ObjectName,
531                                    OBJ_CASE_INSENSITIVE,
532                                    NULL,
533                                    NULL);
534 
535         Status = ObOpenObjectByName(&ObjectAttributes,
536                                     ExWindowStationObjectType,
537                                     KernelMode,
538                                     NULL,
539                                     WINSTA_ACCESS_ALL,
540                                     NULL,
541                                     (HANDLE*)hWinSta);
542 
543         if (!NT_SUCCESS(Status))
544         {
545             SetLastNtError(Status);
546             ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName );
547             return Status;
548         }
549     }
550 
551     if (*hDesktop == NULL)
552     {
553         RtlInitUnicodeString(&ObjectName, pwstrDesktop);
554 
555         TRACE("parsed initial desktop: %wZ\n", &ObjectName);
556 
557         /* Open the desktop object */
558         InitializeObjectAttributes(&ObjectAttributes,
559                                    &ObjectName,
560                                    OBJ_CASE_INSENSITIVE,
561                                    *hWinSta,
562                                    NULL);
563 
564         Status = ObOpenObjectByName(&ObjectAttributes,
565                                     ExDesktopObjectType,
566                                     KernelMode,
567                                     NULL,
568                                     DESKTOP_ALL_ACCESS,
569                                     NULL,
570                                     (HANDLE*)hDesktop);
571 
572         if (!NT_SUCCESS(Status))
573         {
574             *hDesktop = NULL;
575             NtClose(*hWinSta);
576             *hWinSta = NULL;
577             SetLastNtError(Status);
578             ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName);
579             return Status;
580         }
581     }
582     return STATUS_SUCCESS;
583 }
584 
585 /*
586  * IntValidateDesktopHandle
587  *
588  * Validates the desktop handle.
589  *
590  * Remarks
591  *    If the function succeeds, the handle remains referenced. If the
592  *    fucntion fails, last error is set.
593  */
594 
595 NTSTATUS FASTCALL
596 IntValidateDesktopHandle(
597     HDESK Desktop,
598     KPROCESSOR_MODE AccessMode,
599     ACCESS_MASK DesiredAccess,
600     PDESKTOP *Object)
601 {
602     NTSTATUS Status;
603 
604     Status = ObReferenceObjectByHandle(
605                 Desktop,
606                 DesiredAccess,
607                 ExDesktopObjectType,
608                 AccessMode,
609                 (PVOID*)Object,
610                 NULL);
611 
612     TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
613           Desktop, *Object, DesiredAccess, Status);
614 
615     if (!NT_SUCCESS(Status))
616         SetLastNtError(Status);
617 
618     return Status;
619 }
620 
621 PDESKTOP FASTCALL
622 IntGetActiveDesktop(VOID)
623 {
624     return gpdeskInputDesktop;
625 }
626 
627 /*
628  * Returns or creates a handle to the desktop object
629  */
630 HDESK FASTCALL
631 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
632 {
633     NTSTATUS Status;
634     HDESK Ret;
635 
636     ASSERT(DesktopObject);
637 
638     if (!ObFindHandleForObject(PsGetCurrentProcess(),
639                                DesktopObject,
640                                ExDesktopObjectType,
641                                NULL,
642                                (PHANDLE)&Ret))
643     {
644         Status = ObOpenObjectByPointer(DesktopObject,
645                                        0,
646                                        NULL,
647                                        0,
648                                        ExDesktopObjectType,
649                                        UserMode,
650                                        (PHANDLE)&Ret);
651         if (!NT_SUCCESS(Status))
652         {
653             /* Unable to create a handle */
654             ERR("Unable to create a desktop handle\n");
655             return NULL;
656         }
657     }
658     else
659     {
660         TRACE("Got handle: %p\n", Ret);
661     }
662 
663     return Ret;
664 }
665 
666 PUSER_MESSAGE_QUEUE FASTCALL
667 IntGetFocusMessageQueue(VOID)
668 {
669     PDESKTOP pdo = IntGetActiveDesktop();
670     if (!pdo)
671     {
672         TRACE("No active desktop\n");
673         return(NULL);
674     }
675     return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
676 }
677 
678 VOID FASTCALL
679 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
680 {
681     PUSER_MESSAGE_QUEUE Old;
682     PDESKTOP pdo = IntGetActiveDesktop();
683     if (!pdo)
684     {
685         TRACE("No active desktop\n");
686         return;
687     }
688     if (NewQueue != NULL)
689     {
690         if (NewQueue->Desktop != NULL)
691         {
692             TRACE("Message Queue already attached to another desktop!\n");
693             return;
694         }
695         IntReferenceMessageQueue(NewQueue);
696         (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
697     }
698     Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
699     if (Old != NULL)
700     {
701         (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
702         gpqForegroundPrev = Old;
703         IntDereferenceMessageQueue(Old);
704     }
705     // Only one Q can have active foreground even when there are more than one desktop.
706     if (NewQueue)
707     {
708         gpqForeground = pdo->ActiveMessageQueue;
709     }
710     else
711     {
712         gpqForeground = NULL;
713         ERR("ptiLastInput is CLEARED!!\n");
714         ptiLastInput = NULL; // ReactOS hacks,,,, should check for process death.
715     }
716 }
717 
718 PWND FASTCALL
719 IntGetThreadDesktopWindow(PTHREADINFO pti)
720 {
721     if (!pti) pti = PsGetCurrentThreadWin32Thread();
722     if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
723     return NULL;
724 }
725 
726 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
727 {
728     if (pWnd->head.rpdesk &&
729         pWnd->head.rpdesk->pDeskInfo)
730         return pWnd->head.rpdesk->pDeskInfo->spwnd;
731     return NULL;
732 }
733 
734 HWND FASTCALL IntGetDesktopWindow(VOID)
735 {
736     PDESKTOP pdo = IntGetActiveDesktop();
737     if (!pdo)
738     {
739         TRACE("No active desktop\n");
740         return NULL;
741     }
742     return pdo->DesktopWindow;
743 }
744 
745 PWND FASTCALL UserGetDesktopWindow(VOID)
746 {
747     PDESKTOP pdo = IntGetActiveDesktop();
748 
749     if (!pdo)
750     {
751         TRACE("No active desktop\n");
752         return NULL;
753     }
754     // return pdo->pDeskInfo->spwnd;
755     return UserGetWindowObject(pdo->DesktopWindow);
756 }
757 
758 HWND FASTCALL IntGetMessageWindow(VOID)
759 {
760     PDESKTOP pdo = IntGetActiveDesktop();
761 
762     if (!pdo)
763     {
764         TRACE("No active desktop\n");
765         return NULL;
766     }
767     return pdo->spwndMessage->head.h;
768 }
769 
770 PWND FASTCALL UserGetMessageWindow(VOID)
771 {
772     PDESKTOP pdo = IntGetActiveDesktop();
773 
774     if (!pdo)
775     {
776         TRACE("No active desktop\n");
777         return NULL;
778     }
779     return pdo->spwndMessage;
780 }
781 
782 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
783 {
784     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
785     PDESKTOP pdo = pti->rpdesk;
786     if (NULL == pdo)
787     {
788         ERR("Thread doesn't have a desktop\n");
789         return NULL;
790     }
791     return pdo->DesktopWindow;
792 }
793 
794 /* PUBLIC FUNCTIONS ***********************************************************/
795 
796 BOOL FASTCALL
797 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
798 {
799     PAINTSTRUCT Ps;
800     ULONG Value;
801     //ERR("DesktopWindowProc\n");
802 
803     *lResult = 0;
804 
805     switch (Msg)
806     {
807         case WM_NCCREATE:
808             if (!Wnd->fnid)
809             {
810                 Wnd->fnid = FNID_DESKTOP;
811             }
812             *lResult = (LRESULT)TRUE;
813             return TRUE;
814 
815         case WM_CREATE:
816             Value = HandleToULong(PsGetCurrentProcessId());
817             // Save Process ID
818             co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
819             Value = HandleToULong(PsGetCurrentThreadId());
820             // Save Thread ID
821             co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
822         case WM_CLOSE:
823             return TRUE;
824 
825         case WM_DISPLAYCHANGE:
826             co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
827             return TRUE;
828 
829         case WM_ERASEBKGND:
830             IntPaintDesktop((HDC)wParam);
831             *lResult = 1;
832             return TRUE;
833 
834         case WM_PAINT:
835         {
836             if (IntBeginPaint(Wnd, &Ps))
837             {
838                 IntEndPaint(Wnd, &Ps);
839             }
840             return TRUE;
841         }
842         case WM_SYSCOLORCHANGE:
843             co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
844             return TRUE;
845 
846         case WM_SETCURSOR:
847         {
848             PCURICON_OBJECT pcurOld, pcurNew;
849             pcurNew = UserGetCurIconObject(gDesktopCursor);
850             if (!pcurNew)
851             {
852                 return TRUE;
853             }
854 
855             pcurNew->CURSORF_flags |= CURSORF_CURRENT;
856             pcurOld = UserSetCursor(pcurNew, FALSE);
857             if (pcurOld)
858             {
859                 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
860                 UserDereferenceObject(pcurOld);
861             }
862             return TRUE;
863         }
864 
865         case WM_WINDOWPOSCHANGING:
866         {
867             PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
868             if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
869             {
870                 HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop);
871                 IntSetThreadDesktop(hdesk, FALSE);
872             }
873             break;
874         }
875         default:
876             TRACE("DWP calling IDWP Msg %d\n",Msg);
877             //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
878     }
879     return TRUE; /* We are done. Do not do any callbacks to user mode */
880 }
881 
882 BOOL FASTCALL
883 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
884 {
885     *lResult = 0;
886 
887     switch(Msg)
888     {
889     case WM_NCCREATE:
890         pwnd->fnid |= FNID_MESSAGEWND;
891         *lResult = (LRESULT)TRUE;
892         break;
893     case WM_DESTROY:
894         pwnd->fnid |= FNID_DESTROY;
895         break;
896     default:
897         ERR("UMWP calling IDWP\n");
898         *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE);
899     }
900 
901     return TRUE; /* We are done. Do not do any callbacks to user mode */
902 }
903 
904 VOID NTAPI DesktopThreadMain(VOID)
905 {
906     BOOL Ret;
907     MSG Msg;
908 
909     gptiDesktopThread = PsGetCurrentThreadWin32Thread();
910 
911     UserEnterExclusive();
912 
913     /* Register system classes. This thread does not belong to any desktop so the
914        classes will be allocated from the shared heap */
915     UserRegisterSystemClasses();
916 
917     while (TRUE)
918     {
919         Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
920         if (Ret)
921         {
922             IntDispatchMessage(&Msg);
923         }
924     }
925 
926     UserLeave();
927 }
928 
929 HDC FASTCALL
930 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
931 {
932     PWND DesktopObject = 0;
933     HDC DesktopHDC = 0;
934 
935     /* This can be called from GDI/DX, so acquire the USER lock */
936     UserEnterExclusive();
937 
938     if (DcType == DC_TYPE_DIRECT)
939     {
940         DesktopObject = UserGetDesktopWindow();
941         DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
942     }
943     else
944     {
945         PMONITOR pMonitor = UserGetPrimaryMonitor();
946         DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
947     }
948 
949     UserLeave();
950 
951     return DesktopHDC;
952 }
953 
954 VOID APIENTRY
955 UserRedrawDesktop(VOID)
956 {
957     PWND Window = NULL;
958     PREGION Rgn;
959 
960     Window = UserGetDesktopWindow();
961     Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
962 
963     IntInvalidateWindows( Window,
964                              Rgn,
965                        RDW_FRAME |
966                        RDW_ERASE |
967                   RDW_INVALIDATE |
968                  RDW_ALLCHILDREN);
969 
970     REGION_Delete(Rgn);
971 }
972 
973 
974 NTSTATUS FASTCALL
975 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
976 {
977     PWND pwnd = Desktop->pDeskInfo->spwnd;
978     UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
979     ASSERT(pwnd);
980 
981     if (!bRedraw)
982         flags |= SWP_NOREDRAW;
983 
984     co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
985 
986     if (bRedraw)
987         co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
988 
989     return STATUS_SUCCESS;
990 }
991 
992 NTSTATUS FASTCALL
993 IntHideDesktop(PDESKTOP Desktop)
994 {
995     PWND DesktopWnd;
996 
997     DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
998     if (! DesktopWnd)
999     {
1000         return ERROR_INVALID_WINDOW_HANDLE;
1001     }
1002     DesktopWnd->style &= ~WS_VISIBLE;
1003 
1004     return STATUS_SUCCESS;
1005 }
1006 
1007 static
1008 HWND* FASTCALL
1009 UserBuildShellHookHwndList(PDESKTOP Desktop)
1010 {
1011     ULONG entries=0;
1012     PLIST_ENTRY ListEntry;
1013     PSHELL_HOOK_WINDOW Current;
1014     HWND* list;
1015 
1016     /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1017     ListEntry = Desktop->ShellHookWindows.Flink;
1018     while (ListEntry != &Desktop->ShellHookWindows)
1019     {
1020         ListEntry = ListEntry->Flink;
1021         entries++;
1022     }
1023 
1024     if (!entries) return NULL;
1025 
1026     list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
1027     if (list)
1028     {
1029         HWND* cursor = list;
1030 
1031         ListEntry = Desktop->ShellHookWindows.Flink;
1032         while (ListEntry != &Desktop->ShellHookWindows)
1033         {
1034             Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1035             ListEntry = ListEntry->Flink;
1036             *cursor++ = Current->hWnd;
1037         }
1038 
1039         *cursor = NULL; /* Nullterm list */
1040     }
1041 
1042     return list;
1043 }
1044 
1045 /*
1046  * Send the Message to the windows registered for ShellHook
1047  * notifications. The lParam contents depend on the Message. See
1048  * MSDN for more details (RegisterShellHookWindow)
1049  */
1050 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
1051 {
1052     PDESKTOP Desktop = IntGetActiveDesktop();
1053     HWND* HwndList;
1054 
1055     if (!gpsi->uiShellMsg)
1056     {
1057         gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
1058 
1059         TRACE("MsgType = %x\n", gpsi->uiShellMsg);
1060         if (!gpsi->uiShellMsg)
1061             ERR("LastError: %x\n", EngGetLastError());
1062     }
1063 
1064     if (!Desktop)
1065     {
1066         TRACE("IntShellHookNotify: No desktop!\n");
1067         return;
1068     }
1069 
1070     // Allow other devices have a shot at foreground.
1071     if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
1072 
1073     // FIXME: System Tray Support.
1074 
1075     HwndList = UserBuildShellHookHwndList(Desktop);
1076     if (HwndList)
1077     {
1078         HWND* cursor = HwndList;
1079 
1080         for (; *cursor; cursor++)
1081         {
1082             TRACE("Sending notify\n");
1083             UserPostMessage(*cursor,
1084                             gpsi->uiShellMsg,
1085                             Message,
1086                             (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
1087 /*            co_IntPostOrSendMessage(*cursor,
1088                                     gpsi->uiShellMsg,
1089                                     Message,
1090                                     (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1091         }
1092 
1093         ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
1094     }
1095 
1096     if (ISITHOOKED(WH_SHELL))
1097     {
1098         co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
1099     }
1100 }
1101 
1102 /*
1103  * Add the window to the ShellHookWindows list. The windows
1104  * on that list get notifications that are important to shell
1105  * type applications.
1106  *
1107  * TODO: Validate the window? I'm not sure if sending these messages to
1108  * an unsuspecting application that is not your own is a nice thing to do.
1109  */
1110 BOOL IntRegisterShellHookWindow(HWND hWnd)
1111 {
1112     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1113     PDESKTOP Desktop = pti->rpdesk;
1114     PSHELL_HOOK_WINDOW Entry;
1115 
1116     TRACE("IntRegisterShellHookWindow\n");
1117 
1118     /* First deregister the window, so we can be sure it's never twice in the
1119      * list.
1120      */
1121     IntDeRegisterShellHookWindow(hWnd);
1122 
1123     Entry = ExAllocatePoolWithTag(PagedPool,
1124                                   sizeof(SHELL_HOOK_WINDOW),
1125                                   TAG_WINSTA);
1126 
1127     if (!Entry)
1128         return FALSE;
1129 
1130     Entry->hWnd = hWnd;
1131 
1132     InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
1133 
1134     return TRUE;
1135 }
1136 
1137 /*
1138  * Remove the window from the ShellHookWindows list. The windows
1139  * on that list get notifications that are important to shell
1140  * type applications.
1141  */
1142 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
1143 {
1144     PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1145     PDESKTOP Desktop = pti->rpdesk;
1146     PLIST_ENTRY ListEntry;
1147     PSHELL_HOOK_WINDOW Current;
1148 
1149     ListEntry = Desktop->ShellHookWindows.Flink;
1150     while (ListEntry != &Desktop->ShellHookWindows)
1151     {
1152         Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1153         ListEntry = ListEntry->Flink;
1154         if (Current->hWnd == hWnd)
1155         {
1156             RemoveEntryList(&Current->ListEntry);
1157             ExFreePoolWithTag(Current, TAG_WINSTA);
1158             return TRUE;
1159         }
1160     }
1161 
1162     return FALSE;
1163 }
1164 
1165 static VOID
1166 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
1167 {
1168     /* FIXME: Disable until unmapping works in mm */
1169 #if 0
1170     if (Desktop->pheapDesktop != NULL)
1171     {
1172         MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
1173         Desktop->pheapDesktop = NULL;
1174     }
1175 
1176     if (Desktop->hsectionDesktop != NULL)
1177     {
1178         ObDereferenceObject(Desktop->hsectionDesktop);
1179         Desktop->hsectionDesktop = NULL;
1180     }
1181 #endif
1182 }
1183 
1184 BOOL FASTCALL
1185 IntPaintDesktop(HDC hDC)
1186 {
1187     static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize!
1188 
1189     RECTL Rect;
1190     HBRUSH DesktopBrush, PreviousBrush;
1191     HWND hWndDesktop;
1192     BOOL doPatBlt = TRUE;
1193     PWND WndDesktop;
1194     BOOLEAN InSafeMode;
1195 
1196     if (GdiGetClipBox(hDC, &Rect) == ERROR)
1197         return FALSE;
1198 
1199     hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1200 
1201     WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
1202     if (!WndDesktop)
1203         return FALSE;
1204 
1205     /* Retrieve the current SafeMode state */
1206     InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1207 
1208     if (!InSafeMode)
1209     {
1210         DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1211 
1212         /*
1213          * Paint desktop background
1214          */
1215         if (gspv.hbmWallpaper != NULL)
1216         {
1217             SIZE sz;
1218             int x, y;
1219             int scaledWidth, scaledHeight;
1220             int wallpaperX, wallpaperY, wallpaperWidth, wallpaperHeight;
1221             HDC hWallpaperDC;
1222 
1223             sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
1224             sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
1225 
1226             if (gspv.WallpaperMode == wmFit ||
1227                 gspv.WallpaperMode == wmFill)
1228             {
1229                 int scaleNum, scaleDen;
1230 
1231                 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
1232                 if ((sz.cx * gspv.cyWallpaper) > (sz.cy * gspv.cxWallpaper))
1233                 {
1234                     if (gspv.WallpaperMode == wmFit)
1235                     {
1236                         scaleNum = sz.cy;
1237                         scaleDen = gspv.cyWallpaper;
1238                     }
1239                     else
1240                     {
1241                         scaleNum = sz.cx;
1242                         scaleDen = gspv.cxWallpaper;
1243                     }
1244                 }
1245                 else
1246                 {
1247                     if (gspv.WallpaperMode == wmFit)
1248                     {
1249                         scaleNum = sz.cx;
1250                         scaleDen = gspv.cxWallpaper;
1251                     }
1252                     else
1253                     {
1254                         scaleNum = sz.cy;
1255                         scaleDen = gspv.cyWallpaper;
1256                     }
1257                 }
1258 
1259                 scaledWidth = EngMulDiv(gspv.cxWallpaper, scaleNum, scaleDen);
1260                 scaledHeight = EngMulDiv(gspv.cyWallpaper, scaleNum, scaleDen);
1261 
1262                 if (gspv.WallpaperMode == wmFill)
1263                 {
1264                     wallpaperX = (((scaledWidth - sz.cx) * gspv.cxWallpaper) / (2 * scaledWidth));
1265                     wallpaperY = (((scaledHeight - sz.cy) * gspv.cyWallpaper) / (2 * scaledHeight));
1266 
1267                     wallpaperWidth = (sz.cx * gspv.cxWallpaper) / scaledWidth;
1268                     wallpaperHeight = (sz.cy * gspv.cyWallpaper) / scaledHeight;
1269                 }
1270             }
1271 
1272             if (gspv.WallpaperMode == wmStretch ||
1273                 gspv.WallpaperMode == wmTile ||
1274                 gspv.WallpaperMode == wmFill)
1275             {
1276                 x = 0;
1277                 y = 0;
1278             }
1279             else if (gspv.WallpaperMode == wmFit)
1280             {
1281                 x = (sz.cx - scaledWidth) / 2;
1282                 y = (sz.cy - scaledHeight) / 2;
1283             }
1284             else
1285             {
1286                 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1287                 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
1288                 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
1289             }
1290 
1291             hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1292             if (hWallpaperDC != NULL)
1293             {
1294                 HBITMAP hOldBitmap;
1295 
1296                 /* Fill in the area that the bitmap is not going to cover */
1297                 if (x > 0 || y > 0)
1298                 {
1299                     /* FIXME: Clip out the bitmap
1300                        can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1301                        once we support DSTINVERT */
1302                     PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1303                     NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1304                     NtGdiSelectBrush(hDC, PreviousBrush);
1305                 }
1306 
1307                 /* Do not fill the background after it is painted no matter the size of the picture */
1308                 doPatBlt = FALSE;
1309 
1310                 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1311 
1312                 if (gspv.WallpaperMode == wmStretch)
1313                 {
1314                     if (Rect.right && Rect.bottom)
1315                         NtGdiStretchBlt(hDC,
1316                                         x,
1317                                         y,
1318                                         sz.cx,
1319                                         sz.cy,
1320                                         hWallpaperDC,
1321                                         0,
1322                                         0,
1323                                         gspv.cxWallpaper,
1324                                         gspv.cyWallpaper,
1325                                         SRCCOPY,
1326                                         0);
1327                 }
1328                 else if (gspv.WallpaperMode == wmTile)
1329                 {
1330                     /* Paint the bitmap across the screen then down */
1331                     for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1332                     {
1333                         for (x = 0; x < Rect.right; x += gspv.cxWallpaper)
1334                         {
1335                             NtGdiBitBlt(hDC,
1336                                         x,
1337                                         y,
1338                                         gspv.cxWallpaper,
1339                                         gspv.cyWallpaper,
1340                                         hWallpaperDC,
1341                                         0,
1342                                         0,
1343                                         SRCCOPY,
1344                                         0,
1345                                         0);
1346                         }
1347                     }
1348                 }
1349                 else if (gspv.WallpaperMode == wmFit)
1350                 {
1351                     if (Rect.right && Rect.bottom)
1352                     {
1353                         NtGdiStretchBlt(hDC,
1354                                         x,
1355                                         y,
1356                                         scaledWidth,
1357                                         scaledHeight,
1358                                         hWallpaperDC,
1359                                         0,
1360                                         0,
1361                                         gspv.cxWallpaper,
1362                                         gspv.cyWallpaper,
1363                                         SRCCOPY,
1364                                         0);
1365                     }
1366                 }
1367                 else if (gspv.WallpaperMode == wmFill)
1368                 {
1369                     if (Rect.right && Rect.bottom)
1370                     {
1371                         NtGdiStretchBlt(hDC,
1372                                         x,
1373                                         y,
1374                                         sz.cx,
1375                                         sz.cy,
1376                                         hWallpaperDC,
1377                                         wallpaperX,
1378                                         wallpaperY,
1379                                         wallpaperWidth,
1380                                         wallpaperHeight,
1381                                         SRCCOPY,
1382                                         0);
1383                     }
1384                 }
1385                 else
1386                 {
1387                     NtGdiBitBlt(hDC,
1388                                 x,
1389                                 y,
1390                                 gspv.cxWallpaper,
1391                                 gspv.cyWallpaper,
1392                                 hWallpaperDC,
1393                                 0,
1394                                 0,
1395                                 SRCCOPY,
1396                                 0,
1397                                 0);
1398                 }
1399                 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
1400                 NtGdiDeleteObjectApp(hWallpaperDC);
1401             }
1402         }
1403     }
1404     else
1405     {
1406         /* Black desktop background in Safe Mode */
1407         DesktopBrush = StockObjects[BLACK_BRUSH];
1408     }
1409 
1410     /* Background is set to none, clear the screen */
1411     if (doPatBlt)
1412     {
1413         PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1414         NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1415         NtGdiSelectBrush(hDC, PreviousBrush);
1416     }
1417 
1418     /*
1419      * Display the system version on the desktop background
1420      */
1421     if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion)
1422     {
1423         NTSTATUS Status;
1424         static WCHAR wszzVersion[1024] = L"\0";
1425 
1426         /* Only used in normal mode */
1427         // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
1428         static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}};
1429         INT i = 0;
1430         INT len;
1431 
1432         HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL;
1433         COLORREF crText, color_old;
1434         UINT align_old;
1435         INT mode_old;
1436         PDC pdc;
1437 
1438         if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0))
1439         {
1440             Rect.left = Rect.top = 0;
1441             Rect.right  = UserGetSystemMetrics(SM_CXSCREEN);
1442             Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1443         }
1444         else
1445         {
1446             RECTL_vOffsetRect(&Rect, -Rect.left, -Rect.top);
1447         }
1448 
1449         /*
1450          * Set up the fonts (otherwise use default ones)
1451          */
1452 
1453         /* Font for the principal version string */
1454         hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
1455         /* Font for the secondary version strings */
1456         hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
1457 
1458         if (hFont1)
1459             hOldFont = NtGdiSelectFont(hDC, hFont1);
1460 
1461         if (gspv.hbmWallpaper == NULL)
1462         {
1463             /* Retrieve the brush fill colour */
1464             // TODO: The following code constitutes "GreGetBrushColor".
1465             PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1466             pdc = DC_LockDc(hDC);
1467             if (pdc)
1468             {
1469                 crText = pdc->eboFill.ulRGBColor;
1470                 DC_UnlockDc(pdc);
1471             }
1472             else
1473             {
1474                 crText = RGB(0, 0, 0);
1475             }
1476             NtGdiSelectBrush(hDC, PreviousBrush);
1477 
1478             /* Adjust text colour according to the brush */
1479             if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
1480                 crText = RGB(0, 0, 0);
1481             else
1482                 crText = RGB(255, 255, 255);
1483         }
1484         else
1485         {
1486             /* Always use white when the text is displayed on top of a wallpaper */
1487             crText = RGB(255, 255, 255);
1488         }
1489 
1490         color_old = IntGdiSetTextColor(hDC, crText);
1491         align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1492         mode_old  = IntGdiSetBkMode(hDC, TRANSPARENT);
1493 
1494         /* Display the system version information */
1495         if (!*wszzVersion)
1496         {
1497             Status = GetSystemVersionString(wszzVersion,
1498                                             ARRAYSIZE(wszzVersion),
1499                                             InSafeMode,
1500                                             g_AlwaysDisplayVersion);
1501             if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
1502             {
1503                 PWCHAR pstr = wszzVersion;
1504                 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
1505                 {
1506                     VerStrs[i].n = wcslen(pstr);
1507                     VerStrs[i].lpstr = pstr;
1508                     pstr += (VerStrs[i].n + 1);
1509                 }
1510             }
1511         }
1512         else
1513         {
1514             Status = STATUS_SUCCESS;
1515         }
1516         if (NT_SUCCESS(Status) && *wszzVersion)
1517         {
1518             if (!InSafeMode)
1519             {
1520                 SIZE Size = {0, 0};
1521                 LONG TotalHeight = 0;
1522 
1523                 /* Normal Mode: multiple version information text separated by newlines */
1524                 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1525 
1526                 /* Compute the heights of the strings */
1527                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1528                 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1529                 {
1530                     if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1531                         break;
1532 
1533                     GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
1534                     VerStrs[i].y = Size.cy; // Store the string height
1535                     TotalHeight += Size.cy;
1536 
1537                     /* While the first string was using hFont1, all the others use hFont2 */
1538                     if (hFont2) NtGdiSelectFont(hDC, hFont2);
1539                 }
1540                 /* The total height must not exceed the screen height */
1541                 TotalHeight = min(TotalHeight, Rect.bottom);
1542 
1543                 /* Display the strings */
1544                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1545                 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1546                 {
1547                     if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1548                         break;
1549 
1550                     TotalHeight -= VerStrs[i].y;
1551                     GreExtTextOutW(hDC,
1552                                    Rect.right - 5,
1553                                    Rect.bottom - TotalHeight - 5,
1554                                    0, NULL,
1555                                    VerStrs[i].lpstr,
1556                                    VerStrs[i].n,
1557                                    NULL, 0);
1558 
1559                     /* While the first string was using hFont1, all the others use hFont2 */
1560                     if (hFont2) NtGdiSelectFont(hDC, hFont2);
1561                 }
1562             }
1563             else
1564             {
1565                 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1566 
1567                 /* Safe Mode: single version information text in top center */
1568                 len = wcslen(wszzVersion);
1569 
1570                 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
1571                 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
1572             }
1573         }
1574 
1575         if (InSafeMode)
1576         {
1577             if (hFont1) NtGdiSelectFont(hDC, hFont1);
1578 
1579             /* Print Safe Mode text in corners */
1580             len = wcslen(s_wszSafeMode);
1581 
1582             IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
1583             GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1584             IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
1585             GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1586             IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
1587             GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1588             IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1589             GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1590         }
1591 
1592         IntGdiSetBkMode(hDC, mode_old);
1593         IntGdiSetTextAlign(hDC, align_old);
1594         IntGdiSetTextColor(hDC, color_old);
1595 
1596         if (hFont2)
1597             GreDeleteObject(hFont2);
1598 
1599         if (hFont1)
1600         {
1601             NtGdiSelectFont(hDC, hOldFont);
1602             GreDeleteObject(hFont1);
1603         }
1604     }
1605 
1606     return TRUE;
1607 }
1608 
1609 static NTSTATUS
1610 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
1611 {
1612     PVOID DesktopHeapSystemBase = NULL;
1613     ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024;
1614     SIZE_T DesktopInfoSize;
1615     ULONG i;
1616 
1617     TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
1618 
1619     RtlZeroMemory(pdesk, sizeof(DESKTOP));
1620 
1621     /* Link the desktop with the parent window station */
1622     ObReferenceObject(pwinsta);
1623     pdesk->rpwinstaParent = pwinsta;
1624     InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
1625 
1626     /* Create the desktop heap */
1627     pdesk->hsectionDesktop = NULL;
1628     pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
1629                                          &DesktopHeapSystemBase,
1630                                          HeapSize);
1631     if (pdesk->pheapDesktop == NULL)
1632     {
1633         ERR("Failed to create desktop heap!\n");
1634         return STATUS_NO_MEMORY;
1635     }
1636 
1637     /* Create DESKTOPINFO */
1638     DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
1639     pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
1640                                        HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
1641                                        DesktopInfoSize);
1642     if (pdesk->pDeskInfo == NULL)
1643     {
1644         ERR("Failed to create the DESKTOP structure!\n");
1645         return STATUS_NO_MEMORY;
1646     }
1647 
1648     /* Initialize the DESKTOPINFO */
1649     pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1650     pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1651     RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
1652                   DesktopName->Buffer,
1653                   DesktopName->Length + sizeof(WCHAR));
1654     for (i = 0; i < NB_HOOKS; i++)
1655     {
1656         InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
1657     }
1658 
1659     InitializeListHead(&pdesk->ShellHookWindows);
1660     InitializeListHead(&pdesk->PtiList);
1661 
1662     return STATUS_SUCCESS;
1663 }
1664 
1665 /* SYSCALLS *******************************************************************/
1666 
1667 /*
1668  * NtUserCreateDesktop
1669  *
1670  * Creates a new desktop.
1671  *
1672  * Parameters
1673  *    poaAttribs
1674  *       Object Attributes.
1675  *
1676  *    lpszDesktopDevice
1677  *       Name of the device.
1678  *
1679  *    pDeviceMode
1680  *       Device Mode.
1681  *
1682  *    dwFlags
1683  *       Interaction flags.
1684  *
1685  *    dwDesiredAccess
1686  *       Requested type of access.
1687  *
1688  *
1689  * Return Value
1690  *    If the function succeeds, the return value is a handle to the newly
1691  *    created desktop. If the specified desktop already exists, the function
1692  *    succeeds and returns a handle to the existing desktop. When you are
1693  *    finished using the handle, call the CloseDesktop function to close it.
1694  *    If the function fails, the return value is NULL.
1695  *
1696  * Status
1697  *    @implemented
1698  */
1699 
1700 HDESK APIENTRY
1701 NtUserCreateDesktop(
1702     POBJECT_ATTRIBUTES ObjectAttributes,
1703     PUNICODE_STRING lpszDesktopDevice,
1704     LPDEVMODEW lpdmw,
1705     DWORD dwFlags,
1706     ACCESS_MASK dwDesiredAccess)
1707 {
1708     PDESKTOP pdesk = NULL;
1709     NTSTATUS Status = STATUS_SUCCESS;
1710     HDESK hdesk;
1711     BOOLEAN Context = FALSE;
1712     UNICODE_STRING ClassName;
1713     LARGE_STRING WindowName;
1714     BOOL NoHooks = FALSE;
1715     PWND pWnd = NULL;
1716     CREATESTRUCTW Cs;
1717     PTHREADINFO ptiCurrent;
1718     PCLS pcls;
1719 
1720     DECLARE_RETURN(HDESK);
1721 
1722     TRACE("Enter NtUserCreateDesktop\n");
1723     UserEnterExclusive();
1724 
1725     ptiCurrent = PsGetCurrentThreadWin32Thread();
1726     ASSERT(ptiCurrent);
1727     ASSERT(gptiDesktopThread);
1728 
1729     /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1730     NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1731     ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1732     ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1733 
1734     /*
1735      * Try to open already existing desktop
1736      */
1737     Status = ObOpenObjectByName(
1738                  ObjectAttributes,
1739                  ExDesktopObjectType,
1740                  UserMode,
1741                  NULL,
1742                  dwDesiredAccess,
1743                  (PVOID)&Context,
1744                  (HANDLE*)&hdesk);
1745     if (!NT_SUCCESS(Status))
1746     {
1747         ERR("ObOpenObjectByName failed to open/create desktop\n");
1748         SetLastNtError(Status);
1749         RETURN(NULL);
1750     }
1751 
1752     /* In case the object was not created (eg if it existed), return now */
1753     if (Context == FALSE)
1754     {
1755         TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1756         RETURN( hdesk);
1757     }
1758 
1759     /* Reference the desktop */
1760     Status = ObReferenceObjectByHandle(hdesk,
1761                                        0,
1762                                        ExDesktopObjectType,
1763                                        KernelMode,
1764                                        (PVOID*)&pdesk,
1765                                        NULL);
1766     if (!NT_SUCCESS(Status))
1767     {
1768         ERR("Failed to reference desktop object\n");
1769         SetLastNtError(Status);
1770         RETURN(NULL);
1771     }
1772 
1773     /* Get the desktop window class. The thread desktop does not belong to any desktop
1774      * so the classes created there (including the desktop class) are allocated in the shared heap
1775      * It would cause problems if we used a class that belongs to the caller
1776      */
1777     ClassName.Buffer = WC_DESKTOP;
1778     ClassName.Length = 0;
1779     pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1780     if (pcls == NULL)
1781     {
1782         ASSERT(FALSE);
1783         RETURN(NULL);
1784     }
1785 
1786     RtlZeroMemory(&WindowName, sizeof(WindowName));
1787     RtlZeroMemory(&Cs, sizeof(Cs));
1788     Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
1789     Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
1790     Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
1791     Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
1792     Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1793     Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1794     Cs.lpszName = (LPCWSTR) &WindowName;
1795     Cs.lpszClass = (LPCWSTR) &ClassName;
1796 
1797     /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1798     pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1799     if (pWnd == NULL)
1800     {
1801         ERR("Failed to create desktop window for the new desktop\n");
1802         RETURN(NULL);
1803     }
1804 
1805     pdesk->dwSessionId = PsGetCurrentProcessSessionId();
1806     pdesk->DesktopWindow = pWnd->head.h;
1807     pdesk->pDeskInfo->spwnd = pWnd;
1808     pWnd->fnid = FNID_DESKTOP;
1809 
1810     ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
1811     ClassName.Length = 0;
1812     pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1813     if (pcls == NULL)
1814     {
1815         ASSERT(FALSE);
1816         RETURN(NULL);
1817     }
1818 
1819     RtlZeroMemory(&WindowName, sizeof(WindowName));
1820     RtlZeroMemory(&Cs, sizeof(Cs));
1821     Cs.cx = Cs.cy = 100;
1822     Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1823     Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1824     Cs.lpszName = (LPCWSTR) &WindowName;
1825     Cs.lpszClass = (LPCWSTR) &ClassName;
1826     pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1827     if (pWnd == NULL)
1828     {
1829         ERR("Failed to create message window for the new desktop\n");
1830         RETURN(NULL);
1831     }
1832 
1833     pdesk->spwndMessage = pWnd;
1834     pWnd->fnid = FNID_MESSAGEWND;
1835 
1836     /* Now,,,
1837        if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1838        Create Tooltip. Saved in DesktopObject->spwndTooltip.
1839        Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1840        hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1841        The rest is same as message window.
1842        http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1843     */
1844     RETURN( hdesk);
1845 
1846 CLEANUP:
1847     if (pdesk != NULL)
1848     {
1849         ObDereferenceObject(pdesk);
1850     }
1851     if (_ret_ == NULL && hdesk != NULL)
1852     {
1853         ObCloseHandle(hdesk, UserMode);
1854     }
1855     if (!NoHooks)
1856     {
1857         ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1858         ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1859     }
1860     TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1861     UserLeave();
1862     END_CLEANUP;
1863 }
1864 
1865 /*
1866  * NtUserOpenDesktop
1867  *
1868  * Opens an existing desktop.
1869  *
1870  * Parameters
1871  *    lpszDesktopName
1872  *       Name of the existing desktop.
1873  *
1874  *    dwFlags
1875  *       Interaction flags.
1876  *
1877  *    dwDesiredAccess
1878  *       Requested type of access.
1879  *
1880  * Return Value
1881  *    Handle to the desktop or zero on failure.
1882  *
1883  * Status
1884  *    @implemented
1885  */
1886 
1887 HDESK APIENTRY
1888 NtUserOpenDesktop(
1889     POBJECT_ATTRIBUTES ObjectAttributes,
1890     DWORD dwFlags,
1891     ACCESS_MASK dwDesiredAccess)
1892 {
1893     NTSTATUS Status;
1894     HDESK Desktop;
1895 
1896     Status = ObOpenObjectByName(
1897                  ObjectAttributes,
1898                  ExDesktopObjectType,
1899                  UserMode,
1900                  NULL,
1901                  dwDesiredAccess,
1902                  NULL,
1903                  (HANDLE*)&Desktop);
1904 
1905     if (!NT_SUCCESS(Status))
1906     {
1907         ERR("Failed to open desktop\n");
1908         SetLastNtError(Status);
1909         return NULL;
1910     }
1911 
1912     TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1913 
1914     return Desktop;
1915 }
1916 
1917 /*
1918  * NtUserOpenInputDesktop
1919  *
1920  * Opens the input (interactive) desktop.
1921  *
1922  * Parameters
1923  *    dwFlags
1924  *       Interaction flags.
1925  *
1926  *    fInherit
1927  *       Inheritance option.
1928  *
1929  *    dwDesiredAccess
1930  *       Requested type of access.
1931  *
1932  * Return Value
1933  *    Handle to the input desktop or zero on failure.
1934  *
1935  * Status
1936  *    @implemented
1937  */
1938 
1939 HDESK APIENTRY
1940 NtUserOpenInputDesktop(
1941     DWORD dwFlags,
1942     BOOL fInherit,
1943     ACCESS_MASK dwDesiredAccess)
1944 {
1945     NTSTATUS Status;
1946     HDESK hdesk = NULL;
1947     ULONG HandleAttributes = 0;
1948 
1949     UserEnterExclusive();
1950     TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1951 
1952     if (fInherit) HandleAttributes = OBJ_INHERIT;
1953 
1954     /* Create a new handle to the object */
1955     Status = ObOpenObjectByPointer(
1956                  gpdeskInputDesktop,
1957                  HandleAttributes,
1958                  NULL,
1959                  dwDesiredAccess,
1960                  ExDesktopObjectType,
1961                  UserMode,
1962                  (PHANDLE)&hdesk);
1963 
1964     if (!NT_SUCCESS(Status))
1965     {
1966         ERR("Failed to open input desktop object\n");
1967         SetLastNtError(Status);
1968     }
1969 
1970     TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1971     UserLeave();
1972     return hdesk;
1973 }
1974 
1975 /*
1976  * NtUserCloseDesktop
1977  *
1978  * Closes a desktop handle.
1979  *
1980  * Parameters
1981  *    hDesktop
1982  *       Handle to the desktop.
1983  *
1984  * Return Value
1985  *   Status
1986  *
1987  * Remarks
1988  *   The desktop handle can be created with NtUserCreateDesktop or
1989  *   NtUserOpenDesktop. This function will fail if any thread in the calling
1990  *   process is using the specified desktop handle or if the handle refers
1991  *   to the initial desktop of the calling process.
1992  *
1993  * Status
1994  *    @implemented
1995  */
1996 
1997 BOOL APIENTRY
1998 NtUserCloseDesktop(HDESK hDesktop)
1999 {
2000     PDESKTOP pdesk;
2001     NTSTATUS Status;
2002     DECLARE_RETURN(BOOL);
2003 
2004     TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
2005     UserEnterExclusive();
2006 
2007     if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
2008     {
2009         ERR("Attempted to close thread desktop\n");
2010         EngSetLastError(ERROR_BUSY);
2011         RETURN(FALSE);
2012     }
2013 
2014     Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
2015     if (!NT_SUCCESS(Status))
2016     {
2017         ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
2018         RETURN(FALSE);
2019     }
2020 
2021     ObDereferenceObject(pdesk);
2022 
2023     Status = ZwClose(hDesktop);
2024     if (!NT_SUCCESS(Status))
2025     {
2026         ERR("Failed to close desktop handle 0x%p\n", hDesktop);
2027         SetLastNtError(Status);
2028         RETURN(FALSE);
2029     }
2030 
2031     RETURN(TRUE);
2032 
2033 CLEANUP:
2034     TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
2035     UserLeave();
2036     END_CLEANUP;
2037 }
2038 
2039 /*
2040  * NtUserPaintDesktop
2041  *
2042  * The NtUserPaintDesktop function fills the clipping region in the
2043  * specified device context with the desktop pattern or wallpaper. The
2044  * function is provided primarily for shell desktops.
2045  *
2046  * Parameters
2047  *    hDC
2048  *       Handle to the device context.
2049  *
2050  * Status
2051  *    @implemented
2052  */
2053 
2054 BOOL APIENTRY
2055 NtUserPaintDesktop(HDC hDC)
2056 {
2057     BOOL Ret;
2058     UserEnterExclusive();
2059     TRACE("Enter NtUserPaintDesktop\n");
2060     Ret = IntPaintDesktop(hDC);
2061     TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
2062     UserLeave();
2063     return Ret;
2064 }
2065 
2066 /*
2067  * NtUserResolveDesktop
2068  *
2069  * The NtUserResolveDesktop function retrieves handles to the desktop and
2070  * the window station specified by the desktop path string.
2071  *
2072  * Parameters
2073  *    ProcessHandle
2074  *       Handle to a user process.
2075  *
2076  *    DesktopPath
2077  *       The desktop path string.
2078  *
2079  * Return Value
2080  *    Handle to the desktop (direct return value) and
2081  *    handle to the associated window station (by pointer).
2082  *    NULL in case of failure.
2083  *
2084  * Remarks
2085  *    Callable by CSRSS only.
2086  *
2087  * Status
2088  *    @implemented
2089  */
2090 
2091 HDESK
2092 APIENTRY
2093 NtUserResolveDesktop(
2094     IN HANDLE ProcessHandle,
2095     IN PUNICODE_STRING DesktopPath,
2096     DWORD dwUnknown,
2097     OUT HWINSTA* phWinSta)
2098 {
2099     NTSTATUS Status;
2100     PEPROCESS Process = NULL;
2101     HWINSTA hWinSta = NULL;
2102     HDESK hDesktop  = NULL;
2103 
2104     /* Allow only the Console Server to perform this operation (via CSRSS) */
2105     if (PsGetCurrentProcess() != gpepCSRSS)
2106         return NULL;
2107 
2108     /* Get the process object the user handle was referencing */
2109     Status = ObReferenceObjectByHandle(ProcessHandle,
2110                                        PROCESS_QUERY_INFORMATION,
2111                                        *PsProcessType,
2112                                        UserMode,
2113                                        (PVOID*)&Process,
2114                                        NULL);
2115     if (!NT_SUCCESS(Status)) return NULL;
2116 
2117     // UserEnterShared();
2118 
2119     _SEH2_TRY
2120     {
2121         UNICODE_STRING CapturedDesktopPath;
2122 
2123         /* Capture the user desktop path string */
2124         Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
2125                                                        DesktopPath);
2126         if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
2127 
2128         /* Call the internal function */
2129         Status = IntParseDesktopPath(Process,
2130                                      &CapturedDesktopPath,
2131                                      &hWinSta,
2132                                      &hDesktop);
2133         if (!NT_SUCCESS(Status))
2134         {
2135             ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
2136             hWinSta  = NULL;
2137             hDesktop = NULL;
2138         }
2139 
2140         /* Return the window station handle */
2141         *phWinSta = hWinSta;
2142 
2143         /* Free the captured string */
2144         if (CapturedDesktopPath.Buffer)
2145             ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
2146     }
2147     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2148     {
2149         Status = _SEH2_GetExceptionCode();
2150     }
2151     _SEH2_END;
2152 
2153 Quit:
2154     // UserLeave();
2155 
2156     /* Dereference the process object */
2157     ObDereferenceObject(Process);
2158 
2159     /* Return the desktop handle */
2160     return hDesktop;
2161 }
2162 
2163 /*
2164  * NtUserSwitchDesktop
2165  *
2166  * Sets the current input (interactive) desktop.
2167  *
2168  * Parameters
2169  *    hDesktop
2170  *       Handle to desktop.
2171  *
2172  * Return Value
2173  *    Status
2174  *
2175  * Status
2176  *    @unimplemented
2177  */
2178 
2179 BOOL APIENTRY
2180 NtUserSwitchDesktop(HDESK hdesk)
2181 {
2182     PDESKTOP pdesk;
2183     NTSTATUS Status;
2184     BOOL bRedrawDesktop;
2185     DECLARE_RETURN(BOOL);
2186 
2187     UserEnterExclusive();
2188     TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
2189 
2190     Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
2191     if (!NT_SUCCESS(Status))
2192     {
2193         ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
2194         RETURN(FALSE);
2195     }
2196 
2197     if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
2198     {
2199         ObDereferenceObject(pdesk);
2200         ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2201         RETURN(FALSE);
2202     }
2203 
2204     if (pdesk == gpdeskInputDesktop)
2205     {
2206         ObDereferenceObject(pdesk);
2207         WARN("NtUserSwitchDesktop called for active desktop\n");
2208         RETURN(TRUE);
2209     }
2210 
2211     /*
2212      * Don't allow applications switch the desktop if it's locked, unless the caller
2213      * is the logon application itself
2214      */
2215     if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
2216         gpidLogon != PsGetCurrentProcessId())
2217     {
2218         ObDereferenceObject(pdesk);
2219         ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
2220         RETURN(FALSE);
2221     }
2222 
2223     if (pdesk->rpwinstaParent != InputWindowStation)
2224     {
2225         ObDereferenceObject(pdesk);
2226         ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
2227         RETURN(FALSE);
2228     }
2229 
2230     /* FIXME: Fail if the process is associated with a secured
2231               desktop such as Winlogon or Screen-Saver */
2232     /* FIXME: Connect to input device */
2233 
2234     TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
2235 
2236     bRedrawDesktop = FALSE;
2237 
2238     /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2239     if (gpdeskInputDesktop != NULL)
2240     {
2241         if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
2242             bRedrawDesktop = TRUE;
2243 
2244         /* Hide the previous desktop window */
2245         IntHideDesktop(gpdeskInputDesktop);
2246     }
2247 
2248     /* Set the active desktop in the desktop's window station. */
2249     InputWindowStation->ActiveDesktop = pdesk;
2250 
2251     /* Set the global state. */
2252     gpdeskInputDesktop = pdesk;
2253 
2254     /* Show the new desktop window */
2255     co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
2256 
2257     TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
2258     ObDereferenceObject(pdesk);
2259 
2260     RETURN(TRUE);
2261 
2262 CLEANUP:
2263     TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
2264     UserLeave();
2265     END_CLEANUP;
2266 }
2267 
2268 /*
2269  * NtUserGetThreadDesktop
2270  *
2271  * Status
2272  *    @implemented
2273  */
2274 
2275 HDESK APIENTRY
2276 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
2277 {
2278     NTSTATUS Status;
2279     PETHREAD Thread;
2280     PDESKTOP DesktopObject;
2281     HDESK Ret, hThreadDesktop;
2282     OBJECT_HANDLE_INFORMATION HandleInformation;
2283     DECLARE_RETURN(HDESK);
2284 
2285     UserEnterExclusive();
2286     TRACE("Enter NtUserGetThreadDesktop\n");
2287 
2288     if (!dwThreadId)
2289     {
2290         EngSetLastError(ERROR_INVALID_PARAMETER);
2291         RETURN(0);
2292     }
2293 
2294     Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
2295     if (!NT_SUCCESS(Status))
2296     {
2297         EngSetLastError(ERROR_INVALID_PARAMETER);
2298         RETURN(0);
2299     }
2300 
2301     if (Thread->ThreadsProcess == PsGetCurrentProcess())
2302     {
2303         /* Just return the handle, we queried the desktop handle of a thread running
2304            in the same context */
2305         Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
2306         ObDereferenceObject(Thread);
2307         RETURN(Ret);
2308     }
2309 
2310     /* Get the desktop handle and the desktop of the thread */
2311     if (!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
2312         !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
2313     {
2314         ObDereferenceObject(Thread);
2315         ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
2316         RETURN(NULL);
2317     }
2318 
2319     /* We could just use DesktopObject instead of looking up the handle, but latter
2320        may be a bit safer (e.g. when the desktop is being destroyed */
2321     /* Switch into the context of the thread we're trying to get the desktop from,
2322        so we can use the handle */
2323     KeAttachProcess(&Thread->ThreadsProcess->Pcb);
2324     Status = ObReferenceObjectByHandle(hThreadDesktop,
2325                                        GENERIC_ALL,
2326                                        ExDesktopObjectType,
2327                                        UserMode,
2328                                        (PVOID*)&DesktopObject,
2329                                        &HandleInformation);
2330     KeDetachProcess();
2331 
2332     /* The handle couldn't be found, there's nothing to get... */
2333     if (!NT_SUCCESS(Status))
2334     {
2335         ObDereferenceObject(Thread);
2336         RETURN(NULL);
2337     }
2338 
2339     /* Lookup our handle table if we can find a handle to the desktop object,
2340        if not, create one */
2341     Ret = IntGetDesktopObjectHandle(DesktopObject);
2342 
2343     /* All done, we got a valid handle to the desktop */
2344     ObDereferenceObject(DesktopObject);
2345     ObDereferenceObject(Thread);
2346     RETURN(Ret);
2347 
2348 CLEANUP:
2349     TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
2350     UserLeave();
2351     END_CLEANUP;
2352 }
2353 
2354 static NTSTATUS
2355 IntUnmapDesktopView(IN PDESKTOP pdesk)
2356 {
2357     PPROCESSINFO ppi;
2358     PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2359     NTSTATUS Status = STATUS_SUCCESS;
2360 
2361     TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
2362 
2363     ppi = PsGetCurrentProcessWin32Process();
2364 
2365     /*
2366      * Unmap if we're the last thread using the desktop.
2367      * Start the search at the next mapping: skip the first entry
2368      * as it must be the global user heap mapping.
2369      */
2370     PrevLink = &ppi->HeapMappings.Next;
2371     HeapMapping = *PrevLink;
2372     while (HeapMapping != NULL)
2373     {
2374         if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2375         {
2376             if (--HeapMapping->Count == 0)
2377             {
2378                 *PrevLink = HeapMapping->Next;
2379 
2380                 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
2381                 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
2382                                               HeapMapping->UserMapping);
2383 
2384                 ObDereferenceObject(pdesk);
2385 
2386                 UserHeapFree(HeapMapping);
2387                 break;
2388             }
2389         }
2390 
2391         PrevLink = &HeapMapping->Next;
2392         HeapMapping = HeapMapping->Next;
2393     }
2394 
2395     return Status;
2396 }
2397 
2398 static NTSTATUS
2399 IntMapDesktopView(IN PDESKTOP pdesk)
2400 {
2401     PPROCESSINFO ppi;
2402     PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2403     PVOID UserBase = NULL;
2404     SIZE_T ViewSize = 0;
2405     LARGE_INTEGER Offset;
2406     NTSTATUS Status;
2407 
2408     TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
2409 
2410     ppi = PsGetCurrentProcessWin32Process();
2411 
2412     /*
2413      * Find out if another thread already mapped the desktop heap.
2414      * Start the search at the next mapping: skip the first entry
2415      * as it must be the global user heap mapping.
2416      */
2417     PrevLink = &ppi->HeapMappings.Next;
2418     HeapMapping = *PrevLink;
2419     while (HeapMapping != NULL)
2420     {
2421         if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2422         {
2423             HeapMapping->Count++;
2424             return STATUS_SUCCESS;
2425         }
2426 
2427         PrevLink = &HeapMapping->Next;
2428         HeapMapping = HeapMapping->Next;
2429     }
2430 
2431     /* We're the first, map the heap */
2432     Offset.QuadPart = 0;
2433     Status = MmMapViewOfSection(pdesk->hsectionDesktop,
2434                                 PsGetCurrentProcess(),
2435                                 &UserBase,
2436                                 0,
2437                                 0,
2438                                 &Offset,
2439                                 &ViewSize,
2440                                 ViewUnmap,
2441                                 SEC_NO_CHANGE,
2442                                 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2443     if (!NT_SUCCESS(Status))
2444     {
2445         ERR("Failed to map desktop\n");
2446         return Status;
2447     }
2448 
2449     TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
2450 
2451     /* Add the mapping */
2452     HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
2453     if (HeapMapping == NULL)
2454     {
2455         MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
2456         ERR("UserHeapAlloc() failed!\n");
2457         return STATUS_NO_MEMORY;
2458     }
2459 
2460     HeapMapping->Next = NULL;
2461     HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
2462     HeapMapping->UserMapping = UserBase;
2463     HeapMapping->Limit = ViewSize;
2464     HeapMapping->Count = 1;
2465     *PrevLink = HeapMapping;
2466 
2467     ObReferenceObject(pdesk);
2468 
2469     return STATUS_SUCCESS;
2470 }
2471 
2472 BOOL
2473 IntSetThreadDesktop(IN HDESK hDesktop,
2474                     IN BOOL FreeOnFailure)
2475 {
2476     PDESKTOP pdesk = NULL, pdeskOld;
2477     PTHREADINFO pti;
2478     NTSTATUS Status;
2479     PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
2480     PCLIENTINFO pci;
2481 
2482     ASSERT(NtCurrentTeb());
2483 
2484     TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
2485 
2486     pti = PsGetCurrentThreadWin32Thread();
2487     pci = pti->pClientInfo;
2488 
2489     /* If the caller gave us a desktop, ensure it is valid */
2490     if (hDesktop != NULL)
2491     {
2492         /* Validate the new desktop. */
2493         Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
2494         if (!NT_SUCCESS(Status))
2495         {
2496             ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
2497             return FALSE;
2498         }
2499 
2500         if (pti->rpdesk == pdesk)
2501         {
2502             /* Nothing to do */
2503             ObDereferenceObject(pdesk);
2504             return TRUE;
2505         }
2506     }
2507 
2508     /* Make sure that we don't own any window in the current desktop */
2509     if (!IsListEmpty(&pti->WindowListHead))
2510     {
2511         if (pdesk)
2512             ObDereferenceObject(pdesk);
2513         ERR("Attempted to change thread desktop although the thread has windows!\n");
2514         EngSetLastError(ERROR_BUSY);
2515         return FALSE;
2516     }
2517 
2518     /* Desktop is being re-set so clear out foreground. */
2519     if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
2520     {
2521         // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2522         IntSetFocusMessageQueue(NULL);
2523     }
2524 
2525     /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2526     if (pdesk != NULL)
2527     {
2528         Status = IntMapDesktopView(pdesk);
2529         if (!NT_SUCCESS(Status))
2530         {
2531             ERR("Failed to map desktop heap!\n");
2532             ObDereferenceObject(pdesk);
2533             SetLastNtError(Status);
2534             return FALSE;
2535         }
2536 
2537         pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
2538         if (pctiNew == NULL)
2539         {
2540             ERR("Failed to allocate new pcti\n");
2541             IntUnmapDesktopView(pdesk);
2542             ObDereferenceObject(pdesk);
2543             EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2544             return FALSE;
2545         }
2546     }
2547 
2548     /* free all classes or move them to the shared heap */
2549     if (pti->rpdesk != NULL)
2550     {
2551         if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
2552         {
2553             ERR("Failed to move process classes to shared heap!\n");
2554             if (pdesk)
2555             {
2556                 DesktopHeapFree(pdesk, pctiNew);
2557                 IntUnmapDesktopView(pdesk);
2558                 ObDereferenceObject(pdesk);
2559             }
2560             return FALSE;
2561         }
2562     }
2563 
2564     pdeskOld = pti->rpdesk;
2565     if (pti->pcti != &pti->cti)
2566         pctiOld = pti->pcti;
2567     else
2568         pctiOld = NULL;
2569 
2570     /* do the switch */
2571     if (pdesk != NULL)
2572     {
2573         pti->rpdesk = pdesk;
2574         pti->hdesk = hDesktop;
2575         pti->pDeskInfo = pti->rpdesk->pDeskInfo;
2576         pti->pcti = pctiNew;
2577 
2578         pci->ulClientDelta = DesktopHeapGetUserDelta();
2579         pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
2580         pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
2581 
2582         /* initialize the new pcti */
2583         if (pctiOld != NULL)
2584         {
2585             RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
2586         }
2587         else
2588         {
2589             RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
2590             pci->fsHooks = pti->fsHooks;
2591             pci->dwTIFlags = pti->TIF_flags;
2592         }
2593     }
2594     else
2595     {
2596         pti->rpdesk = NULL;
2597         pti->hdesk = NULL;
2598         pti->pDeskInfo = NULL;
2599         pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
2600         pci->ulClientDelta = 0;
2601         pci->pDeskInfo = NULL;
2602         pci->pClientThreadInfo = NULL;
2603     }
2604 
2605     /* clean up the old desktop */
2606     if (pdeskOld != NULL)
2607     {
2608         RemoveEntryList(&pti->PtiLink);
2609         if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
2610         IntUnmapDesktopView(pdeskOld);
2611         ObDereferenceObject(pdeskOld);
2612     }
2613 
2614     if (pdesk)
2615     {
2616         InsertTailList(&pdesk->PtiList, &pti->PtiLink);
2617     }
2618 
2619     TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
2620 
2621     return TRUE;
2622 }
2623 
2624 /*
2625  * NtUserSetThreadDesktop
2626  *
2627  * Status
2628  *    @implemented
2629  */
2630 
2631 BOOL APIENTRY
2632 NtUserSetThreadDesktop(HDESK hDesktop)
2633 {
2634     BOOL ret = FALSE;
2635 
2636     UserEnterExclusive();
2637 
2638     // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2639     // here too and set the NT error level. Q. Is it necessary to have the validation
2640     // in IntSetThreadDesktop? Is it needed there too?
2641     if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
2642         ret = IntSetThreadDesktop(hDesktop, FALSE);
2643 
2644     UserLeave();
2645 
2646     return ret;
2647 }
2648 
2649 /* EOF */
2650