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 NTSTATUS FASTCALL 451 IntParseDesktopPath(PEPROCESS Process, 452 PUNICODE_STRING DesktopPath, 453 HWINSTA *hWinSta, 454 HDESK *hDesktop) 455 { 456 OBJECT_ATTRIBUTES ObjectAttributes; 457 UNICODE_STRING ObjectName; 458 NTSTATUS Status; 459 WCHAR wstrWinstaFullName[MAX_PATH], *pwstrWinsta = NULL, *pwstrDesktop = NULL; 460 461 ASSERT(hWinSta); 462 ASSERT(hDesktop); 463 ASSERT(DesktopPath); 464 465 *hWinSta = NULL; 466 *hDesktop = NULL; 467 468 if (DesktopPath->Buffer != NULL && DesktopPath->Length > sizeof(WCHAR)) 469 { 470 /* 471 * Parse the desktop path string which can be in the form "WinSta\Desktop" 472 * or just "Desktop". In latter case WinSta0 will be used. 473 */ 474 475 pwstrDesktop = wcschr(DesktopPath->Buffer, L'\\'); 476 if (pwstrDesktop != NULL) 477 { 478 *pwstrDesktop = 0; 479 pwstrDesktop++; 480 pwstrWinsta = DesktopPath->Buffer; 481 } 482 else 483 { 484 pwstrDesktop = DesktopPath->Buffer; 485 pwstrWinsta = NULL; 486 } 487 488 TRACE("IntParseDesktopPath pwstrWinsta:%S pwstrDesktop:%S\n", pwstrWinsta, pwstrDesktop); 489 } 490 491 #if 0 492 /* Search the process handle table for (inherited) window station 493 handles, use a more appropriate one than WinSta0 if possible. */ 494 if (!ObFindHandleForObject(Process, 495 NULL, 496 ExWindowStationObjectType, 497 NULL, 498 (PHANDLE)hWinSta)) 499 #endif 500 { 501 /* We had no luck searching for opened handles, use WinSta0 now */ 502 if (!pwstrWinsta) 503 pwstrWinsta = L"WinSta0"; 504 } 505 506 #if 0 507 /* Search the process handle table for (inherited) desktop 508 handles, use a more appropriate one than Default if possible. */ 509 if (!ObFindHandleForObject(Process, 510 NULL, 511 ExDesktopObjectType, 512 NULL, 513 (PHANDLE)hDesktop)) 514 #endif 515 { 516 /* We had no luck searching for opened handles, use Desktop now */ 517 if (!pwstrDesktop) 518 pwstrDesktop = L"Default"; 519 } 520 521 if (*hWinSta == NULL) 522 { 523 swprintf(wstrWinstaFullName, L"%wZ\\%ws", &gustrWindowStationsDir, pwstrWinsta); 524 RtlInitUnicodeString( &ObjectName, wstrWinstaFullName); 525 526 TRACE("parsed initial winsta: %wZ\n", &ObjectName); 527 528 /* Open the window station */ 529 InitializeObjectAttributes(&ObjectAttributes, 530 &ObjectName, 531 OBJ_CASE_INSENSITIVE, 532 NULL, 533 NULL); 534 535 Status = ObOpenObjectByName(&ObjectAttributes, 536 ExWindowStationObjectType, 537 KernelMode, 538 NULL, 539 WINSTA_ACCESS_ALL, 540 NULL, 541 (HANDLE*)hWinSta); 542 543 if (!NT_SUCCESS(Status)) 544 { 545 SetLastNtError(Status); 546 ERR("Failed to reference window station %wZ PID: --!\n", &ObjectName ); 547 return Status; 548 } 549 } 550 551 if (*hDesktop == NULL) 552 { 553 RtlInitUnicodeString(&ObjectName, pwstrDesktop); 554 555 TRACE("parsed initial desktop: %wZ\n", &ObjectName); 556 557 /* Open the desktop object */ 558 InitializeObjectAttributes(&ObjectAttributes, 559 &ObjectName, 560 OBJ_CASE_INSENSITIVE, 561 *hWinSta, 562 NULL); 563 564 Status = ObOpenObjectByName(&ObjectAttributes, 565 ExDesktopObjectType, 566 KernelMode, 567 NULL, 568 DESKTOP_ALL_ACCESS, 569 NULL, 570 (HANDLE*)hDesktop); 571 572 if (!NT_SUCCESS(Status)) 573 { 574 *hDesktop = NULL; 575 NtClose(*hWinSta); 576 *hWinSta = NULL; 577 SetLastNtError(Status); 578 ERR("Failed to reference desktop %wZ PID: --!\n", &ObjectName); 579 return Status; 580 } 581 } 582 return STATUS_SUCCESS; 583 } 584 585 /* 586 * IntValidateDesktopHandle 587 * 588 * Validates the desktop handle. 589 * 590 * Remarks 591 * If the function succeeds, the handle remains referenced. If the 592 * fucntion fails, last error is set. 593 */ 594 595 NTSTATUS FASTCALL 596 IntValidateDesktopHandle( 597 HDESK Desktop, 598 KPROCESSOR_MODE AccessMode, 599 ACCESS_MASK DesiredAccess, 600 PDESKTOP *Object) 601 { 602 NTSTATUS Status; 603 604 Status = ObReferenceObjectByHandle( 605 Desktop, 606 DesiredAccess, 607 ExDesktopObjectType, 608 AccessMode, 609 (PVOID*)Object, 610 NULL); 611 612 TRACE("IntValidateDesktopHandle: handle:0x%p obj:0x%p access:0x%x Status:0x%lx\n", 613 Desktop, *Object, DesiredAccess, Status); 614 615 if (!NT_SUCCESS(Status)) 616 SetLastNtError(Status); 617 618 return Status; 619 } 620 621 PDESKTOP FASTCALL 622 IntGetActiveDesktop(VOID) 623 { 624 return gpdeskInputDesktop; 625 } 626 627 /* 628 * Returns or creates a handle to the desktop object 629 */ 630 HDESK FASTCALL 631 IntGetDesktopObjectHandle(PDESKTOP DesktopObject) 632 { 633 NTSTATUS Status; 634 HDESK Ret; 635 636 ASSERT(DesktopObject); 637 638 if (!ObFindHandleForObject(PsGetCurrentProcess(), 639 DesktopObject, 640 ExDesktopObjectType, 641 NULL, 642 (PHANDLE)&Ret)) 643 { 644 Status = ObOpenObjectByPointer(DesktopObject, 645 0, 646 NULL, 647 0, 648 ExDesktopObjectType, 649 UserMode, 650 (PHANDLE)&Ret); 651 if (!NT_SUCCESS(Status)) 652 { 653 /* Unable to create a handle */ 654 ERR("Unable to create a desktop handle\n"); 655 return NULL; 656 } 657 } 658 else 659 { 660 TRACE("Got handle: %p\n", Ret); 661 } 662 663 return Ret; 664 } 665 666 PUSER_MESSAGE_QUEUE FASTCALL 667 IntGetFocusMessageQueue(VOID) 668 { 669 PDESKTOP pdo = IntGetActiveDesktop(); 670 if (!pdo) 671 { 672 TRACE("No active desktop\n"); 673 return(NULL); 674 } 675 return (PUSER_MESSAGE_QUEUE)pdo->ActiveMessageQueue; 676 } 677 678 VOID FASTCALL 679 IntSetFocusMessageQueue(PUSER_MESSAGE_QUEUE NewQueue) 680 { 681 PUSER_MESSAGE_QUEUE Old; 682 PDESKTOP pdo = IntGetActiveDesktop(); 683 if (!pdo) 684 { 685 TRACE("No active desktop\n"); 686 return; 687 } 688 if (NewQueue != NULL) 689 { 690 if (NewQueue->Desktop != NULL) 691 { 692 TRACE("Message Queue already attached to another desktop!\n"); 693 return; 694 } 695 IntReferenceMessageQueue(NewQueue); 696 (void)InterlockedExchangePointer((PVOID*)&NewQueue->Desktop, pdo); 697 } 698 Old = (PUSER_MESSAGE_QUEUE)InterlockedExchangePointer((PVOID*)&pdo->ActiveMessageQueue, NewQueue); 699 if (Old != NULL) 700 { 701 (void)InterlockedExchangePointer((PVOID*)&Old->Desktop, 0); 702 gpqForegroundPrev = Old; 703 IntDereferenceMessageQueue(Old); 704 } 705 // Only one Q can have active foreground even when there are more than one desktop. 706 if (NewQueue) 707 { 708 gpqForeground = pdo->ActiveMessageQueue; 709 } 710 else 711 { 712 gpqForeground = NULL; 713 ERR("ptiLastInput is CLEARED!!\n"); 714 ptiLastInput = NULL; // ReactOS hacks,,,, should check for process death. 715 } 716 } 717 718 PWND FASTCALL 719 IntGetThreadDesktopWindow(PTHREADINFO pti) 720 { 721 if (!pti) pti = PsGetCurrentThreadWin32Thread(); 722 if (pti->pDeskInfo) return pti->pDeskInfo->spwnd; 723 return NULL; 724 } 725 726 PWND FASTCALL co_GetDesktopWindow(PWND pWnd) 727 { 728 if (pWnd->head.rpdesk && 729 pWnd->head.rpdesk->pDeskInfo) 730 return pWnd->head.rpdesk->pDeskInfo->spwnd; 731 return NULL; 732 } 733 734 HWND FASTCALL IntGetDesktopWindow(VOID) 735 { 736 PDESKTOP pdo = IntGetActiveDesktop(); 737 if (!pdo) 738 { 739 TRACE("No active desktop\n"); 740 return NULL; 741 } 742 return pdo->DesktopWindow; 743 } 744 745 PWND FASTCALL UserGetDesktopWindow(VOID) 746 { 747 PDESKTOP pdo = IntGetActiveDesktop(); 748 749 if (!pdo) 750 { 751 TRACE("No active desktop\n"); 752 return NULL; 753 } 754 // return pdo->pDeskInfo->spwnd; 755 return UserGetWindowObject(pdo->DesktopWindow); 756 } 757 758 HWND FASTCALL IntGetMessageWindow(VOID) 759 { 760 PDESKTOP pdo = IntGetActiveDesktop(); 761 762 if (!pdo) 763 { 764 TRACE("No active desktop\n"); 765 return NULL; 766 } 767 return pdo->spwndMessage->head.h; 768 } 769 770 PWND FASTCALL UserGetMessageWindow(VOID) 771 { 772 PDESKTOP pdo = IntGetActiveDesktop(); 773 774 if (!pdo) 775 { 776 TRACE("No active desktop\n"); 777 return NULL; 778 } 779 return pdo->spwndMessage; 780 } 781 782 HWND FASTCALL IntGetCurrentThreadDesktopWindow(VOID) 783 { 784 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 785 PDESKTOP pdo = pti->rpdesk; 786 if (NULL == pdo) 787 { 788 ERR("Thread doesn't have a desktop\n"); 789 return NULL; 790 } 791 return pdo->DesktopWindow; 792 } 793 794 /* PUBLIC FUNCTIONS ***********************************************************/ 795 796 BOOL FASTCALL 797 DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult) 798 { 799 PAINTSTRUCT Ps; 800 ULONG Value; 801 //ERR("DesktopWindowProc\n"); 802 803 *lResult = 0; 804 805 switch (Msg) 806 { 807 case WM_NCCREATE: 808 if (!Wnd->fnid) 809 { 810 Wnd->fnid = FNID_DESKTOP; 811 } 812 *lResult = (LRESULT)TRUE; 813 return TRUE; 814 815 case WM_CREATE: 816 Value = HandleToULong(PsGetCurrentProcessId()); 817 // Save Process ID 818 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_PROCESSID, Value, FALSE); 819 Value = HandleToULong(PsGetCurrentThreadId()); 820 // Save Thread ID 821 co_UserSetWindowLong(UserHMGetHandle(Wnd), DT_GWL_THREADID, Value, FALSE); 822 case WM_CLOSE: 823 return TRUE; 824 825 case WM_DISPLAYCHANGE: 826 co_WinPosSetWindowPos(Wnd, 0, 0, 0, LOWORD(lParam), HIWORD(lParam), SWP_NOZORDER | SWP_NOACTIVATE); 827 return TRUE; 828 829 case WM_ERASEBKGND: 830 IntPaintDesktop((HDC)wParam); 831 *lResult = 1; 832 return TRUE; 833 834 case WM_PAINT: 835 { 836 if (IntBeginPaint(Wnd, &Ps)) 837 { 838 IntEndPaint(Wnd, &Ps); 839 } 840 return TRUE; 841 } 842 case WM_SYSCOLORCHANGE: 843 co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN); 844 return TRUE; 845 846 case WM_SETCURSOR: 847 { 848 PCURICON_OBJECT pcurOld, pcurNew; 849 pcurNew = UserGetCurIconObject(gDesktopCursor); 850 if (!pcurNew) 851 { 852 return TRUE; 853 } 854 855 pcurNew->CURSORF_flags |= CURSORF_CURRENT; 856 pcurOld = UserSetCursor(pcurNew, FALSE); 857 if (pcurOld) 858 { 859 pcurOld->CURSORF_flags &= ~CURSORF_CURRENT; 860 UserDereferenceObject(pcurOld); 861 } 862 return TRUE; 863 } 864 865 case WM_WINDOWPOSCHANGING: 866 { 867 PWINDOWPOS pWindowPos = (PWINDOWPOS)lParam; 868 if ((pWindowPos->flags & SWP_SHOWWINDOW) != 0) 869 { 870 HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop); 871 IntSetThreadDesktop(hdesk, FALSE); 872 } 873 break; 874 } 875 default: 876 TRACE("DWP calling IDWP Msg %d\n",Msg); 877 //*lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE); 878 } 879 return TRUE; /* We are done. Do not do any callbacks to user mode */ 880 } 881 882 BOOL FASTCALL 883 UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lResult) 884 { 885 *lResult = 0; 886 887 switch(Msg) 888 { 889 case WM_NCCREATE: 890 pwnd->fnid |= FNID_MESSAGEWND; 891 *lResult = (LRESULT)TRUE; 892 break; 893 case WM_DESTROY: 894 pwnd->fnid |= FNID_DESTROY; 895 break; 896 default: 897 ERR("UMWP calling IDWP\n"); 898 *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE); 899 } 900 901 return TRUE; /* We are done. Do not do any callbacks to user mode */ 902 } 903 904 VOID NTAPI DesktopThreadMain(VOID) 905 { 906 BOOL Ret; 907 MSG Msg; 908 909 gptiDesktopThread = PsGetCurrentThreadWin32Thread(); 910 911 UserEnterExclusive(); 912 913 /* Register system classes. This thread does not belong to any desktop so the 914 classes will be allocated from the shared heap */ 915 UserRegisterSystemClasses(); 916 917 while (TRUE) 918 { 919 Ret = co_IntGetPeekMessage(&Msg, 0, 0, 0, PM_REMOVE, TRUE); 920 if (Ret) 921 { 922 IntDispatchMessage(&Msg); 923 } 924 } 925 926 UserLeave(); 927 } 928 929 HDC FASTCALL 930 UserGetDesktopDC(ULONG DcType, BOOL EmptyDC, BOOL ValidatehWnd) 931 { 932 PWND DesktopObject = 0; 933 HDC DesktopHDC = 0; 934 935 /* This can be called from GDI/DX, so acquire the USER lock */ 936 UserEnterExclusive(); 937 938 if (DcType == DC_TYPE_DIRECT) 939 { 940 DesktopObject = UserGetDesktopWindow(); 941 DesktopHDC = (HDC)UserGetWindowDC(DesktopObject); 942 } 943 else 944 { 945 PMONITOR pMonitor = UserGetPrimaryMonitor(); 946 DesktopHDC = IntGdiCreateDisplayDC(pMonitor->hDev, DcType, EmptyDC); 947 } 948 949 UserLeave(); 950 951 return DesktopHDC; 952 } 953 954 VOID APIENTRY 955 UserRedrawDesktop(VOID) 956 { 957 PWND Window = NULL; 958 PREGION Rgn; 959 960 Window = UserGetDesktopWindow(); 961 Rgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow); 962 963 IntInvalidateWindows( Window, 964 Rgn, 965 RDW_FRAME | 966 RDW_ERASE | 967 RDW_INVALIDATE | 968 RDW_ALLCHILDREN); 969 970 REGION_Delete(Rgn); 971 } 972 973 974 NTSTATUS FASTCALL 975 co_IntShowDesktop(PDESKTOP Desktop, ULONG Width, ULONG Height, BOOL bRedraw) 976 { 977 PWND pwnd = Desktop->pDeskInfo->spwnd; 978 UINT flags = SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW; 979 ASSERT(pwnd); 980 981 if (!bRedraw) 982 flags |= SWP_NOREDRAW; 983 984 co_WinPosSetWindowPos(pwnd, NULL, 0, 0, Width, Height, flags); 985 986 if (bRedraw) 987 co_UserRedrawWindow( pwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN | RDW_INVALIDATE ); 988 989 return STATUS_SUCCESS; 990 } 991 992 NTSTATUS FASTCALL 993 IntHideDesktop(PDESKTOP Desktop) 994 { 995 PWND DesktopWnd; 996 997 DesktopWnd = IntGetWindowObject(Desktop->DesktopWindow); 998 if (! DesktopWnd) 999 { 1000 return ERROR_INVALID_WINDOW_HANDLE; 1001 } 1002 DesktopWnd->style &= ~WS_VISIBLE; 1003 1004 return STATUS_SUCCESS; 1005 } 1006 1007 static 1008 HWND* FASTCALL 1009 UserBuildShellHookHwndList(PDESKTOP Desktop) 1010 { 1011 ULONG entries=0; 1012 PLIST_ENTRY ListEntry; 1013 PSHELL_HOOK_WINDOW Current; 1014 HWND* list; 1015 1016 /* FIXME: If we save nb elements in desktop, we don't have to loop to find nb entries */ 1017 ListEntry = Desktop->ShellHookWindows.Flink; 1018 while (ListEntry != &Desktop->ShellHookWindows) 1019 { 1020 ListEntry = ListEntry->Flink; 1021 entries++; 1022 } 1023 1024 if (!entries) return NULL; 1025 1026 list = ExAllocatePoolWithTag(PagedPool, sizeof(HWND) * (entries + 1), USERTAG_WINDOWLIST); /* alloc one extra for nullterm */ 1027 if (list) 1028 { 1029 HWND* cursor = list; 1030 1031 ListEntry = Desktop->ShellHookWindows.Flink; 1032 while (ListEntry != &Desktop->ShellHookWindows) 1033 { 1034 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry); 1035 ListEntry = ListEntry->Flink; 1036 *cursor++ = Current->hWnd; 1037 } 1038 1039 *cursor = NULL; /* Nullterm list */ 1040 } 1041 1042 return list; 1043 } 1044 1045 /* 1046 * Send the Message to the windows registered for ShellHook 1047 * notifications. The lParam contents depend on the Message. See 1048 * MSDN for more details (RegisterShellHookWindow) 1049 */ 1050 VOID co_IntShellHookNotify(WPARAM Message, WPARAM wParam, LPARAM lParam) 1051 { 1052 PDESKTOP Desktop = IntGetActiveDesktop(); 1053 HWND* HwndList; 1054 1055 if (!gpsi->uiShellMsg) 1056 { 1057 gpsi->uiShellMsg = IntAddAtom(L"SHELLHOOK"); 1058 1059 TRACE("MsgType = %x\n", gpsi->uiShellMsg); 1060 if (!gpsi->uiShellMsg) 1061 ERR("LastError: %x\n", EngGetLastError()); 1062 } 1063 1064 if (!Desktop) 1065 { 1066 TRACE("IntShellHookNotify: No desktop!\n"); 1067 return; 1068 } 1069 1070 // Allow other devices have a shot at foreground. 1071 if (Message == HSHELL_APPCOMMAND) ptiLastInput = NULL; 1072 1073 // FIXME: System Tray Support. 1074 1075 HwndList = UserBuildShellHookHwndList(Desktop); 1076 if (HwndList) 1077 { 1078 HWND* cursor = HwndList; 1079 1080 for (; *cursor; cursor++) 1081 { 1082 TRACE("Sending notify\n"); 1083 UserPostMessage(*cursor, 1084 gpsi->uiShellMsg, 1085 Message, 1086 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) ); 1087 /* co_IntPostOrSendMessage(*cursor, 1088 gpsi->uiShellMsg, 1089 Message, 1090 (Message == HSHELL_LANGUAGE ? lParam : (LPARAM)wParam) );*/ 1091 } 1092 1093 ExFreePoolWithTag(HwndList, USERTAG_WINDOWLIST); 1094 } 1095 1096 if (ISITHOOKED(WH_SHELL)) 1097 { 1098 co_HOOK_CallHooks(WH_SHELL, Message, wParam, lParam); 1099 } 1100 } 1101 1102 /* 1103 * Add the window to the ShellHookWindows list. The windows 1104 * on that list get notifications that are important to shell 1105 * type applications. 1106 * 1107 * TODO: Validate the window? I'm not sure if sending these messages to 1108 * an unsuspecting application that is not your own is a nice thing to do. 1109 */ 1110 BOOL IntRegisterShellHookWindow(HWND hWnd) 1111 { 1112 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 1113 PDESKTOP Desktop = pti->rpdesk; 1114 PSHELL_HOOK_WINDOW Entry; 1115 1116 TRACE("IntRegisterShellHookWindow\n"); 1117 1118 /* First deregister the window, so we can be sure it's never twice in the 1119 * list. 1120 */ 1121 IntDeRegisterShellHookWindow(hWnd); 1122 1123 Entry = ExAllocatePoolWithTag(PagedPool, 1124 sizeof(SHELL_HOOK_WINDOW), 1125 TAG_WINSTA); 1126 1127 if (!Entry) 1128 return FALSE; 1129 1130 Entry->hWnd = hWnd; 1131 1132 InsertTailList(&Desktop->ShellHookWindows, &Entry->ListEntry); 1133 1134 return TRUE; 1135 } 1136 1137 /* 1138 * Remove the window from the ShellHookWindows list. The windows 1139 * on that list get notifications that are important to shell 1140 * type applications. 1141 */ 1142 BOOL IntDeRegisterShellHookWindow(HWND hWnd) 1143 { 1144 PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); 1145 PDESKTOP Desktop = pti->rpdesk; 1146 PLIST_ENTRY ListEntry; 1147 PSHELL_HOOK_WINDOW Current; 1148 1149 ListEntry = Desktop->ShellHookWindows.Flink; 1150 while (ListEntry != &Desktop->ShellHookWindows) 1151 { 1152 Current = CONTAINING_RECORD(ListEntry, SHELL_HOOK_WINDOW, ListEntry); 1153 ListEntry = ListEntry->Flink; 1154 if (Current->hWnd == hWnd) 1155 { 1156 RemoveEntryList(&Current->ListEntry); 1157 ExFreePoolWithTag(Current, TAG_WINSTA); 1158 return TRUE; 1159 } 1160 } 1161 1162 return FALSE; 1163 } 1164 1165 static VOID 1166 IntFreeDesktopHeap(IN OUT PDESKTOP Desktop) 1167 { 1168 /* FIXME: Disable until unmapping works in mm */ 1169 #if 0 1170 if (Desktop->pheapDesktop != NULL) 1171 { 1172 MmUnmapViewInSessionSpace(Desktop->pheapDesktop); 1173 Desktop->pheapDesktop = NULL; 1174 } 1175 1176 if (Desktop->hsectionDesktop != NULL) 1177 { 1178 ObDereferenceObject(Desktop->hsectionDesktop); 1179 Desktop->hsectionDesktop = NULL; 1180 } 1181 #endif 1182 } 1183 1184 BOOL FASTCALL 1185 IntPaintDesktop(HDC hDC) 1186 { 1187 static WCHAR s_wszSafeMode[] = L"Safe Mode"; // FIXME: Localize! 1188 1189 RECTL Rect; 1190 HBRUSH DesktopBrush, PreviousBrush; 1191 HWND hWndDesktop; 1192 BOOL doPatBlt = TRUE; 1193 PWND WndDesktop; 1194 BOOLEAN InSafeMode; 1195 1196 if (GdiGetClipBox(hDC, &Rect) == ERROR) 1197 return FALSE; 1198 1199 hWndDesktop = IntGetDesktopWindow(); // rpdesk->DesktopWindow; 1200 1201 WndDesktop = UserGetWindowObject(hWndDesktop); // rpdesk->pDeskInfo->spwnd; 1202 if (!WndDesktop) 1203 return FALSE; 1204 1205 /* Retrieve the current SafeMode state */ 1206 InSafeMode = (UserGetSystemMetrics(SM_CLEANBOOT) != 0); // gpsi->aiSysMet[SM_CLEANBOOT]; 1207 1208 if (!InSafeMode) 1209 { 1210 DesktopBrush = (HBRUSH)WndDesktop->pcls->hbrBackground; 1211 1212 /* 1213 * Paint desktop background 1214 */ 1215 if (gspv.hbmWallpaper != NULL) 1216 { 1217 SIZE sz; 1218 int x, y; 1219 int scaledWidth, scaledHeight; 1220 int wallpaperX, wallpaperY, wallpaperWidth, wallpaperHeight; 1221 HDC hWallpaperDC; 1222 1223 sz.cx = WndDesktop->rcWindow.right - WndDesktop->rcWindow.left; 1224 sz.cy = WndDesktop->rcWindow.bottom - WndDesktop->rcWindow.top; 1225 1226 if (gspv.WallpaperMode == wmFit || 1227 gspv.WallpaperMode == wmFill) 1228 { 1229 int scaleNum, scaleDen; 1230 1231 // Precision improvement over ((sz.cx / gspv.cxWallpaper) > (sz.cy / gspv.cyWallpaper)) 1232 if ((sz.cx * gspv.cyWallpaper) > (sz.cy * gspv.cxWallpaper)) 1233 { 1234 if (gspv.WallpaperMode == wmFit) 1235 { 1236 scaleNum = sz.cy; 1237 scaleDen = gspv.cyWallpaper; 1238 } 1239 else 1240 { 1241 scaleNum = sz.cx; 1242 scaleDen = gspv.cxWallpaper; 1243 } 1244 } 1245 else 1246 { 1247 if (gspv.WallpaperMode == wmFit) 1248 { 1249 scaleNum = sz.cx; 1250 scaleDen = gspv.cxWallpaper; 1251 } 1252 else 1253 { 1254 scaleNum = sz.cy; 1255 scaleDen = gspv.cyWallpaper; 1256 } 1257 } 1258 1259 scaledWidth = EngMulDiv(gspv.cxWallpaper, scaleNum, scaleDen); 1260 scaledHeight = EngMulDiv(gspv.cyWallpaper, scaleNum, scaleDen); 1261 1262 if (gspv.WallpaperMode == wmFill) 1263 { 1264 wallpaperX = (((scaledWidth - sz.cx) * gspv.cxWallpaper) / (2 * scaledWidth)); 1265 wallpaperY = (((scaledHeight - sz.cy) * gspv.cyWallpaper) / (2 * scaledHeight)); 1266 1267 wallpaperWidth = (sz.cx * gspv.cxWallpaper) / scaledWidth; 1268 wallpaperHeight = (sz.cy * gspv.cyWallpaper) / scaledHeight; 1269 } 1270 } 1271 1272 if (gspv.WallpaperMode == wmStretch || 1273 gspv.WallpaperMode == wmTile || 1274 gspv.WallpaperMode == wmFill) 1275 { 1276 x = 0; 1277 y = 0; 1278 } 1279 else if (gspv.WallpaperMode == wmFit) 1280 { 1281 x = (sz.cx - scaledWidth) / 2; 1282 y = (sz.cy - scaledHeight) / 2; 1283 } 1284 else 1285 { 1286 /* Find the upper left corner, can be negative if the bitmap is bigger than the screen */ 1287 x = (sz.cx / 2) - (gspv.cxWallpaper / 2); 1288 y = (sz.cy / 2) - (gspv.cyWallpaper / 2); 1289 } 1290 1291 hWallpaperDC = NtGdiCreateCompatibleDC(hDC); 1292 if (hWallpaperDC != NULL) 1293 { 1294 HBITMAP hOldBitmap; 1295 1296 /* Fill in the area that the bitmap is not going to cover */ 1297 if (x > 0 || y > 0) 1298 { 1299 /* FIXME: Clip out the bitmap 1300 can be replaced with "NtGdiPatBlt(hDC, x, y, gspv.cxWallpaper, gspv.cyWallpaper, PATCOPY | DSTINVERT);" 1301 once we support DSTINVERT */ 1302 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush); 1303 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY); 1304 NtGdiSelectBrush(hDC, PreviousBrush); 1305 } 1306 1307 /* Do not fill the background after it is painted no matter the size of the picture */ 1308 doPatBlt = FALSE; 1309 1310 hOldBitmap = NtGdiSelectBitmap(hWallpaperDC, gspv.hbmWallpaper); 1311 1312 if (gspv.WallpaperMode == wmStretch) 1313 { 1314 if (Rect.right && Rect.bottom) 1315 NtGdiStretchBlt(hDC, 1316 x, 1317 y, 1318 sz.cx, 1319 sz.cy, 1320 hWallpaperDC, 1321 0, 1322 0, 1323 gspv.cxWallpaper, 1324 gspv.cyWallpaper, 1325 SRCCOPY, 1326 0); 1327 } 1328 else if (gspv.WallpaperMode == wmTile) 1329 { 1330 /* Paint the bitmap across the screen then down */ 1331 for (y = 0; y < Rect.bottom; y += gspv.cyWallpaper) 1332 { 1333 for (x = 0; x < Rect.right; x += gspv.cxWallpaper) 1334 { 1335 NtGdiBitBlt(hDC, 1336 x, 1337 y, 1338 gspv.cxWallpaper, 1339 gspv.cyWallpaper, 1340 hWallpaperDC, 1341 0, 1342 0, 1343 SRCCOPY, 1344 0, 1345 0); 1346 } 1347 } 1348 } 1349 else if (gspv.WallpaperMode == wmFit) 1350 { 1351 if (Rect.right && Rect.bottom) 1352 { 1353 NtGdiStretchBlt(hDC, 1354 x, 1355 y, 1356 scaledWidth, 1357 scaledHeight, 1358 hWallpaperDC, 1359 0, 1360 0, 1361 gspv.cxWallpaper, 1362 gspv.cyWallpaper, 1363 SRCCOPY, 1364 0); 1365 } 1366 } 1367 else if (gspv.WallpaperMode == wmFill) 1368 { 1369 if (Rect.right && Rect.bottom) 1370 { 1371 NtGdiStretchBlt(hDC, 1372 x, 1373 y, 1374 sz.cx, 1375 sz.cy, 1376 hWallpaperDC, 1377 wallpaperX, 1378 wallpaperY, 1379 wallpaperWidth, 1380 wallpaperHeight, 1381 SRCCOPY, 1382 0); 1383 } 1384 } 1385 else 1386 { 1387 NtGdiBitBlt(hDC, 1388 x, 1389 y, 1390 gspv.cxWallpaper, 1391 gspv.cyWallpaper, 1392 hWallpaperDC, 1393 0, 1394 0, 1395 SRCCOPY, 1396 0, 1397 0); 1398 } 1399 NtGdiSelectBitmap(hWallpaperDC, hOldBitmap); 1400 NtGdiDeleteObjectApp(hWallpaperDC); 1401 } 1402 } 1403 } 1404 else 1405 { 1406 /* Black desktop background in Safe Mode */ 1407 DesktopBrush = StockObjects[BLACK_BRUSH]; 1408 } 1409 1410 /* Background is set to none, clear the screen */ 1411 if (doPatBlt) 1412 { 1413 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush); 1414 NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right, Rect.bottom, PATCOPY); 1415 NtGdiSelectBrush(hDC, PreviousBrush); 1416 } 1417 1418 /* 1419 * Display the system version on the desktop background 1420 */ 1421 if (InSafeMode || g_AlwaysDisplayVersion || g_PaintDesktopVersion) 1422 { 1423 NTSTATUS Status; 1424 static WCHAR wszzVersion[1024] = L"\0"; 1425 1426 /* Only used in normal mode */ 1427 // We expect at most 4 strings (3 for version, 1 for optional NtSystemRoot) 1428 static POLYTEXTW VerStrs[4] = {{0},{0},{0},{0}}; 1429 INT i = 0; 1430 INT len; 1431 1432 HFONT hFont1 = NULL, hFont2 = NULL, hOldFont = NULL; 1433 COLORREF crText, color_old; 1434 UINT align_old; 1435 INT mode_old; 1436 PDC pdc; 1437 1438 if (!UserSystemParametersInfo(SPI_GETWORKAREA, 0, &Rect, 0)) 1439 { 1440 Rect.left = Rect.top = 0; 1441 Rect.right = UserGetSystemMetrics(SM_CXSCREEN); 1442 Rect.bottom = UserGetSystemMetrics(SM_CYSCREEN); 1443 } 1444 else 1445 { 1446 RECTL_vOffsetRect(&Rect, -Rect.left, -Rect.top); 1447 } 1448 1449 /* 1450 * Set up the fonts (otherwise use default ones) 1451 */ 1452 1453 /* Font for the principal version string */ 1454 hFont1 = GreCreateFontIndirectW(&gspv.ncm.lfCaptionFont); 1455 /* Font for the secondary version strings */ 1456 hFont2 = GreCreateFontIndirectW(&gspv.ncm.lfMenuFont); 1457 1458 if (hFont1) 1459 hOldFont = NtGdiSelectFont(hDC, hFont1); 1460 1461 if (gspv.hbmWallpaper == NULL) 1462 { 1463 /* Retrieve the brush fill colour */ 1464 // TODO: The following code constitutes "GreGetBrushColor". 1465 PreviousBrush = NtGdiSelectBrush(hDC, DesktopBrush); 1466 pdc = DC_LockDc(hDC); 1467 if (pdc) 1468 { 1469 crText = pdc->eboFill.ulRGBColor; 1470 DC_UnlockDc(pdc); 1471 } 1472 else 1473 { 1474 crText = RGB(0, 0, 0); 1475 } 1476 NtGdiSelectBrush(hDC, PreviousBrush); 1477 1478 /* Adjust text colour according to the brush */ 1479 if (GetRValue(crText) + GetGValue(crText) + GetBValue(crText) > 128 * 3) 1480 crText = RGB(0, 0, 0); 1481 else 1482 crText = RGB(255, 255, 255); 1483 } 1484 else 1485 { 1486 /* Always use white when the text is displayed on top of a wallpaper */ 1487 crText = RGB(255, 255, 255); 1488 } 1489 1490 color_old = IntGdiSetTextColor(hDC, crText); 1491 align_old = IntGdiSetTextAlign(hDC, TA_RIGHT); 1492 mode_old = IntGdiSetBkMode(hDC, TRANSPARENT); 1493 1494 /* Display the system version information */ 1495 if (!*wszzVersion) 1496 { 1497 Status = GetSystemVersionString(wszzVersion, 1498 ARRAYSIZE(wszzVersion), 1499 InSafeMode, 1500 g_AlwaysDisplayVersion); 1501 if (!InSafeMode && NT_SUCCESS(Status) && *wszzVersion) 1502 { 1503 PWCHAR pstr = wszzVersion; 1504 for (i = 0; (i < ARRAYSIZE(VerStrs)) && *pstr; ++i) 1505 { 1506 VerStrs[i].n = wcslen(pstr); 1507 VerStrs[i].lpstr = pstr; 1508 pstr += (VerStrs[i].n + 1); 1509 } 1510 } 1511 } 1512 else 1513 { 1514 Status = STATUS_SUCCESS; 1515 } 1516 if (NT_SUCCESS(Status) && *wszzVersion) 1517 { 1518 if (!InSafeMode) 1519 { 1520 SIZE Size = {0, 0}; 1521 LONG TotalHeight = 0; 1522 1523 /* Normal Mode: multiple version information text separated by newlines */ 1524 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM); 1525 1526 /* Compute the heights of the strings */ 1527 if (hFont1) NtGdiSelectFont(hDC, hFont1); 1528 for (i = 0; i < ARRAYSIZE(VerStrs); ++i) 1529 { 1530 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0)) 1531 break; 1532 1533 GreGetTextExtentW(hDC, VerStrs[i].lpstr, VerStrs[i].n, &Size, 1); 1534 VerStrs[i].y = Size.cy; // Store the string height 1535 TotalHeight += Size.cy; 1536 1537 /* While the first string was using hFont1, all the others use hFont2 */ 1538 if (hFont2) NtGdiSelectFont(hDC, hFont2); 1539 } 1540 /* The total height must not exceed the screen height */ 1541 TotalHeight = min(TotalHeight, Rect.bottom); 1542 1543 /* Display the strings */ 1544 if (hFont1) NtGdiSelectFont(hDC, hFont1); 1545 for (i = 0; i < ARRAYSIZE(VerStrs); ++i) 1546 { 1547 if (!VerStrs[i].lpstr || !*VerStrs[i].lpstr || (VerStrs[i].n == 0)) 1548 break; 1549 1550 TotalHeight -= VerStrs[i].y; 1551 GreExtTextOutW(hDC, 1552 Rect.right - 5, 1553 Rect.bottom - TotalHeight - 5, 1554 0, NULL, 1555 VerStrs[i].lpstr, 1556 VerStrs[i].n, 1557 NULL, 0); 1558 1559 /* While the first string was using hFont1, all the others use hFont2 */ 1560 if (hFont2) NtGdiSelectFont(hDC, hFont2); 1561 } 1562 } 1563 else 1564 { 1565 if (hFont1) NtGdiSelectFont(hDC, hFont1); 1566 1567 /* Safe Mode: single version information text in top center */ 1568 len = wcslen(wszzVersion); 1569 1570 IntGdiSetTextAlign(hDC, TA_CENTER | TA_TOP); 1571 GreExtTextOutW(hDC, (Rect.right + Rect.left)/2, Rect.top + 3, 0, NULL, wszzVersion, len, NULL, 0); 1572 } 1573 } 1574 1575 if (InSafeMode) 1576 { 1577 if (hFont1) NtGdiSelectFont(hDC, hFont1); 1578 1579 /* Print Safe Mode text in corners */ 1580 len = wcslen(s_wszSafeMode); 1581 1582 IntGdiSetTextAlign(hDC, TA_LEFT | TA_TOP); 1583 GreExtTextOutW(hDC, Rect.left, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0); 1584 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_TOP); 1585 GreExtTextOutW(hDC, Rect.right, Rect.top + 3, 0, NULL, s_wszSafeMode, len, NULL, 0); 1586 IntGdiSetTextAlign(hDC, TA_LEFT | TA_BOTTOM); 1587 GreExtTextOutW(hDC, Rect.left, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0); 1588 IntGdiSetTextAlign(hDC, TA_RIGHT | TA_BOTTOM); 1589 GreExtTextOutW(hDC, Rect.right, Rect.bottom - 5, 0, NULL, s_wszSafeMode, len, NULL, 0); 1590 } 1591 1592 IntGdiSetBkMode(hDC, mode_old); 1593 IntGdiSetTextAlign(hDC, align_old); 1594 IntGdiSetTextColor(hDC, color_old); 1595 1596 if (hFont2) 1597 GreDeleteObject(hFont2); 1598 1599 if (hFont1) 1600 { 1601 NtGdiSelectFont(hDC, hOldFont); 1602 GreDeleteObject(hFont1); 1603 } 1604 } 1605 1606 return TRUE; 1607 } 1608 1609 static NTSTATUS 1610 UserInitializeDesktop(PDESKTOP pdesk, PUNICODE_STRING DesktopName, PWINSTATION_OBJECT pwinsta) 1611 { 1612 PVOID DesktopHeapSystemBase = NULL; 1613 ULONG_PTR HeapSize = gdwDesktopSectionSize * 1024; 1614 SIZE_T DesktopInfoSize; 1615 ULONG i; 1616 1617 TRACE("UserInitializeDesktop desktop 0x%p with name %wZ\n", pdesk, DesktopName); 1618 1619 RtlZeroMemory(pdesk, sizeof(DESKTOP)); 1620 1621 /* Link the desktop with the parent window station */ 1622 ObReferenceObject(pwinsta); 1623 pdesk->rpwinstaParent = pwinsta; 1624 InsertTailList(&pwinsta->DesktopListHead, &pdesk->ListEntry); 1625 1626 /* Create the desktop heap */ 1627 pdesk->hsectionDesktop = NULL; 1628 pdesk->pheapDesktop = UserCreateHeap(&pdesk->hsectionDesktop, 1629 &DesktopHeapSystemBase, 1630 HeapSize); 1631 if (pdesk->pheapDesktop == NULL) 1632 { 1633 ERR("Failed to create desktop heap!\n"); 1634 return STATUS_NO_MEMORY; 1635 } 1636 1637 /* Create DESKTOPINFO */ 1638 DesktopInfoSize = sizeof(DESKTOPINFO) + DesktopName->Length + sizeof(WCHAR); 1639 pdesk->pDeskInfo = RtlAllocateHeap(pdesk->pheapDesktop, 1640 HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY, 1641 DesktopInfoSize); 1642 if (pdesk->pDeskInfo == NULL) 1643 { 1644 ERR("Failed to create the DESKTOP structure!\n"); 1645 return STATUS_NO_MEMORY; 1646 } 1647 1648 /* Initialize the DESKTOPINFO */ 1649 pdesk->pDeskInfo->pvDesktopBase = DesktopHeapSystemBase; 1650 pdesk->pDeskInfo->pvDesktopLimit = (PVOID)((ULONG_PTR)DesktopHeapSystemBase + HeapSize); 1651 RtlCopyMemory(pdesk->pDeskInfo->szDesktopName, 1652 DesktopName->Buffer, 1653 DesktopName->Length + sizeof(WCHAR)); 1654 for (i = 0; i < NB_HOOKS; i++) 1655 { 1656 InitializeListHead(&pdesk->pDeskInfo->aphkStart[i]); 1657 } 1658 1659 InitializeListHead(&pdesk->ShellHookWindows); 1660 InitializeListHead(&pdesk->PtiList); 1661 1662 return STATUS_SUCCESS; 1663 } 1664 1665 /* SYSCALLS *******************************************************************/ 1666 1667 /* 1668 * NtUserCreateDesktop 1669 * 1670 * Creates a new desktop. 1671 * 1672 * Parameters 1673 * poaAttribs 1674 * Object Attributes. 1675 * 1676 * lpszDesktopDevice 1677 * Name of the device. 1678 * 1679 * pDeviceMode 1680 * Device Mode. 1681 * 1682 * dwFlags 1683 * Interaction flags. 1684 * 1685 * dwDesiredAccess 1686 * Requested type of access. 1687 * 1688 * 1689 * Return Value 1690 * If the function succeeds, the return value is a handle to the newly 1691 * created desktop. If the specified desktop already exists, the function 1692 * succeeds and returns a handle to the existing desktop. When you are 1693 * finished using the handle, call the CloseDesktop function to close it. 1694 * If the function fails, the return value is NULL. 1695 * 1696 * Status 1697 * @implemented 1698 */ 1699 1700 HDESK APIENTRY 1701 NtUserCreateDesktop( 1702 POBJECT_ATTRIBUTES ObjectAttributes, 1703 PUNICODE_STRING lpszDesktopDevice, 1704 LPDEVMODEW lpdmw, 1705 DWORD dwFlags, 1706 ACCESS_MASK dwDesiredAccess) 1707 { 1708 PDESKTOP pdesk = NULL; 1709 NTSTATUS Status = STATUS_SUCCESS; 1710 HDESK hdesk; 1711 BOOLEAN Context = FALSE; 1712 UNICODE_STRING ClassName; 1713 LARGE_STRING WindowName; 1714 BOOL NoHooks = FALSE; 1715 PWND pWnd = NULL; 1716 CREATESTRUCTW Cs; 1717 PTHREADINFO ptiCurrent; 1718 PCLS pcls; 1719 1720 DECLARE_RETURN(HDESK); 1721 1722 TRACE("Enter NtUserCreateDesktop\n"); 1723 UserEnterExclusive(); 1724 1725 ptiCurrent = PsGetCurrentThreadWin32Thread(); 1726 ASSERT(ptiCurrent); 1727 ASSERT(gptiDesktopThread); 1728 1729 /* Turn off hooks when calling any CreateWindowEx from inside win32k. */ 1730 NoHooks = (ptiCurrent->TIF_flags & TIF_DISABLEHOOKS); 1731 ptiCurrent->TIF_flags |= TIF_DISABLEHOOKS; 1732 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags; 1733 1734 /* 1735 * Try to open already existing desktop 1736 */ 1737 Status = ObOpenObjectByName( 1738 ObjectAttributes, 1739 ExDesktopObjectType, 1740 UserMode, 1741 NULL, 1742 dwDesiredAccess, 1743 (PVOID)&Context, 1744 (HANDLE*)&hdesk); 1745 if (!NT_SUCCESS(Status)) 1746 { 1747 ERR("ObOpenObjectByName failed to open/create desktop\n"); 1748 SetLastNtError(Status); 1749 RETURN(NULL); 1750 } 1751 1752 /* In case the object was not created (eg if it existed), return now */ 1753 if (Context == FALSE) 1754 { 1755 TRACE("NtUserCreateDesktop opened desktop %wZ\n", ObjectAttributes->ObjectName); 1756 RETURN( hdesk); 1757 } 1758 1759 /* Reference the desktop */ 1760 Status = ObReferenceObjectByHandle(hdesk, 1761 0, 1762 ExDesktopObjectType, 1763 KernelMode, 1764 (PVOID*)&pdesk, 1765 NULL); 1766 if (!NT_SUCCESS(Status)) 1767 { 1768 ERR("Failed to reference desktop object\n"); 1769 SetLastNtError(Status); 1770 RETURN(NULL); 1771 } 1772 1773 /* Get the desktop window class. The thread desktop does not belong to any desktop 1774 * so the classes created there (including the desktop class) are allocated in the shared heap 1775 * It would cause problems if we used a class that belongs to the caller 1776 */ 1777 ClassName.Buffer = WC_DESKTOP; 1778 ClassName.Length = 0; 1779 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE); 1780 if (pcls == NULL) 1781 { 1782 ASSERT(FALSE); 1783 RETURN(NULL); 1784 } 1785 1786 RtlZeroMemory(&WindowName, sizeof(WindowName)); 1787 RtlZeroMemory(&Cs, sizeof(Cs)); 1788 Cs.x = UserGetSystemMetrics(SM_XVIRTUALSCREEN), 1789 Cs.y = UserGetSystemMetrics(SM_YVIRTUALSCREEN), 1790 Cs.cx = UserGetSystemMetrics(SM_CXVIRTUALSCREEN), 1791 Cs.cy = UserGetSystemMetrics(SM_CYVIRTUALSCREEN), 1792 Cs.style = WS_POPUP|WS_CLIPCHILDREN; 1793 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! 1794 Cs.lpszName = (LPCWSTR) &WindowName; 1795 Cs.lpszClass = (LPCWSTR) &ClassName; 1796 1797 /* Use IntCreateWindow instead of co_UserCreateWindowEx cause the later expects a thread with a desktop */ 1798 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk); 1799 if (pWnd == NULL) 1800 { 1801 ERR("Failed to create desktop window for the new desktop\n"); 1802 RETURN(NULL); 1803 } 1804 1805 pdesk->dwSessionId = PsGetCurrentProcessSessionId(); 1806 pdesk->DesktopWindow = pWnd->head.h; 1807 pdesk->pDeskInfo->spwnd = pWnd; 1808 pWnd->fnid = FNID_DESKTOP; 1809 1810 ClassName.Buffer = MAKEINTATOM(gpsi->atomSysClass[ICLS_HWNDMESSAGE]); 1811 ClassName.Length = 0; 1812 pcls = IntGetAndReferenceClass(&ClassName, 0, TRUE); 1813 if (pcls == NULL) 1814 { 1815 ASSERT(FALSE); 1816 RETURN(NULL); 1817 } 1818 1819 RtlZeroMemory(&WindowName, sizeof(WindowName)); 1820 RtlZeroMemory(&Cs, sizeof(Cs)); 1821 Cs.cx = Cs.cy = 100; 1822 Cs.style = WS_POPUP|WS_CLIPCHILDREN; 1823 Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! 1824 Cs.lpszName = (LPCWSTR) &WindowName; 1825 Cs.lpszClass = (LPCWSTR) &ClassName; 1826 pWnd = IntCreateWindow(&Cs, &WindowName, pcls, NULL, NULL, NULL, pdesk); 1827 if (pWnd == NULL) 1828 { 1829 ERR("Failed to create message window for the new desktop\n"); 1830 RETURN(NULL); 1831 } 1832 1833 pdesk->spwndMessage = pWnd; 1834 pWnd->fnid = FNID_MESSAGEWND; 1835 1836 /* Now,,, 1837 if !(WinStaObject->Flags & WSF_NOIO) is (not set) for desktop input output mode (see wiki) 1838 Create Tooltip. Saved in DesktopObject->spwndTooltip. 1839 Tooltip dwExStyle: WS_EX_TOOLWINDOW|WS_EX_TOPMOST 1840 hWndParent are spwndMessage. Use hModuleWin for server side winproc! 1841 The rest is same as message window. 1842 http://msdn.microsoft.com/en-us/library/bb760250(VS.85).aspx 1843 */ 1844 RETURN( hdesk); 1845 1846 CLEANUP: 1847 if (pdesk != NULL) 1848 { 1849 ObDereferenceObject(pdesk); 1850 } 1851 if (_ret_ == NULL && hdesk != NULL) 1852 { 1853 ObCloseHandle(hdesk, UserMode); 1854 } 1855 if (!NoHooks) 1856 { 1857 ptiCurrent->TIF_flags &= ~TIF_DISABLEHOOKS; 1858 ptiCurrent->pClientInfo->dwTIFlags = ptiCurrent->TIF_flags; 1859 } 1860 TRACE("Leave NtUserCreateDesktop, ret=%p\n",_ret_); 1861 UserLeave(); 1862 END_CLEANUP; 1863 } 1864 1865 /* 1866 * NtUserOpenDesktop 1867 * 1868 * Opens an existing desktop. 1869 * 1870 * Parameters 1871 * lpszDesktopName 1872 * Name of the existing desktop. 1873 * 1874 * dwFlags 1875 * Interaction flags. 1876 * 1877 * dwDesiredAccess 1878 * Requested type of access. 1879 * 1880 * Return Value 1881 * Handle to the desktop or zero on failure. 1882 * 1883 * Status 1884 * @implemented 1885 */ 1886 1887 HDESK APIENTRY 1888 NtUserOpenDesktop( 1889 POBJECT_ATTRIBUTES ObjectAttributes, 1890 DWORD dwFlags, 1891 ACCESS_MASK dwDesiredAccess) 1892 { 1893 NTSTATUS Status; 1894 HDESK Desktop; 1895 1896 Status = ObOpenObjectByName( 1897 ObjectAttributes, 1898 ExDesktopObjectType, 1899 UserMode, 1900 NULL, 1901 dwDesiredAccess, 1902 NULL, 1903 (HANDLE*)&Desktop); 1904 1905 if (!NT_SUCCESS(Status)) 1906 { 1907 ERR("Failed to open desktop\n"); 1908 SetLastNtError(Status); 1909 return NULL; 1910 } 1911 1912 TRACE("Opened desktop %S with handle 0x%p\n", ObjectAttributes->ObjectName->Buffer, Desktop); 1913 1914 return Desktop; 1915 } 1916 1917 /* 1918 * NtUserOpenInputDesktop 1919 * 1920 * Opens the input (interactive) desktop. 1921 * 1922 * Parameters 1923 * dwFlags 1924 * Interaction flags. 1925 * 1926 * fInherit 1927 * Inheritance option. 1928 * 1929 * dwDesiredAccess 1930 * Requested type of access. 1931 * 1932 * Return Value 1933 * Handle to the input desktop or zero on failure. 1934 * 1935 * Status 1936 * @implemented 1937 */ 1938 1939 HDESK APIENTRY 1940 NtUserOpenInputDesktop( 1941 DWORD dwFlags, 1942 BOOL fInherit, 1943 ACCESS_MASK dwDesiredAccess) 1944 { 1945 NTSTATUS Status; 1946 HDESK hdesk = NULL; 1947 ULONG HandleAttributes = 0; 1948 1949 UserEnterExclusive(); 1950 TRACE("Enter NtUserOpenInputDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop); 1951 1952 if (fInherit) HandleAttributes = OBJ_INHERIT; 1953 1954 /* Create a new handle to the object */ 1955 Status = ObOpenObjectByPointer( 1956 gpdeskInputDesktop, 1957 HandleAttributes, 1958 NULL, 1959 dwDesiredAccess, 1960 ExDesktopObjectType, 1961 UserMode, 1962 (PHANDLE)&hdesk); 1963 1964 if (!NT_SUCCESS(Status)) 1965 { 1966 ERR("Failed to open input desktop object\n"); 1967 SetLastNtError(Status); 1968 } 1969 1970 TRACE("NtUserOpenInputDesktop returning 0x%p\n",hdesk); 1971 UserLeave(); 1972 return hdesk; 1973 } 1974 1975 /* 1976 * NtUserCloseDesktop 1977 * 1978 * Closes a desktop handle. 1979 * 1980 * Parameters 1981 * hDesktop 1982 * Handle to the desktop. 1983 * 1984 * Return Value 1985 * Status 1986 * 1987 * Remarks 1988 * The desktop handle can be created with NtUserCreateDesktop or 1989 * NtUserOpenDesktop. This function will fail if any thread in the calling 1990 * process is using the specified desktop handle or if the handle refers 1991 * to the initial desktop of the calling process. 1992 * 1993 * Status 1994 * @implemented 1995 */ 1996 1997 BOOL APIENTRY 1998 NtUserCloseDesktop(HDESK hDesktop) 1999 { 2000 PDESKTOP pdesk; 2001 NTSTATUS Status; 2002 DECLARE_RETURN(BOOL); 2003 2004 TRACE("NtUserCloseDesktop called (0x%p)\n", hDesktop); 2005 UserEnterExclusive(); 2006 2007 if (hDesktop == gptiCurrent->hdesk || hDesktop == gptiCurrent->ppi->hdeskStartup) 2008 { 2009 ERR("Attempted to close thread desktop\n"); 2010 EngSetLastError(ERROR_BUSY); 2011 RETURN(FALSE); 2012 } 2013 2014 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk); 2015 if (!NT_SUCCESS(Status)) 2016 { 2017 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop); 2018 RETURN(FALSE); 2019 } 2020 2021 ObDereferenceObject(pdesk); 2022 2023 Status = ZwClose(hDesktop); 2024 if (!NT_SUCCESS(Status)) 2025 { 2026 ERR("Failed to close desktop handle 0x%p\n", hDesktop); 2027 SetLastNtError(Status); 2028 RETURN(FALSE); 2029 } 2030 2031 RETURN(TRUE); 2032 2033 CLEANUP: 2034 TRACE("Leave NtUserCloseDesktop, ret=%i\n",_ret_); 2035 UserLeave(); 2036 END_CLEANUP; 2037 } 2038 2039 /* 2040 * NtUserPaintDesktop 2041 * 2042 * The NtUserPaintDesktop function fills the clipping region in the 2043 * specified device context with the desktop pattern or wallpaper. The 2044 * function is provided primarily for shell desktops. 2045 * 2046 * Parameters 2047 * hDC 2048 * Handle to the device context. 2049 * 2050 * Status 2051 * @implemented 2052 */ 2053 2054 BOOL APIENTRY 2055 NtUserPaintDesktop(HDC hDC) 2056 { 2057 BOOL Ret; 2058 UserEnterExclusive(); 2059 TRACE("Enter NtUserPaintDesktop\n"); 2060 Ret = IntPaintDesktop(hDC); 2061 TRACE("Leave NtUserPaintDesktop, ret=%i\n",Ret); 2062 UserLeave(); 2063 return Ret; 2064 } 2065 2066 /* 2067 * NtUserResolveDesktop 2068 * 2069 * The NtUserResolveDesktop function retrieves handles to the desktop and 2070 * the window station specified by the desktop path string. 2071 * 2072 * Parameters 2073 * ProcessHandle 2074 * Handle to a user process. 2075 * 2076 * DesktopPath 2077 * The desktop path string. 2078 * 2079 * Return Value 2080 * Handle to the desktop (direct return value) and 2081 * handle to the associated window station (by pointer). 2082 * NULL in case of failure. 2083 * 2084 * Remarks 2085 * Callable by CSRSS only. 2086 * 2087 * Status 2088 * @implemented 2089 */ 2090 2091 HDESK 2092 APIENTRY 2093 NtUserResolveDesktop( 2094 IN HANDLE ProcessHandle, 2095 IN PUNICODE_STRING DesktopPath, 2096 DWORD dwUnknown, 2097 OUT HWINSTA* phWinSta) 2098 { 2099 NTSTATUS Status; 2100 PEPROCESS Process = NULL; 2101 HWINSTA hWinSta = NULL; 2102 HDESK hDesktop = NULL; 2103 2104 /* Allow only the Console Server to perform this operation (via CSRSS) */ 2105 if (PsGetCurrentProcess() != gpepCSRSS) 2106 return NULL; 2107 2108 /* Get the process object the user handle was referencing */ 2109 Status = ObReferenceObjectByHandle(ProcessHandle, 2110 PROCESS_QUERY_INFORMATION, 2111 *PsProcessType, 2112 UserMode, 2113 (PVOID*)&Process, 2114 NULL); 2115 if (!NT_SUCCESS(Status)) return NULL; 2116 2117 // UserEnterShared(); 2118 2119 _SEH2_TRY 2120 { 2121 UNICODE_STRING CapturedDesktopPath; 2122 2123 /* Capture the user desktop path string */ 2124 Status = IntSafeCopyUnicodeStringTerminateNULL(&CapturedDesktopPath, 2125 DesktopPath); 2126 if (!NT_SUCCESS(Status)) _SEH2_YIELD(goto Quit); 2127 2128 /* Call the internal function */ 2129 Status = IntParseDesktopPath(Process, 2130 &CapturedDesktopPath, 2131 &hWinSta, 2132 &hDesktop); 2133 if (!NT_SUCCESS(Status)) 2134 { 2135 ERR("IntParseDesktopPath failed, Status = 0x%08lx\n", Status); 2136 hWinSta = NULL; 2137 hDesktop = NULL; 2138 } 2139 2140 /* Return the window station handle */ 2141 *phWinSta = hWinSta; 2142 2143 /* Free the captured string */ 2144 if (CapturedDesktopPath.Buffer) 2145 ExFreePoolWithTag(CapturedDesktopPath.Buffer, TAG_STRING); 2146 } 2147 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 2148 { 2149 Status = _SEH2_GetExceptionCode(); 2150 } 2151 _SEH2_END; 2152 2153 Quit: 2154 // UserLeave(); 2155 2156 /* Dereference the process object */ 2157 ObDereferenceObject(Process); 2158 2159 /* Return the desktop handle */ 2160 return hDesktop; 2161 } 2162 2163 /* 2164 * NtUserSwitchDesktop 2165 * 2166 * Sets the current input (interactive) desktop. 2167 * 2168 * Parameters 2169 * hDesktop 2170 * Handle to desktop. 2171 * 2172 * Return Value 2173 * Status 2174 * 2175 * Status 2176 * @unimplemented 2177 */ 2178 2179 BOOL APIENTRY 2180 NtUserSwitchDesktop(HDESK hdesk) 2181 { 2182 PDESKTOP pdesk; 2183 NTSTATUS Status; 2184 BOOL bRedrawDesktop; 2185 DECLARE_RETURN(BOOL); 2186 2187 UserEnterExclusive(); 2188 TRACE("Enter NtUserSwitchDesktop(0x%p)\n", hdesk); 2189 2190 Status = IntValidateDesktopHandle( hdesk, UserMode, 0, &pdesk); 2191 if (!NT_SUCCESS(Status)) 2192 { 2193 ERR("Validation of desktop handle (0x%p) failed\n", hdesk); 2194 RETURN(FALSE); 2195 } 2196 2197 if (PsGetCurrentProcessSessionId() != pdesk->rpwinstaParent->dwSessionId) 2198 { 2199 ObDereferenceObject(pdesk); 2200 ERR("NtUserSwitchDesktop called for a desktop of a different session\n"); 2201 RETURN(FALSE); 2202 } 2203 2204 if (pdesk == gpdeskInputDesktop) 2205 { 2206 ObDereferenceObject(pdesk); 2207 WARN("NtUserSwitchDesktop called for active desktop\n"); 2208 RETURN(TRUE); 2209 } 2210 2211 /* 2212 * Don't allow applications switch the desktop if it's locked, unless the caller 2213 * is the logon application itself 2214 */ 2215 if ((pdesk->rpwinstaParent->Flags & WSS_LOCKED) && 2216 gpidLogon != PsGetCurrentProcessId()) 2217 { 2218 ObDereferenceObject(pdesk); 2219 ERR("Switching desktop 0x%p denied because the window station is locked!\n", hdesk); 2220 RETURN(FALSE); 2221 } 2222 2223 if (pdesk->rpwinstaParent != InputWindowStation) 2224 { 2225 ObDereferenceObject(pdesk); 2226 ERR("Switching desktop 0x%p denied because desktop doesn't belong to the interactive winsta!\n", hdesk); 2227 RETURN(FALSE); 2228 } 2229 2230 /* FIXME: Fail if the process is associated with a secured 2231 desktop such as Winlogon or Screen-Saver */ 2232 /* FIXME: Connect to input device */ 2233 2234 TRACE("Switching from desktop 0x%p to 0x%p\n", gpdeskInputDesktop, pdesk); 2235 2236 bRedrawDesktop = FALSE; 2237 2238 /* The first time SwitchDesktop is called, gpdeskInputDesktop is NULL */ 2239 if (gpdeskInputDesktop != NULL) 2240 { 2241 if ((gpdeskInputDesktop->pDeskInfo->spwnd->style & WS_VISIBLE) == WS_VISIBLE) 2242 bRedrawDesktop = TRUE; 2243 2244 /* Hide the previous desktop window */ 2245 IntHideDesktop(gpdeskInputDesktop); 2246 } 2247 2248 /* Set the active desktop in the desktop's window station. */ 2249 InputWindowStation->ActiveDesktop = pdesk; 2250 2251 /* Set the global state. */ 2252 gpdeskInputDesktop = pdesk; 2253 2254 /* Show the new desktop window */ 2255 co_IntShowDesktop(pdesk, UserGetSystemMetrics(SM_CXSCREEN), UserGetSystemMetrics(SM_CYSCREEN), bRedrawDesktop); 2256 2257 TRACE("SwitchDesktop gpdeskInputDesktop 0x%p\n",gpdeskInputDesktop); 2258 ObDereferenceObject(pdesk); 2259 2260 RETURN(TRUE); 2261 2262 CLEANUP: 2263 TRACE("Leave NtUserSwitchDesktop, ret=%i\n",_ret_); 2264 UserLeave(); 2265 END_CLEANUP; 2266 } 2267 2268 /* 2269 * NtUserGetThreadDesktop 2270 * 2271 * Status 2272 * @implemented 2273 */ 2274 2275 HDESK APIENTRY 2276 NtUserGetThreadDesktop(DWORD dwThreadId, DWORD Unknown1) 2277 { 2278 NTSTATUS Status; 2279 PETHREAD Thread; 2280 PDESKTOP DesktopObject; 2281 HDESK Ret, hThreadDesktop; 2282 OBJECT_HANDLE_INFORMATION HandleInformation; 2283 DECLARE_RETURN(HDESK); 2284 2285 UserEnterExclusive(); 2286 TRACE("Enter NtUserGetThreadDesktop\n"); 2287 2288 if (!dwThreadId) 2289 { 2290 EngSetLastError(ERROR_INVALID_PARAMETER); 2291 RETURN(0); 2292 } 2293 2294 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)dwThreadId, &Thread); 2295 if (!NT_SUCCESS(Status)) 2296 { 2297 EngSetLastError(ERROR_INVALID_PARAMETER); 2298 RETURN(0); 2299 } 2300 2301 if (Thread->ThreadsProcess == PsGetCurrentProcess()) 2302 { 2303 /* Just return the handle, we queried the desktop handle of a thread running 2304 in the same context */ 2305 Ret = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk; 2306 ObDereferenceObject(Thread); 2307 RETURN(Ret); 2308 } 2309 2310 /* Get the desktop handle and the desktop of the thread */ 2311 if (!(hThreadDesktop = ((PTHREADINFO)Thread->Tcb.Win32Thread)->hdesk) || 2312 !(DesktopObject = ((PTHREADINFO)Thread->Tcb.Win32Thread)->rpdesk)) 2313 { 2314 ObDereferenceObject(Thread); 2315 ERR("Desktop information of thread 0x%x broken!?\n", dwThreadId); 2316 RETURN(NULL); 2317 } 2318 2319 /* We could just use DesktopObject instead of looking up the handle, but latter 2320 may be a bit safer (e.g. when the desktop is being destroyed */ 2321 /* Switch into the context of the thread we're trying to get the desktop from, 2322 so we can use the handle */ 2323 KeAttachProcess(&Thread->ThreadsProcess->Pcb); 2324 Status = ObReferenceObjectByHandle(hThreadDesktop, 2325 GENERIC_ALL, 2326 ExDesktopObjectType, 2327 UserMode, 2328 (PVOID*)&DesktopObject, 2329 &HandleInformation); 2330 KeDetachProcess(); 2331 2332 /* The handle couldn't be found, there's nothing to get... */ 2333 if (!NT_SUCCESS(Status)) 2334 { 2335 ObDereferenceObject(Thread); 2336 RETURN(NULL); 2337 } 2338 2339 /* Lookup our handle table if we can find a handle to the desktop object, 2340 if not, create one */ 2341 Ret = IntGetDesktopObjectHandle(DesktopObject); 2342 2343 /* All done, we got a valid handle to the desktop */ 2344 ObDereferenceObject(DesktopObject); 2345 ObDereferenceObject(Thread); 2346 RETURN(Ret); 2347 2348 CLEANUP: 2349 TRACE("Leave NtUserGetThreadDesktop, ret=%p\n",_ret_); 2350 UserLeave(); 2351 END_CLEANUP; 2352 } 2353 2354 static NTSTATUS 2355 IntUnmapDesktopView(IN PDESKTOP pdesk) 2356 { 2357 PPROCESSINFO ppi; 2358 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink; 2359 NTSTATUS Status = STATUS_SUCCESS; 2360 2361 TRACE("IntUnmapDesktopView called for desktop object %p\n", pdesk); 2362 2363 ppi = PsGetCurrentProcessWin32Process(); 2364 2365 /* 2366 * Unmap if we're the last thread using the desktop. 2367 * Start the search at the next mapping: skip the first entry 2368 * as it must be the global user heap mapping. 2369 */ 2370 PrevLink = &ppi->HeapMappings.Next; 2371 HeapMapping = *PrevLink; 2372 while (HeapMapping != NULL) 2373 { 2374 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop) 2375 { 2376 if (--HeapMapping->Count == 0) 2377 { 2378 *PrevLink = HeapMapping->Next; 2379 2380 TRACE("ppi 0x%p unmapped heap of desktop 0x%p\n", ppi, pdesk); 2381 Status = MmUnmapViewOfSection(PsGetCurrentProcess(), 2382 HeapMapping->UserMapping); 2383 2384 ObDereferenceObject(pdesk); 2385 2386 UserHeapFree(HeapMapping); 2387 break; 2388 } 2389 } 2390 2391 PrevLink = &HeapMapping->Next; 2392 HeapMapping = HeapMapping->Next; 2393 } 2394 2395 return Status; 2396 } 2397 2398 static NTSTATUS 2399 IntMapDesktopView(IN PDESKTOP pdesk) 2400 { 2401 PPROCESSINFO ppi; 2402 PW32HEAP_USER_MAPPING HeapMapping, *PrevLink; 2403 PVOID UserBase = NULL; 2404 SIZE_T ViewSize = 0; 2405 LARGE_INTEGER Offset; 2406 NTSTATUS Status; 2407 2408 TRACE("IntMapDesktopView called for desktop object 0x%p\n", pdesk); 2409 2410 ppi = PsGetCurrentProcessWin32Process(); 2411 2412 /* 2413 * Find out if another thread already mapped the desktop heap. 2414 * Start the search at the next mapping: skip the first entry 2415 * as it must be the global user heap mapping. 2416 */ 2417 PrevLink = &ppi->HeapMappings.Next; 2418 HeapMapping = *PrevLink; 2419 while (HeapMapping != NULL) 2420 { 2421 if (HeapMapping->KernelMapping == (PVOID)pdesk->pheapDesktop) 2422 { 2423 HeapMapping->Count++; 2424 return STATUS_SUCCESS; 2425 } 2426 2427 PrevLink = &HeapMapping->Next; 2428 HeapMapping = HeapMapping->Next; 2429 } 2430 2431 /* We're the first, map the heap */ 2432 Offset.QuadPart = 0; 2433 Status = MmMapViewOfSection(pdesk->hsectionDesktop, 2434 PsGetCurrentProcess(), 2435 &UserBase, 2436 0, 2437 0, 2438 &Offset, 2439 &ViewSize, 2440 ViewUnmap, 2441 SEC_NO_CHANGE, 2442 PAGE_EXECUTE_READ); /* Would prefer PAGE_READONLY, but thanks to RTL heaps... */ 2443 if (!NT_SUCCESS(Status)) 2444 { 2445 ERR("Failed to map desktop\n"); 2446 return Status; 2447 } 2448 2449 TRACE("ppi 0x%p mapped heap of desktop 0x%p\n", ppi, pdesk); 2450 2451 /* Add the mapping */ 2452 HeapMapping = UserHeapAlloc(sizeof(*HeapMapping)); 2453 if (HeapMapping == NULL) 2454 { 2455 MmUnmapViewOfSection(PsGetCurrentProcess(), UserBase); 2456 ERR("UserHeapAlloc() failed!\n"); 2457 return STATUS_NO_MEMORY; 2458 } 2459 2460 HeapMapping->Next = NULL; 2461 HeapMapping->KernelMapping = (PVOID)pdesk->pheapDesktop; 2462 HeapMapping->UserMapping = UserBase; 2463 HeapMapping->Limit = ViewSize; 2464 HeapMapping->Count = 1; 2465 *PrevLink = HeapMapping; 2466 2467 ObReferenceObject(pdesk); 2468 2469 return STATUS_SUCCESS; 2470 } 2471 2472 BOOL 2473 IntSetThreadDesktop(IN HDESK hDesktop, 2474 IN BOOL FreeOnFailure) 2475 { 2476 PDESKTOP pdesk = NULL, pdeskOld; 2477 PTHREADINFO pti; 2478 NTSTATUS Status; 2479 PCLIENTTHREADINFO pctiOld, pctiNew = NULL; 2480 PCLIENTINFO pci; 2481 2482 ASSERT(NtCurrentTeb()); 2483 2484 TRACE("IntSetThreadDesktop hDesktop:0x%p, FOF:%i\n",hDesktop, FreeOnFailure); 2485 2486 pti = PsGetCurrentThreadWin32Thread(); 2487 pci = pti->pClientInfo; 2488 2489 /* If the caller gave us a desktop, ensure it is valid */ 2490 if (hDesktop != NULL) 2491 { 2492 /* Validate the new desktop. */ 2493 Status = IntValidateDesktopHandle( hDesktop, UserMode, 0, &pdesk); 2494 if (!NT_SUCCESS(Status)) 2495 { 2496 ERR("Validation of desktop handle (0x%p) failed\n", hDesktop); 2497 return FALSE; 2498 } 2499 2500 if (pti->rpdesk == pdesk) 2501 { 2502 /* Nothing to do */ 2503 ObDereferenceObject(pdesk); 2504 return TRUE; 2505 } 2506 } 2507 2508 /* Make sure that we don't own any window in the current desktop */ 2509 if (!IsListEmpty(&pti->WindowListHead)) 2510 { 2511 if (pdesk) 2512 ObDereferenceObject(pdesk); 2513 ERR("Attempted to change thread desktop although the thread has windows!\n"); 2514 EngSetLastError(ERROR_BUSY); 2515 return FALSE; 2516 } 2517 2518 /* Desktop is being re-set so clear out foreground. */ 2519 if (pti->rpdesk != pdesk && pti->MessageQueue == gpqForeground) 2520 { 2521 // Like above, there shouldn't be any windows, hooks or anything active on this threads desktop! 2522 IntSetFocusMessageQueue(NULL); 2523 } 2524 2525 /* Before doing the switch, map the new desktop heap and allocate the new pcti */ 2526 if (pdesk != NULL) 2527 { 2528 Status = IntMapDesktopView(pdesk); 2529 if (!NT_SUCCESS(Status)) 2530 { 2531 ERR("Failed to map desktop heap!\n"); 2532 ObDereferenceObject(pdesk); 2533 SetLastNtError(Status); 2534 return FALSE; 2535 } 2536 2537 pctiNew = DesktopHeapAlloc( pdesk, sizeof(CLIENTTHREADINFO)); 2538 if (pctiNew == NULL) 2539 { 2540 ERR("Failed to allocate new pcti\n"); 2541 IntUnmapDesktopView(pdesk); 2542 ObDereferenceObject(pdesk); 2543 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); 2544 return FALSE; 2545 } 2546 } 2547 2548 /* free all classes or move them to the shared heap */ 2549 if (pti->rpdesk != NULL) 2550 { 2551 if (!IntCheckProcessDesktopClasses(pti->rpdesk, FreeOnFailure)) 2552 { 2553 ERR("Failed to move process classes to shared heap!\n"); 2554 if (pdesk) 2555 { 2556 DesktopHeapFree(pdesk, pctiNew); 2557 IntUnmapDesktopView(pdesk); 2558 ObDereferenceObject(pdesk); 2559 } 2560 return FALSE; 2561 } 2562 } 2563 2564 pdeskOld = pti->rpdesk; 2565 if (pti->pcti != &pti->cti) 2566 pctiOld = pti->pcti; 2567 else 2568 pctiOld = NULL; 2569 2570 /* do the switch */ 2571 if (pdesk != NULL) 2572 { 2573 pti->rpdesk = pdesk; 2574 pti->hdesk = hDesktop; 2575 pti->pDeskInfo = pti->rpdesk->pDeskInfo; 2576 pti->pcti = pctiNew; 2577 2578 pci->ulClientDelta = DesktopHeapGetUserDelta(); 2579 pci->pDeskInfo = (PVOID)((ULONG_PTR)pti->pDeskInfo - pci->ulClientDelta); 2580 pci->pClientThreadInfo = (PVOID)((ULONG_PTR)pti->pcti - pci->ulClientDelta); 2581 2582 /* initialize the new pcti */ 2583 if (pctiOld != NULL) 2584 { 2585 RtlCopyMemory(pctiNew, pctiOld, sizeof(CLIENTTHREADINFO)); 2586 } 2587 else 2588 { 2589 RtlZeroMemory(pctiNew, sizeof(CLIENTTHREADINFO)); 2590 pci->fsHooks = pti->fsHooks; 2591 pci->dwTIFlags = pti->TIF_flags; 2592 } 2593 } 2594 else 2595 { 2596 pti->rpdesk = NULL; 2597 pti->hdesk = NULL; 2598 pti->pDeskInfo = NULL; 2599 pti->pcti = &pti->cti; // Always point inside so there will be no crash when posting or sending msg's! 2600 pci->ulClientDelta = 0; 2601 pci->pDeskInfo = NULL; 2602 pci->pClientThreadInfo = NULL; 2603 } 2604 2605 /* clean up the old desktop */ 2606 if (pdeskOld != NULL) 2607 { 2608 RemoveEntryList(&pti->PtiLink); 2609 if (pctiOld) DesktopHeapFree(pdeskOld, pctiOld); 2610 IntUnmapDesktopView(pdeskOld); 2611 ObDereferenceObject(pdeskOld); 2612 } 2613 2614 if (pdesk) 2615 { 2616 InsertTailList(&pdesk->PtiList, &pti->PtiLink); 2617 } 2618 2619 TRACE("IntSetThreadDesktop: pti 0x%p ppi 0x%p switched from object 0x%p to 0x%p\n", pti, pti->ppi, pdeskOld, pdesk); 2620 2621 return TRUE; 2622 } 2623 2624 /* 2625 * NtUserSetThreadDesktop 2626 * 2627 * Status 2628 * @implemented 2629 */ 2630 2631 BOOL APIENTRY 2632 NtUserSetThreadDesktop(HDESK hDesktop) 2633 { 2634 BOOL ret = FALSE; 2635 2636 UserEnterExclusive(); 2637 2638 // FIXME: IntSetThreadDesktop validates the desktop handle, it should happen 2639 // here too and set the NT error level. Q. Is it necessary to have the validation 2640 // in IntSetThreadDesktop? Is it needed there too? 2641 if (hDesktop || (!hDesktop && PsGetCurrentProcess() == gpepCSRSS)) 2642 ret = IntSetThreadDesktop(hDesktop, FALSE); 2643 2644 UserLeave(); 2645 2646 return ret; 2647 } 2648 2649 /* EOF */ 2650