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