[WIN32K:NTUSER] -IntSetThreadDesktop: Don't close the previous desktop handle. Fixes...
[reactos.git] / reactos / win32ss / user / ntuser / desktop.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Desktops
5 * FILE: subsystems/win32/win32k/ntuser/desktop.c
6 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <win32k.h>
12 DBG_DEFAULT_CHANNEL(UserDesktop);
13
14 #include <reactos/buildno.h>
15
16 static NTSTATUS
17 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta);
18
19 static NTSTATUS
20 IntMapDesktopView(IN PDESKTOP pdesk);
21
22 static NTSTATUS
23 IntUnmapDesktopView(IN PDESKTOP pdesk);
24
25 static VOID
26 IntFreeDesktopHeap(IN PDESKTOP pdesk);
27
28 /* GLOBALS *******************************************************************/
29
30 /* These can be changed via csrss startup, these are defaults */
31 DWORD gdwDesktopSectionSize = 512;
32 DWORD gdwNOIOSectionSize = 128; // A guess, for one or more of the first three system desktops.
33
34 /* Currently active desktop */
35 PDESKTOP gpdeskInputDesktop = NULL;
36 HDC ScreenDeviceContext = NULL;
37 PTHREADINFO gptiDesktopThread = NULL;
38 HCURSOR gDesktopCursor = NULL;
39
40 /* OBJECT CALLBACKS **********************************************************/
41
42 NTSTATUS
43 APIENTRY
44 IntDesktopObjectParse(IN PVOID ParseObject,
45 IN PVOID ObjectType,
46 IN OUT PACCESS_STATE AccessState,
47 IN KPROCESSOR_MODE AccessMode,
48 IN ULONG Attributes,
49 IN OUT PUNICODE_STRING CompleteName,
50 IN OUT PUNICODE_STRING RemainingName,
51 IN OUT PVOID Context OPTIONAL,
52 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
53 OUT PVOID *Object)
54 {
55 NTSTATUS Status;
56 PDESKTOP Desktop;
57 OBJECT_ATTRIBUTES ObjectAttributes;
58 PLIST_ENTRY NextEntry, ListHead;
59 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
60 UNICODE_STRING DesktopName;
61 PBOOLEAN pContext = (PBOOLEAN) Context;
62
63 if (pContext)
64 *pContext = FALSE;
65
66 /* Set the list pointers and loop the window station */
67 ListHead = &WinStaObject->DesktopListHead;
68 NextEntry = ListHead->Flink;
69 while (NextEntry != ListHead)
70 {
71 /* Get the current desktop */
72 Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
73
74 /* Get the desktop name */
75 ASSERT(Desktop->pDeskInfo != NULL);
76 RtlInitUnicodeString(&DesktopName, Desktop->pDeskInfo->szDesktopName);
77
78 /* Compare the name */
79 if (RtlEqualUnicodeString(RemainingName,
80 &DesktopName,
81 (Attributes & OBJ_CASE_INSENSITIVE) != 0))
82 {
83 /* We found a match. Did this come from a create? */
84 if (Context)
85 {
86 /* Unless OPEN_IF was given, fail with an error */
87 if (!(Attributes & OBJ_OPENIF))
88 {
89 /* Name collision */
90 return STATUS_OBJECT_NAME_COLLISION;
91 }
92 else
93 {
94 /* Otherwise, return with a warning only */
95 Status = STATUS_OBJECT_NAME_EXISTS;
96 }
97 }
98 else
99 {
100 /* This was a real open, so this is OK */
101 Status = STATUS_SUCCESS;
102 }
103
104 /* Reference the desktop and return it */
105 ObReferenceObject(Desktop);
106 *Object = Desktop;
107 return Status;
108 }
109
110 /* Go to the next desktop */
111 NextEntry = NextEntry->Flink;
112 }
113
114 /* If we got here but this isn't a create, just fail */
115 if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
116
117 /* Create the desktop object */
118 InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
119 Status = ObCreateObject(KernelMode,
120 ExDesktopObjectType,
121 &ObjectAttributes,
122 KernelMode,
123 NULL,
124 sizeof(DESKTOP),
125 0,
126 0,
127 (PVOID*)&Desktop);
128 if (!NT_SUCCESS(Status)) return Status;
129
130 /* Initialize the desktop */
131 Status = UserInitializeDesktop(Desktop, RemainingName, WinStaObject);
132 if (!NT_SUCCESS(Status))
133 {
134 ObDereferenceObject(Desktop);
135 return Status;
136 }
137
138 /* Set the desktop object and return success */
139 *Object = Desktop;
140 *pContext = TRUE;
141 return STATUS_SUCCESS;
142 }
143
144 NTSTATUS
145 NTAPI
146 IntDesktopObjectDelete(
147 _In_ PVOID Parameters)
148 {
149 PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
150 PDESKTOP pdesk = (PDESKTOP)DeleteParameters->Object;
151
152 TRACE("Deleting desktop object 0x%p\n", pdesk);
153
154 if (pdesk->pDeskInfo &&
155 pdesk->pDeskInfo->spwnd)
156 {
157 ASSERT(pdesk->pDeskInfo->spwnd->spwndChild == NULL);
158 co_UserDestroyWindow(pdesk->pDeskInfo->spwnd);
159 }
160
161 if (pdesk->spwndMessage)
162 co_UserDestroyWindow(pdesk->spwndMessage);
163
164 /* Remove the desktop from the window station's list of associcated desktops */
165 RemoveEntryList(&pdesk->ListEntry);
166
167 /* Free the heap */
168 IntFreeDesktopHeap(pdesk);
169
170 ObDereferenceObject(pdesk->rpwinstaParent);
171
172 return STATUS_SUCCESS;
173 }
174
175 NTSTATUS
176 NTAPI
177 IntDesktopOkToClose(
178 _In_ PVOID Parameters)
179 {
180 PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
181 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
182
183 if (pti == NULL)
184 {
185 /* This happens when we leak desktop handles */
186 return STATUS_SUCCESS;
187 }
188
189 /* Do not allow the current desktop or the initial desktop to be closed */
190 if (OkToCloseParameters->Handle == pti->ppi->hdeskStartup ||
191 OkToCloseParameters->Handle == pti->hdesk)
192 {
193 return STATUS_ACCESS_DENIED;
194 }
195
196 return STATUS_SUCCESS;
197 }
198
199 NTSTATUS
200 NTAPI
201 IntDesktopObjectOpen(
202 _In_ PVOID Parameters)
203 {
204 PWIN32_OPENMETHOD_PARAMETERS OpenParameters = Parameters;
205 PPROCESSINFO ppi = PsGetProcessWin32Process(OpenParameters->Process);
206 if (ppi == NULL)
207 return STATUS_SUCCESS;
208
209 return IntMapDesktopView((PDESKTOP)OpenParameters->Object);
210 }
211
212 NTSTATUS
213 NTAPI
214 IntDesktopObjectClose(
215 _In_ PVOID Parameters)
216 {
217 PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters = Parameters;
218 PPROCESSINFO ppi = PsGetProcessWin32Process(CloseParameters->Process);
219 if (ppi == NULL)
220 {
221 /* This happens when the process leaks desktop handles.
222 * At this point the PPROCESSINFO is already destroyed */
223 return STATUS_SUCCESS;
224 }
225
226 return IntUnmapDesktopView((PDESKTOP)CloseParameters->Object);
227 }
228
229
230 /* PRIVATE FUNCTIONS **********************************************************/
231
232 INIT_FUNCTION
233 NTSTATUS
234 NTAPI
235 InitDesktopImpl(VOID)
236 {
237 GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
238 DESKTOP_WRITE,
239 DESKTOP_EXECUTE,
240 DESKTOP_ALL_ACCESS};
241
242 /* Set Desktop Object Attributes */
243 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
244 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
245 ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
246 return STATUS_SUCCESS;
247 }
248
249 static NTSTATUS
250 GetSystemVersionString(OUT PWSTR pwszzVersion,
251 IN SIZE_T cchDest,
252 IN BOOLEAN InSafeMode,
253 IN BOOLEAN AppendNtSystemRoot)
254 {
255 NTSTATUS Status;
256
257 RTL_OSVERSIONINFOEXW VerInfo;
258 UNICODE_STRING BuildLabString;
259 UNICODE_STRING CSDVersionString;
260 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable[] =
261 {
262 {
263 NULL,
264 RTL_QUERY_REGISTRY_DIRECT,
265 L"BuildLab",
266 &BuildLabString,
267 REG_NONE, NULL, 0
268 },
269 {
270 NULL,
271 RTL_QUERY_REGISTRY_DIRECT,
272 L"CSDVersion",
273 &CSDVersionString,
274 REG_NONE, NULL, 0
275 },
276
277 {0}
278 };
279
280 WCHAR BuildLabBuffer[256];
281 WCHAR VersionBuffer[256];
282 PWCHAR EndBuffer;
283
284 VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
285
286 /*
287 * This call is uniquely used to retrieve the current CSD numbers.
288 * All the rest (major, minor, ...) is either retrieved from the
289 * SharedUserData structure, or from the registry.
290 */
291 RtlGetVersion((PRTL_OSVERSIONINFOW)&VerInfo);
292
293 /*
294 * - Retrieve the BuildLab string from the registry (set by the kernel).
295 * - In kernel-mode, szCSDVersion is not initialized. Initialize it
296 * and query its value from the registry.
297 */
298 RtlZeroMemory(BuildLabBuffer, sizeof(BuildLabBuffer));
299 RtlInitEmptyUnicodeString(&BuildLabString,
300 BuildLabBuffer,
301 sizeof(BuildLabBuffer));
302 RtlZeroMemory(VerInfo.szCSDVersion, sizeof(VerInfo.szCSDVersion));
303 RtlInitEmptyUnicodeString(&CSDVersionString,
304 VerInfo.szCSDVersion,
305 sizeof(VerInfo.szCSDVersion));
306 Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
307 L"",
308 VersionConfigurationTable,
309 NULL,
310 NULL);
311 if (!NT_SUCCESS(Status))
312 {
313 /* Indicate nothing is there */
314 BuildLabString.Length = 0;
315 CSDVersionString.Length = 0;
316 }
317 /* NULL-terminate the strings */
318 BuildLabString.Buffer[BuildLabString.Length / sizeof(WCHAR)] = UNICODE_NULL;
319 CSDVersionString.Buffer[CSDVersionString.Length / sizeof(WCHAR)] = UNICODE_NULL;
320
321 EndBuffer = VersionBuffer;
322 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString.Length)
323 {
324 /* Print the version string */
325 Status = RtlStringCbPrintfExW(VersionBuffer,
326 sizeof(VersionBuffer),
327 &EndBuffer,
328 NULL,
329 0,
330 L": %wZ",
331 &CSDVersionString);
332 if (!NT_SUCCESS(Status))
333 {
334 /* No version, NULL-terminate the string */
335 *EndBuffer = UNICODE_NULL;
336 }
337 }
338 else
339 {
340 /* No version, NULL-terminate the string */
341 *EndBuffer = UNICODE_NULL;
342 }
343
344 if (InSafeMode)
345 {
346 /* String for Safe Mode */
347 Status = RtlStringCchPrintfW(pwszzVersion,
348 cchDest,
349 L"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
350 KERNEL_VERSION_STR,
351 &BuildLabString,
352 SharedUserData->NtMajorVersion,
353 SharedUserData->NtMinorVersion,
354 (VerInfo.dwBuildNumber & 0xFFFF),
355 VersionBuffer);
356
357 if (AppendNtSystemRoot && NT_SUCCESS(Status))
358 {
359 Status = RtlStringCbPrintfW(VersionBuffer,
360 sizeof(VersionBuffer),
361 L" - %s\n",
362 SharedUserData->NtSystemRoot);
363 if (NT_SUCCESS(Status))
364 {
365 /* Replace the last newline by a NULL, before concatenating */
366 EndBuffer = wcsrchr(pwszzVersion, L'\n');
367 if (EndBuffer) *EndBuffer = UNICODE_NULL;
368
369 /* The concatenated string has a terminating newline */
370 Status = RtlStringCchCatW(pwszzVersion,
371 cchDest,
372 VersionBuffer);
373 if (!NT_SUCCESS(Status))
374 {
375 /* Concatenation failed, put back the newline */
376 if (EndBuffer) *EndBuffer = L'\n';
377 }
378 }
379
380 /* Override any failures as the NtSystemRoot string is optional */
381 Status = STATUS_SUCCESS;
382 }
383 }
384 else
385 {
386 /* Multi-string for Normal Mode */
387 Status = RtlStringCchPrintfW(pwszzVersion,
388 cchDest,
389 L"ReactOS Version %S\n"
390 L"Build %wZ\n"
391 L"Reporting NT %u.%u (Build %u%s)\n",
392 KERNEL_VERSION_STR,
393 &BuildLabString,
394 SharedUserData->NtMajorVersion,
395 SharedUserData->NtMinorVersion,
396 (VerInfo.dwBuildNumber & 0xFFFF),
397 VersionBuffer);
398
399 if (AppendNtSystemRoot && NT_SUCCESS(Status))
400 {
401 Status = RtlStringCbPrintfW(VersionBuffer,
402 sizeof(VersionBuffer),
403 L"%s\n",
404 SharedUserData->NtSystemRoot);
405 if (NT_SUCCESS(Status))
406 {
407 Status = RtlStringCchCatW(pwszzVersion,
408 cchDest,
409 VersionBuffer);
410 }
411
412 /* Override any failures as the NtSystemRoot string is optional */
413 Status = STATUS_SUCCESS;
414 }
415 }
416
417 if (!NT_SUCCESS(Status))
418 {
419 /* Fall-back string */
420 Status = RtlStringCchPrintfW(pwszzVersion,
421 cchDest,
422 L"ReactOS Version %S %wZ\n",
423 KERNEL_VERSION_STR,
424 &BuildLabString);
425 if (!NT_SUCCESS(Status))
426 {
427 /* General failure, NULL-terminate the string */
428 pwszzVersion[0] = UNICODE_NULL;
429 }
430 }
431
432 /*
433 * Convert the string separators (newlines) into NULLs
434 * and NULL-terminate the multi-string.
435 */
436 while (*pwszzVersion)
437 {
438 EndBuffer = wcschr(pwszzVersion, L'\n');
439 if (!EndBuffer) break;
440 pwszzVersion = EndBuffer;
441
442 *pwszzVersion++ = UNICODE_NULL;
443 }
444 *pwszzVersion = UNICODE_NULL;
445
446 return Status;
447 }
448
449
450 NTSTATUS FASTCALL
451 IntParseDesktopPath(PEPROCESS Process,
452 PUNICODE_STRING DesktopPath,
453 HWINSTA *hWinSta,
454 HDESK *hDesktop)
455 {
456 OBJECT_ATTRIBUTES ObjectAttributes;
457 UNICODE_STRING ObjectName;
458 NTSTATUS Status;
459 WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL;
460
461 ASSERT(hWinSta);
462 ASSERT(hDesktop);
463 ASSERT(DesktopPath);
464
465 *hWinSta = NULL;
466 *hDesktop = NULL;
467
468 if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
469 {
470 /*
471 * Parse the desktop path string which can be in the form "WinSta\Desktop"
472 * or just "Desktop". In latter case WinSta0 will be used.
473 */
474
475 pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\');
476 if (pwstrDesktop != NULL)
477 {
478 *pwstrDesktop = 0;
479 pwstrDesktop++;
480 pwstrWinsta = DesktopPath->Buffer;
481 }
482 else
483 {
484 pwstrDesktop = DesktopPath->Buffer;
485 pwstrWinsta = NULL;
486 }
487
488 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop);
489 }
490
491 #if 0
492 /* Search the process handle table for (inherited) window station
493 handles, use a more appropriate one than WinSta0 if possible. */
494 if (!ObFindHandleForObject(Process,
495 NULL,
496 ExWindowStationObjectType,
497 NULL,
498 (PHANDLE)hWinSta))
499 #endif
500 {
501 /* We had no luck searching for opened handles, use WinSta0 now */
502 if (!pwstrWinsta)
503 pwstrWinsta = L"WinSta0";
504 }
505
506 #if 0
507 /* Search the process handle table for (inherited) desktop
508 handles, use a more appropriate one than Default if possible. */
509 if (!ObFindHandleForObject(Process,
510 NULL,
511 ExDesktopObjectType,
512 NULL,
513 (PHANDLE)hDesktop))
514 #endif
515 {
516 /* We had no luck searching for opened handles, use Desktop now */
517 if (!pwstrDesktop)
518 pwstrDesktop = L"Default";
519 }
520
521 if (*hWinSta == NULL)
522 {
523 swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta);
524 RtlInitUnicodeString( &ObjectName, wstrWinstaFullName);
525
526 TRACE("parsed initial winsta: %wZ\n", &ObjectName);
527
528 /* Open the window station */
529 InitializeObjectAttributes(&ObjectAttributes,
530 &ObjectName,
531 OBJ_CASE_INSENSITIVE,
532 NULL,
533 NULL);
534
535 Status = ObOpenObjectByName(&ObjectAttributes,
536 ExWindowStationObjectType,
537 KernelMode,
538 NULL,
539 WINSTA_ACCESS_ALL,
540 NULL,
541 (HANDLE*)hWinSta);
542
543 if (!NT_SUCCESS(Status))
544 {
545 SetLastNtError(Status);
546 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName );
547 return Status;
548 }
549 }
550
551 if (*hDesktop == NULL)
552 {
553 RtlInitUnicodeString(&ObjectName, pwstrDesktop);
554
555 TRACE("parsed initial desktop: %wZ\n", &ObjectName);
556
557 /* Open the desktop object */
558 InitializeObjectAttributes(&ObjectAttributes,
559 &ObjectName,
560 OBJ_CASE_INSENSITIVE,
561 *hWinSta,
562 NULL);
563
564 Status = ObOpenObjectByName(&ObjectAttributes,
565 ExDesktopObjectType,
566 KernelMode,
567 NULL,
568 DESKTOP_ALL_ACCESS,
569 NULL,
570 (HANDLE*)hDesktop);
571
572 if (!NT_SUCCESS(Status))
573 {
574 *hDesktop = NULL;
575 NtClose(*hWinSta);
576 *hWinSta = NULL;
577 SetLastNtError(Status);
578 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName);
579 return Status;
580 }
581 }
582 return STATUS_SUCCESS;
583 }
584
585 /*
586 * IntValidateDesktopHandle
587 *
588 * Validates the desktop handle.
589 *
590 * Remarks
591 * If the function succeeds, the handle remains referenced. If the
592 * fucntion fails, last error is set.
593 */
594
595 NTSTATUS FASTCALL
596 IntValidateDesktopHandle(
597 HDESK Desktop,
598 KPROCESSOR_MODE AccessMode,
599 ACCESS_MASK DesiredAccess,
600 PDESKTOP *Object)
601 {
602 NTSTATUS Status;
603
604 Status = ObReferenceObjectByHandle(
605 Desktop,
606 DesiredAccess,
607 ExDesktopObjectType,
608 AccessMode,
609 (PVOID*)Object,
610 NULL);
611
612 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
613 Desktop, *Object, DesiredAccess, Status);
614
615 if (!NT_SUCCESS(Status))
616 SetLastNtError(Status);
617
618 return Status;
619 }
620
621 PDESKTOP FASTCALL
622 IntGetActiveDesktop(VOID)
623 {
624 return gpdeskInputDesktop;
625 }
626
627 /*
628 * Returns or creates a handle to the desktop object
629 */
630 HDESK FASTCALL
631 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
632 {
633 NTSTATUS Status;
634 HDESK Ret;
635
636 ASSERT(DesktopObject);
637
638 if (!ObFindHandleForObject(PsGetCurrentProcess(),
639 DesktopObject,
640 ExDesktopObjectType,
641 NULL,
642 (PHANDLE)&Ret))
643 {
644 Status = ObOpenObjectByPointer(DesktopObject,
645 0,
646 NULL,
647 0,
648 ExDesktopObjectType,
649 UserMode,
650 (PHANDLE)&Ret);
651 if (!NT_SUCCESS(Status))
652 {
653 /* Unable to create a handle */
654 ERR("Unable to create a desktop handle\n");
655 return NULL;
656 }
657 }
658 else
659 {
660 TRACE("Got handle: %p\n", Ret);
661 }
662
663 return Ret;
664 }
665
666 PUSER_MESSAGE_QUEUE FASTCALL
667 IntGetFocusMessageQueue(VOID)
668 {
669 PDESKTOP pdo = IntGetActiveDesktop();
670 if (!pdo)
671 {
672 TRACE("No active desktop\n");
673 return(NULL);
674 }
675 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
676 }
677
678 VOID FASTCALL
679 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
680 {
681 PUSER_MESSAGE_QUEUE Old;
682 PDESKTOP pdo = IntGetActiveDesktop();
683 if (!pdo)
684 {
685 TRACE("No active desktop\n");
686 return;
687 }
688 if (NewQueue != NULL)
689 {
690 if (NewQueue->Desktop != NULL)
691 {
692 TRACE("Message Queue already attached to another desktop!\n");
693 return;
694 }
695 IntReferenceMessageQueue(NewQueue);
696 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
697 }
698 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
699 if (Old != NULL)
700 {
701 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
702 gpqForegroundPrev = Old;
703 IntDereferenceMessageQueue(Old);
704 }
705 // Only one Q can have active foreground even when there are more than one desktop.
706 if (NewQueue)
707 {
708 gpqForeground = pdo->ActiveMessageQueue;
709 }
710 else
711 {
712 gpqForeground = NULL;
713 ERR("ptiLastInput is CLEARED!!\n");
714 ptiLastInput = NULL; // ReactOS hacks,,,, should check for process death.
715 }
716 }
717
718 PWND FASTCALL
719 IntGetThreadDesktopWindow(PTHREADINFO pti)
720 {
721 if (!pti) pti = PsGetCurrentThreadWin32Thread();
722 if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
723 return NULL;
724 }
725
726 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
727 {
728 if (pWnd->head.rpdesk &&
729 pWnd->head.rpdesk->pDeskInfo)
730 return pWnd->head.rpdesk->pDeskInfo->spwnd;
731 return NULL;
732 }
733
734 HWND FASTCALL IntGetDesktopWindow(VOID)
735 {
736 PDESKTOP pdo = IntGetActiveDesktop();
737 if (!pdo)
738 {
739 TRACE("No active desktop\n");
740 return NULL;
741 }
742 return pdo->DesktopWindow;
743 }
744
745 PWND FASTCALL UserGetDesktopWindow(VOID)
746 {
747 PDESKTOP pdo = IntGetActiveDesktop();
748
749 if (!pdo)
750 {
751 TRACE("No active desktop\n");
752 return NULL;
753 }
754 // return pdo->pDeskInfo->spwnd;
755 return UserGetWindowObject(pdo->DesktopWindow);
756 }
757
758 HWND FASTCALL IntGetMessageWindow(VOID)
759 {
760 PDESKTOP pdo = IntGetActiveDesktop();
761
762 if (!pdo)
763 {
764 TRACE("No active desktop\n");
765 return NULL;
766 }
767 return pdo->spwndMessage->head.h;
768 }
769
770 PWND FASTCALL UserGetMessageWindow(VOID)
771 {
772 PDESKTOP pdo = IntGetActiveDesktop();
773
774 if (!pdo)
775 {
776 TRACE("No active desktop\n");
777 return NULL;
778 }
779 return pdo->spwndMessage;
780 }
781
782 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
783 {
784 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
785 PDESKTOP pdo = pti->rpdesk;
786 if (NULL == pdo)
787 {
788 ERR("Thread doesn't have a desktop\n");
789 return NULL;
790 }
791 return pdo->DesktopWindow;
792 }
793
794 /* PUBLIC FUNCTIONS ***********************************************************/
795
796 BOOL FASTCALL
797 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
798 {
799 PAINTSTRUCT Ps;
800 ULONG Value;
801 //ERR("DesktopWindowProc\n");
802
803 *lResult = 0;
804
805 switch (Msg)
806 {
807 case WM_NCCREATE:
808 if (!Wnd->fnid)
809 {
810 Wnd->fnid = FNID_DESKTOP;
811 }
812 *lResult = (LRESULT)TRUE;
813 return TRUE;
814
815 case WM_CREATE:
816 Value = HandleToULong(PsGetCurrentProcessId());
817 // Save Process ID
818 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
819 Value = HandleToULong(PsGetCurrentThreadId());
820 // Save Thread ID
821 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
822 case WM_CLOSE:
823 return TRUE;
824
825 case WM_DISPLAYCHANGE:
826 co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
827 return TRUE;
828
829 case WM_ERASEBKGND:
830 IntPaintDesktop((HDC)wParam);
831 *lResult = 1;
832 return TRUE;
833
834 case WM_PAINT:
835 {
836 if (IntBeginPaint(Wnd, &Ps))
837 {
838 IntEndPaint(Wnd, &Ps);
839 }
840 return TRUE;
841 }
842 case WM_SYSCOLORCHANGE:
843 co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
844 return TRUE;
845
846 case WM_SETCURSOR:
847 {
848 PCURICON_OBJECT pcurOld, pcurNew;
849 pcurNew = UserGetCurIconObject(gDesktopCursor);
850 if (!pcurNew)
851 {
852 return TRUE;
853 }
854
855 pcurNew->CURSORF_flags |= CURSORF_CURRENT;
856 pcurOld = UserSetCursor(pcurNew, FALSE);
857 if (pcurOld)
858 {
859 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
860 UserDereferenceObject(pcurOld);
861 }
862 return TRUE;
863 }
864
865 case WM_WINDOWPOSCHANGING:
866 {
867 PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
868 if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
869 {
870 HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop);
871 IntSetThreadDesktop(hdesk, FALSE);
872 }
873 break;
874 }
875 default:
876 TRACE("DWP calling IDWP Msg %d\n",Msg);
877 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
878 }
879 return TRUE; /* We are done. Do not do any callbacks to user mode */
880 }
881
882 BOOL FASTCALL
883 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
884 {
885 *lResult = 0;
886
887 switch(Msg)
888 {
889 case WM_NCCREATE:
890 pwnd->fnid |= FNID_MESSAGEWND;
891 *lResult = (LRESULT)TRUE;
892 break;
893 case WM_DESTROY:
894 pwnd->fnid |= FNID_DESTROY;
895 break;
896 default:
897 ERR("UMWP calling IDWP\n");
898 *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE);
899 }
900
901 return TRUE; /* We are done. Do not do any callbacks to user mode */
902 }
903
904 VOID NTAPI DesktopThreadMain(VOID)
905 {
906 BOOL Ret;
907 MSG Msg;
908
909 gptiDesktopThread = PsGetCurrentThreadWin32Thread();
910
911 UserEnterExclusive();
912
913 /* Register system classes. This thread does not belong to any desktop so the
914 classes will be allocated from the shared heap */
915 UserRegisterSystemClasses();
916
917 while (TRUE)
918 {
919 Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
920 if (Ret)
921 {
922 IntDispatchMessage(&Msg);
923 }
924 }
925
926 UserLeave();
927 }
928
929 HDC FASTCALL
930 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd)
931 {
932 PWND DesktopObject = 0;
933 HDC DesktopHDC = 0;
934
935 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.right = UserGetSystemMetrics(SM_CXSCREEN);
1350 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
1351 }
1352
1353 /*
1354 * Set up the fonts (otherwise use default ones)
1355 */
1356
1357 /* Font for the principal version string */
1358 hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
1359 /* Font for the secondary version strings */
1360 hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
1361
1362 if (hFont1)
1363 hOldFont = NtGdiSelectFont(hDC, hFont1);
1364
1365 if (gspv.hbmWallpaper == NULL)
1366 {
1367 /* Retrieve the brush fill colour */
1368 // TODO: The following code constitutes "GreGetBrushColor".
1369 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1370 pdc = DC_LockDc(hDC);
1371 if (pdc)
1372 {
1373 crText = pdc->eboFill.ulRGBColor;
1374 DC_UnlockDc(pdc);
1375 }
1376 else
1377 {
1378 crText = RGB(0, 0, 0);
1379 }
1380 NtGdiSelectBrush(hDC, PreviousBrush);
1381
1382 /* Adjust text colour according to the brush */
1383 if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
1384 crText = RGB(0, 0, 0);
1385 else
1386 crText = RGB(255, 255, 255);
1387 }
1388 else
1389 {
1390 /* Always use white when the text is displayed on top of a wallpaper */
1391 crText = RGB(255, 255, 255);
1392 }
1393
1394 color_old = IntGdiSetTextColor(hDC, crText);
1395 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
1396 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
1397
1398 /* Display the system version information */
1399 if (!*wszzVersion)
1400 {
1401 Status = GetSystemVersionString(wszzVersion,
1402 ARRAYSIZE(wszzVersion),
1403 InSafeMode,
1404 g_AlwaysDisplayVersion);
1405 if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
1406 {
1407 PWCHAR pstr = wszzVersion;
1408 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
1409 {
1410 VerStrs[i].n = wcslen(pstr);
1411 VerStrs[i].lpstr = pstr;
1412 pstr += (VerStrs[i].n + 1);
1413 }
1414 }
1415 }
1416 else
1417 {
1418 Status = STATUS_SUCCESS;
1419 }
1420 if (NT_SUCCESS(Status) && *wszzVersion)
1421 {
1422 if (!InSafeMode)
1423 {
1424 SIZE Size = {0, 0};
1425 LONG TotalHeight = 0;
1426
1427 /* Normal Mode: multiple version information text separated by newlines */
1428 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1429
1430 /* Compute the heights of the strings */
1431 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1432 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1433 {
1434 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1435 break;
1436
1437 GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
1438 VerStrs[i].y = Size.cy; // Store the string height
1439 TotalHeight += Size.cy;
1440
1441 /* While the first string was using hFont1, all the others use hFont2 */
1442 if (hFont2) NtGdiSelectFont(hDC, hFont2);
1443 }
1444 /* The total height must not exceed the screen height */
1445 TotalHeight = min(TotalHeight, Rect.bottom);
1446
1447 /* Display the strings */
1448 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1449 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
1450 {
1451 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
1452 break;
1453
1454 TotalHeight -= VerStrs[i].y;
1455 GreExtTextOutW(hDC,
1456 Rect.right - 5,
1457 Rect.bottom - TotalHeight - 5,
1458 0, NULL,
1459 VerStrs[i].lpstr,
1460 VerStrs[i].n,
1461 NULL, 0);
1462
1463 /* While the first string was using hFont1, all the others use hFont2 */
1464 if (hFont2) NtGdiSelectFont(hDC, hFont2);
1465 }
1466 }
1467 else
1468 {
1469 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1470
1471 /* Safe Mode: single version information text in top center */
1472 len = wcslen(wszzVersion);
1473
1474 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
1475 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
1476 }
1477 }
1478
1479 if (InSafeMode)
1480 {
1481 if (hFont1) NtGdiSelectFont(hDC, hFont1);
1482
1483 /* Print Safe Mode text in corners */
1484 len = wcslen(s_wszSafeMode);
1485
1486 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
1487 GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1488 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
1489 GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
1490 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
1491 GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1492 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
1493 GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
1494 }
1495
1496 IntGdiSetBkMode(hDC, mode_old);
1497 IntGdiSetTextAlign(hDC, align_old);
1498 IntGdiSetTextColor(hDC, color_old);
1499
1500 if (hFont2)
1501 GreDeleteObject(hFont2);
1502
1503 if (hFont1)
1504 {
1505 NtGdiSelectFont(hDC, hOldFont);
1506 GreDeleteObject(hFont1);
1507 }
1508 }
1509
1510 return TRUE;
1511 }
1512
1513 static NTSTATUS
1514 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
1515 {
1516 PVOID DesktopHeapSystemBase = NULL;
1517 ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024;
1518 SIZE_T DesktopInfoSize;
1519 ULONG i;
1520
1521 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
1522
1523 RtlZeroMemory(pdesk, sizeof(DESKTOP));
1524
1525 /* Link the desktop with the parent window station */
1526 ObReferenceObject(pwinsta);
1527 pdesk->rpwinstaParent = pwinsta;
1528 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
1529
1530 /* Create the desktop heap */
1531 pdesk->hsectionDesktop = NULL;
1532 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
1533 &DesktopHeapSystemBase,
1534 HeapSize);
1535 if (pdesk->pheapDesktop == NULL)
1536 {
1537 ERR("Failed to create desktop heap!\n");
1538 return STATUS_NO_MEMORY;
1539 }
1540
1541 /* Create DESKTOPINFO */
1542 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
1543 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
1544 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
1545 DesktopInfoSize);
1546 if (pdesk->pDeskInfo == NULL)
1547 {
1548 ERR("Failed to create the DESKTOP structure!\n");
1549 return STATUS_NO_MEMORY;
1550 }
1551
1552 /* Initialize the DESKTOPINFO */
1553 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
1554 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
1555 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
1556 DesktopName->Buffer,
1557 DesktopName->Length + sizeof(WCHAR));
1558 for (i = 0; i < NB_HOOKS; i++)
1559 {
1560 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
1561 }
1562
1563 InitializeListHead(&pdesk->ShellHookWindows);
1564 InitializeListHead(&pdesk->PtiList);
1565
1566 return STATUS_SUCCESS;
1567 }
1568
1569 /* SYSCALLS *******************************************************************/
1570
1571 /*
1572 * NtUserCreateDesktop
1573 *
1574 * Creates a new desktop.
1575 *
1576 * Parameters
1577 * poaAttribs
1578 * Object Attributes.
1579 *
1580 * lpszDesktopDevice
1581 * Name of the device.
1582 *
1583 * pDeviceMode
1584 * Device Mode.
1585 *
1586 * dwFlags
1587 * Interaction flags.
1588 *
1589 * dwDesiredAccess
1590 * Requested type of access.
1591 *
1592 *
1593 * Return Value
1594 * If the function succeeds, the return value is a handle to the newly
1595 * created desktop. If the specified desktop already exists, the function
1596 * succeeds and returns a handle to the existing desktop. When you are
1597 * finished using the handle, call the CloseDesktop function to close it.
1598 * If the function fails, the return value is NULL.
1599 *
1600 * Status
1601 * @implemented
1602 */
1603
1604 HDESK APIENTRY
1605 NtUserCreateDesktop(
1606 POBJECT_ATTRIBUTES ObjectAttributes,
1607 PUNICODE_STRING lpszDesktopDevice,
1608 LPDEVMODEW lpdmw,
1609 DWORD dwFlags,
1610 ACCESS_MASK dwDesiredAccess)
1611 {
1612 PDESKTOP pdesk = NULL;
1613 NTSTATUS Status = STATUS_SUCCESS;
1614 HDESK hdesk;
1615 BOOLEAN Context = FALSE;
1616 UNICODE_STRING ClassName;
1617 LARGE_STRING WindowName;
1618 BOOL NoHooks = FALSE;
1619 PWND pWnd = NULL;
1620 CREATESTRUCTW Cs;
1621 PTHREADINFO ptiCurrent;
1622 PCLS pcls;
1623
1624 DECLARE_RETURN(HDESK);
1625
1626 TRACE("Enter NtUserCreateDesktop\n");
1627 UserEnterExclusive();
1628
1629 ptiCurrent = PsGetCurrentThreadWin32Thread();
1630 ASSERT(ptiCurrent);
1631 ASSERT(gptiDesktopThread);
1632
1633 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */
1634 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
1635 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
1636 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1637
1638 /*
1639 * Try to open already existing desktop
1640 */
1641 Status = ObOpenObjectByName(
1642 ObjectAttributes,
1643 ExDesktopObjectType,
1644 UserMode,
1645 NULL,
1646 dwDesiredAccess,
1647 (PVOID)&Context,
1648 (HANDLE*)&hdesk);
1649 if (!NT_SUCCESS(Status))
1650 {
1651 ERR("ObOpenObjectByName failed to open/create desktop\n");
1652 SetLastNtError(Status);
1653 RETURN(NULL);
1654 }
1655
1656 /* In case the object was not created (eg if it existed), return now */
1657 if (Context == FALSE)
1658 {
1659 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName);
1660 RETURN( hdesk);
1661 }
1662
1663 /* Reference the desktop */
1664 Status = ObReferenceObjectByHandle(hdesk,
1665 0,
1666 ExDesktopObjectType,
1667 KernelMode,
1668 (PVOID*)&pdesk,
1669 NULL);
1670 if (!NT_SUCCESS(Status))
1671 {
1672 ERR("Failed to reference desktop object\n");
1673 SetLastNtError(Status);
1674 RETURN(NULL);
1675 }
1676
1677 /* Get the desktop window class. The thread desktop does not belong to any desktop
1678 * so the classes created there (including the desktop class) are allocated in the shared heap
1679 * It would cause problems if we used a class that belongs to the caller
1680 */
1681 ClassName.Buffer = WC_DESKTOP;
1682 ClassName.Length = 0;
1683 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1684 if (pcls == NULL)
1685 {
1686 ASSERT(FALSE);
1687 RETURN(NULL);
1688 }
1689
1690 RtlZeroMemory(&WindowName, sizeof(WindowName));
1691 RtlZeroMemory(&Cs, sizeof(Cs));
1692 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
1693 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
1694 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
1695 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
1696 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1697 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1698 Cs.lpszName = (LPCWSTR) &WindowName;
1699 Cs.lpszClass = (LPCWSTR) &ClassName;
1700
1701 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */
1702 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1703 if (pWnd == NULL)
1704 {
1705 ERR("Failed to create desktop window for the new desktop\n");
1706 RETURN(NULL);
1707 }
1708
1709 pdesk->dwSessionId = PsGetCurrentProcessSessionId();
1710 pdesk->DesktopWindow = pWnd->head.h;
1711 pdesk->pDeskInfo->spwnd = pWnd;
1712 pWnd->fnid = FNID_DESKTOP;
1713
1714 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
1715 ClassName.Length = 0;
1716 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
1717 if (pcls == NULL)
1718 {
1719 ASSERT(FALSE);
1720 RETURN(NULL);
1721 }
1722
1723 RtlZeroMemory(&WindowName, sizeof(WindowName));
1724 RtlZeroMemory(&Cs, sizeof(Cs));
1725 Cs.cx = Cs.cy = 100;
1726 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
1727 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
1728 Cs.lpszName = (LPCWSTR) &WindowName;
1729 Cs.lpszClass = (LPCWSTR) &ClassName;
1730 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk);
1731 if (pWnd == NULL)
1732 {
1733 ERR("Failed to create message window for the new desktop\n");
1734 RETURN(NULL);
1735 }
1736
1737 pdesk->spwndMessage = pWnd;
1738 pWnd->fnid = FNID_MESSAGEWND;
1739
1740 /* Now,,,
1741 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
1742 Create Tooltip. Saved in DesktopObject->spwndTooltip.
1743 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
1744 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
1745 The rest is same as message window.
1746 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
1747 */
1748 RETURN( hdesk);
1749
1750 CLEANUP:
1751 if (pdesk != NULL)
1752 {
1753 ObDereferenceObject(pdesk);
1754 }
1755 if (_ret_ == NULL && hdesk != NULL)
1756 {
1757 ObCloseHandle(hdesk, UserMode);
1758 }
1759 if (!NoHooks)
1760 {
1761 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
1762 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
1763 }
1764 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_);
1765 UserLeave();
1766 END_CLEANUP;
1767 }
1768
1769 /*
1770 * NtUserOpenDesktop
1771 *
1772 * Opens an existing desktop.
1773 *
1774 * Parameters
1775 * lpszDesktopName
1776 * Name of the existing desktop.
1777 *
1778 * dwFlags
1779 * Interaction flags.
1780 *
1781 * dwDesiredAccess
1782 * Requested type of access.
1783 *
1784 * Return Value
1785 * Handle to the desktop or zero on failure.
1786 *
1787 * Status
1788 * @implemented
1789 */
1790
1791 HDESK APIENTRY
1792 NtUserOpenDesktop(
1793 POBJECT_ATTRIBUTES ObjectAttributes,
1794 DWORD dwFlags,
1795 ACCESS_MASK dwDesiredAccess)
1796 {
1797 NTSTATUS Status;
1798 HDESK Desktop;
1799
1800 Status = ObOpenObjectByName(
1801 ObjectAttributes,
1802 ExDesktopObjectType,
1803 UserMode,
1804 NULL,
1805 dwDesiredAccess,
1806 NULL,
1807 (HANDLE*)&Desktop);
1808
1809 if (!NT_SUCCESS(Status))
1810 {
1811 ERR("Failed to open desktop\n");
1812 SetLastNtError(Status);
1813 return NULL;
1814 }
1815
1816 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
1817
1818 return Desktop;
1819 }
1820
1821 /*
1822 * NtUserOpenInputDesktop
1823 *
1824 * Opens the input (interactive) desktop.
1825 *
1826 * Parameters
1827 * dwFlags
1828 * Interaction flags.
1829 *
1830 * fInherit
1831 * Inheritance option.
1832 *
1833 * dwDesiredAccess
1834 * Requested type of access.
1835 *
1836 * Return Value
1837 * Handle to the input desktop or zero on failure.
1838 *
1839 * Status
1840 * @implemented
1841 */
1842
1843 HDESK APIENTRY
1844 NtUserOpenInputDesktop(
1845 DWORD dwFlags,
1846 BOOL fInherit,
1847 ACCESS_MASK dwDesiredAccess)
1848 {
1849 NTSTATUS Status;
1850 HDESK hdesk = NULL;
1851 ULONG HandleAttributes = 0;
1852
1853 UserEnterExclusive();
1854 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
1855
1856 if (fInherit) HandleAttributes = OBJ_INHERIT;
1857
1858 /* Create a new handle to the object */
1859 Status = ObOpenObjectByPointer(
1860 gpdeskInputDesktop,
1861 HandleAttributes,
1862 NULL,
1863 dwDesiredAccess,
1864 ExDesktopObjectType,
1865 UserMode,
1866 (PHANDLE)&hdesk);
1867
1868 if (!NT_SUCCESS(Status))
1869 {
1870 ERR("Failed to open input desktop object\n");
1871 SetLastNtError(Status);
1872 }
1873
1874 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
1875 UserLeave();
1876 return hdesk;
1877 }
1878
1879 /*
1880 * NtUserCloseDesktop
1881 *
1882 * Closes a desktop handle.
1883 *
1884 * Parameters
1885 * hDesktop
1886 * Handle to the desktop.
1887 *
1888 * Return Value
1889 * Status
1890 *
1891 * Remarks
1892 * The desktop handle can be created with NtUserCreateDesktop or
1893 * NtUserOpenDesktop. This function will fail if any thread in the calling
1894 * process is using the specified desktop handle or if the handle refers
1895 * to the initial desktop of the calling process.
1896 *
1897 * Status
1898 * @implemented
1899 */
1900
1901 BOOL APIENTRY
1902 NtUserCloseDesktop(HDESK hDesktop)
1903 {
1904 PDESKTOP pdesk;
1905 NTSTATUS Status;
1906 DECLARE_RETURN(BOOL);
1907
1908 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop);
1909 UserEnterExclusive();
1910
1911 if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
1912 {
1913 ERR("Attempted to close thread desktop\n");
1914 EngSetLastError(ERROR_BUSY);
1915 RETURN(FALSE);
1916 }
1917
1918 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
1919 if (!NT_SUCCESS(Status))
1920 {
1921 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
1922 RETURN(FALSE);
1923 }
1924
1925 ObDereferenceObject(pdesk);
1926
1927 Status = ZwClose(hDesktop);
1928 if (!NT_SUCCESS(Status))
1929 {
1930 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
1931 SetLastNtError(Status);
1932 RETURN(FALSE);
1933 }
1934
1935 RETURN(TRUE);
1936
1937 CLEANUP:
1938 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
1939 UserLeave();
1940 END_CLEANUP;
1941 }
1942
1943 /*
1944 * NtUserPaintDesktop
1945 *
1946 * The NtUserPaintDesktop function fills the clipping region in the
1947 * specified device context with the desktop pattern or wallpaper. The
1948 * function is provided primarily for shell desktops.
1949 *
1950 * Parameters
1951 * hDC
1952 * Handle to the device context.
1953 *
1954 * Status
1955 * @implemented
1956 */
1957
1958 BOOL APIENTRY
1959 NtUserPaintDesktop(HDC hDC)
1960 {
1961 BOOL Ret;
1962 UserEnterExclusive();
1963 TRACE("Enter NtUserPaintDesktop\n");
1964 Ret = IntPaintDesktop(hDC);
1965 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
1966 UserLeave();
1967 return Ret;
1968 }
1969
1970 /*
1971 * NtUserResolveDesktop
1972 *
1973 * The NtUserResolveDesktop function retrieves handles to the desktop and
1974 * the window station specified by the desktop path string.
1975 *
1976 * Parameters
1977 * ProcessHandle
1978 * Handle to a user process.
1979 *
1980 * DesktopPath
1981 * The desktop path string.
1982 *
1983 * Return Value
1984 * Handle to the desktop (direct return value) and
1985 * handle to the associated window station (by pointer).
1986 * NULL in case of failure.
1987 *
1988 * Remarks
1989 * Callable by CSRSS only.
1990 *
1991 * Status
1992 * @implemented
1993 */
1994
1995 HDESK
1996 APIENTRY
1997 NtUserResolveDesktop(
1998 IN HANDLE ProcessHandle,
1999 IN PUNICODE_STRING DesktopPath,
2000 DWORD dwUnknown,
2001 OUT HWINSTA* phWinSta)
2002 {
2003 NTSTATUS Status;
2004 PEPROCESS Process = NULL;
2005 HWINSTA hWinSta = NULL;
2006 HDESK hDesktop = NULL;
2007
2008 /* Allow only the Console Server to perform this operation (via CSRSS) */
2009 if (PsGetCurrentProcess() != gpepCSRSS)
2010 return NULL;
2011
2012 /* Get the process object the user handle was referencing */
2013 Status = ObReferenceObjectByHandle(ProcessHandle,
2014 PROCESS_QUERY_INFORMATION,
2015 *PsProcessType,
2016 UserMode,
2017 (PVOID*)&Process,
2018 NULL);
2019 if (!NT_SUCCESS(Status)) return NULL;
2020
2021 // UserEnterShared();
2022
2023 _SEH2_TRY
2024 {
2025 UNICODE_STRING CapturedDesktopPath;
2026
2027 /* Capture the user desktop path string */
2028 Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath,
2029 DesktopPath);
2030 if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit);
2031
2032 /* Call the internal function */
2033 Status = IntParseDesktopPath(Process,
2034 &CapturedDesktopPath,
2035 &hWinSta,
2036 &hDesktop);
2037 if (!NT_SUCCESS(Status))
2038 {
2039 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status);
2040 hWinSta = NULL;
2041 hDesktop = NULL;
2042 }
2043
2044 /* Return the window station handle */
2045 *phWinSta = hWinSta;
2046
2047 /* Free the captured string */
2048 if (CapturedDesktopPath.Buffer)
2049 ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING);
2050 }
2051 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2052 {
2053 Status = _SEH2_GetExceptionCode();
2054 }
2055 _SEH2_END;
2056
2057 Quit:
2058 // UserLeave();
2059
2060 /* Dereference the process object */
2061 ObDereferenceObject(Process);
2062
2063 /* Return the desktop handle */
2064 return hDesktop;
2065 }
2066
2067 /*
2068 * NtUserSwitchDesktop
2069 *
2070 * Sets the current input (interactive) desktop.
2071 *
2072 * Parameters
2073 * hDesktop
2074 * Handle to desktop.
2075 *
2076 * Return Value
2077 * Status
2078 *
2079 * Status
2080 * @unimplemented
2081 */
2082
2083 BOOL APIENTRY
2084 NtUserSwitchDesktop(HDESK hdesk)
2085 {
2086 PDESKTOP pdesk;
2087 NTSTATUS Status;
2088 BOOL bRedrawDesktop;
2089 DECLARE_RETURN(BOOL);
2090
2091 UserEnterExclusive();
2092 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
2093
2094 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk);
2095 if (!NT_SUCCESS(Status))
2096 {
2097 ERR("Validation of desktop handle (0x%p) failed\n", hdesk);
2098 RETURN(FALSE);
2099 }
2100
2101 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
2102 {
2103 ObDereferenceObject(pdesk);
2104 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2105 RETURN(FALSE);
2106 }
2107
2108 if (pdesk == gpdeskInputDesktop)
2109 {
2110 ObDereferenceObject(pdesk);
2111 WARN("NtUserSwitchDesktop called for active desktop\n");
2112 RETURN(TRUE);
2113 }
2114
2115 /*
2116 * Don't allow applications switch the desktop if it's locked, unless the caller
2117 * is the logon application itself
2118 */
2119 if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
2120 gpidLogon != PsGetCurrentProcessId())
2121 {
2122 ObDereferenceObject(pdesk);
2123 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
2124 RETURN(FALSE);
2125 }
2126
2127 if (pdesk->rpwinstaParent != InputWindowStation)
2128 {
2129 ObDereferenceObject(pdesk);
2130 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
2131 RETURN(FALSE);
2132 }
2133
2134 /* FIXME: Fail if the process is associated with a secured
2135 desktop such as Winlogon or Screen-Saver */
2136 /* FIXME: Connect to input device */
2137
2138 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
2139
2140 bRedrawDesktop = FALSE;
2141
2142 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2143 if (gpdeskInputDesktop != NULL)
2144 {
2145 if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
2146 bRedrawDesktop = TRUE;
2147
2148 /* Hide the previous desktop window */
2149 IntHideDesktop(gpdeskInputDesktop);
2150 }
2151
2152 /* Set the active desktop in the desktop's window station. */
2153 InputWindowStation->ActiveDesktop = pdesk;
2154
2155 /* Set the global state. */
2156 gpdeskInputDesktop = pdesk;
2157
2158 /* Show the new desktop window */
2159 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
2160
2161 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
2162 ObDereferenceObject(pdesk);
2163
2164 RETURN(TRUE);
2165
2166 CLEANUP:
2167 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
2168 UserLeave();
2169 END_CLEANUP;
2170 }
2171
2172 /*
2173 * NtUserGetThreadDesktop
2174 *
2175 * Status
2176 * @implemented
2177 */
2178
2179 HDESK APIENTRY
2180 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1)
2181 {
2182 NTSTATUS Status;
2183 PETHREAD Thread;
2184 PDESKTOP DesktopObject;
2185 HDESK Ret, hThreadDesktop;
2186 OBJECT_HANDLE_INFORMATION HandleInformation;
2187 DECLARE_RETURN(HDESK);
2188
2189 UserEnterExclusive();
2190 TRACE("Enter NtUserGetThreadDesktop\n");
2191
2192 if (!dwThreadId)
2193 {
2194 EngSetLastError(ERROR_INVALID_PARAMETER);
2195 RETURN(0);
2196 }
2197
2198 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread);
2199 if (!NT_SUCCESS(Status))
2200 {
2201 EngSetLastError(ERROR_INVALID_PARAMETER);
2202 RETURN(0);
2203 }
2204
2205 if (Thread->ThreadsProcess == PsGetCurrentProcess())
2206 {
2207 /* Just return the handle, we queried the desktop handle of a thread running
2208 in the same context */
2209 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk;
2210 ObDereferenceObject(Thread);
2211 RETURN(Ret);
2212 }
2213
2214 /* Get the desktop handle and the desktop of the thread */
2215 if (!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) ||
2216 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk))
2217 {
2218 ObDereferenceObject(Thread);
2219 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
2220 RETURN(NULL);
2221 }
2222
2223 /* We could just use DesktopObject instead of looking up the handle, but latter
2224 may be a bit safer (e.g. when the desktop is being destroyed */
2225 /* Switch into the context of the thread we're trying to get the desktop from,
2226 so we can use the handle */
2227 KeAttachProcess(&Thread->ThreadsProcess->Pcb);
2228 Status = ObReferenceObjectByHandle(hThreadDesktop,
2229 GENERIC_ALL,
2230 ExDesktopObjectType,
2231 UserMode,
2232 (PVOID*)&DesktopObject,
2233 &HandleInformation);
2234 KeDetachProcess();
2235
2236 /* The handle couldn't be found, there's nothing to get... */
2237 if (!NT_SUCCESS(Status))
2238 {
2239 ObDereferenceObject(Thread);
2240 RETURN(NULL);
2241 }
2242
2243 /* Lookup our handle table if we can find a handle to the desktop object,
2244 if not, create one */
2245 Ret = IntGetDesktopObjectHandle(DesktopObject);
2246
2247 /* All done, we got a valid handle to the desktop */
2248 ObDereferenceObject(DesktopObject);
2249 ObDereferenceObject(Thread);
2250 RETURN(Ret);
2251
2252 CLEANUP:
2253 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_);
2254 UserLeave();
2255 END_CLEANUP;
2256 }
2257
2258 static NTSTATUS
2259 IntUnmapDesktopView(IN PDESKTOP pdesk)
2260 {
2261 PPROCESSINFO ppi;
2262 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2263 NTSTATUS Status = STATUS_SUCCESS;
2264
2265 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
2266
2267 ppi = PsGetCurrentProcessWin32Process();
2268
2269 /*
2270 * Unmap if we're the last thread using the desktop.
2271 * Start the search at the next mapping: skip the first entry
2272 * as it must be the global user heap mapping.
2273 */
2274 PrevLink = &ppi->HeapMappings.Next;
2275 HeapMapping = *PrevLink;
2276 while (HeapMapping != NULL)
2277 {
2278 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2279 {
2280 if (--HeapMapping->Count == 0)
2281 {
2282 *PrevLink = HeapMapping->Next;
2283
2284 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
2285 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
2286 HeapMapping->UserMapping);
2287
2288 ObDereferenceObject(pdesk);
2289
2290 UserHeapFree(HeapMapping);
2291 break;
2292 }
2293 }
2294
2295 PrevLink = &HeapMapping->Next;
2296 HeapMapping = HeapMapping->Next;
2297 }
2298
2299 return Status;
2300 }
2301
2302 static NTSTATUS
2303 IntMapDesktopView(IN PDESKTOP pdesk)
2304 {
2305 PPROCESSINFO ppi;
2306 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
2307 PVOID UserBase = NULL;
2308 SIZE_T ViewSize = 0;
2309 LARGE_INTEGER Offset;
2310 NTSTATUS Status;
2311
2312 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
2313
2314 ppi = PsGetCurrentProcessWin32Process();
2315
2316 /*
2317 * Find out if another thread already mapped the desktop heap.
2318 * Start the search at the next mapping: skip the first entry
2319 * as it must be the global user heap mapping.
2320 */
2321 PrevLink = &ppi->HeapMappings.Next;
2322 HeapMapping = *PrevLink;
2323 while (HeapMapping != NULL)
2324 {
2325 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
2326 {
2327 HeapMapping->Count++;
2328 return STATUS_SUCCESS;
2329 }
2330
2331 PrevLink = &HeapMapping->Next;
2332 HeapMapping = HeapMapping->Next;
2333 }
2334
2335 /* We're the first, map the heap */
2336 Offset.QuadPart = 0;
2337 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
2338 PsGetCurrentProcess(),
2339 &UserBase,
2340 0,
2341 0,
2342 &Offset,
2343 &ViewSize,
2344 ViewUnmap,
2345 SEC_NO_CHANGE,
2346 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
2347 if (!NT_SUCCESS(Status))
2348 {
2349 ERR("Failed to map desktop\n");
2350 return Status;
2351 }
2352
2353 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
2354
2355 /* Add the mapping */
2356 HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
2357 if (HeapMapping == NULL)
2358 {
2359 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
2360 ERR("UserHeapAlloc() failed!\n");
2361 return STATUS_NO_MEMORY;
2362 }
2363
2364 HeapMapping->Next = NULL;
2365 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
2366 HeapMapping->UserMapping = UserBase;
2367 HeapMapping->Limit = ViewSize;
2368 HeapMapping->Count = 1;
2369 *PrevLink = HeapMapping;
2370
2371 ObReferenceObject(pdesk);
2372
2373 return STATUS_SUCCESS;
2374 }
2375
2376 BOOL
2377 IntSetThreadDesktop(IN HDESK hDesktop,
2378 IN BOOL FreeOnFailure)
2379 {
2380 PDESKTOP pdesk = NULL, pdeskOld;
2381 PTHREADINFO pti;
2382 NTSTATUS Status;
2383 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
2384 PCLIENTINFO pci;
2385
2386 ASSERT(NtCurrentTeb());
2387
2388 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
2389
2390 pti = PsGetCurrentThreadWin32Thread();
2391 pci = pti->pClientInfo;
2392
2393 /* If the caller gave us a desktop, ensure it is valid */
2394 if (hDesktop != NULL)
2395 {
2396 /* Validate the new desktop. */
2397 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk);
2398 if (!NT_SUCCESS(Status))
2399 {
2400 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop);
2401 return FALSE;
2402 }
2403
2404 if (pti->rpdesk == pdesk)
2405 {
2406 /* Nothing to do */
2407 ObDereferenceObject(pdesk);
2408 return TRUE;
2409 }
2410 }
2411
2412 /* Make sure that we don't own any window in the current desktop */
2413 if (!IsListEmpty(&pti->WindowListHead))
2414 {
2415 if (pdesk)
2416 ObDereferenceObject(pdesk);
2417 ERR("Attempted to change thread desktop although the thread has windows!\n");
2418 EngSetLastError(ERROR_BUSY);
2419 return FALSE;
2420 }
2421
2422 /* Desktop is being re-set so clear out foreground. */
2423 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
2424 {
2425 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
2426 IntSetFocusMessageQueue(NULL);
2427 }
2428
2429 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
2430 if (pdesk != NULL)
2431 {
2432 Status = IntMapDesktopView(pdesk);
2433 if (!NT_SUCCESS(Status))
2434 {
2435 ERR("Failed to map desktop heap!\n");
2436 ObDereferenceObject(pdesk);
2437 SetLastNtError(Status);
2438 return FALSE;
2439 }
2440
2441 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO));
2442 if (pctiNew == NULL)
2443 {
2444 ERR("Failed to allocate new pcti\n");
2445 IntUnmapDesktopView(pdesk);
2446 ObDereferenceObject(pdesk);
2447 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2448 return FALSE;
2449 }
2450 }
2451
2452 /* free all classes or move them to the shared heap */
2453 if (pti->rpdesk != NULL)
2454 {
2455 if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
2456 {
2457 ERR("Failed to move process classes to shared heap!\n");
2458 if (pdesk)
2459 {
2460 DesktopHeapFree(pdesk, pctiNew);
2461 IntUnmapDesktopView(pdesk);
2462 ObDereferenceObject(pdesk);
2463 }
2464 return FALSE;
2465 }
2466 }
2467
2468 pdeskOld = pti->rpdesk;
2469 if (pti->pcti != &pti->cti)
2470 pctiOld = pti->pcti;
2471 else
2472 pctiOld = NULL;
2473
2474 /* do the switch */
2475 if (pdesk != NULL)
2476 {
2477 pti->rpdesk = pdesk;
2478 pti->hdesk = hDesktop;
2479 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
2480 pti->pcti = pctiNew;
2481
2482 pci->ulClientDelta = DesktopHeapGetUserDelta();
2483 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
2484 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
2485
2486 /* initialize the new pcti */
2487 if (pctiOld != NULL)
2488 {
2489 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
2490 }
2491 else
2492 {
2493 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
2494 pci->fsHooks = pti->fsHooks;
2495 pci->dwTIFlags = pti->TIF_flags;
2496 }
2497 }
2498 else
2499 {
2500 pti->rpdesk = NULL;
2501 pti->hdesk = NULL;
2502 pti->pDeskInfo = NULL;
2503 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
2504 pci->ulClientDelta = 0;
2505 pci->pDeskInfo = NULL;
2506 pci->pClientThreadInfo = NULL;
2507 }
2508
2509 /* clean up the old desktop */
2510 if (pdeskOld != NULL)
2511 {
2512 RemoveEntryList(&pti->PtiLink);
2513 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
2514 IntUnmapDesktopView(pdeskOld);
2515 ObDereferenceObject(pdeskOld);
2516 }
2517
2518 if (pdesk)
2519 {
2520 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
2521 }
2522
2523 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
2524
2525 return TRUE;
2526 }
2527
2528 /*
2529 * NtUserSetThreadDesktop
2530 *
2531 * Status
2532 * @implemented
2533 */
2534
2535 BOOL APIENTRY
2536 NtUserSetThreadDesktop(HDESK hDesktop)
2537 {
2538 BOOL ret = FALSE;
2539
2540 UserEnterExclusive();
2541
2542 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
2543 // here too and set the NT error level. Q. Is it necessary to have the validation
2544 // in IntSetThreadDesktop? Is it needed there too?
2545 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
2546 ret = IntSetThreadDesktop(hDesktop, FALSE);
2547
2548 UserLeave();
2549
2550 return ret;
2551 }
2552
2553 /* EOF */