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