[NtUser]
[reactos.git] / reactos / win32ss / user / ntuser / desktop.c
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 if (DcType == DC_TYPE_DIRECT)
936 {
937 DesktopObject = UserGetDesktopWindow();
938 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
939 }
940 else
941 {
942 PMONITOR pMonitor = UserGetPrimaryMonitor();
943 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC);
944 }
945
946 return DesktopHDC;
947 }
948
949 VOID APIENTRY
950 UserRedrawDesktop(VOID)
951 {
952 PWND Window = NULL;
953 PREGION Rgn;
954
955 Window = UserGetDesktopWindow();
956 Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
957
958 IntInvalidateWindows( Window,
959 Rgn,
960 RDW_FRAME |
961 RDW_ERASE |
962 RDW_INVALIDATE |
963 RDW_ALLCHILDREN);
964
965 REGION_Delete(Rgn);
966 }
967
968
969 NTSTATUS FASTCALL
970 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
971 {
972 PWND pwnd = Desktop->pDeskInfo->spwnd;
973 UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
974 ASSERT(pwnd);
975
976 if (!bRedraw)
977 flags |= SWP_NOREDRAW;
978
979 co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
980
981 if (bRedraw)
982 co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
983
984 return STATUS_SUCCESS;
985 }
986
987 NTSTATUS FASTCALL
988 IntHideDesktop(PDESKTOP Desktop)
989 {
990 PWND DesktopWnd;
991
992 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
993 if (! DesktopWnd)
994 {
995 return ERROR_INVALID_WINDOW_HANDLE;
996 }
997 DesktopWnd->style &= ~WS_VISIBLE;
998
999 return STATUS_SUCCESS;
1000 }
1001
1002 static
1003 HWND* FASTCALL
1004 UserBuildShellHookHwndList(PDESKTOP Desktop)
1005 {
1006 ULONG entries=0;
1007 PLIST_ENTRY ListEntry;
1008 PSHELL_HOOK_WINDOW Current;
1009 HWND* list;
1010
1011 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1012 ListEntry = Desktop->ShellHookWindows.Flink;
1013 while (ListEntry != &Desktop->ShellHookWindows)
1014 {
1015 ListEntry = ListEntry->Flink;
1016 entries++;
1017 }
1018
1019 if (!entries) return NULL;
1020
1021 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
1022 if (list)
1023 {
1024 HWND* cursor = list;
1025
1026 ListEntry = Desktop->ShellHookWindows.Flink;
1027 while (ListEntry != &Desktop->ShellHookWindows)
1028 {
1029 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1030 ListEntry = ListEntry->Flink;
1031 *cursor++ = Current->hWnd;
1032 }
1033
1034 *cursor = NULL; /* Nullterm list */
1035 }
1036
1037 return list;
1038 }
1039
1040 /*
1041 * Send the Message to the windows registered for ShellHook
1042 * notifications. The lParam contents depend on the Message. See
1043 * MSDN for more details (RegisterShellHookWindow)
1044 */
1045 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
1046 {
1047 PDESKTOP Desktop = IntGetActiveDesktop();
1048 HWND* HwndList;
1049
1050 if (!gpsi->uiShellMsg)
1051 {
1052 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
1053
1054 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
1055 if (!gpsi->uiShellMsg)
1056 ERR("LastError: %x\n", EngGetLastError());
1057 }
1058
1059 if (!Desktop)
1060 {
1061 TRACE("IntShellHookNotify: No desktop!\n");
1062 return;
1063 }
1064
1065 // Allow other devices have a shot at foreground.
1066 if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
1067
1068 // FIXME: System Tray Support.
1069
1070 HwndList = UserBuildShellHookHwndList(Desktop);
1071 if (HwndList)
1072 {
1073 HWND* cursor = HwndList;
1074
1075 for (; *cursor; cursor++)
1076 {
1077 TRACE("Sending notify\n");
1078 UserPostMessage(*cursor,
1079 gpsi->uiShellMsg,
1080 Message,
1081 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
1082 /* co_IntPostOrSendMessage(*cursor,
1083 gpsi->uiShellMsg,
1084 Message,
1085 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1086 }
1087
1088 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
1089 }
1090
1091 if (ISITHOOKED(WH_SHELL))
1092 {
1093 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
1094 }
1095 }
1096
1097 /*
1098 * Add the window to the ShellHookWindows list. The windows
1099 * on that list get notifications that are important to shell
1100 * type applications.
1101 *
1102 * TODO: Validate the window? I'm not sure if sending these messages to
1103 * an unsuspecting application that is not your own is a nice thing to do.
1104 */
1105 BOOL IntRegisterShellHookWindow(HWND hWnd)
1106 {
1107 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1108 PDESKTOP Desktop = pti->rpdesk;
1109 PSHELL_HOOK_WINDOW Entry;
1110
1111 TRACE("IntRegisterShellHookWindow\n");
1112
1113 /* First deregister the window, so we can be sure it's never twice in the
1114 * list.
1115 */
1116 IntDeRegisterShellHookWindow(hWnd);
1117
1118 Entry = ExAllocatePoolWithTag(PagedPool,
1119 sizeof(SHELL_HOOK_WINDOW),
1120 TAG_WINSTA);
1121
1122 if (!Entry)
1123 return FALSE;
1124
1125 Entry->hWnd = hWnd;
1126
1127 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
1128
1129 return TRUE;
1130 }
1131
1132 /*
1133 * Remove the window from the ShellHookWindows list. The windows
1134 * on that list get notifications that are important to shell
1135 * type applications.
1136 */
1137 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
1138 {
1139 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1140 PDESKTOP Desktop = pti->rpdesk;
1141 PLIST_ENTRY ListEntry;
1142 PSHELL_HOOK_WINDOW Current;
1143
1144 ListEntry = Desktop->ShellHookWindows.Flink;
1145 while (ListEntry != &Desktop->ShellHookWindows)
1146 {
1147 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1148 ListEntry = ListEntry->Flink;
1149 if (Current->hWnd == hWnd)
1150 {
1151 RemoveEntryList(&Current->ListEntry);
1152 ExFreePoolWithTag(Current, TAG_WINSTA);
1153 return TRUE;
1154 }
1155 }
1156
1157 return FALSE;
1158 }
1159
1160 static VOID
1161 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
1162 {
1163 /* FIXME: Disable until unmapping works in mm */
1164 #if 0
1165 if (Desktop->pheapDesktop != NULL)
1166 {
1167 MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
1168 Desktop->pheapDesktop = NULL;
1169 }
1170
1171 if (Desktop->hsectionDesktop != NULL)
1172 {
1173 ObDereferenceObject(Desktop->hsectionDesktop);
1174 Desktop->hsectionDesktop = NULL;
1175 }
1176 #endif
1177 }
1178
1179 BOOL FASTCALL
1180 IntPaintDesktop(HDC hDC)
1181 {
1182 static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize!
1183
1184 RECTL Rect;
1185 HBRUSH DesktopBrush, PreviousBrush;
1186 HWND hWndDesktop;
1187 BOOL doPatBlt = TRUE;
1188 PWND WndDesktop;
1189 BOOLEAN InSafeMode;
1190
1191 if (GdiGetClipBox(hDC, &Rect) == ERROR)
1192 return FALSE;
1193
1194 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1195
1196 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
1197 if (!WndDesktop)
1198 return FALSE;
1199
1200 /* Retrieve the current SafeMode state */
1201 InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1202
1203 if (!InSafeMode)
1204 {
1205 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1206
1207 /*
1208 * Paint desktop background
1209 */
1210 if (gspv.hbmWallpaper != NULL)
1211 {
1212 SIZE sz;
1213 int x, y;
1214 HDC hWallpaperDC;
1215
1216 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
1217 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
1218
1219 if (gspv.WallpaperMode == wmStretch ||
1220 gspv.WallpaperMode == wmTile)
1221 {
1222 x = 0;
1223 y = 0;
1224 }
1225 else
1226 {
1227 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1228 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
1229 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
1230 }
1231
1232 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1233 if (hWallpaperDC != NULL)
1234 {
1235 HBITMAP hOldBitmap;
1236
1237 /* Fill in the area that the bitmap is not going to cover */
1238 if (x > 0 || y > 0)
1239 {
1240 /* FIXME: Clip out the bitmap
1241 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1242 once we support DSTINVERT */
1243 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1244 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1245 NtGdiSelectBrush(hDC, PreviousBrush);
1246 }
1247
1248 /* Do not fill the background after it is painted no matter the size of the picture */
1249 doPatBlt = FALSE;
1250
1251 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1252
1253 if (gspv.WallpaperMode == wmStretch)
1254 {
1255 if (Rect.right && Rect.bottom)
1256 NtGdiStretchBlt(hDC,
1257 x,
1258 y,
1259 sz.cx,
1260 sz.cy,
1261 hWallpaperDC,
1262 0,
1263 0,
1264 gspv.cxWallpaper,
1265 gspv.cyWallpaper,
1266 SRCCOPY,
1267 0);
1268 }
1269 else if (gspv.WallpaperMode == wmTile)
1270 {
1271 /* Paint the bitmap across the screen then down */
1272 for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1273 {
1274 for (x = 0; x < Rect.right; x += gspv.cxWallpaper)
1275 {
1276 NtGdiBitBlt(hDC,
1277 x,
1278 y,
1279 gspv.cxWallpaper,
1280 gspv.cyWallpaper,
1281 hWallpaperDC,
1282 0,
1283 0,
1284 SRCCOPY,
1285 0,
1286 0);
1287 }
1288 }
1289 }
1290 else
1291 {
1292 NtGdiBitBlt(hDC,
1293 x,
1294 y,
1295 gspv.cxWallpaper,
1296 gspv.cyWallpaper,
1297 hWallpaperDC,
1298 0,
1299 0,
1300 SRCCOPY,
1301 0,
1302 0);
1303 }
1304 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
1305 NtGdiDeleteObjectApp(hWallpaperDC);
1306 }
1307 }
1308 }
1309 else
1310 {
1311 /* Black desktop background in Safe Mode */
1312 DesktopBrush = StockObjects[BLACK_BRUSH];
1313 }
1314
1315 /* Background is set to none, clear the screen */
1316 if (doPatBlt)
1317 {
1318 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1319 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1320 NtGdiSelectBrush(hDC, PreviousBrush);
1321 }
1322
1323 /*
1324 * Display the system version on the desktop background
1325 */
1326 if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion)
1327 {
1328 NTSTATUS Status;
1329 static WCHAR wszzVersion[1024] = L"\0";
1330
1331 /* Only used in normal mode */
1332 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
1333 static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}};
1334 INT i = 0;
1335 INT len;
1336
1337 HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL;
1338 COLORREF crText, color_old;
1339 UINT align_old;
1340 INT mode_old;
1341 PDC pdc;
1342
1343 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0))
1344 {
1345 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
1346 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1347 }
1348
1349 /*
1350 * Set up the fonts (otherwise use default ones)
1351 */
1352
1353 /* Font for the principal version string */
1354 hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
1355 /* Font for the secondary version strings */
1356 hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
1357
1358 if (hFont1)
1359 hOldFont = NtGdiSelectFont(hDC, hFont1);
1360
1361 if (gspv.hbmWallpaper == NULL)
1362 {
1363 /* Retrieve the brush fill colour */
1364 // TODO: The following code constitutes "GreGetBrushColor".
1365 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1366 pdc = DC_LockDc(hDC);
1367 if (pdc)
1368 {
1369 crText = pdc->eboFill.ulRGBColor;
1370 DC_UnlockDc(pdc);
1371 }
1372 else
1373 {
1374 crText = RGB(0, 0, 0);
1375 }
1376 NtGdiSelectBrush(hDC, PreviousBrush);
1377
1378 /* Adjust text colour according to the brush */
1379 if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
1380 crText = RGB(0, 0, 0);
1381 else
1382 crText = RGB(255, 255, 255);
1383 }
1384 else
1385 {
1386 /* Always use white when the text is displayed on top of a wallpaper */
1387 crText = RGB(255, 255, 255);
1388 }
1389
1390 color_old = IntGdiSetTextColor(hDC, crText);
1391 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1392 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1393
1394 /* Display the system version information */
1395 if (!*wszzVersion)
1396 {
1397 Status = GetSystemVersionString(wszzVersion,
1398 ARRAYSIZE(wszzVersion),
1399 InSafeMode,
1400 g_AlwaysDisplayVersion);
1401 if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
1402 {
1403 PWCHAR pstr = wszzVersion;
1404 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
1405 {
1406 VerStrs[i].n = wcslen(pstr);
1407 VerStrs[i].lpstr = pstr;
1408 pstr += (VerStrs[i].n + 1);
1409 }
1410 }
1411 }
1412 else
1413 {
1414 Status = STATUS_SUCCESS;
1415 }
1416 if (NT_SUCCESS(Status) && *wszzVersion)
1417 {
1418 if (!InSafeMode)
1419 {
1420 SIZE Size = {0, 0};
1421 LONG TotalHeight = 0;
1422
1423 /* Normal Mode: multiple version information text separated by newlines */
1424 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1425
1426 /* Compute the heights of the strings */
1427 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1428 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1429 {
1430 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1431 break;
1432
1433 GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
1434 VerStrs[i].y = Size.cy; // Store the string height
1435 TotalHeight += Size.cy;
1436
1437 /* While the first string was using hFont1, all the others use hFont2 */
1438 if (hFont2) NtGdiSelectFont(hDC, hFont2);
1439 }
1440 /* The total height must not exceed the screen height */
1441 TotalHeight = min(TotalHeight, Rect.bottom);
1442
1443 /* Display the strings */
1444 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1445 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1446 {
1447 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1448 break;
1449
1450 TotalHeight -= VerStrs[i].y;
1451 GreExtTextOutW(hDC,
1452 Rect.right - 5,
1453 Rect.bottom - TotalHeight - 5,
1454 0, NULL,
1455 VerStrs[i].lpstr,
1456 VerStrs[i].n,
1457 NULL, 0);
1458
1459 /* While the first string was using hFont1, all the others use hFont2 */
1460 if (hFont2) NtGdiSelectFont(hDC, hFont2);
1461 }
1462 }
1463 else
1464 {
1465 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1466
1467 /* Safe Mode: single version information text in top center */
1468 len = wcslen(wszzVersion);
1469
1470 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
1471 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
1472 }
1473 }
1474
1475 if (InSafeMode)
1476 {
1477 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1478
1479 /* Print Safe Mode text in corners */
1480 len = wcslen(s_wszSafeMode);
1481
1482 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
1483 GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1484 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
1485 GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1486 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
1487 GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1488 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1489 GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1490 }
1491
1492 IntGdiSetBkMode(hDC, mode_old);
1493 IntGdiSetTextAlign(hDC, align_old);
1494 IntGdiSetTextColor(hDC, color_old);
1495
1496 if (hFont2)
1497 GreDeleteObject(hFont2);
1498
1499 if (hFont1)
1500 {
1501 NtGdiSelectFont(hDC, hOldFont);
1502 GreDeleteObject(hFont1);
1503 }
1504 }
1505
1506 return TRUE;
1507 }
1508
1509 static NTSTATUS
1510 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
1511 {
1512 PVOID DesktopHeapSystemBase = NULL;
1513 ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024;
1514 SIZE_T DesktopInfoSize;
1515 ULONG i;
1516
1517 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
1518
1519 RtlZeroMemory(pdesk, sizeof(DESKTOP));
1520
1521 /* Link the desktop with the parent window station */
1522 ObReferenceObject(pwinsta);
1523 pdesk->rpwinstaParent = pwinsta;
1524 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
1525
1526 /* Create the desktop heap */
1527 pdesk->hsectionDesktop = NULL;
1528 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
1529 &DesktopHeapSystemBase,
1530 HeapSize);
1531 if (pdesk->pheapDesktop == NULL)
1532 {
1533 ERR("Failed to create desktop heap!\n");
1534 return STATUS_NO_MEMORY;
1535 }
1536
1537 /* Create DESKTOPINFO */
1538 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
1539 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
1540 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
1541 DesktopInfoSize);
1542 if (pdesk->pDeskInfo == NULL)
1543 {
1544 ERR("Failed to create the DESKTOP structure!\n");
1545 return STATUS_NO_MEMORY;
1546 }
1547
1548 /* Initialize the DESKTOPINFO */
1549 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1550 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1551 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
1552 DesktopName->Buffer,
1553 DesktopName->Length + sizeof(WCHAR));
1554 for (i = 0; i < NB_HOOKS; i++)
1555 {
1556 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
1557 }
1558
1559 InitializeListHead(&pdesk->ShellHookWindows);
1560 InitializeListHead(&pdesk->PtiList);
1561
1562 return STATUS_SUCCESS;
1563 }
1564
1565 /* SYSCALLS *******************************************************************/
1566
1567 /*
1568 * NtUserCreateDesktop
1569 *
1570 * Creates a new desktop.
1571 *
1572 * Parameters
1573 * poaAttribs
1574 * Object Attributes.
1575 *
1576 * lpszDesktopDevice
1577 * Name of the device.
1578 *
1579 * pDeviceMode
1580 * Device Mode.
1581 *
1582 * dwFlags
1583 * Interaction flags.
1584 *
1585 * dwDesiredAccess
1586 * Requested type of access.
1587 *
1588 *
1589 * Return Value
1590 * If the function succeeds, the return value is a handle to the newly
1591 * created desktop. If the specified desktop already exists, the function
1592 * succeeds and returns a handle to the existing desktop. When you are
1593 * finished using the handle, call the CloseDesktop function to close it.
1594 * If the function fails, the return value is NULL.
1595 *
1596 * Status
1597 * @implemented
1598 */
1599
1600 HDESK APIENTRY
1601 NtUserCreateDesktop(
1602 POBJECT_ATTRIBUTES ObjectAttributes,
1603 PUNICODE_STRING lpszDesktopDevice,
1604 LPDEVMODEW lpdmw,
1605 DWORD dwFlags,
1606 ACCESS_MASK dwDesiredAccess)
1607 {
1608 PDESKTOP pdesk = NULL;
1609 NTSTATUS Status = STATUS_SUCCESS;
1610 HDESK hdesk;
1611 BOOLEAN Context = FALSE;
1612 UNICODE_STRING ClassName;
1613 LARGE_STRING WindowName;
1614 BOOL NoHooks = FALSE;
1615 PWND pWnd = NULL;
1616 CREATESTRUCTW Cs;
1617 PTHREADINFO ptiCurrent;
1618 PCLS pcls;
1619
1620 DECLARE_RETURN(HDESK);
1621
1622 TRACE("Enter NtUserCreateDesktop\n");
1623 UserEnterExclusive();
1624
1625 ptiCurrent = PsGetCurrentThreadWin32Thread();
1626 ASSERT(ptiCurrent);
1627 ASSERT(gptiDesktopThread);
1628
1629 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1630 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1631 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1632 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1633
1634 /*
1635 * Try to open already existing desktop
1636 */
1637 Status = ObOpenObjectByName(
1638 ObjectAttributes,
1639 ExDesktopObjectType,
1640 UserMode,
1641 NULL,
1642 dwDesiredAccess,
1643 (PVOID)&Context,
1644 (HANDLE*)&hdesk);
1645 if (!NT_SUCCESS(Status))
1646 {
1647 ERR("ObOpenObjectByName failed to open/create desktop\n");
1648 SetLastNtError(Status);
1649 RETURN(NULL);
1650 }
1651
1652 /* In case the object was not created (eg if it existed), return now */
1653 if (Context == FALSE)
1654 {
1655 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1656 RETURN( hdesk);
1657 }
1658
1659 /* Reference the desktop */
1660 Status = ObReferenceObjectByHandle(hdesk,
1661 0,
1662 ExDesktopObjectType,
1663 KernelMode,
1664 (PVOID*)&pdesk,
1665 NULL);
1666 if (!NT_SUCCESS(Status))
1667 {
1668 ERR("Failed to reference desktop object\n");
1669 SetLastNtError(Status);
1670 RETURN(NULL);
1671 }
1672
1673 /* Get the desktop window class. The thread desktop does not belong to any desktop
1674 * so the classes created there (including the desktop class) are allocated in the shared heap
1675 * It would cause problems if we used a class that belongs to the caller
1676 */
1677 ClassName.Buffer = WC_DESKTOP;
1678 ClassName.Length = 0;
1679 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1680 if (pcls == NULL)
1681 {
1682 ASSERT(FALSE);
1683 RETURN(NULL);
1684 }
1685
1686 RtlZeroMemory(&WindowName, sizeof(WindowName));
1687 RtlZeroMemory(&Cs, sizeof(Cs));
1688 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
1689 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
1690 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
1691 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
1692 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1693 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1694 Cs.lpszName = (LPCWSTR) &WindowName;
1695 Cs.lpszClass = (LPCWSTR) &ClassName;
1696
1697 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1698 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1699 if (pWnd == NULL)
1700 {
1701 ERR("Failed to create desktop window for the new desktop\n");
1702 RETURN(NULL);
1703 }
1704
1705 pdesk->dwSessionId = PsGetCurrentProcessSessionId();
1706 pdesk->DesktopWindow = pWnd->head.h;
1707 pdesk->pDeskInfo->spwnd = pWnd;
1708 pWnd->fnid = FNID_DESKTOP;
1709
1710 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
1711 ClassName.Length = 0;
1712 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1713 if (pcls == NULL)
1714 {
1715 ASSERT(FALSE);
1716 RETURN(NULL);
1717 }
1718
1719 RtlZeroMemory(&WindowName, sizeof(WindowName));
1720 RtlZeroMemory(&Cs, sizeof(Cs));
1721 Cs.cx = Cs.cy = 100;
1722 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1723 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1724 Cs.lpszName = (LPCWSTR) &WindowName;
1725 Cs.lpszClass = (LPCWSTR) &ClassName;
1726 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1727 if (pWnd == NULL)
1728 {
1729 ERR("Failed to create message window for the new desktop\n");
1730 RETURN(NULL);
1731 }
1732
1733 pdesk->spwndMessage = pWnd;
1734 pWnd->fnid = FNID_MESSAGEWND;
1735
1736 /* Now,,,
1737 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1738 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1739 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1740 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1741 The rest is same as message window.
1742 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1743 */
1744 RETURN( hdesk);
1745
1746 CLEANUP:
1747 if (pdesk != NULL)
1748 {
1749 ObDereferenceObject(pdesk);
1750 }
1751 if (_ret_ == NULL && hdesk != NULL)
1752 {
1753 ObCloseHandle(hdesk, UserMode);
1754 }
1755 if (!NoHooks)
1756 {
1757 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1758 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1759 }
1760 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1761 UserLeave();
1762 END_CLEANUP;
1763 }
1764
1765 /*
1766 * NtUserOpenDesktop
1767 *
1768 * Opens an existing desktop.
1769 *
1770 * Parameters
1771 * lpszDesktopName
1772 * Name of the existing desktop.
1773 *
1774 * dwFlags
1775 * Interaction flags.
1776 *
1777 * dwDesiredAccess
1778 * Requested type of access.
1779 *
1780 * Return Value
1781 * Handle to the desktop or zero on failure.
1782 *
1783 * Status
1784 * @implemented
1785 */
1786
1787 HDESK APIENTRY
1788 NtUserOpenDesktop(
1789 POBJECT_ATTRIBUTES ObjectAttributes,
1790 DWORD dwFlags,
1791 ACCESS_MASK dwDesiredAccess)
1792 {
1793 NTSTATUS Status;
1794 HDESK Desktop;
1795
1796 Status = ObOpenObjectByName(
1797 ObjectAttributes,
1798 ExDesktopObjectType,
1799 UserMode,
1800 NULL,
1801 dwDesiredAccess,
1802 NULL,
1803 (HANDLE*)&Desktop);
1804
1805 if (!NT_SUCCESS(Status))
1806 {
1807 ERR("Failed to open desktop\n");
1808 SetLastNtError(Status);
1809 return 0;
1810 }
1811
1812 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1813
1814 return Desktop;
1815 }
1816
1817 /*
1818 * NtUserOpenInputDesktop
1819 *
1820 * Opens the input (interactive) desktop.
1821 *
1822 * Parameters
1823 * dwFlags
1824 * Interaction flags.
1825 *
1826 * fInherit
1827 * Inheritance option.
1828 *
1829 * dwDesiredAccess
1830 * Requested type of access.
1831 *
1832 * Return Value
1833 * Handle to the input desktop or zero on failure.
1834 *
1835 * Status
1836 * @implemented
1837 */
1838
1839 HDESK APIENTRY
1840 NtUserOpenInputDesktop(
1841 DWORD dwFlags,
1842 BOOL fInherit,
1843 ACCESS_MASK dwDesiredAccess)
1844 {
1845 NTSTATUS Status;
1846 HDESK hdesk = NULL;
1847 ULONG HandleAttributes = 0;
1848
1849 UserEnterExclusive();
1850 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1851
1852 if (fInherit) HandleAttributes = OBJ_INHERIT;
1853
1854 /* Create a new handle to the object */
1855 Status = ObOpenObjectByPointer(
1856 gpdeskInputDesktop,
1857 HandleAttributes,
1858 NULL,
1859 dwDesiredAccess,
1860 ExDesktopObjectType,
1861 UserMode,
1862 (PHANDLE)&hdesk);
1863
1864 if (!NT_SUCCESS(Status))
1865 {
1866 ERR("Failed to open input desktop object\n");
1867 SetLastNtError(Status);
1868 }
1869
1870 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1871 UserLeave();
1872 return hdesk;
1873 }
1874
1875 /*
1876 * NtUserCloseDesktop
1877 *
1878 * Closes a desktop handle.
1879 *
1880 * Parameters
1881 * hDesktop
1882 * Handle to the desktop.
1883 *
1884 * Return Value
1885 * Status
1886 *
1887 * Remarks
1888 * The desktop handle can be created with NtUserCreateDesktop or
1889 * NtUserOpenDesktop. This function will fail if any thread in the calling
1890 * process is using the specified desktop handle or if the handle refers
1891 * to the initial desktop of the calling process.
1892 *
1893 * Status
1894 * @implemented
1895 */
1896
1897 BOOL APIENTRY
1898 NtUserCloseDesktop(HDESK hDesktop)
1899 {
1900 PDESKTOP pdesk;
1901 NTSTATUS Status;
1902 DECLARE_RETURN(BOOL);
1903
1904 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1905 UserEnterExclusive();
1906
1907 if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1908 {
1909 ERR("Attempted to close thread desktop\n");
1910 EngSetLastError(ERROR_BUSY);
1911 RETURN(FALSE);
1912 }
1913
1914 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1915 if (!NT_SUCCESS(Status))
1916 {
1917 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1918 RETURN(FALSE);
1919 }
1920
1921 ObDereferenceObject(pdesk);
1922
1923 Status = ZwClose(hDesktop);
1924 if (!NT_SUCCESS(Status))
1925 {
1926 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1927 SetLastNtError(Status);
1928 RETURN(FALSE);
1929 }
1930
1931 RETURN(TRUE);
1932
1933 CLEANUP:
1934 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1935 UserLeave();
1936 END_CLEANUP;
1937 }
1938
1939 /*
1940 * NtUserPaintDesktop
1941 *
1942 * The NtUserPaintDesktop function fills the clipping region in the
1943 * specified device context with the desktop pattern or wallpaper. The
1944 * function is provided primarily for shell desktops.
1945 *
1946 * Parameters
1947 * hDC
1948 * Handle to the device context.
1949 *
1950 * Status
1951 * @implemented
1952 */
1953
1954 BOOL APIENTRY
1955 NtUserPaintDesktop(HDC hDC)
1956 {
1957 BOOL Ret;
1958 UserEnterExclusive();
1959 TRACE("Enter NtUserPaintDesktop\n");
1960 Ret = IntPaintDesktop(hDC);
1961 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1962 UserLeave();
1963 return Ret;
1964 }
1965
1966 /*
1967 * NtUserResolveDesktop
1968 *
1969 * The NtUserResolveDesktop function retrieves handles to the desktop and
1970 * the window station specified by the desktop path string.
1971 *
1972 * Parameters
1973 * ProcessHandle
1974 * Handle to a user process.
1975 *
1976 * DesktopPath
1977 * The desktop path string.
1978 *
1979 * Return Value
1980 * Handle to the desktop (direct return value) and
1981 * handle to the associated window station (by pointer).
1982 * NULL in case of failure.
1983 *
1984 * Remarks
1985 * Callable by CSRSS only.
1986 *
1987 * Status
1988 * @implemented
1989 */
1990
1991 HDESK
1992 APIENTRY
1993 NtUserResolveDesktop(
1994 IN HANDLE ProcessHandle,
1995 IN PUNICODE_STRING DesktopPath,
1996 DWORD dwUnknown,
1997 OUT HWINSTA* phWinSta)
1998 {
1999 NTSTATUS Status;
2000 PEPROCESS Process = NULL;
2001 HWINSTA hWinSta = NULL;
2002 HDESK hDesktop = NULL;
2003
2004 /* Allow only the Console Server to perform this operation (via CSRSS) */
2005 if (PsGetCurrentProcess() != gpepCSRSS)
2006 return NULL;
2007
2008 /* Get the process object the user handle was referencing */
2009 Status = ObReferenceObjectByHandle(ProcessHandle,
2010 PROCESS_QUERY_INFORMATION,
2011 *PsProcessType,
2012 UserMode,
2013 (PVOID*)&Process,
2014 NULL);
2015 if (!NT_SUCCESS(Status)) return NULL;
2016
2017 // UserEnterShared();
2018
2019 _SEH2_TRY
2020 {
2021 UNICODE_STRING CapturedDesktopPath;
2022
2023 /* Capture the user desktop path string */
2024 Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
2025 DesktopPath);
2026 if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
2027
2028 /* Call the internal function */
2029 Status = IntParseDesktopPath(Process,
2030 &CapturedDesktopPath,
2031 &hWinSta,
2032 &hDesktop);
2033 if (!NT_SUCCESS(Status))
2034 {
2035 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
2036 hWinSta = NULL;
2037 hDesktop = NULL;
2038 }
2039
2040 /* Return the window station handle */
2041 *phWinSta = hWinSta;
2042
2043 /* Free the captured string */
2044 if (CapturedDesktopPath.Buffer)
2045 ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
2046 }
2047 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2048 {
2049 Status = _SEH2_GetExceptionCode();
2050 }
2051 _SEH2_END;
2052
2053 Quit:
2054 // UserLeave();
2055
2056 /* Dereference the process object */
2057 ObDereferenceObject(Process);
2058
2059 /* Return the desktop handle */
2060 return hDesktop;
2061 }
2062
2063 /*
2064 * NtUserSwitchDesktop
2065 *
2066 * Sets the current input (interactive) desktop.
2067 *
2068 * Parameters
2069 * hDesktop
2070 * Handle to desktop.
2071 *
2072 * Return Value
2073 * Status
2074 *
2075 * Status
2076 * @unimplemented
2077 */
2078
2079 BOOL APIENTRY
2080 NtUserSwitchDesktop(HDESK hdesk)
2081 {
2082 PDESKTOP pdesk;
2083 NTSTATUS Status;
2084 BOOL bRedrawDesktop;
2085 DECLARE_RETURN(BOOL);
2086
2087 UserEnterExclusive();
2088 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
2089
2090 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
2091 if (!NT_SUCCESS(Status))
2092 {
2093 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
2094 RETURN(FALSE);
2095 }
2096
2097 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
2098 {
2099 ObDereferenceObject(pdesk);
2100 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2101 RETURN(FALSE);
2102 }
2103
2104 if (pdesk == gpdeskInputDesktop)
2105 {
2106 ObDereferenceObject(pdesk);
2107 WARN("NtUserSwitchDesktop called for active desktop\n");
2108 RETURN(TRUE);
2109 }
2110
2111 /*
2112 * Don't allow applications switch the desktop if it's locked, unless the caller
2113 * is the logon application itself
2114 */
2115 if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
2116 gpidLogon != PsGetCurrentProcessId())
2117 {
2118 ObDereferenceObject(pdesk);
2119 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
2120 RETURN(FALSE);
2121 }
2122
2123 if (pdesk->rpwinstaParent != InputWindowStation)
2124 {
2125 ObDereferenceObject(pdesk);
2126 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
2127 RETURN(FALSE);
2128 }
2129
2130 /* FIXME: Fail if the process is associated with a secured
2131 desktop such as Winlogon or Screen-Saver */
2132 /* FIXME: Connect to input device */
2133
2134 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
2135
2136 bRedrawDesktop = FALSE;
2137
2138 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2139 if (gpdeskInputDesktop != NULL)
2140 {
2141 if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
2142 bRedrawDesktop = TRUE;
2143
2144 /* Hide the previous desktop window */
2145 IntHideDesktop(gpdeskInputDesktop);
2146 }
2147
2148 /* Set the active desktop in the desktop's window station. */
2149 InputWindowStation->ActiveDesktop = pdesk;
2150
2151 /* Set the global state. */
2152 gpdeskInputDesktop = pdesk;
2153
2154 /* Show the new desktop window */
2155 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
2156
2157 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
2158 ObDereferenceObject(pdesk);
2159
2160 RETURN(TRUE);
2161
2162 CLEANUP:
2163 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
2164 UserLeave();
2165 END_CLEANUP;
2166 }
2167
2168 /*
2169 * NtUserGetThreadDesktop
2170 *
2171 * Status
2172 * @implemented
2173 */
2174
2175 HDESK APIENTRY
2176 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
2177 {
2178 NTSTATUS Status;
2179 PETHREAD Thread;
2180 PDESKTOP DesktopObject;
2181 HDESK Ret, hThreadDesktop;
2182 OBJECT_HANDLE_INFORMATION HandleInformation;
2183 DECLARE_RETURN(HDESK);
2184
2185 UserEnterExclusive();
2186 TRACE("Enter NtUserGetThreadDesktop\n");
2187
2188 if (!dwThreadId)
2189 {
2190 EngSetLastError(ERROR_INVALID_PARAMETER);
2191 RETURN(0);
2192 }
2193
2194 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
2195 if (!NT_SUCCESS(Status))
2196 {
2197 EngSetLastError(ERROR_INVALID_PARAMETER);
2198 RETURN(0);
2199 }
2200
2201 if (Thread->ThreadsProcess == PsGetCurrentProcess())
2202 {
2203 /* Just return the handle, we queried the desktop handle of a thread running
2204 in the same context */
2205 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
2206 ObDereferenceObject(Thread);
2207 RETURN(Ret);
2208 }
2209
2210 /* Get the desktop handle and the desktop of the thread */
2211 if (!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
2212 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
2213 {
2214 ObDereferenceObject(Thread);
2215 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
2216 RETURN(NULL);
2217 }
2218
2219 /* We could just use DesktopObject instead of looking up the handle, but latter
2220 may be a bit safer (e.g. when the desktop is being destroyed */
2221 /* Switch into the context of the thread we're trying to get the desktop from,
2222 so we can use the handle */
2223 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
2224 Status = ObReferenceObjectByHandle(hThreadDesktop,
2225 GENERIC_ALL,
2226 ExDesktopObjectType,
2227 UserMode,
2228 (PVOID*)&DesktopObject,
2229 &HandleInformation);
2230 KeDetachProcess();
2231
2232 /* The handle couldn't be found, there's nothing to get... */
2233 if (!NT_SUCCESS(Status))
2234 {
2235 ObDereferenceObject(Thread);
2236 RETURN(NULL);
2237 }
2238
2239 /* Lookup our handle table if we can find a handle to the desktop object,
2240 if not, create one */
2241 Ret = IntGetDesktopObjectHandle(DesktopObject);
2242
2243 /* All done, we got a valid handle to the desktop */
2244 ObDereferenceObject(DesktopObject);
2245 ObDereferenceObject(Thread);
2246 RETURN(Ret);
2247
2248 CLEANUP:
2249 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
2250 UserLeave();
2251 END_CLEANUP;
2252 }
2253
2254 static NTSTATUS
2255 IntUnmapDesktopView(IN PDESKTOP pdesk)
2256 {
2257 PPROCESSINFO ppi;
2258 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2259 NTSTATUS Status = STATUS_SUCCESS;
2260
2261 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
2262
2263 ppi = PsGetCurrentProcessWin32Process();
2264
2265 /*
2266 * Unmap if we're the last thread using the desktop.
2267 * Start the search at the next mapping: skip the first entry
2268 * as it must be the global user heap mapping.
2269 */
2270 PrevLink = &ppi->HeapMappings.Next;
2271 HeapMapping = *PrevLink;
2272 while (HeapMapping != NULL)
2273 {
2274 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2275 {
2276 if (--HeapMapping->Count == 0)
2277 {
2278 *PrevLink = HeapMapping->Next;
2279
2280 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
2281 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
2282 HeapMapping->UserMapping);
2283
2284 ObDereferenceObject(pdesk);
2285
2286 UserHeapFree(HeapMapping);
2287 break;
2288 }
2289 }
2290
2291 PrevLink = &HeapMapping->Next;
2292 HeapMapping = HeapMapping->Next;
2293 }
2294
2295 return Status;
2296 }
2297
2298 static NTSTATUS
2299 IntMapDesktopView(IN PDESKTOP pdesk)
2300 {
2301 PPROCESSINFO ppi;
2302 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2303 PVOID UserBase = NULL;
2304 SIZE_T ViewSize = 0;
2305 LARGE_INTEGER Offset;
2306 NTSTATUS Status;
2307
2308 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
2309
2310 ppi = PsGetCurrentProcessWin32Process();
2311
2312 /*
2313 * Find out if another thread already mapped the desktop heap.
2314 * Start the search at the next mapping: skip the first entry
2315 * as it must be the global user heap mapping.
2316 */
2317 PrevLink = &ppi->HeapMappings.Next;
2318 HeapMapping = *PrevLink;
2319 while (HeapMapping != NULL)
2320 {
2321 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2322 {
2323 HeapMapping->Count++;
2324 return STATUS_SUCCESS;
2325 }
2326
2327 PrevLink = &HeapMapping->Next;
2328 HeapMapping = HeapMapping->Next;
2329 }
2330
2331 /* We're the first, map the heap */
2332 Offset.QuadPart = 0;
2333 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
2334 PsGetCurrentProcess(),
2335 &UserBase,
2336 0,
2337 0,
2338 &Offset,
2339 &ViewSize,
2340 ViewUnmap,
2341 SEC_NO_CHANGE,
2342 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2343 if (!NT_SUCCESS(Status))
2344 {
2345 ERR("Failed to map desktop\n");
2346 return Status;
2347 }
2348
2349 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
2350
2351 /* Add the mapping */
2352 HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
2353 if (HeapMapping == NULL)
2354 {
2355 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
2356 ERR("UserHeapAlloc() failed!\n");
2357 return STATUS_NO_MEMORY;
2358 }
2359
2360 HeapMapping->Next = NULL;
2361 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
2362 HeapMapping->UserMapping = UserBase;
2363 HeapMapping->Limit = ViewSize;
2364 HeapMapping->Count = 1;
2365 *PrevLink = HeapMapping;
2366
2367 ObReferenceObject(pdesk);
2368
2369 return STATUS_SUCCESS;
2370 }
2371
2372 BOOL
2373 IntSetThreadDesktop(IN HDESK hDesktop,
2374 IN BOOL FreeOnFailure)
2375 {
2376 PDESKTOP pdesk = NULL, pdeskOld;
2377 HDESK hdeskOld;
2378 PTHREADINFO pti;
2379 NTSTATUS Status;
2380 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
2381 PCLIENTINFO pci;
2382
2383 ASSERT(NtCurrentTeb());
2384
2385 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
2386
2387 pti = PsGetCurrentThreadWin32Thread();
2388 pci = pti->pClientInfo;
2389
2390 /* If the caller gave us a desktop, ensure it is valid */
2391 if (hDesktop != NULL)
2392 {
2393 /* Validate the new desktop. */
2394 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
2395 if (!NT_SUCCESS(Status))
2396 {
2397 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
2398 return FALSE;
2399 }
2400
2401 if (pti->rpdesk == pdesk)
2402 {
2403 /* Nothing to do */
2404 ObDereferenceObject(pdesk);
2405 return TRUE;
2406 }
2407 }
2408
2409 /* Make sure that we don't own any window in the current desktop */
2410 if (!IsListEmpty(&pti->WindowListHead))
2411 {
2412 if (pdesk)
2413 ObDereferenceObject(pdesk);
2414 ERR("Attempted to change thread desktop although the thread has windows!\n");
2415 EngSetLastError(ERROR_BUSY);
2416 return FALSE;
2417 }
2418
2419 /* Desktop is being re-set so clear out foreground. */
2420 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
2421 {
2422 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2423 IntSetFocusMessageQueue(NULL);
2424 }
2425
2426 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2427 if (pdesk != NULL)
2428 {
2429 Status = IntMapDesktopView(pdesk);
2430 if (!NT_SUCCESS(Status))
2431 {
2432 ERR("Failed to map desktop heap!\n");
2433 ObDereferenceObject(pdesk);
2434 SetLastNtError(Status);
2435 return FALSE;
2436 }
2437
2438 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
2439 if (pctiNew == NULL)
2440 {
2441 ERR("Failed to allocate new pcti\n");
2442 IntUnmapDesktopView(pdesk);
2443 ObDereferenceObject(pdesk);
2444 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2445 return FALSE;
2446 }
2447 }
2448
2449 /* free all classes or move them to the shared heap */
2450 if (pti->rpdesk != NULL)
2451 {
2452 if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
2453 {
2454 ERR("Failed to move process classes to shared heap!\n");
2455 if (pdesk)
2456 {
2457 DesktopHeapFree(pdesk, pctiNew);
2458 IntUnmapDesktopView(pdesk);
2459 ObDereferenceObject(pdesk);
2460 }
2461 return FALSE;
2462 }
2463 }
2464
2465 pdeskOld = pti->rpdesk;
2466 hdeskOld = pti->hdesk;
2467 if (pti->pcti != &pti->cti)
2468 pctiOld = pti->pcti;
2469 else
2470 pctiOld = NULL;
2471
2472 /* do the switch */
2473 if (pdesk != NULL)
2474 {
2475 pti->rpdesk = pdesk;
2476 pti->hdesk = hDesktop;
2477 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
2478 pti->pcti = pctiNew;
2479
2480 pci->ulClientDelta = DesktopHeapGetUserDelta();
2481 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
2482 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
2483
2484 /* initialize the new pcti */
2485 if (pctiOld != NULL)
2486 {
2487 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
2488 }
2489 else
2490 {
2491 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
2492 pci->fsHooks = pti->fsHooks;
2493 pci->dwTIFlags = pti->TIF_flags;
2494 }
2495 }
2496 else
2497 {
2498 pti->rpdesk = NULL;
2499 pti->hdesk = NULL;
2500 pti->pDeskInfo = NULL;
2501 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
2502 pci->ulClientDelta = 0;
2503 pci->pDeskInfo = NULL;
2504 pci->pClientThreadInfo = NULL;
2505 }
2506
2507 /* clean up the old desktop */
2508 if (pdeskOld != NULL)
2509 {
2510 RemoveEntryList(&pti->PtiLink);
2511 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
2512 IntUnmapDesktopView(pdeskOld);
2513 ObDereferenceObject(pdeskOld);
2514 ZwClose(hdeskOld);
2515 }
2516
2517 if (pdesk)
2518 {
2519 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
2520 }
2521
2522 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
2523
2524 return TRUE;
2525 }
2526
2527 /*
2528 * NtUserSetThreadDesktop
2529 *
2530 * Status
2531 * @implemented
2532 */
2533
2534 BOOL APIENTRY
2535 NtUserSetThreadDesktop(HDESK hDesktop)
2536 {
2537 BOOL ret = FALSE;
2538
2539 UserEnterExclusive();
2540
2541 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2542 // here too and set the NT error level. Q. Is it necessary to have the validation
2543 // in IntSetThreadDesktop? Is it needed there too?
2544 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
2545 ret = IntSetThreadDesktop(hDesktop, FALSE);
2546
2547 UserLeave();
2548
2549 return ret;
2550 }
2551
2552 /* EOF */