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