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