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