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