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