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