[REACTOS] Cleanup INIT and some PAGE section allocations
[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 PKEVENT gpDesktopThreadStartedEvent = NULL;
40
41 /* OBJECT CALLBACKS **********************************************************/
42
43 NTSTATUS
44 APIENTRY
45 IntDesktopObjectParse(IN PVOID ParseObject,
46 IN PVOID ObjectType,
47 IN OUT PACCESS_STATE AccessState,
48 IN KPROCESSOR_MODE AccessMode,
49 IN ULONG Attributes,
50 IN OUT PUNICODE_STRING CompleteName,
51 IN OUT PUNICODE_STRING RemainingName,
52 IN OUT PVOID Context OPTIONAL,
53 IN PSECURITY_QUALITY_OF_SERVICE SecurityQos OPTIONAL,
54 OUT PVOID *Object)
55 {
56 NTSTATUS Status;
57 PDESKTOP Desktop;
58 OBJECT_ATTRIBUTES ObjectAttributes;
59 PLIST_ENTRY NextEntry, ListHead;
60 PWINSTATION_OBJECT WinStaObject = (PWINSTATION_OBJECT)ParseObject;
61 UNICODE_STRING DesktopName;
62 PBOOLEAN pContext = (PBOOLEAN) Context;
63
64 if (pContext)
65 *pContext = FALSE;
66
67 /* Set the list pointers and loop the window station */
68 ListHead = &WinStaObject->DesktopListHead;
69 NextEntry = ListHead->Flink;
70 while (NextEntry != ListHead)
71 {
72 /* Get the current desktop */
73 Desktop = CONTAINING_RECORD(NextEntry, DESKTOP, ListEntry);
74
75 /* Get the desktop name */
76 ASSERT(Desktop->pDeskInfo != NULL);
77 RtlInitUnicodeString(&DesktopName, Desktop->pDeskInfo->szDesktopName);
78
79 /* Compare the name */
80 if (RtlEqualUnicodeString(RemainingName,
81 &DesktopName,
82 (Attributes & OBJ_CASE_INSENSITIVE) != 0))
83 {
84 /* We found a match. Did this come from a create? */
85 if (Context)
86 {
87 /* Unless OPEN_IF was given, fail with an error */
88 if (!(Attributes & OBJ_OPENIF))
89 {
90 /* Name collision */
91 return STATUS_OBJECT_NAME_COLLISION;
92 }
93 else
94 {
95 /* Otherwise, return with a warning only */
96 Status = STATUS_OBJECT_NAME_EXISTS;
97 }
98 }
99 else
100 {
101 /* This was a real open, so this is OK */
102 Status = STATUS_SUCCESS;
103 }
104
105 /* Reference the desktop and return it */
106 ObReferenceObject(Desktop);
107 *Object = Desktop;
108 return Status;
109 }
110
111 /* Go to the next desktop */
112 NextEntry = NextEntry->Flink;
113 }
114
115 /* If we got here but this isn't a create, just fail */
116 if (!Context) return STATUS_OBJECT_NAME_NOT_FOUND;
117
118 /* Create the desktop object */
119 InitializeObjectAttributes(&ObjectAttributes, RemainingName, 0, NULL, NULL);
120 Status = ObCreateObject(KernelMode,
121 ExDesktopObjectType,
122 &ObjectAttributes,
123 KernelMode,
124 NULL,
125 sizeof(DESKTOP),
126 0,
127 0,
128 (PVOID*)&Desktop);
129 if (!NT_SUCCESS(Status)) return Status;
130
131 /* Initialize the desktop */
132 Status = UserInitializeDesktop(Desktop, RemainingName, WinStaObject);
133 if (!NT_SUCCESS(Status))
134 {
135 ObDereferenceObject(Desktop);
136 return Status;
137 }
138
139 /* Set the desktop object and return success */
140 *Object = Desktop;
141 *pContext = TRUE;
142 return STATUS_SUCCESS;
143 }
144
145 NTSTATUS
146 NTAPI
147 IntDesktopObjectDelete(
148 _In_ PVOID Parameters)
149 {
150 PWIN32_DELETEMETHOD_PARAMETERS DeleteParameters = Parameters;
151 PDESKTOP pdesk = (PDESKTOP)DeleteParameters->Object;
152
153 TRACE("Deleting desktop object 0x%p\n", pdesk);
154
155 if (pdesk->pDeskInfo &&
156 pdesk->pDeskInfo->spwnd)
157 {
158 ASSERT(pdesk->pDeskInfo->spwnd->spwndChild == NULL);
159 co_UserDestroyWindow(pdesk->pDeskInfo->spwnd);
160 }
161
162 if (pdesk->spwndMessage)
163 co_UserDestroyWindow(pdesk->spwndMessage);
164
165 /* Remove the desktop from the window station's list of associcated desktops */
166 RemoveEntryList(&pdesk->ListEntry);
167
168 /* Free the heap */
169 IntFreeDesktopHeap(pdesk);
170
171 ObDereferenceObject(pdesk->rpwinstaParent);
172
173 return STATUS_SUCCESS;
174 }
175
176 NTSTATUS
177 NTAPI
178 IntDesktopOkToClose(
179 _In_ PVOID Parameters)
180 {
181 PWIN32_OKAYTOCLOSEMETHOD_PARAMETERS OkToCloseParameters = Parameters;
182 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
183
184 if (pti == NULL)
185 {
186 /* This happens when we leak desktop handles */
187 return STATUS_SUCCESS;
188 }
189
190 /* Do not allow the current desktop or the initial desktop to be closed */
191 if (OkToCloseParameters->Handle == pti->ppi->hdeskStartup ||
192 OkToCloseParameters->Handle == pti->hdesk)
193 {
194 return STATUS_ACCESS_DENIED;
195 }
196
197 return STATUS_SUCCESS;
198 }
199
200 NTSTATUS
201 NTAPI
202 IntDesktopObjectOpen(
203 _In_ PVOID Parameters)
204 {
205 PWIN32_OPENMETHOD_PARAMETERS OpenParameters = Parameters;
206 PPROCESSINFO ppi = PsGetProcessWin32Process(OpenParameters->Process);
207 if (ppi == NULL)
208 return STATUS_SUCCESS;
209
210 return IntMapDesktopView((PDESKTOP)OpenParameters->Object);
211 }
212
213 NTSTATUS
214 NTAPI
215 IntDesktopObjectClose(
216 _In_ PVOID Parameters)
217 {
218 PWIN32_CLOSEMETHOD_PARAMETERS CloseParameters = Parameters;
219 PPROCESSINFO ppi = PsGetProcessWin32Process(CloseParameters->Process);
220 if (ppi == NULL)
221 {
222 /* This happens when the process leaks desktop handles.
223 * At this point the PPROCESSINFO is already destroyed */
224 return STATUS_SUCCESS;
225 }
226
227 return IntUnmapDesktopView((PDESKTOP)CloseParameters->Object);
228 }
229
230
231 /* PRIVATE FUNCTIONS **********************************************************/
232
233 CODE_SEG("INIT")
234 NTSTATUS
235 NTAPI
236 InitDesktopImpl(VOID)
237 {
238 GENERIC_MAPPING IntDesktopMapping = { DESKTOP_READ,
239 DESKTOP_WRITE,
240 DESKTOP_EXECUTE,
241 DESKTOP_ALL_ACCESS};
242
243 /* Set Desktop Object Attributes */
244 ExDesktopObjectType->TypeInfo.DefaultNonPagedPoolCharge = sizeof(DESKTOP);
245 ExDesktopObjectType->TypeInfo.GenericMapping = IntDesktopMapping;
246 ExDesktopObjectType->TypeInfo.ValidAccessMask = DESKTOP_ALL_ACCESS;
247
248 /* Allocate memory for the event structure */
249 gpDesktopThreadStartedEvent = ExAllocatePoolWithTag(NonPagedPool,
250 sizeof(KEVENT),
251 USERTAG_EVENT);
252 if (!gpDesktopThreadStartedEvent)
253 {
254 ERR("Failed to allocate event!\n");
255 return STATUS_NO_MEMORY;
256 }
257
258 /* Initialize the kernel event */
259 KeInitializeEvent(gpDesktopThreadStartedEvent,
260 SynchronizationEvent,
261 FALSE);
262
263 return STATUS_SUCCESS;
264 }
265
266 static NTSTATUS
267 GetSystemVersionString(OUT PWSTR pwszzVersion,
268 IN SIZE_T cchDest,
269 IN BOOLEAN InSafeMode,
270 IN BOOLEAN AppendNtSystemRoot)
271 {
272 NTSTATUS Status;
273
274 RTL_OSVERSIONINFOEXW VerInfo;
275 UNICODE_STRING BuildLabString;
276 UNICODE_STRING CSDVersionString;
277 RTL_QUERY_REGISTRY_TABLE VersionConfigurationTable[] =
278 {
279 {
280 NULL,
281 RTL_QUERY_REGISTRY_DIRECT,
282 L"BuildLab",
283 &BuildLabString,
284 REG_NONE, NULL, 0
285 },
286 {
287 NULL,
288 RTL_QUERY_REGISTRY_DIRECT,
289 L"CSDVersion",
290 &CSDVersionString,
291 REG_NONE, NULL, 0
292 },
293
294 {0}
295 };
296
297 WCHAR BuildLabBuffer[256];
298 WCHAR VersionBuffer[256];
299 PWCHAR EndBuffer;
300
301 VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
302
303 /*
304 * This call is uniquely used to retrieve the current CSD numbers.
305 * All the rest (major, minor, ...) is either retrieved from the
306 * SharedUserData structure, or from the registry.
307 */
308 RtlGetVersion((PRTL_OSVERSIONINFOW)&VerInfo);
309
310 /*
311 * - Retrieve the BuildLab string from the registry (set by the kernel).
312 * - In kernel-mode, szCSDVersion is not initialized. Initialize it
313 * and query its value from the registry.
314 */
315 RtlZeroMemory(BuildLabBuffer, sizeof(BuildLabBuffer));
316 RtlInitEmptyUnicodeString(&BuildLabString,
317 BuildLabBuffer,
318 sizeof(BuildLabBuffer));
319 RtlZeroMemory(VerInfo.szCSDVersion, sizeof(VerInfo.szCSDVersion));
320 RtlInitEmptyUnicodeString(&CSDVersionString,
321 VerInfo.szCSDVersion,
322 sizeof(VerInfo.szCSDVersion));
323 Status = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT,
324 L"",
325 VersionConfigurationTable,
326 NULL,
327 NULL);
328 if (!NT_SUCCESS(Status))
329 {
330 /* Indicate nothing is there */
331 BuildLabString.Length = 0;
332 CSDVersionString.Length = 0;
333 }
334 /* NULL-terminate the strings */
335 BuildLabString.Buffer[BuildLabString.Length / sizeof(WCHAR)] = UNICODE_NULL;
336 CSDVersionString.Buffer[CSDVersionString.Length / sizeof(WCHAR)] = UNICODE_NULL;
337
338 EndBuffer = VersionBuffer;
339 if ( /* VerInfo.wServicePackMajor != 0 && */ CSDVersionString.Length)
340 {
341 /* Print the version string */
342 Status = RtlStringCbPrintfExW(VersionBuffer,
343 sizeof(VersionBuffer),
344 &EndBuffer,
345 NULL,
346 0,
347 L": %wZ",
348 &CSDVersionString);
349 if (!NT_SUCCESS(Status))
350 {
351 /* No version, NULL-terminate the string */
352 *EndBuffer = UNICODE_NULL;
353 }
354 }
355 else
356 {
357 /* No version, NULL-terminate the string */
358 *EndBuffer = UNICODE_NULL;
359 }
360
361 if (InSafeMode)
362 {
363 /* String for Safe Mode */
364 Status = RtlStringCchPrintfW(pwszzVersion,
365 cchDest,
366 L"ReactOS Version %S %wZ (NT %u.%u Build %u%s)\n",
367 KERNEL_VERSION_STR,
368 &BuildLabString,
369 SharedUserData->NtMajorVersion,
370 SharedUserData->NtMinorVersion,
371 (VerInfo.dwBuildNumber & 0xFFFF),
372 VersionBuffer);
373
374 if (AppendNtSystemRoot && NT_SUCCESS(Status))
375 {
376 Status = RtlStringCbPrintfW(VersionBuffer,
377 sizeof(VersionBuffer),
378 L" - %s\n",
379 SharedUserData->NtSystemRoot);
380 if (NT_SUCCESS(Status))
381 {
382 /* Replace the last newline by a NULL, before concatenating */
383 EndBuffer = wcsrchr(pwszzVersion, L'\n');
384 if (EndBuffer) *EndBuffer = UNICODE_NULL;
385
386 /* The concatenated string has a terminating newline */
387 Status = RtlStringCchCatW(pwszzVersion,
388 cchDest,
389 VersionBuffer);
390 if (!NT_SUCCESS(Status))
391 {
392 /* Concatenation failed, put back the newline */
393 if (EndBuffer) *EndBuffer = L'\n';
394 }
395 }
396
397 /* Override any failures as the NtSystemRoot string is optional */
398 Status = STATUS_SUCCESS;
399 }
400 }
401 else
402 {
403 /* Multi-string for Normal Mode */
404 Status = RtlStringCchPrintfW(pwszzVersion,
405 cchDest,
406 L"ReactOS Version %S\n"
407 L"Build %wZ\n"
408 L"Reporting NT %u.%u (Build %u%s)\n",
409 KERNEL_VERSION_STR,
410 &BuildLabString,
411 SharedUserData->NtMajorVersion,
412 SharedUserData->NtMinorVersion,
413 (VerInfo.dwBuildNumber & 0xFFFF),
414 VersionBuffer);
415
416 if (AppendNtSystemRoot && NT_SUCCESS(Status))
417 {
418 Status = RtlStringCbPrintfW(VersionBuffer,
419 sizeof(VersionBuffer),
420 L"%s\n",
421 SharedUserData->NtSystemRoot);
422 if (NT_SUCCESS(Status))
423 {
424 Status = RtlStringCchCatW(pwszzVersion,
425 cchDest,
426 VersionBuffer);
427 }
428
429 /* Override any failures as the NtSystemRoot string is optional */
430 Status = STATUS_SUCCESS;
431 }
432 }
433
434 if (!NT_SUCCESS(Status))
435 {
436 /* Fall-back string */
437 Status = RtlStringCchPrintfW(pwszzVersion,
438 cchDest,
439 L"ReactOS Version %S %wZ\n",
440 KERNEL_VERSION_STR,
441 &BuildLabString);
442 if (!NT_SUCCESS(Status))
443 {
444 /* General failure, NULL-terminate the string */
445 pwszzVersion[0] = UNICODE_NULL;
446 }
447 }
448
449 /*
450 * Convert the string separators (newlines) into NULLs
451 * and NULL-terminate the multi-string.
452 */
453 while (*pwszzVersion)
454 {
455 EndBuffer = wcschr(pwszzVersion, L'\n');
456 if (!EndBuffer) break;
457 pwszzVersion = EndBuffer;
458
459 *pwszzVersion++ = UNICODE_NULL;
460 }
461 *pwszzVersion = UNICODE_NULL;
462
463 return Status;
464 }
465
466
467 /*
468 * IntResolveDesktop
469 *
470 * The IntResolveDesktop function attempts to retrieve valid handles to
471 * a desktop and a window station suitable for the specified process.
472 * The specified desktop path string is used only as a hint for the resolution.
473 *
474 * - If the process is already assigned to a window station and a desktop,
475 * handles to these objects are returned directly regardless of the specified
476 * desktop path string. This is what happens when this function is called for
477 * a process that has been already started and connected to the Win32 USER.
478 *
479 * - If the process is being connected to the Win32 USER, or is in a state
480 * where a window station is assigned to it but no desktop yet, the desktop
481 * path string is used as a hint for the resolution.
482 * A specified window station (if any, otherwise "WinSta0" is used as default)
483 * is tested for existence and accessibility. If the checks are OK a handle
484 * to it is returned. Otherwise we either fail (the window station does not
485 * exist) or, in case a default window station was used, we attempt to open
486 * or create a non-interactive Service-0xXXXX-YYYY$ window station. This is
487 * typically what happens when a non-interactive process is started while
488 * the WinSta0 window station was used as the default one.
489 * A specified desktop (if any, otherwise "Default" is used as default)
490 * is then tested for existence on the opened window station.
491 *
492 * - Rules for the choice of the default window station, when none is specified
493 * in the desktop path:
494 *
495 * 1. By default, a SYSTEM process connects to a non-interactive window
496 * station, either the Service-0x0-3e7$ (from the SYSTEM LUID) station,
497 * or one that has been inherited and that is non-interactive.
498 * Only when the interactive window station WinSta0 is specified that
499 * the process can connect to it (e.g. the case of interactive services).
500 *
501 * 2. An interactive process, i.e. a process whose LUID is the same as the
502 * one assigned to WinSta0 by Winlogon on user logon, connects by default
503 * to the WinSta0 window station, unless it has inherited from another
504 * interactive window station (which must be... none other than WinSta0).
505 *
506 * 3. A non-interactive (but not SYSTEM) process connects by default to
507 * a non-interactive Service-0xXXXX-YYYY$ window station (whose name
508 * is derived from the process' LUID), or to another non-interactive
509 * window station that has been inherited.
510 * Otherwise it may be able connect to the interactive WinSta0 only if
511 * it has explicit access rights to it.
512 *
513 * Parameters
514 * Process
515 * The user process object.
516 *
517 * DesktopPath
518 * The desktop path string used as a hint for desktop resolution.
519 *
520 * bInherit
521 * Whether or not the returned handles are inheritable.
522 *
523 * phWinSta
524 * Pointer to a window station handle.
525 *
526 * phDesktop
527 * Pointer to a desktop handle.
528 *
529 * Return Value
530 * Status code.
531 */
532
533 NTSTATUS
534 FASTCALL
535 IntResolveDesktop(
536 IN PEPROCESS Process,
537 IN PUNICODE_STRING DesktopPath,
538 IN BOOL bInherit,
539 OUT HWINSTA* phWinSta,
540 OUT HDESK* phDesktop)
541 {
542 NTSTATUS Status;
543 HWINSTA hWinSta = NULL, hWinStaDup = NULL;
544 HDESK hDesktop = NULL, hDesktopDup = NULL;
545 PPROCESSINFO ppi;
546 HANDLE hProcess = NULL;
547 LUID ProcessLuid;
548 USHORT StrSize;
549 SIZE_T MemSize;
550 POBJECT_ATTRIBUTES ObjectAttributes = NULL;
551 PUNICODE_STRING ObjectName;
552 UNICODE_STRING WinStaName, DesktopName;
553 const UNICODE_STRING WinSta0Name = RTL_CONSTANT_STRING(L"WinSta0");
554 PWINSTATION_OBJECT WinStaObject;
555 HWINSTA hTempWinSta = NULL;
556 BOOLEAN bUseDefaultWinSta = FALSE;
557 BOOLEAN bInteractive = FALSE;
558 BOOLEAN bAccessAllowed = FALSE;
559
560 ASSERT(phWinSta);
561 ASSERT(phDesktop);
562 ASSERT(DesktopPath);
563
564 *phWinSta = NULL;
565 *phDesktop = NULL;
566
567 ppi = PsGetProcessWin32Process(Process);
568 /* ppi is typically NULL for console applications that connect to Win32 USER */
569 if (!ppi) TRACE("IntResolveDesktop: ppi is NULL!\n");
570
571 if (ppi && ppi->hwinsta != NULL && ppi->hdeskStartup != NULL)
572 {
573 /*
574 * If this process is the current one, just return the cached handles.
575 * Otherwise, open the window station and desktop objects.
576 */
577 if (Process == PsGetCurrentProcess())
578 {
579 hWinSta = ppi->hwinsta;
580 hDesktop = ppi->hdeskStartup;
581 }
582 else
583 {
584 Status = ObOpenObjectByPointer(ppi->prpwinsta,
585 0,
586 NULL,
587 MAXIMUM_ALLOWED,
588 ExWindowStationObjectType,
589 UserMode,
590 (PHANDLE)&hWinSta);
591 if (!NT_SUCCESS(Status))
592 {
593 ERR("IntResolveDesktop: Could not reference window station 0x%p\n", ppi->prpwinsta);
594 SetLastNtError(Status);
595 return Status;
596 }
597
598 Status = ObOpenObjectByPointer(ppi->rpdeskStartup,
599 0,
600 NULL,
601 MAXIMUM_ALLOWED,
602 ExDesktopObjectType,
603 UserMode,
604 (PHANDLE)&hDesktop);
605 if (!NT_SUCCESS(Status))
606 {
607 ERR("IntResolveDesktop: Could not reference desktop 0x%p\n", ppi->rpdeskStartup);
608 ObCloseHandle(hWinSta, UserMode);
609 SetLastNtError(Status);
610 return Status;
611 }
612 }
613
614 *phWinSta = hWinSta;
615 *phDesktop = hDesktop;
616 return STATUS_SUCCESS;
617 }
618
619 /* We will by default use the default window station and desktop */
620 RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
621 RtlInitEmptyUnicodeString(&DesktopName, NULL, 0);
622
623 /*
624 * Parse the desktop path string which can be of the form "WinSta\Desktop"
625 * or just "Desktop". In the latter case we use the default window station
626 * on which the process is attached to (or if none, "WinSta0").
627 */
628 if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR))
629 {
630 DesktopName = *DesktopPath;
631
632 /* Find the separator */
633 while (DesktopName.Length > 0 && *DesktopName.Buffer &&
634 *DesktopName.Buffer != OBJ_NAME_PATH_SEPARATOR)
635 {
636 DesktopName.Buffer++;
637 DesktopName.Length -= sizeof(WCHAR);
638 DesktopName.MaximumLength -= sizeof(WCHAR);
639 }
640 if (DesktopName.Length > 0)
641 {
642 RtlInitEmptyUnicodeString(&WinStaName, DesktopPath->Buffer,
643 DesktopPath->Length - DesktopName.Length);
644 // (USHORT)((ULONG_PTR)DesktopName.Buffer - (ULONG_PTR)DesktopPath->Buffer);
645 WinStaName.Length = WinStaName.MaximumLength;
646
647 /* Skip the separator */
648 DesktopName.Buffer++;
649 DesktopName.Length -= sizeof(WCHAR);
650 DesktopName.MaximumLength -= sizeof(WCHAR);
651 }
652 else
653 {
654 RtlInitEmptyUnicodeString(&WinStaName, NULL, 0);
655 DesktopName = *DesktopPath;
656 }
657 }
658
659 TRACE("IntResolveDesktop: WinStaName:'%wZ' ; DesktopName:'%wZ'\n", &WinStaName, &DesktopName);
660
661 /* Retrieve the process LUID */
662 Status = GetProcessLuid(NULL, Process, &ProcessLuid);
663 if (!NT_SUCCESS(Status))
664 {
665 ERR("IntResolveDesktop: Failed to retrieve the process LUID, Status 0x%08lx\n", Status);
666 SetLastNtError(Status);
667 return Status;
668 }
669
670 /*
671 * If this process is not the current one, obtain a temporary handle
672 * to it so that we can perform handles duplication later.
673 */
674 if (Process != PsGetCurrentProcess())
675 {
676 Status = ObOpenObjectByPointer(Process,
677 OBJ_KERNEL_HANDLE,
678 NULL,
679 0,
680 *PsProcessType,
681 KernelMode,
682 &hProcess);
683 if (!NT_SUCCESS(Status))
684 {
685 ERR("IntResolveDesktop: Failed to obtain a handle to process 0x%p, Status 0x%08lx\n", Process, Status);
686 SetLastNtError(Status);
687 return Status;
688 }
689 ASSERT(hProcess);
690 }
691
692 /*
693 * If no window station has been specified, search the process handle table
694 * for inherited window station handles, otherwise use a default one.
695 */
696 if (WinStaName.Buffer == NULL)
697 {
698 /*
699 * We want to find a suitable default window station.
700 * For applications that can be interactive, i.e. that have allowed
701 * access to the single interactive window station on the system,
702 * the default window station is 'WinSta0'.
703 * For applications that cannot be interactive, i.e. that do not have
704 * access to 'WinSta0' (e.g. non-interactive services), the default
705 * window station is 'Service-0xXXXX-YYYY$' (created if needed).
706 * Precedence will however be taken by any inherited window station
707 * that possesses the required interactivity property.
708 */
709 bUseDefaultWinSta = TRUE;
710
711 /*
712 * Use the default 'WinSta0' window station. Whether we should
713 * use 'Service-0xXXXX-YYYY$' instead will be determined later.
714 */
715 // RtlInitUnicodeString(&WinStaName, L"WinSta0");
716 WinStaName = WinSta0Name;
717
718 if (ObFindHandleForObject(Process,
719 NULL,
720 ExWindowStationObjectType,
721 NULL,
722 (PHANDLE)&hWinSta))
723 {
724 TRACE("IntResolveDesktop: Inherited window station is: 0x%p\n", hWinSta);
725 }
726 }
727
728 /*
729 * If no desktop has been specified, search the process handle table
730 * for inherited desktop handles, otherwise use the Default desktop.
731 * Note that the inherited desktop that we may use, may not belong
732 * to the window station we will connect to.
733 */
734 if (DesktopName.Buffer == NULL)
735 {
736 /* Use a default desktop name */
737 RtlInitUnicodeString(&DesktopName, L"Default");
738
739 if (ObFindHandleForObject(Process,
740 NULL,
741 ExDesktopObjectType,
742 NULL,
743 (PHANDLE)&hDesktop))
744 {
745 TRACE("IntResolveDesktop: Inherited desktop is: 0x%p\n", hDesktop);
746 }
747 }
748
749
750 /*
751 * We are going to open either a window station or a desktop.
752 * Even if this operation is done from kernel-mode, we should
753 * "emulate" an opening from user-mode (i.e. using an ObjectAttributes
754 * allocated in user-mode, with AccessMode == UserMode) for the
755 * Object Manager to perform proper access validation to the
756 * window station or desktop.
757 */
758
759 /*
760 * Estimate the maximum size needed for the window station name
761 * and desktop name to be given to ObjectAttributes->ObjectName.
762 */
763 StrSize = 0;
764
765 /* Window station name */
766 MemSize = _scwprintf(L"Service-0x%x-%x$", MAXULONG, MAXULONG) * sizeof(WCHAR);
767 MemSize = gustrWindowStationsDir.Length + sizeof(OBJ_NAME_PATH_SEPARATOR)
768 + max(WinStaName.Length, MemSize) + sizeof(UNICODE_NULL);
769 if (MemSize > MAXUSHORT)
770 {
771 ERR("IntResolveDesktop: Window station name length is too long.\n");
772 Status = STATUS_NAME_TOO_LONG;
773 goto Quit;
774 }
775 StrSize = max(StrSize, (USHORT)MemSize);
776
777 /* Desktop name */
778 MemSize = max(DesktopName.Length + sizeof(UNICODE_NULL), sizeof(L"Default"));
779 StrSize = max(StrSize, (USHORT)MemSize);
780
781 /* Size for the OBJECT_ATTRIBUTES */
782 MemSize = ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID));
783
784 /* Add the string size */
785 MemSize += ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID));
786 MemSize += StrSize;
787
788 /* Allocate the memory in user-mode */
789 Status = ZwAllocateVirtualMemory(ZwCurrentProcess(),
790 (PVOID*)&ObjectAttributes,
791 0,
792 &MemSize,
793 MEM_COMMIT,
794 PAGE_READWRITE);
795 if (!NT_SUCCESS(Status))
796 {
797 ERR("ZwAllocateVirtualMemory() failed, Status 0x%08lx\n", Status);
798 goto Quit;
799 }
800
801 ObjectName = (PUNICODE_STRING)((ULONG_PTR)ObjectAttributes +
802 ALIGN_UP(sizeof(OBJECT_ATTRIBUTES), sizeof(PVOID)));
803
804 RtlInitEmptyUnicodeString(ObjectName,
805 (PWCHAR)((ULONG_PTR)ObjectName +
806 ALIGN_UP(sizeof(UNICODE_STRING), sizeof(PVOID))),
807 StrSize);
808
809
810 /* If we got an inherited window station handle, duplicate and use it */
811 if (hWinSta)
812 {
813 ASSERT(bUseDefaultWinSta);
814
815 /* Duplicate the handle if it belongs to another process than the current one */
816 if (Process != PsGetCurrentProcess())
817 {
818 ASSERT(hProcess);
819 Status = ZwDuplicateObject(hProcess,
820 hWinSta,
821 ZwCurrentProcess(),
822 (PHANDLE)&hWinStaDup,
823 0,
824 0,
825 DUPLICATE_SAME_ACCESS);
826 if (!NT_SUCCESS(Status))
827 {
828 ERR("IntResolveDesktop: Failed to duplicate the window station handle, Status 0x%08lx\n", Status);
829 /* We will use a default window station */
830 hWinSta = NULL;
831 }
832 else
833 {
834 hWinSta = hWinStaDup;
835 }
836 }
837 }
838
839 /*
840 * If we have an inherited window station, check whether
841 * it is interactive and remember that for later.
842 */
843 if (hWinSta)
844 {
845 ASSERT(bUseDefaultWinSta);
846
847 /* Reference the inherited window station */
848 Status = ObReferenceObjectByHandle(hWinSta,
849 0,
850 ExWindowStationObjectType,
851 KernelMode,
852 (PVOID*)&WinStaObject,
853 NULL);
854 if (!NT_SUCCESS(Status))
855 {
856 ERR("Failed to reference the inherited window station, Status 0x%08lx\n", Status);
857 /* We will use a default window station */
858 if (hWinStaDup)
859 {
860 ASSERT(hWinSta == hWinStaDup);
861 ObCloseHandle(hWinStaDup, UserMode);
862 hWinStaDup = NULL;
863 }
864 hWinSta = NULL;
865 }
866 else
867 {
868 ERR("Process LUID is: 0x%x-%x, inherited window station LUID is: 0x%x-%x\n",
869 ProcessLuid.HighPart, ProcessLuid.LowPart,
870 WinStaObject->luidUser.HighPart, WinStaObject->luidUser.LowPart);
871
872 /* Check whether this window station is interactive, and remember it for later */
873 bInteractive = !(WinStaObject->Flags & WSS_NOIO);
874
875 /* Dereference the window station */
876 ObDereferenceObject(WinStaObject);
877 }
878 }
879
880 /* Build a valid window station name */
881 Status = RtlStringCbPrintfW(ObjectName->Buffer,
882 ObjectName->MaximumLength,
883 L"%wZ\\%wZ",
884 &gustrWindowStationsDir,
885 &WinStaName);
886 if (!NT_SUCCESS(Status))
887 {
888 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
889 goto Quit;
890 }
891 ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
892
893 TRACE("Parsed initial window station: '%wZ'\n", ObjectName);
894
895 /* Try to open the window station */
896 InitializeObjectAttributes(ObjectAttributes,
897 ObjectName,
898 OBJ_CASE_INSENSITIVE,
899 NULL,
900 NULL);
901 if (bInherit)
902 ObjectAttributes->Attributes |= OBJ_INHERIT;
903
904 Status = ObOpenObjectByName(ObjectAttributes,
905 ExWindowStationObjectType,
906 UserMode,
907 NULL,
908 WINSTA_ACCESS_ALL,
909 NULL,
910 (PHANDLE)&hTempWinSta);
911 if (!NT_SUCCESS(Status))
912 {
913 ERR("Failed to open the window station '%wZ', Status 0x%08lx\n", ObjectName, Status);
914 }
915 else
916 {
917 //
918 // FIXME TODO: Perform a window station access check!!
919 // If we fail AND bUseDefaultWinSta == FALSE we just quit.
920 //
921
922 /*
923 * Check whether we are opening the (single) interactive
924 * window station, and if so, perform an access check.
925 */
926 /* Check whether we are allowed to perform interactions */
927 if (RtlEqualUnicodeString(&WinStaName, &WinSta0Name, TRUE))
928 {
929 LUID SystemLuid = SYSTEM_LUID;
930
931 /* Interactive window station: check for user LUID */
932 WinStaObject = InputWindowStation;
933
934 Status = STATUS_ACCESS_DENIED;
935
936 // TODO: Check also that we compare wrt. window station WinSta0
937 // which is the only one that can be interactive on the system.
938 if (((!bUseDefaultWinSta || bInherit) && RtlEqualLuid(&ProcessLuid, &SystemLuid)) ||
939 RtlEqualLuid(&ProcessLuid, &WinStaObject->luidUser))
940 {
941 /* We are interactive on this window station */
942 bAccessAllowed = TRUE;
943 Status = STATUS_SUCCESS;
944 }
945 }
946 else
947 {
948 /* Non-interactive window station: we have access since we were able to open it */
949 bAccessAllowed = TRUE;
950 Status = STATUS_SUCCESS;
951 }
952 }
953
954 /* If we failed, bail out if we were not trying to open the default window station */
955 if (!NT_SUCCESS(Status) && !bUseDefaultWinSta) // if (!bAccessAllowed)
956 goto Quit;
957
958 if (/* bAccessAllowed && */ bInteractive || !bAccessAllowed)
959 {
960 /*
961 * Close WinSta0 if the inherited window station is interactive so that
962 * we can use it, or we do not have access to the interactive WinSta0.
963 */
964 ObCloseHandle(hTempWinSta, UserMode);
965 hTempWinSta = NULL;
966 }
967 if (bInteractive == bAccessAllowed)
968 {
969 /* Keep using the inherited window station */
970 NOTHING;
971 }
972 else // if (bInteractive != bAccessAllowed)
973 {
974 /*
975 * Close the inherited window station, we will either keep using
976 * the interactive WinSta0, or use Service-0xXXXX-YYYY$.
977 */
978 if (hWinStaDup)
979 {
980 ASSERT(hWinSta == hWinStaDup);
981 ObCloseHandle(hWinStaDup, UserMode);
982 hWinStaDup = NULL;
983 }
984 hWinSta = hTempWinSta; // hTempWinSta is NULL in case bAccessAllowed == FALSE
985 }
986
987 if (bUseDefaultWinSta)
988 {
989 if (hWinSta == NULL && !bInteractive)
990 {
991 /* Build a valid window station name from the LUID */
992 Status = RtlStringCbPrintfW(ObjectName->Buffer,
993 ObjectName->MaximumLength,
994 L"%wZ\\Service-0x%x-%x$",
995 &gustrWindowStationsDir,
996 ProcessLuid.HighPart,
997 ProcessLuid.LowPart);
998 if (!NT_SUCCESS(Status))
999 {
1000 ERR("Impossible to build a valid window station name, Status 0x%08lx\n", Status);
1001 goto Quit;
1002 }
1003 ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1004
1005 /*
1006 * Create or open the non-interactive window station.
1007 * NOTE: The non-interactive window station handle is never inheritable.
1008 */
1009 // FIXME: Set security!
1010 InitializeObjectAttributes(ObjectAttributes,
1011 ObjectName,
1012 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
1013 NULL,
1014 NULL);
1015
1016 Status = IntCreateWindowStation(&hWinSta,
1017 ObjectAttributes,
1018 UserMode,
1019 KernelMode,
1020 MAXIMUM_ALLOWED,
1021 0, 0, 0, 0, 0);
1022 if (!NT_SUCCESS(Status))
1023 {
1024 ASSERT(hWinSta == NULL);
1025 ERR("Failed to create or open the non-interactive window station '%wZ', Status 0x%08lx\n",
1026 ObjectName, Status);
1027 goto Quit;
1028 }
1029
1030 //
1031 // FIXME: We might not need to always create or open the "Default"
1032 // desktop on the Service-0xXXXX-YYYY$ window station; we may need
1033 // to use another one....
1034 //
1035
1036 /* Create or open the Default desktop on the window station */
1037 Status = RtlStringCbCopyW(ObjectName->Buffer,
1038 ObjectName->MaximumLength,
1039 L"Default");
1040 if (!NT_SUCCESS(Status))
1041 {
1042 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
1043 goto Quit;
1044 }
1045 ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1046
1047 /* NOTE: The non-interactive desktop handle is never inheritable. */
1048 // FIXME: Set security!
1049 InitializeObjectAttributes(ObjectAttributes,
1050 ObjectName,
1051 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
1052 hWinSta,
1053 NULL);
1054
1055 Status = IntCreateDesktop(&hDesktop,
1056 ObjectAttributes,
1057 UserMode,
1058 NULL,
1059 NULL,
1060 0,
1061 MAXIMUM_ALLOWED);
1062 if (!NT_SUCCESS(Status))
1063 {
1064 ASSERT(hDesktop == NULL);
1065 ERR("Failed to create or open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1066 ObjectName, hWinSta, Status);
1067 }
1068
1069 goto Quit;
1070 }
1071 /*
1072 if (hWinSta == NULL)
1073 {
1074 Status = STATUS_UNSUCCESSFUL;
1075 goto Quit;
1076 }
1077 */
1078 }
1079
1080 /*
1081 * If we got an inherited desktop handle, duplicate and use it,
1082 * otherwise open a new desktop.
1083 */
1084 if (hDesktop != NULL)
1085 {
1086 /* Duplicate the handle if it belongs to another process than the current one */
1087 if (Process != PsGetCurrentProcess())
1088 {
1089 ASSERT(hProcess);
1090 Status = ZwDuplicateObject(hProcess,
1091 hDesktop,
1092 ZwCurrentProcess(),
1093 (PHANDLE)&hDesktopDup,
1094 0,
1095 0,
1096 DUPLICATE_SAME_ACCESS);
1097 if (!NT_SUCCESS(Status))
1098 {
1099 ERR("IntResolveDesktop: Failed to duplicate the desktop handle, Status 0x%08lx\n", Status);
1100 /* We will use a default desktop */
1101 hDesktop = NULL;
1102 }
1103 else
1104 {
1105 hDesktop = hDesktopDup;
1106 }
1107 }
1108 }
1109
1110 if ((hWinSta != NULL) && (hDesktop == NULL))
1111 {
1112 Status = RtlStringCbCopyNW(ObjectName->Buffer,
1113 ObjectName->MaximumLength,
1114 DesktopName.Buffer,
1115 DesktopName.Length);
1116 if (!NT_SUCCESS(Status))
1117 {
1118 ERR("Impossible to build a valid desktop name, Status 0x%08lx\n", Status);
1119 goto Quit;
1120 }
1121 ObjectName->Length = (USHORT)(wcslen(ObjectName->Buffer) * sizeof(WCHAR));
1122
1123 TRACE("Parsed initial desktop: '%wZ'\n", ObjectName);
1124
1125 /* Open the desktop object */
1126 InitializeObjectAttributes(ObjectAttributes,
1127 ObjectName,
1128 OBJ_CASE_INSENSITIVE,
1129 hWinSta,
1130 NULL);
1131 if (bInherit)
1132 ObjectAttributes->Attributes |= OBJ_INHERIT;
1133
1134 Status = ObOpenObjectByName(ObjectAttributes,
1135 ExDesktopObjectType,
1136 UserMode,
1137 NULL,
1138 DESKTOP_ALL_ACCESS,
1139 NULL,
1140 (PHANDLE)&hDesktop);
1141 if (!NT_SUCCESS(Status))
1142 {
1143 ERR("Failed to open the desktop '%wZ' on window station 0x%p, Status 0x%08lx\n",
1144 ObjectName, hWinSta, Status);
1145 goto Quit;
1146 }
1147 }
1148
1149 Quit:
1150 /* Release the object attributes */
1151 if (ObjectAttributes)
1152 {
1153 MemSize = 0;
1154 ZwFreeVirtualMemory(ZwCurrentProcess(),
1155 (PVOID*)&ObjectAttributes,
1156 &MemSize,
1157 MEM_RELEASE);
1158 }
1159
1160 /* Close the temporary process handle */
1161 if (hProcess) // if (Process != PsGetCurrentProcess())
1162 ObCloseHandle(hProcess, KernelMode);
1163
1164 if (NT_SUCCESS(Status))
1165 {
1166 *phWinSta = hWinSta;
1167 *phDesktop = hDesktop;
1168 return STATUS_SUCCESS;
1169 }
1170 else
1171 {
1172 ERR("IntResolveDesktop(%wZ) failed, Status 0x%08lx\n", DesktopPath, Status);
1173
1174 if (hDesktopDup)
1175 ObCloseHandle(hDesktopDup, UserMode);
1176 if (hWinStaDup)
1177 ObCloseHandle(hWinStaDup, UserMode);
1178
1179 if (hDesktop)
1180 ObCloseHandle(hDesktop, UserMode);
1181 if (hWinSta)
1182 ObCloseHandle(hWinSta, UserMode);
1183
1184 SetLastNtError(Status);
1185 return Status;
1186 }
1187 }
1188
1189 /*
1190 * IntValidateDesktopHandle
1191 *
1192 * Validates the desktop handle.
1193 *
1194 * Remarks
1195 * If the function succeeds, the handle remains referenced. If the
1196 * fucntion fails, last error is set.
1197 */
1198
1199 NTSTATUS FASTCALL
1200 IntValidateDesktopHandle(
1201 HDESK Desktop,
1202 KPROCESSOR_MODE AccessMode,
1203 ACCESS_MASK DesiredAccess,
1204 PDESKTOP *Object)
1205 {
1206 NTSTATUS Status;
1207
1208 Status = ObReferenceObjectByHandle(
1209 Desktop,
1210 DesiredAccess,
1211 ExDesktopObjectType,
1212 AccessMode,
1213 (PVOID*)Object,
1214 NULL);
1215
1216 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n",
1217 Desktop, *Object, DesiredAccess, Status);
1218
1219 if (!NT_SUCCESS(Status))
1220 SetLastNtError(Status);
1221
1222 return Status;
1223 }
1224
1225 PDESKTOP FASTCALL
1226 IntGetActiveDesktop(VOID)
1227 {
1228 return gpdeskInputDesktop;
1229 }
1230
1231 /*
1232 * Returns or creates a handle to the desktop object
1233 */
1234 HDESK FASTCALL
1235 IntGetDesktopObjectHandle(PDESKTOP DesktopObject)
1236 {
1237 NTSTATUS Status;
1238 HDESK hDesk;
1239
1240 ASSERT(DesktopObject);
1241
1242 if (!ObFindHandleForObject(PsGetCurrentProcess(),
1243 DesktopObject,
1244 ExDesktopObjectType,
1245 NULL,
1246 (PHANDLE)&hDesk))
1247 {
1248 Status = ObOpenObjectByPointer(DesktopObject,
1249 0,
1250 NULL,
1251 0,
1252 ExDesktopObjectType,
1253 UserMode,
1254 (PHANDLE)&hDesk);
1255 if (!NT_SUCCESS(Status))
1256 {
1257 /* Unable to create a handle */
1258 ERR("Unable to create a desktop handle\n");
1259 return NULL;
1260 }
1261 }
1262 else
1263 {
1264 TRACE("Got handle: 0x%p\n", hDesk);
1265 }
1266
1267 return hDesk;
1268 }
1269
1270 PUSER_MESSAGE_QUEUE FASTCALL
1271 IntGetFocusMessageQueue(VOID)
1272 {
1273 PDESKTOP pdo = IntGetActiveDesktop();
1274 if (!pdo)
1275 {
1276 TRACE("No active desktop\n");
1277 return(NULL);
1278 }
1279 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue;
1280 }
1281
1282 VOID FASTCALL
1283 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue)
1284 {
1285 PUSER_MESSAGE_QUEUE Old;
1286 PDESKTOP pdo = IntGetActiveDesktop();
1287 if (!pdo)
1288 {
1289 TRACE("No active desktop\n");
1290 return;
1291 }
1292 if (NewQueue != NULL)
1293 {
1294 if (NewQueue->Desktop != NULL)
1295 {
1296 TRACE("Message Queue already attached to another desktop!\n");
1297 return;
1298 }
1299 IntReferenceMessageQueue(NewQueue);
1300 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo);
1301 }
1302 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue);
1303 if (Old != NULL)
1304 {
1305 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0);
1306 gpqForegroundPrev = Old;
1307 IntDereferenceMessageQueue(Old);
1308 }
1309 // Only one Q can have active foreground even when there are more than one desktop.
1310 if (NewQueue)
1311 {
1312 gpqForeground = pdo->ActiveMessageQueue;
1313 }
1314 else
1315 {
1316 gpqForeground = NULL;
1317 ERR("ptiLastInput is CLEARED!!\n");
1318 ptiLastInput = NULL; // ReactOS hacks,,,, should check for process death.
1319 }
1320 }
1321
1322 PWND FASTCALL
1323 IntGetThreadDesktopWindow(PTHREADINFO pti)
1324 {
1325 if (!pti) pti = PsGetCurrentThreadWin32Thread();
1326 if (pti->pDeskInfo) return pti->pDeskInfo->spwnd;
1327 return NULL;
1328 }
1329
1330 PWND FASTCALL co_GetDesktopWindow(PWND pWnd)
1331 {
1332 if (pWnd->head.rpdesk &&
1333 pWnd->head.rpdesk->pDeskInfo)
1334 return pWnd->head.rpdesk->pDeskInfo->spwnd;
1335 return NULL;
1336 }
1337
1338 HWND FASTCALL IntGetDesktopWindow(VOID)
1339 {
1340 PDESKTOP pdo = IntGetActiveDesktop();
1341 if (!pdo)
1342 {
1343 TRACE("No active desktop\n");
1344 return NULL;
1345 }
1346 return pdo->DesktopWindow;
1347 }
1348
1349 PWND FASTCALL UserGetDesktopWindow(VOID)
1350 {
1351 PDESKTOP pdo = IntGetActiveDesktop();
1352
1353 if (!pdo)
1354 {
1355 TRACE("No active desktop\n");
1356 return NULL;
1357 }
1358 // return pdo->pDeskInfo->spwnd;
1359 return UserGetWindowObject(pdo->DesktopWindow);
1360 }
1361
1362 HWND FASTCALL IntGetMessageWindow(VOID)
1363 {
1364 PDESKTOP pdo = IntGetActiveDesktop();
1365
1366 if (!pdo)
1367 {
1368 TRACE("No active desktop\n");
1369 return NULL;
1370 }
1371 return pdo->spwndMessage->head.h;
1372 }
1373
1374 PWND FASTCALL UserGetMessageWindow(VOID)
1375 {
1376 PDESKTOP pdo = IntGetActiveDesktop();
1377
1378 if (!pdo)
1379 {
1380 TRACE("No active desktop\n");
1381 return NULL;
1382 }
1383 return pdo->spwndMessage;
1384 }
1385
1386 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID)
1387 {
1388 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1389 PDESKTOP pdo = pti->rpdesk;
1390 if (NULL == pdo)
1391 {
1392 ERR("Thread doesn't have a desktop\n");
1393 return NULL;
1394 }
1395 return pdo->DesktopWindow;
1396 }
1397
1398 /* PUBLIC FUNCTIONS ***********************************************************/
1399
1400 BOOL FASTCALL
1401 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
1402 {
1403 PAINTSTRUCT Ps;
1404 ULONG Value;
1405 //ERR("DesktopWindowProc\n");
1406
1407 *lResult = 0;
1408
1409 switch (Msg)
1410 {
1411 case WM_NCCREATE:
1412 if (!Wnd->fnid)
1413 {
1414 Wnd->fnid = FNID_DESKTOP;
1415 }
1416 *lResult = (LRESULT)TRUE;
1417 return TRUE;
1418
1419 case WM_CREATE:
1420 Value = HandleToULong(PsGetCurrentProcessId());
1421 // Save Process ID
1422 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE);
1423 Value = HandleToULong(PsGetCurrentThreadId());
1424 // Save Thread ID
1425 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE);
1426 case WM_CLOSE:
1427 return TRUE;
1428
1429 case WM_DISPLAYCHANGE:
1430 co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE);
1431 return TRUE;
1432
1433 case WM_ERASEBKGND:
1434 IntPaintDesktop((HDC)wParam);
1435 *lResult = 1;
1436 return TRUE;
1437
1438 case WM_PAINT:
1439 {
1440 if (IntBeginPaint(Wnd, &Ps))
1441 {
1442 IntEndPaint(Wnd, &Ps);
1443 }
1444 return TRUE;
1445 }
1446 case WM_SYSCOLORCHANGE:
1447 co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
1448 return TRUE;
1449
1450 case WM_SETCURSOR:
1451 {
1452 PCURICON_OBJECT pcurOld, pcurNew;
1453 pcurNew = UserGetCurIconObject(gDesktopCursor);
1454 if (!pcurNew)
1455 {
1456 return TRUE;
1457 }
1458
1459 pcurNew->CURSORF_flags |= CURSORF_CURRENT;
1460 pcurOld = UserSetCursor(pcurNew, FALSE);
1461 if (pcurOld)
1462 {
1463 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT;
1464 UserDereferenceObject(pcurOld);
1465 }
1466 return TRUE;
1467 }
1468
1469 case WM_WINDOWPOSCHANGING:
1470 {
1471 PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam;
1472 if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0)
1473 {
1474 HDESK hdesk = UserOpenInputDesktop(0, FALSE, DESKTOP_ALL_ACCESS);
1475 IntSetThreadDesktop(hdesk, FALSE);
1476 }
1477 break;
1478 }
1479 default:
1480 TRACE("DWP calling IDWP Msg %d\n",Msg);
1481 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE);
1482 }
1483 return TRUE; /* We are done. Do not do any callbacks to user mode */
1484 }
1485
1486 BOOL FASTCALL
1487 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
1488 {
1489 *lResult = 0;
1490
1491 switch(Msg)
1492 {
1493 case WM_NCCREATE:
1494 pwnd->fnid |= FNID_MESSAGEWND;
1495 *lResult = (LRESULT)TRUE;
1496 break;
1497 case WM_DESTROY:
1498 pwnd->fnid |= FNID_DESTROY;
1499 break;
1500 default:
1501 ERR("UMWP calling IDWP\n");
1502 *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE);
1503 }
1504
1505 return TRUE; /* We are done. Do not do any callbacks to user mode */
1506 }
1507
1508 VOID NTAPI DesktopThreadMain(VOID)
1509 {
1510 BOOL Ret;
1511 MSG Msg;
1512
1513 gptiDesktopThread = PsGetCurrentThreadWin32Thread();
1514
1515 UserEnterExclusive();
1516
1517 /* Register system classes. This thread does not belong to any desktop so the
1518 classes will be allocated from the shared heap */
1519 UserRegisterSystemClasses();
1520
1521 KeSetEvent(gpDesktopThreadStartedEvent, IO_NO_INCREMENT, FALSE);
1522
1523 while (TRUE)
1524 {
1525 Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE);
1526 if (Ret)
1527 {
1528 IntDispatchMessage(&Msg);
1529 }
1530 }
1531
1532 UserLeave();
1533 }
1534
1535 HDC FASTCALL
1536 UserGetDesktopDC(ULONG DcType, BOOL bAltDc, BOOL ValidatehWnd)
1537 {
1538 PWND DesktopObject = 0;
1539 HDC DesktopHDC = 0;
1540
1541 /* This can be called from GDI/DX, so acquire the USER lock */
1542 UserEnterExclusive();
1543
1544 if (DcType == DC_TYPE_DIRECT)
1545 {
1546 DesktopObject = UserGetDesktopWindow();
1547 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject);
1548 }
1549 else
1550 {
1551 PMONITOR pMonitor = UserGetPrimaryMonitor();
1552 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, bAltDc);
1553 }
1554
1555 UserLeave();
1556
1557 return DesktopHDC;
1558 }
1559
1560 VOID APIENTRY
1561 UserRedrawDesktop(VOID)
1562 {
1563 PWND Window = NULL;
1564 PREGION Rgn;
1565
1566 Window = UserGetDesktopWindow();
1567 Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
1568
1569 IntInvalidateWindows( Window,
1570 Rgn,
1571 RDW_FRAME |
1572 RDW_ERASE |
1573 RDW_INVALIDATE |
1574 RDW_ALLCHILDREN);
1575
1576 REGION_Delete(Rgn);
1577 }
1578
1579
1580 NTSTATUS FASTCALL
1581 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw)
1582 {
1583 PWND pwnd = Desktop->pDeskInfo->spwnd;
1584 UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW;
1585 ASSERT(pwnd);
1586
1587 if (!bRedraw)
1588 flags |= SWP_NOREDRAW;
1589
1590 co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags);
1591
1592 if (bRedraw)
1593 co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE );
1594
1595 return STATUS_SUCCESS;
1596 }
1597
1598 NTSTATUS FASTCALL
1599 IntHideDesktop(PDESKTOP Desktop)
1600 {
1601 PWND DesktopWnd;
1602
1603 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow);
1604 if (! DesktopWnd)
1605 {
1606 return ERROR_INVALID_WINDOW_HANDLE;
1607 }
1608 DesktopWnd->style &= ~WS_VISIBLE;
1609
1610 return STATUS_SUCCESS;
1611 }
1612
1613 static
1614 HWND* FASTCALL
1615 UserBuildShellHookHwndList(PDESKTOP Desktop)
1616 {
1617 ULONG entries=0;
1618 PLIST_ENTRY ListEntry;
1619 PSHELL_HOOK_WINDOW Current;
1620 HWND* list;
1621
1622 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */
1623 ListEntry = Desktop->ShellHookWindows.Flink;
1624 while (ListEntry != &Desktop->ShellHookWindows)
1625 {
1626 ListEntry = ListEntry->Flink;
1627 entries++;
1628 }
1629
1630 if (!entries) return NULL;
1631
1632 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */
1633 if (list)
1634 {
1635 HWND* cursor = list;
1636
1637 ListEntry = Desktop->ShellHookWindows.Flink;
1638 while (ListEntry != &Desktop->ShellHookWindows)
1639 {
1640 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1641 ListEntry = ListEntry->Flink;
1642 *cursor++ = Current->hWnd;
1643 }
1644
1645 *cursor = NULL; /* Nullterm list */
1646 }
1647
1648 return list;
1649 }
1650
1651 /*
1652 * Send the Message to the windows registered for ShellHook
1653 * notifications. The lParam contents depend on the Message. See
1654 * MSDN for more details (RegisterShellHookWindow)
1655 */
1656 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam)
1657 {
1658 PDESKTOP Desktop = IntGetActiveDesktop();
1659 HWND* HwndList;
1660
1661 if (!gpsi->uiShellMsg)
1662 {
1663 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK");
1664
1665 TRACE("MsgType = %x\n", gpsi->uiShellMsg);
1666 if (!gpsi->uiShellMsg)
1667 ERR("LastError: %x\n", EngGetLastError());
1668 }
1669
1670 if (!Desktop)
1671 {
1672 TRACE("IntShellHookNotify: No desktop!\n");
1673 return;
1674 }
1675
1676 // Allow other devices have a shot at foreground.
1677 if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL;
1678
1679 // FIXME: System Tray Support.
1680
1681 HwndList = UserBuildShellHookHwndList(Desktop);
1682 if (HwndList)
1683 {
1684 HWND* cursor = HwndList;
1685
1686 for (; *cursor; cursor++)
1687 {
1688 TRACE("Sending notify\n");
1689 UserPostMessage(*cursor,
1690 gpsi->uiShellMsg,
1691 Message,
1692 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );
1693 /* co_IntPostOrSendMessage(*cursor,
1694 gpsi->uiShellMsg,
1695 Message,
1696 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/
1697 }
1698
1699 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST);
1700 }
1701
1702 if (ISITHOOKED(WH_SHELL))
1703 {
1704 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam);
1705 }
1706 }
1707
1708 /*
1709 * Add the window to the ShellHookWindows list. The windows
1710 * on that list get notifications that are important to shell
1711 * type applications.
1712 *
1713 * TODO: Validate the window? I'm not sure if sending these messages to
1714 * an unsuspecting application that is not your own is a nice thing to do.
1715 */
1716 BOOL IntRegisterShellHookWindow(HWND hWnd)
1717 {
1718 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1719 PDESKTOP Desktop = pti->rpdesk;
1720 PSHELL_HOOK_WINDOW Entry;
1721
1722 TRACE("IntRegisterShellHookWindow\n");
1723
1724 /* First deregister the window, so we can be sure it's never twice in the
1725 * list.
1726 */
1727 IntDeRegisterShellHookWindow(hWnd);
1728
1729 Entry = ExAllocatePoolWithTag(PagedPool,
1730 sizeof(SHELL_HOOK_WINDOW),
1731 TAG_WINSTA);
1732
1733 if (!Entry)
1734 return FALSE;
1735
1736 Entry->hWnd = hWnd;
1737
1738 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry);
1739
1740 return TRUE;
1741 }
1742
1743 /*
1744 * Remove the window from the ShellHookWindows list. The windows
1745 * on that list get notifications that are important to shell
1746 * type applications.
1747 */
1748 BOOL IntDeRegisterShellHookWindow(HWND hWnd)
1749 {
1750 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
1751 PDESKTOP Desktop = pti->rpdesk;
1752 PLIST_ENTRY ListEntry;
1753 PSHELL_HOOK_WINDOW Current;
1754
1755 ListEntry = Desktop->ShellHookWindows.Flink;
1756 while (ListEntry != &Desktop->ShellHookWindows)
1757 {
1758 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry);
1759 ListEntry = ListEntry->Flink;
1760 if (Current->hWnd == hWnd)
1761 {
1762 RemoveEntryList(&Current->ListEntry);
1763 ExFreePoolWithTag(Current, TAG_WINSTA);
1764 return TRUE;
1765 }
1766 }
1767
1768 return FALSE;
1769 }
1770
1771 static VOID
1772 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop)
1773 {
1774 /* FIXME: Disable until unmapping works in mm */
1775 #if 0
1776 if (Desktop->pheapDesktop != NULL)
1777 {
1778 MmUnmapViewInSessionSpace(Desktop->pheapDesktop);
1779 Desktop->pheapDesktop = NULL;
1780 }
1781
1782 if (Desktop->hsectionDesktop != NULL)
1783 {
1784 ObDereferenceObject(Desktop->hsectionDesktop);
1785 Desktop->hsectionDesktop = NULL;
1786 }
1787 #endif
1788 }
1789
1790 BOOL FASTCALL
1791 IntPaintDesktop(HDC hDC)
1792 {
1793 static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize!
1794
1795 RECTL Rect;
1796 HBRUSH DesktopBrush, PreviousBrush;
1797 HWND hWndDesktop;
1798 BOOL doPatBlt = TRUE;
1799 PWND WndDesktop;
1800 BOOLEAN InSafeMode;
1801
1802 if (GdiGetClipBox(hDC, &Rect) == ERROR)
1803 return FALSE;
1804
1805 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow;
1806
1807 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd;
1808 if (!WndDesktop)
1809 return FALSE;
1810
1811 /* Retrieve the current SafeMode state */
1812 InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT];
1813
1814 if (!InSafeMode)
1815 {
1816 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground;
1817
1818 /*
1819 * Paint desktop background
1820 */
1821 if (gspv.hbmWallpaper != NULL)
1822 {
1823 SIZE sz;
1824 int x, y;
1825 int scaledWidth, scaledHeight;
1826 int wallpaperX, wallpaperY, wallpaperWidth, wallpaperHeight;
1827 HDC hWallpaperDC;
1828
1829 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left;
1830 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top;
1831
1832 if (gspv.WallpaperMode == wmFit ||
1833 gspv.WallpaperMode == wmFill)
1834 {
1835 int scaleNum, scaleDen;
1836
1837 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper))
1838 if ((sz.cx * gspv.cyWallpaper) > (sz.cy * gspv.cxWallpaper))
1839 {
1840 if (gspv.WallpaperMode == wmFit)
1841 {
1842 scaleNum = sz.cy;
1843 scaleDen = gspv.cyWallpaper;
1844 }
1845 else
1846 {
1847 scaleNum = sz.cx;
1848 scaleDen = gspv.cxWallpaper;
1849 }
1850 }
1851 else
1852 {
1853 if (gspv.WallpaperMode == wmFit)
1854 {
1855 scaleNum = sz.cx;
1856 scaleDen = gspv.cxWallpaper;
1857 }
1858 else
1859 {
1860 scaleNum = sz.cy;
1861 scaleDen = gspv.cyWallpaper;
1862 }
1863 }
1864
1865 scaledWidth = EngMulDiv(gspv.cxWallpaper, scaleNum, scaleDen);
1866 scaledHeight = EngMulDiv(gspv.cyWallpaper, scaleNum, scaleDen);
1867
1868 if (gspv.WallpaperMode == wmFill)
1869 {
1870 wallpaperX = (((scaledWidth - sz.cx) * gspv.cxWallpaper) / (2 * scaledWidth));
1871 wallpaperY = (((scaledHeight - sz.cy) * gspv.cyWallpaper) / (2 * scaledHeight));
1872
1873 wallpaperWidth = (sz.cx * gspv.cxWallpaper) / scaledWidth;
1874 wallpaperHeight = (sz.cy * gspv.cyWallpaper) / scaledHeight;
1875 }
1876 }
1877
1878 if (gspv.WallpaperMode == wmStretch ||
1879 gspv.WallpaperMode == wmTile ||
1880 gspv.WallpaperMode == wmFill)
1881 {
1882 x = 0;
1883 y = 0;
1884 }
1885 else if (gspv.WallpaperMode == wmFit)
1886 {
1887 x = (sz.cx - scaledWidth) / 2;
1888 y = (sz.cy - scaledHeight) / 2;
1889 }
1890 else
1891 {
1892 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */
1893 x = (sz.cx / 2) - (gspv.cxWallpaper / 2);
1894 y = (sz.cy / 2) - (gspv.cyWallpaper / 2);
1895 }
1896
1897 hWallpaperDC = NtGdiCreateCompatibleDC(hDC);
1898 if (hWallpaperDC != NULL)
1899 {
1900 HBITMAP hOldBitmap;
1901
1902 /* Fill in the area that the bitmap is not going to cover */
1903 if (x > 0 || y > 0)
1904 {
1905 /* FIXME: Clip out the bitmap
1906 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);"
1907 once we support DSTINVERT */
1908 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
1909 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
1910 NtGdiSelectBrush(hDC, PreviousBrush);
1911 }
1912
1913 /* Do not fill the background after it is painted no matter the size of the picture */
1914 doPatBlt = FALSE;
1915
1916 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper);
1917
1918 if (gspv.WallpaperMode == wmStretch)
1919 {
1920 if (Rect.right && Rect.bottom)
1921 NtGdiStretchBlt(hDC,
1922 x,
1923 y,
1924 sz.cx,
1925 sz.cy,
1926 hWallpaperDC,
1927 0,
1928 0,
1929 gspv.cxWallpaper,
1930 gspv.cyWallpaper,
1931 SRCCOPY,
1932 0);
1933 }
1934 else if (gspv.WallpaperMode == wmTile)
1935 {
1936 /* Paint the bitmap across the screen then down */
1937 for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper)
1938 {
1939 for (x = 0; x < Rect.right; x += gspv.cxWallpaper)
1940 {
1941 NtGdiBitBlt(hDC,
1942 x,
1943 y,
1944 gspv.cxWallpaper,
1945 gspv.cyWallpaper,
1946 hWallpaperDC,
1947 0,
1948 0,
1949 SRCCOPY,
1950 0,
1951 0);
1952 }
1953 }
1954 }
1955 else if (gspv.WallpaperMode == wmFit)
1956 {
1957 if (Rect.right && Rect.bottom)
1958 {
1959 NtGdiStretchBlt(hDC,
1960 x,
1961 y,
1962 scaledWidth,
1963 scaledHeight,
1964 hWallpaperDC,
1965 0,
1966 0,
1967 gspv.cxWallpaper,
1968 gspv.cyWallpaper,
1969 SRCCOPY,
1970 0);
1971 }
1972 }
1973 else if (gspv.WallpaperMode == wmFill)
1974 {
1975 if (Rect.right && Rect.bottom)
1976 {
1977 NtGdiStretchBlt(hDC,
1978 x,
1979 y,
1980 sz.cx,
1981 sz.cy,
1982 hWallpaperDC,
1983 wallpaperX,
1984 wallpaperY,
1985 wallpaperWidth,
1986 wallpaperHeight,
1987 SRCCOPY,
1988 0);
1989 }
1990 }
1991 else
1992 {
1993 NtGdiBitBlt(hDC,
1994 x,
1995 y,
1996 gspv.cxWallpaper,
1997 gspv.cyWallpaper,
1998 hWallpaperDC,
1999 0,
2000 0,
2001 SRCCOPY,
2002 0,
2003 0);
2004 }
2005 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap);
2006 NtGdiDeleteObjectApp(hWallpaperDC);
2007 }
2008 }
2009 }
2010 else
2011 {
2012 /* Black desktop background in Safe Mode */
2013 DesktopBrush = StockObjects[BLACK_BRUSH];
2014 }
2015
2016 /* Background is set to none, clear the screen */
2017 if (doPatBlt)
2018 {
2019 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
2020 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY);
2021 NtGdiSelectBrush(hDC, PreviousBrush);
2022 }
2023
2024 /*
2025 * Display the system version on the desktop background
2026 */
2027 if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion)
2028 {
2029 NTSTATUS Status;
2030 static WCHAR wszzVersion[1024] = L"\0";
2031
2032 /* Only used in normal mode */
2033 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot)
2034 static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}};
2035 INT i = 0;
2036 SIZE_T len;
2037
2038 HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL;
2039 COLORREF crText, color_old;
2040 UINT align_old;
2041 INT mode_old;
2042 PDC pdc;
2043
2044 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0))
2045 {
2046 Rect.left = Rect.top = 0;
2047 Rect.right = UserGetSystemMetrics(SM_CXSCREEN);
2048 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN);
2049 }
2050 else
2051 {
2052 RECTL_vOffsetRect(&Rect, -Rect.left, -Rect.top);
2053 }
2054
2055 /*
2056 * Set up the fonts (otherwise use default ones)
2057 */
2058
2059 /* Font for the principal version string */
2060 hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont);
2061 /* Font for the secondary version strings */
2062 hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont);
2063
2064 if (hFont1)
2065 hOldFont = NtGdiSelectFont(hDC, hFont1);
2066
2067 if (gspv.hbmWallpaper == NULL)
2068 {
2069 /* Retrieve the brush fill colour */
2070 // TODO: The following code constitutes "GreGetBrushColor".
2071 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush);
2072 pdc = DC_LockDc(hDC);
2073 if (pdc)
2074 {
2075 crText = pdc->eboFill.ulRGBColor;
2076 DC_UnlockDc(pdc);
2077 }
2078 else
2079 {
2080 crText = RGB(0, 0, 0);
2081 }
2082 NtGdiSelectBrush(hDC, PreviousBrush);
2083
2084 /* Adjust text colour according to the brush */
2085 if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3)
2086 crText = RGB(0, 0, 0);
2087 else
2088 crText = RGB(255, 255, 255);
2089 }
2090 else
2091 {
2092 /* Always use white when the text is displayed on top of a wallpaper */
2093 crText = RGB(255, 255, 255);
2094 }
2095
2096 color_old = IntGdiSetTextColor(hDC, crText);
2097 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT);
2098 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT);
2099
2100 /* Display the system version information */
2101 if (!*wszzVersion)
2102 {
2103 Status = GetSystemVersionString(wszzVersion,
2104 ARRAYSIZE(wszzVersion),
2105 InSafeMode,
2106 g_AlwaysDisplayVersion);
2107 if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion)
2108 {
2109 PWCHAR pstr = wszzVersion;
2110 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i)
2111 {
2112 VerStrs[i].n = lstrlenW(pstr);
2113 VerStrs[i].lpstr = pstr;
2114 pstr += (VerStrs[i].n + 1);
2115 }
2116 }
2117 }
2118 else
2119 {
2120 Status = STATUS_SUCCESS;
2121 }
2122 if (NT_SUCCESS(Status) && *wszzVersion)
2123 {
2124 if (!InSafeMode)
2125 {
2126 SIZE Size = {0, 0};
2127 LONG TotalHeight = 0;
2128
2129 /* Normal Mode: multiple version information text separated by newlines */
2130 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
2131
2132 /* Compute the heights of the strings */
2133 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2134 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
2135 {
2136 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
2137 break;
2138
2139 GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1);
2140 VerStrs[i].y = Size.cy; // Store the string height
2141 TotalHeight += Size.cy;
2142
2143 /* While the first string was using hFont1, all the others use hFont2 */
2144 if (hFont2) NtGdiSelectFont(hDC, hFont2);
2145 }
2146 /* The total height must not exceed the screen height */
2147 TotalHeight = min(TotalHeight, Rect.bottom);
2148
2149 /* Display the strings */
2150 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2151 for (i = 0; i < ARRAYSIZE(VerStrs); ++i)
2152 {
2153 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0))
2154 break;
2155
2156 TotalHeight -= VerStrs[i].y;
2157 GreExtTextOutW(hDC,
2158 Rect.right - 5,
2159 Rect.bottom - TotalHeight - 5,
2160 0, NULL,
2161 VerStrs[i].lpstr,
2162 VerStrs[i].n,
2163 NULL, 0);
2164
2165 /* While the first string was using hFont1, all the others use hFont2 */
2166 if (hFont2) NtGdiSelectFont(hDC, hFont2);
2167 }
2168 }
2169 else
2170 {
2171 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2172
2173 /* Safe Mode: single version information text in top center */
2174 len = wcslen(wszzVersion);
2175
2176 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP);
2177 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0);
2178 }
2179 }
2180
2181 if (InSafeMode)
2182 {
2183 if (hFont1) NtGdiSelectFont(hDC, hFont1);
2184
2185 /* Print Safe Mode text in corners */
2186 len = wcslen(s_wszSafeMode);
2187
2188 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP);
2189 GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
2190 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP);
2191 GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0);
2192 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM);
2193 GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
2194 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM);
2195 GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0);
2196 }
2197
2198 IntGdiSetBkMode(hDC, mode_old);
2199 IntGdiSetTextAlign(hDC, align_old);
2200 IntGdiSetTextColor(hDC, color_old);
2201
2202 if (hFont2)
2203 GreDeleteObject(hFont2);
2204
2205 if (hFont1)
2206 {
2207 NtGdiSelectFont(hDC, hOldFont);
2208 GreDeleteObject(hFont1);
2209 }
2210 }
2211
2212 return TRUE;
2213 }
2214
2215 static NTSTATUS
2216 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta)
2217 {
2218 PVOID DesktopHeapSystemBase = NULL;
2219 ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024;
2220 SIZE_T DesktopInfoSize;
2221 ULONG i;
2222
2223 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName);
2224
2225 RtlZeroMemory(pdesk, sizeof(DESKTOP));
2226
2227 /* Link the desktop with the parent window station */
2228 ObReferenceObject(pwinsta);
2229 pdesk->rpwinstaParent = pwinsta;
2230 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry);
2231
2232 /* Create the desktop heap */
2233 pdesk->hsectionDesktop = NULL;
2234 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop,
2235 &DesktopHeapSystemBase,
2236 HeapSize);
2237 if (pdesk->pheapDesktop == NULL)
2238 {
2239 ERR("Failed to create desktop heap!\n");
2240 return STATUS_NO_MEMORY;
2241 }
2242
2243 /* Create DESKTOPINFO */
2244 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR);
2245 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop,
2246 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY,
2247 DesktopInfoSize);
2248 if (pdesk->pDeskInfo == NULL)
2249 {
2250 ERR("Failed to create the DESKTOP structure!\n");
2251 return STATUS_NO_MEMORY;
2252 }
2253
2254 /* Initialize the DESKTOPINFO */
2255 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase;
2256 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize);
2257 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName,
2258 DesktopName->Buffer,
2259 DesktopName->Length + sizeof(WCHAR));
2260 for (i = 0; i < NB_HOOKS; i++)
2261 {
2262 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]);
2263 }
2264
2265 InitializeListHead(&pdesk->ShellHookWindows);
2266 InitializeListHead(&pdesk->PtiList);
2267
2268 return STATUS_SUCCESS;
2269 }
2270
2271 /* SYSCALLS *******************************************************************/
2272
2273 /*
2274 * NtUserCreateDesktop
2275 *
2276 * Creates a new desktop.
2277 *
2278 * Parameters
2279 * poaAttribs
2280 * Object Attributes.
2281 *
2282 * lpszDesktopDevice
2283 * Name of the device.
2284 *
2285 * pDeviceMode
2286 * Device Mode.
2287 *
2288 * dwFlags
2289 * Interaction flags.
2290 *
2291 * dwDesiredAccess
2292 * Requested type of access.
2293 *
2294 *
2295 * Return Value
2296 * If the function succeeds, the return value is a handle to the newly
2297 * created desktop. If the specified desktop already exists, the function
2298 * succeeds and returns a handle to the existing desktop. When you are
2299 * finished using the handle, call the CloseDesktop function to close it.
2300 * If the function fails, the return value is NULL.
2301 *
2302 * Status
2303 * @implemented
2304 */
2305
2306 NTSTATUS
2307 FASTCALL
2308 IntCreateDesktop(
2309 OUT HDESK* phDesktop,
2310 IN POBJECT_ATTRIBUTES ObjectAttributes,
2311 IN KPROCESSOR_MODE AccessMode,
2312 IN PUNICODE_STRING lpszDesktopDevice OPTIONAL,
2313 IN LPDEVMODEW lpdmw OPTIONAL,
2314 IN DWORD dwFlags,
2315 IN ACCESS_MASK dwDesiredAccess)
2316 {
2317 NTSTATUS Status;
2318 PDESKTOP pdesk = NULL;
2319 HDESK hDesk;
2320 BOOLEAN Context = FALSE;
2321 UNICODE_STRING ClassName;
2322 LARGE_STRING WindowName;
2323 BOOL NoHooks = FALSE;
2324 PWND pWnd = NULL;
2325 CREATESTRUCTW Cs;
2326 PTHREADINFO ptiCurrent;
2327 PCLS pcls;
2328
2329 TRACE("Enter IntCreateDesktop\n");
2330
2331 ASSERT(phDesktop);
2332 *phDesktop = NULL;
2333
2334 ptiCurrent = PsGetCurrentThreadWin32Thread();
2335 ASSERT(ptiCurrent);
2336 ASSERT(gptiDesktopThread);
2337
2338 /* Turn off hooks when calling any CreateWindowEx from inside win32k */
2339 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS);
2340 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS;
2341 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
2342
2343 /*
2344 * Try to open already existing desktop
2345 */
2346 Status = ObOpenObjectByName(ObjectAttributes,
2347 ExDesktopObjectType,
2348 AccessMode,
2349 NULL,
2350 dwDesiredAccess,
2351 (PVOID)&Context,
2352 (PHANDLE)&hDesk);
2353 if (!NT_SUCCESS(Status))
2354 {
2355 ERR("ObOpenObjectByName failed to open/create desktop\n");
2356 goto Quit;
2357 }
2358
2359 /* In case the object was not created (eg if it existed), return now */
2360 if (Context == FALSE)
2361 {
2362 TRACE("IntCreateDesktop opened desktop '%wZ'\n", ObjectAttributes->ObjectName);
2363 Status = STATUS_SUCCESS;
2364 goto Quit;
2365 }
2366
2367 /* Reference the desktop */
2368 Status = ObReferenceObjectByHandle(hDesk,
2369 0,
2370 ExDesktopObjectType,
2371 KernelMode,
2372 (PVOID*)&pdesk,
2373 NULL);
2374 if (!NT_SUCCESS(Status))
2375 {
2376 ERR("Failed to reference desktop object\n");
2377 goto Quit;
2378 }
2379
2380 /* Get the desktop window class. The thread desktop does not belong to any desktop
2381 * so the classes created there (including the desktop class) are allocated in the shared heap
2382 * It would cause problems if we used a class that belongs to the caller
2383 */
2384 ClassName.Buffer = WC_DESKTOP;
2385 ClassName.Length = 0;
2386 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
2387 if (pcls == NULL)
2388 {
2389 ASSERT(FALSE);
2390 Status = STATUS_UNSUCCESSFUL;
2391 goto Quit;
2392 }
2393
2394 RtlZeroMemory(&WindowName, sizeof(WindowName));
2395 RtlZeroMemory(&Cs, sizeof(Cs));
2396 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN),
2397 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN),
2398 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN),
2399 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN),
2400 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
2401 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2402 Cs.lpszName = (LPCWSTR) &WindowName;
2403 Cs.lpszClass = (LPCWSTR) &ClassName;
2404
2405 /* Use IntCreateWindow instead of co_UserCreateWindowEx because the later expects a thread with a desktop */
2406 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
2407 if (pWnd == NULL)
2408 {
2409 ERR("Failed to create desktop window for the new desktop\n");
2410 Status = STATUS_UNSUCCESSFUL;
2411 goto Quit;
2412 }
2413
2414 pdesk->dwSessionId = PsGetCurrentProcessSessionId();
2415 pdesk->DesktopWindow = pWnd->head.h;
2416 pdesk->pDeskInfo->spwnd = pWnd;
2417 pWnd->fnid = FNID_DESKTOP;
2418
2419 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]);
2420 ClassName.Length = 0;
2421 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE);
2422 if (pcls == NULL)
2423 {
2424 ASSERT(FALSE);
2425 Status = STATUS_UNSUCCESSFUL;
2426 goto Quit;
2427 }
2428
2429 RtlZeroMemory(&WindowName, sizeof(WindowName));
2430 RtlZeroMemory(&Cs, sizeof(Cs));
2431 Cs.cx = Cs.cy = 100;
2432 Cs.style = WS_POPUP|WS_CLIPCHILDREN;
2433 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc!
2434 Cs.lpszName = (LPCWSTR)&WindowName;
2435 Cs.lpszClass = (LPCWSTR)&ClassName;
2436 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk, WINVER);
2437 if (pWnd == NULL)
2438 {
2439 ERR("Failed to create message window for the new desktop\n");
2440 Status = STATUS_UNSUCCESSFUL;
2441 goto Quit;
2442 }
2443
2444 pdesk->spwndMessage = pWnd;
2445 pWnd->fnid = FNID_MESSAGEWND;
2446
2447 /* Now,,,
2448 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki)
2449 Create Tooltip. Saved in DesktopObject->spwndTooltip.
2450 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST
2451 hWndParent are spwndMessage. Use hModuleWin for server side winproc!
2452 The rest is same as message window.
2453 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx
2454 */
2455 Status = STATUS_SUCCESS;
2456
2457 Quit:
2458 if (pdesk != NULL)
2459 {
2460 ObDereferenceObject(pdesk);
2461 }
2462 if (!NT_SUCCESS(Status) && hDesk != NULL)
2463 {
2464 ObCloseHandle(hDesk, AccessMode);
2465 hDesk = NULL;
2466 }
2467 if (!NoHooks)
2468 {
2469 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS;
2470 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags;
2471 }
2472
2473 TRACE("Leave IntCreateDesktop, Status 0x%08lx\n", Status);
2474
2475 if (NT_SUCCESS(Status))
2476 *phDesktop = hDesk;
2477 else
2478 SetLastNtError(Status);
2479 return Status;
2480 }
2481
2482 HDESK APIENTRY
2483 NtUserCreateDesktop(
2484 POBJECT_ATTRIBUTES ObjectAttributes,
2485 PUNICODE_STRING lpszDesktopDevice,
2486 LPDEVMODEW lpdmw,
2487 DWORD dwFlags,
2488 ACCESS_MASK dwDesiredAccess)
2489 {
2490 NTSTATUS Status;
2491 HDESK hDesk;
2492
2493 DECLARE_RETURN(HDESK);
2494
2495 TRACE("Enter NtUserCreateDesktop\n");
2496 UserEnterExclusive();
2497
2498 Status = IntCreateDesktop(&hDesk,
2499 ObjectAttributes,
2500 UserMode,
2501 lpszDesktopDevice,
2502 lpdmw,
2503 dwFlags,
2504 dwDesiredAccess);
2505 if (!NT_SUCCESS(Status))
2506 {
2507 ERR("IntCreateDesktop failed, Status 0x%08lx\n", Status);
2508 // SetLastNtError(Status);
2509 RETURN(NULL);
2510 }
2511
2512 RETURN(hDesk);
2513
2514 CLEANUP:
2515 TRACE("Leave NtUserCreateDesktop, ret=0x%p\n", _ret_);
2516 UserLeave();
2517 END_CLEANUP;
2518 }
2519
2520 /*
2521 * NtUserOpenDesktop
2522 *
2523 * Opens an existing desktop.
2524 *
2525 * Parameters
2526 * lpszDesktopName
2527 * Name of the existing desktop.
2528 *
2529 * dwFlags
2530 * Interaction flags.
2531 *
2532 * dwDesiredAccess
2533 * Requested type of access.
2534 *
2535 * Return Value
2536 * Handle to the desktop or zero on failure.
2537 *
2538 * Status
2539 * @implemented
2540 */
2541
2542 HDESK APIENTRY
2543 NtUserOpenDesktop(
2544 POBJECT_ATTRIBUTES ObjectAttributes,
2545 DWORD dwFlags,
2546 ACCESS_MASK dwDesiredAccess)
2547 {
2548 NTSTATUS Status;
2549 HDESK Desktop;
2550
2551 Status = ObOpenObjectByName(
2552 ObjectAttributes,
2553 ExDesktopObjectType,
2554 UserMode,
2555 NULL,
2556 dwDesiredAccess,
2557 NULL,
2558 (HANDLE*)&Desktop);
2559
2560 if (!NT_SUCCESS(Status))
2561 {
2562 ERR("Failed to open desktop\n");
2563 SetLastNtError(Status);
2564 return NULL;
2565 }
2566
2567 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop);
2568
2569 return Desktop;
2570 }
2571
2572 HDESK UserOpenInputDesktop(DWORD dwFlags,
2573 BOOL fInherit,
2574 ACCESS_MASK dwDesiredAccess)
2575 {
2576 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
2577 NTSTATUS Status;
2578 ULONG HandleAttributes = 0;
2579 HDESK hdesk = NULL;
2580
2581 if (!gpdeskInputDesktop)
2582 {
2583 return NULL;
2584 }
2585
2586 if (pti->ppi->prpwinsta != InputWindowStation)
2587 {
2588 ERR("Tried to open input desktop from non interactive winsta!\n");
2589 EngSetLastError(ERROR_INVALID_FUNCTION);
2590 return NULL;
2591 }
2592
2593 if (fInherit) HandleAttributes = OBJ_INHERIT;
2594
2595 /* Create a new handle to the object */
2596 Status = ObOpenObjectByPointer(
2597 gpdeskInputDesktop,
2598 HandleAttributes,
2599 NULL,
2600 dwDesiredAccess,
2601 ExDesktopObjectType,
2602 UserMode,
2603 (PHANDLE)&hdesk);
2604
2605 if (!NT_SUCCESS(Status))
2606 {
2607 ERR("Failed to open input desktop object\n");
2608 SetLastNtError(Status);
2609 }
2610
2611 return hdesk;
2612 }
2613
2614 /*
2615 * NtUserOpenInputDesktop
2616 *
2617 * Opens the input (interactive) desktop.
2618 *
2619 * Parameters
2620 * dwFlags
2621 * Interaction flags.
2622 *
2623 * fInherit
2624 * Inheritance option.
2625 *
2626 * dwDesiredAccess
2627 * Requested type of access.
2628 *
2629 * Return Value
2630 * Handle to the input desktop or zero on failure.
2631 *
2632 * Status
2633 * @implemented
2634 */
2635
2636 HDESK APIENTRY
2637 NtUserOpenInputDesktop(
2638 DWORD dwFlags,
2639 BOOL fInherit,
2640 ACCESS_MASK dwDesiredAccess)
2641 {
2642 HDESK hdesk;
2643
2644 UserEnterExclusive();
2645 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
2646
2647 hdesk = UserOpenInputDesktop(dwFlags, fInherit, dwDesiredAccess);
2648
2649 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk);
2650 UserLeave();
2651 return hdesk;
2652 }
2653
2654 /*
2655 * NtUserCloseDesktop
2656 *
2657 * Closes a desktop handle.
2658 *
2659 * Parameters
2660 * hDesktop
2661 * Handle to the desktop.
2662 *
2663 * Return Value
2664 * Status
2665 *
2666 * Remarks
2667 * The desktop handle can be created with NtUserCreateDesktop or
2668 * NtUserOpenDesktop. This function will fail if any thread in the calling
2669 * process is using the specified desktop handle or if the handle refers
2670 * to the initial desktop of the calling process.
2671 *
2672 * Status
2673 * @implemented
2674 */
2675
2676 BOOL APIENTRY
2677 NtUserCloseDesktop(HDESK hDesktop)
2678 {
2679 PDESKTOP pdesk;
2680 NTSTATUS Status;
2681 DECLARE_RETURN(BOOL);
2682
2683 TRACE("NtUserCloseDesktop(0x%p) called\n", hDesktop);
2684 UserEnterExclusive();
2685
2686 if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup)
2687 {
2688 ERR("Attempted to close thread desktop\n");
2689 EngSetLastError(ERROR_BUSY);
2690 RETURN(FALSE);
2691 }
2692
2693 Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
2694 if (!NT_SUCCESS(Status))
2695 {
2696 ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
2697 RETURN(FALSE);
2698 }
2699
2700 ObDereferenceObject(pdesk);
2701
2702 Status = ObCloseHandle(hDesktop, UserMode);
2703 if (!NT_SUCCESS(Status))
2704 {
2705 ERR("Failed to close desktop handle 0x%p\n", hDesktop);
2706 SetLastNtError(Status);
2707 RETURN(FALSE);
2708 }
2709
2710 RETURN(TRUE);
2711
2712 CLEANUP:
2713 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_);
2714 UserLeave();
2715 END_CLEANUP;
2716 }
2717
2718 /*
2719 * NtUserPaintDesktop
2720 *
2721 * The NtUserPaintDesktop function fills the clipping region in the
2722 * specified device context with the desktop pattern or wallpaper. The
2723 * function is provided primarily for shell desktops.
2724 *
2725 * Parameters
2726 * hDC
2727 * Handle to the device context.
2728 *
2729 * Status
2730 * @implemented
2731 */
2732
2733 BOOL APIENTRY
2734 NtUserPaintDesktop(HDC hDC)
2735 {
2736 BOOL Ret;
2737 UserEnterExclusive();
2738 TRACE("Enter NtUserPaintDesktop\n");
2739 Ret = IntPaintDesktop(hDC);
2740 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret);
2741 UserLeave();
2742 return Ret;
2743 }
2744
2745 /*
2746 * NtUserResolveDesktop
2747 *
2748 * The NtUserResolveDesktop function attempts to retrieve valid handles to
2749 * a desktop and a window station suitable for the specified process.
2750 * The specified desktop path string is used only as a hint for the resolution.
2751 *
2752 * See the description of IntResolveDesktop for more details.
2753 *
2754 * Parameters
2755 * ProcessHandle
2756 * Handle to a user process.
2757 *
2758 * DesktopPath
2759 * The desktop path string used as a hint for desktop resolution.
2760 *
2761 * bInherit
2762 * Whether or not the returned handles are inheritable.
2763 *
2764 * phWinSta
2765 * Pointer to a window station handle.
2766 *
2767 * Return Value
2768 * Handle to the desktop (direct return value) and
2769 * handle to the associated window station (by pointer).
2770 * NULL in case of failure.
2771 *
2772 * Remarks
2773 * Callable by CSRSS only.
2774 *
2775 * Status
2776 * @implemented
2777 */
2778
2779 HDESK
2780 NTAPI
2781 NtUserResolveDesktop(
2782 IN HANDLE ProcessHandle,
2783 IN PUNICODE_STRING DesktopPath,
2784 IN BOOL bInherit,
2785 OUT HWINSTA* phWinSta)
2786 {
2787 NTSTATUS Status;
2788 PEPROCESS Process;
2789 HWINSTA hWinSta = NULL;
2790 HDESK hDesktop = NULL;
2791 UNICODE_STRING CapturedDesktopPath;
2792
2793 /* Allow only the Console Server to perform this operation (via CSRSS) */
2794 if (PsGetCurrentProcess() != gpepCSRSS)
2795 return NULL;
2796
2797 /* Get the process object the user handle was referencing */
2798 Status = ObReferenceObjectByHandle(ProcessHandle,
2799 PROCESS_QUERY_INFORMATION,
2800 *PsProcessType,
2801 UserMode,
2802 (PVOID*)&Process,
2803 NULL);
2804 if (!NT_SUCCESS(Status))
2805 return NULL;
2806
2807 // UserEnterShared();
2808
2809 _SEH2_TRY
2810 {
2811 /* Probe the handle pointer */
2812 // ProbeForWriteHandle
2813 ProbeForWrite(phWinSta, sizeof(HWINSTA), sizeof(HWINSTA));
2814 }
2815 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2816 {
2817 Status = _SEH2_GetExceptionCode();
2818 _SEH2_YIELD(goto Quit);
2819 }
2820 _SEH2_END;
2821
2822 /* Capture the user desktop path string */
2823 Status = ProbeAndCaptureUnicodeString(&CapturedDesktopPath,
2824 UserMode,
2825 DesktopPath);
2826 if (!NT_SUCCESS(Status))
2827 goto Quit;
2828
2829 /* Call the internal function */
2830 Status = IntResolveDesktop(Process,
2831 &CapturedDesktopPath,
2832 bInherit,
2833 &hWinSta,
2834 &hDesktop);
2835 if (!NT_SUCCESS(Status))
2836 {
2837 ERR("IntResolveDesktop failed, Status 0x%08lx\n", Status);
2838 hWinSta = NULL;
2839 hDesktop = NULL;
2840 }
2841
2842 _SEH2_TRY
2843 {
2844 /* Return the window station handle */
2845 *phWinSta = hWinSta;
2846 }
2847 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2848 {
2849 Status = _SEH2_GetExceptionCode();
2850
2851 /* We failed, close the opened desktop and window station */
2852 if (hDesktop) ObCloseHandle(hDesktop, UserMode);
2853 hDesktop = NULL;
2854 if (hWinSta) ObCloseHandle(hWinSta, UserMode);
2855 }
2856 _SEH2_END;
2857
2858 /* Free the captured string */
2859 ReleaseCapturedUnicodeString(&CapturedDesktopPath, UserMode);
2860
2861 Quit:
2862 // UserLeave();
2863
2864 /* Dereference the process object */
2865 ObDereferenceObject(Process);
2866
2867 /* Return the desktop handle */
2868 return hDesktop;
2869 }
2870
2871 /*
2872 * NtUserSwitchDesktop
2873 *
2874 * Sets the current input (interactive) desktop.
2875 *
2876 * Parameters
2877 * hDesktop
2878 * Handle to desktop.
2879 *
2880 * Return Value
2881 * Status
2882 *
2883 * Status
2884 * @unimplemented
2885 */
2886
2887 BOOL APIENTRY
2888 NtUserSwitchDesktop(HDESK hdesk)
2889 {
2890 PDESKTOP pdesk;
2891 NTSTATUS Status;
2892 BOOL bRedrawDesktop;
2893 DECLARE_RETURN(BOOL);
2894
2895 UserEnterExclusive();
2896 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk);
2897
2898 Status = IntValidateDesktopHandle(hdesk, UserMode, 0, &pdesk);
2899 if (!NT_SUCCESS(Status))
2900 {
2901 ERR("Validation of desktop handle 0x%p failed\n", hdesk);
2902 RETURN(FALSE);
2903 }
2904
2905 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId)
2906 {
2907 ObDereferenceObject(pdesk);
2908 ERR("NtUserSwitchDesktop called for a desktop of a different session\n");
2909 RETURN(FALSE);
2910 }
2911
2912 if (pdesk == gpdeskInputDesktop)
2913 {
2914 ObDereferenceObject(pdesk);
2915 WARN("NtUserSwitchDesktop called for active desktop\n");
2916 RETURN(TRUE);
2917 }
2918
2919 /*
2920 * Don't allow applications switch the desktop if it's locked, unless the caller
2921 * is the logon application itself
2922 */
2923 if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) &&
2924 gpidLogon != PsGetCurrentProcessId())
2925 {
2926 ObDereferenceObject(pdesk);
2927 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk);
2928 RETURN(FALSE);
2929 }
2930
2931 if (pdesk->rpwinstaParent != InputWindowStation)
2932 {
2933 ObDereferenceObject(pdesk);
2934 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk);
2935 RETURN(FALSE);
2936 }
2937
2938 /* FIXME: Fail if the process is associated with a secured
2939 desktop such as Winlogon or Screen-Saver */
2940 /* FIXME: Connect to input device */
2941
2942 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk);
2943
2944 bRedrawDesktop = FALSE;
2945
2946 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */
2947 if (gpdeskInputDesktop != NULL)
2948 {
2949 if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE)
2950 bRedrawDesktop = TRUE;
2951
2952 /* Hide the previous desktop window */
2953 IntHideDesktop(gpdeskInputDesktop);
2954 }
2955
2956 /* Set the active desktop in the desktop's window station. */
2957 InputWindowStation->ActiveDesktop = pdesk;
2958
2959 /* Set the global state. */
2960 gpdeskInputDesktop = pdesk;
2961
2962 /* Show the new desktop window */
2963 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop);
2964
2965 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop);
2966 ObDereferenceObject(pdesk);
2967
2968 RETURN(TRUE);
2969
2970 CLEANUP:
2971 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_);
2972 UserLeave();
2973 END_CLEANUP;
2974 }
2975
2976 /*
2977 * NtUserGetThreadDesktop
2978 *
2979 * Status
2980 * @implemented
2981 */
2982
2983 HDESK APIENTRY
2984 NtUserGetThreadDesktop(DWORD dwThreadId, HDESK hConsoleDesktop)
2985 {
2986 HDESK hDesk;
2987 NTSTATUS Status;
2988 PTHREADINFO pti;
2989 PEPROCESS Process;
2990 PDESKTOP DesktopObject;
2991 OBJECT_HANDLE_INFORMATION HandleInformation;
2992
2993 UserEnterExclusive();
2994 TRACE("Enter NtUserGetThreadDesktop\n");
2995
2996 if (!dwThreadId)
2997 {
2998 EngSetLastError(ERROR_INVALID_PARAMETER);
2999 hDesk = NULL;
3000 goto Quit;
3001 }
3002
3003 /* Validate the Win32 thread and retrieve its information */
3004 pti = IntTID2PTI(UlongToHandle(dwThreadId));
3005 if (pti)
3006 {
3007 /* Get the desktop handle of the thread */
3008 hDesk = pti->hdesk;
3009 Process = pti->ppi->peProcess;
3010 }
3011 else if (hConsoleDesktop)
3012 {
3013 /*
3014 * The thread may belong to a console, so attempt to use the provided
3015 * console desktop handle as a fallback. Otherwise this means that the
3016 * thread is either not Win32 or invalid.
3017 */
3018 hDesk = hConsoleDesktop;
3019 Process = gpepCSRSS;
3020 }
3021 else
3022 {
3023 EngSetLastError(ERROR_INVALID_PARAMETER);
3024 hDesk = NULL;
3025 goto Quit;
3026 }
3027
3028 if (!hDesk)
3029 {
3030 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId);
3031 goto Quit;
3032 }
3033
3034 if (Process == PsGetCurrentProcess())
3035 {
3036 /*
3037 * Just return the handle, since we queried the desktop handle
3038 * of a thread running in the same context.
3039 */
3040 goto Quit;
3041 }
3042
3043 /*
3044 * We could just use the cached rpdesk instead of looking up the handle,
3045 * but it may actually be safer to validate the desktop and get a temporary
3046 * reference to it so that it does not disappear under us (e.g. when the
3047 * desktop is being destroyed) during the operation.
3048 */
3049 /*
3050 * Switch into the context of the thread we are trying to get
3051 * the desktop from, so we can use the handle.
3052 */
3053 KeAttachProcess(&Process->Pcb);
3054 Status = ObReferenceObjectByHandle(hDesk,
3055 0,
3056 ExDesktopObjectType,
3057 UserMode,
3058 (PVOID*)&DesktopObject,
3059 &HandleInformation);
3060 KeDetachProcess();
3061
3062 if (NT_SUCCESS(Status))
3063 {
3064 /*
3065 * Lookup our handle table if we can find a handle to the desktop object.
3066 * If not, create one.
3067 * QUESTION: Do we really need to create a handle in case it doesn't exist??
3068 */
3069 hDesk = IntGetDesktopObjectHandle(DesktopObject);
3070
3071 /* All done, we got a valid handle to the desktop */
3072 ObDereferenceObject(DesktopObject);
3073 }
3074 else
3075 {
3076 /* The handle could not be found, there is nothing to get... */
3077 hDesk = NULL;
3078 }
3079
3080 if (!hDesk)
3081 {
3082 ERR("Could not retrieve or access desktop for thread 0x%x\n", dwThreadId);
3083 EngSetLastError(ERROR_ACCESS_DENIED);
3084 }
3085
3086 Quit:
3087 TRACE("Leave NtUserGetThreadDesktop, hDesk = 0x%p\n", hDesk);
3088 UserLeave();
3089 return hDesk;
3090 }
3091
3092 static NTSTATUS
3093 IntUnmapDesktopView(IN PDESKTOP pdesk)
3094 {
3095 PPROCESSINFO ppi;
3096 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
3097 NTSTATUS Status = STATUS_SUCCESS;
3098
3099 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk);
3100
3101 ppi = PsGetCurrentProcessWin32Process();
3102
3103 /*
3104 * Unmap if we're the last thread using the desktop.
3105 * Start the search at the next mapping: skip the first entry
3106 * as it must be the global user heap mapping.
3107 */
3108 PrevLink = &ppi->HeapMappings.Next;
3109 HeapMapping = *PrevLink;
3110 while (HeapMapping != NULL)
3111 {
3112 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
3113 {
3114 if (--HeapMapping->Count == 0)
3115 {
3116 *PrevLink = HeapMapping->Next;
3117
3118 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk);
3119 Status = MmUnmapViewOfSection(PsGetCurrentProcess(),
3120 HeapMapping->UserMapping);
3121
3122 ObDereferenceObject(pdesk);
3123
3124 UserHeapFree(HeapMapping);
3125 break;
3126 }
3127 }
3128
3129 PrevLink = &HeapMapping->Next;
3130 HeapMapping = HeapMapping->Next;
3131 }
3132
3133 return Status;
3134 }
3135
3136 static NTSTATUS
3137 IntMapDesktopView(IN PDESKTOP pdesk)
3138 {
3139 PPROCESSINFO ppi;
3140 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink;
3141 PVOID UserBase = NULL;
3142 SIZE_T ViewSize = 0;
3143 LARGE_INTEGER Offset;
3144 NTSTATUS Status;
3145
3146 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk);
3147
3148 ppi = PsGetCurrentProcessWin32Process();
3149
3150 /*
3151 * Find out if another thread already mapped the desktop heap.
3152 * Start the search at the next mapping: skip the first entry
3153 * as it must be the global user heap mapping.
3154 */
3155 PrevLink = &ppi->HeapMappings.Next;
3156 HeapMapping = *PrevLink;
3157 while (HeapMapping != NULL)
3158 {
3159 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop)
3160 {
3161 HeapMapping->Count++;
3162 return STATUS_SUCCESS;
3163 }
3164
3165 PrevLink = &HeapMapping->Next;
3166 HeapMapping = HeapMapping->Next;
3167 }
3168
3169 /* We're the first, map the heap */
3170 Offset.QuadPart = 0;
3171 Status = MmMapViewOfSection(pdesk->hsectionDesktop,
3172 PsGetCurrentProcess(),
3173 &UserBase,
3174 0,
3175 0,
3176 &Offset,
3177 &ViewSize,
3178 ViewUnmap,
3179 SEC_NO_CHANGE,
3180 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */
3181 if (!NT_SUCCESS(Status))
3182 {
3183 ERR("Failed to map desktop\n");
3184 return Status;
3185 }
3186
3187 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk);
3188
3189 /* Add the mapping */
3190 HeapMapping = UserHeapAlloc(sizeof(*HeapMapping));
3191 if (HeapMapping == NULL)
3192 {
3193 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase);
3194 ERR("UserHeapAlloc() failed!\n");
3195 return STATUS_NO_MEMORY;
3196 }
3197
3198 HeapMapping->Next = NULL;
3199 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop;
3200 HeapMapping->UserMapping = UserBase;
3201 HeapMapping->Limit = ViewSize;
3202 HeapMapping->Count = 1;
3203 *PrevLink = HeapMapping;
3204
3205 ObReferenceObject(pdesk);
3206
3207 return STATUS_SUCCESS;
3208 }
3209
3210 BOOL
3211 IntSetThreadDesktop(IN HDESK hDesktop,
3212 IN BOOL FreeOnFailure)
3213 {
3214 PDESKTOP pdesk = NULL, pdeskOld;
3215 PTHREADINFO pti;
3216 NTSTATUS Status;
3217 PCLIENTTHREADINFO pctiOld, pctiNew = NULL;
3218 PCLIENTINFO pci;
3219
3220 ASSERT(NtCurrentTeb());
3221
3222 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure);
3223
3224 pti = PsGetCurrentThreadWin32Thread();
3225 pci = pti->pClientInfo;
3226
3227 /* If the caller gave us a desktop, ensure it is valid */
3228 if (hDesktop != NULL)
3229 {
3230 /* Validate the new desktop. */
3231 Status = IntValidateDesktopHandle(hDesktop, UserMode, 0, &pdesk);
3232 if (!NT_SUCCESS(Status))
3233 {
3234 ERR("Validation of desktop handle 0x%p failed\n", hDesktop);
3235 return FALSE;
3236 }
3237
3238 if (pti->rpdesk == pdesk)
3239 {
3240 /* Nothing to do */
3241 ObDereferenceObject(pdesk);
3242 return TRUE;
3243 }
3244 }
3245
3246 /* Make sure that we don't own any window in the current desktop */
3247 if (!IsListEmpty(&pti->WindowListHead))
3248 {
3249 if (pdesk)
3250 ObDereferenceObject(pdesk);
3251 ERR("Attempted to change thread desktop although the thread has windows!\n");
3252 EngSetLastError(ERROR_BUSY);
3253 return FALSE;
3254 }
3255
3256 /* Desktop is being re-set so clear out foreground. */
3257 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground)
3258 {
3259 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop!
3260 IntSetFocusMessageQueue(NULL);
3261 }
3262
3263 /* Before doing the switch, map the new desktop heap and allocate the new pcti */
3264 if (pdesk != NULL)
3265 {
3266 Status = IntMapDesktopView(pdesk);
3267 if (!NT_SUCCESS(Status))
3268 {
3269 ERR("Failed to map desktop heap!\n");
3270 ObDereferenceObject(pdesk);
3271 SetLastNtError(Status);
3272 return FALSE;
3273 }
3274
3275 pctiNew = DesktopHeapAlloc(pdesk, sizeof(CLIENTTHREADINFO));
3276 if (pctiNew == NULL)
3277 {
3278 ERR("Failed to allocate new pcti\n");
3279 IntUnmapDesktopView(pdesk);
3280 ObDereferenceObject(pdesk);
3281 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
3282 return FALSE;
3283 }
3284 }
3285
3286 /*
3287 * Processes, in particular Winlogon.exe, that manage window stations
3288 * (especially the interactive WinSta0 window station) and desktops,
3289 * may not be able to connect at startup to a window station and have
3290 * an associated desktop as well, if none exists on the system already.
3291 * Because creating a new window station does not affect the window station
3292 * associated to the process, and because neither by associating a window
3293 * station to the process nor creating a new desktop on it does associate
3294 * a startup desktop to that process, the process has to actually assigns
3295 * one of its threads to a desktop so that it gets automatically an assigned
3296 * startup desktop.
3297 *
3298 * This is what actually happens for Winlogon.exe, which is started without
3299 * any window station and desktop. By creating the first (and therefore
3300 * interactive) WinSta0 window station, then assigning WinSta0 to itself
3301 * and creating the Default desktop on it, and then assigning this desktop
3302 * to its main thread, Winlogon.exe basically does the similar steps that
3303 * would have been done automatically at its startup if there were already
3304 * an existing WinSta0 window station and Default desktop.
3305 *
3306 * Of course all this must not be done if we are a SYSTEM or CSRSS thread.
3307 */
3308 // if (pti->ppi->peProcess != gpepCSRSS)
3309 if (!(pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)) &&
3310 pti->ppi->rpdeskStartup == NULL && hDesktop != NULL)
3311 {
3312 ERR("The process 0x%p '%s' didn't have an assigned startup desktop before, assigning it now!\n",
3313 pti->ppi->peProcess, pti->ppi->peProcess->ImageFileName);
3314
3315 pti->ppi->hdeskStartup = hDesktop;
3316 pti->ppi->rpdeskStartup = pdesk;
3317 }
3318
3319 /* free all classes or move them to the shared heap */
3320 if (pti->rpdesk != NULL)
3321 {
3322 if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure))
3323 {
3324 ERR("Failed to move process classes to shared heap!\n");
3325 if (pdesk)
3326 {
3327 DesktopHeapFree(pdesk, pctiNew);
3328 IntUnmapDesktopView(pdesk);
3329 ObDereferenceObject(pdesk);
3330 }
3331 return FALSE;
3332 }
3333 }
3334
3335 pdeskOld = pti->rpdesk;
3336 if (pti->pcti != &pti->cti)
3337 pctiOld = pti->pcti;
3338 else
3339 pctiOld = NULL;
3340
3341 /* do the switch */
3342 if (pdesk != NULL)
3343 {
3344 pti->rpdesk = pdesk;
3345 pti->hdesk = hDesktop;
3346 pti->pDeskInfo = pti->rpdesk->pDeskInfo;
3347 pti->pcti = pctiNew;
3348
3349 pci->ulClientDelta = DesktopHeapGetUserDelta();
3350 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta);
3351 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta);
3352
3353 /* initialize the new pcti */
3354 if (pctiOld != NULL)
3355 {
3356 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO));
3357 }
3358 else
3359 {
3360 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO));
3361 pci->fsHooks = pti->fsHooks;
3362 pci->dwTIFlags = pti->TIF_flags;
3363 }
3364 }
3365 else
3366 {
3367 pti->rpdesk = NULL;
3368 pti->hdesk = NULL;
3369 pti->pDeskInfo = NULL;
3370 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's!
3371 pci->ulClientDelta = 0;
3372 pci->pDeskInfo = NULL;
3373 pci->pClientThreadInfo = NULL;
3374 }
3375
3376 /* clean up the old desktop */
3377 if (pdeskOld != NULL)
3378 {
3379 RemoveEntryList(&pti->PtiLink);
3380 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld);
3381 IntUnmapDesktopView(pdeskOld);
3382 ObDereferenceObject(pdeskOld);
3383 }
3384
3385 if (pdesk)
3386 {
3387 InsertTailList(&pdesk->PtiList, &pti->PtiLink);
3388 }
3389
3390 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk);
3391
3392 return TRUE;
3393 }
3394
3395 /*
3396 * NtUserSetThreadDesktop
3397 *
3398 * Status
3399 * @implemented
3400 */
3401
3402 BOOL APIENTRY
3403 NtUserSetThreadDesktop(HDESK hDesktop)
3404 {
3405 BOOL ret = FALSE;
3406
3407 UserEnterExclusive();
3408
3409 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen
3410 // here too and set the NT error level. Q. Is it necessary to have the validation
3411 // in IntSetThreadDesktop? Is it needed there too?
3412 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS))
3413 ret = IntSetThreadDesktop(hDesktop, FALSE);
3414
3415 UserLeave();
3416
3417 return ret;
3418 }
3419
3420 /* EOF */