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