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