1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/advapi32/reg/hkcr.c 5 * PURPOSE: Registry functions - HKEY_CLASSES_ROOT abstraction 6 * PROGRAMMER: Jerôme Gardou (jerome.gardou@reactos.org) 7 */ 8 9 #include <advapi32.h> 10 11 #include <ndk/cmfuncs.h> 12 #include <pseh/pseh2.h> 13 14 #include "reg.h" 15 16 WINE_DEFAULT_DEBUG_CHANNEL(reg); 17 18 static const UNICODE_STRING HKLM_ClassesPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Classes"); 19 20 static 21 BOOL 22 ValueExists(_In_ HKEY hNormalKey, _In_ PUNICODE_STRING Name) 23 { 24 KEY_VALUE_PARTIAL_INFORMATION kvi; 25 ULONG total_size = sizeof(kvi); 26 NTSTATUS status; 27 28 ASSERT(!IsHKCRKey(hNormalKey)); 29 status = NtQueryValueKey(hNormalKey, Name, KeyValuePartialInformation, 30 &kvi, total_size, &total_size); 31 return status != STATUS_OBJECT_NAME_NOT_FOUND; 32 } 33 34 static 35 LONG 36 GetKeyName(HKEY hKey, PUNICODE_STRING KeyName) 37 { 38 UNICODE_STRING InfoName; 39 PKEY_NAME_INFORMATION NameInformation; 40 ULONG InfoLength; 41 NTSTATUS Status; 42 43 /* Get info length */ 44 InfoLength = 0; 45 Status = NtQueryKey(hKey, KeyNameInformation, NULL, 0, &InfoLength); 46 if (Status != STATUS_BUFFER_TOO_SMALL) 47 { 48 ERR("NtQueryKey returned unexpected Status: 0x%08x\n", Status); 49 return RtlNtStatusToDosError(Status); 50 } 51 52 /* Get it for real */ 53 NameInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, InfoLength); 54 if (NameInformation == NULL) 55 { 56 ERR("Failed to allocate %lu bytes\n", InfoLength); 57 return ERROR_NOT_ENOUGH_MEMORY; 58 } 59 60 Status = NtQueryKey(hKey, KeyNameInformation, NameInformation, InfoLength, &InfoLength); 61 if (!NT_SUCCESS(Status)) 62 { 63 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation); 64 ERR("NtQueryKey failed: 0x%08x\n", Status); 65 return RtlNtStatusToDosError(Status); 66 } 67 68 /* Make it a proper UNICODE_STRING */ 69 InfoName.Length = NameInformation->NameLength; 70 InfoName.MaximumLength = NameInformation->NameLength; 71 InfoName.Buffer = NameInformation->Name; 72 73 Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &InfoName, KeyName); 74 if (!NT_SUCCESS(Status)) 75 { 76 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation); 77 ERR("RtlDuplicateUnicodeString failed: 0x%08x\n", Status); 78 return RtlNtStatusToDosError(Status); 79 } 80 81 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation); 82 83 return ERROR_SUCCESS; 84 } 85 86 static 87 LONG 88 GetKeySam( 89 _In_ HKEY hKey, 90 _Out_ REGSAM* RegSam) 91 { 92 NTSTATUS Status; 93 OBJECT_BASIC_INFORMATION ObjectInfo; 94 95 Status = NtQueryObject(hKey, ObjectBasicInformation, &ObjectInfo, sizeof(ObjectInfo), NULL); 96 if (!NT_SUCCESS(Status)) 97 { 98 ERR("NtQueryObject failed, Status %x08x\n", Status); 99 return RtlNtStatusToDosError(Status); 100 } 101 102 *RegSam = ObjectInfo.GrantedAccess; 103 return ERROR_SUCCESS; 104 } 105 106 /* 107 * Gets a HKLM key from an HKCU key. 108 */ 109 static 110 LONG 111 GetFallbackHKCRKey( 112 _In_ HKEY hKey, 113 _Out_ HKEY* MachineKey, 114 _In_ BOOL MustCreate) 115 { 116 UNICODE_STRING KeyName; 117 LPWSTR SubKeyName; 118 LONG ErrorCode; 119 REGSAM SamDesired; 120 121 /* Get the key name */ 122 ErrorCode = GetKeyName(hKey, &KeyName); 123 if (ErrorCode != ERROR_SUCCESS) 124 { 125 *MachineKey = hKey; 126 return ErrorCode; 127 } 128 129 /* See if we really need a conversion */ 130 if (RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE)) 131 { 132 RtlFreeUnicodeString(&KeyName); 133 *MachineKey = hKey; 134 return ERROR_SUCCESS; 135 } 136 137 SubKeyName = KeyName.Buffer + 15; /* 15 == wcslen(L"\\Registry\\User\\") */ 138 /* Skip the user token */ 139 while (*SubKeyName++ != L'\\') 140 { 141 if (!*SubKeyName) 142 { 143 ERR("Key name %S is invalid!\n", KeyName.Buffer); 144 return ERROR_INTERNAL_ERROR; 145 } 146 } 147 148 /* Use the same access mask than the original key */ 149 ErrorCode = GetKeySam(hKey, &SamDesired); 150 if (ErrorCode != ERROR_SUCCESS) 151 { 152 RtlFreeUnicodeString(&KeyName); 153 *MachineKey = hKey; 154 return ErrorCode; 155 } 156 157 if (MustCreate) 158 { 159 ErrorCode = RegCreateKeyExW( 160 HKEY_LOCAL_MACHINE, 161 SubKeyName, 162 0, 163 NULL, 164 0, 165 SamDesired, 166 NULL, 167 MachineKey, 168 NULL); 169 } 170 else 171 { 172 /* Open the key. */ 173 ErrorCode = RegOpenKeyExW( 174 HKEY_LOCAL_MACHINE, 175 SubKeyName, 176 0, 177 SamDesired, 178 MachineKey); 179 } 180 181 RtlFreeUnicodeString(&KeyName); 182 183 return ErrorCode; 184 } 185 186 /* Get the HKCU key (if it exists) from an HKCR key */ 187 static 188 LONG 189 GetPreferredHKCRKey( 190 _In_ HKEY hKey, 191 _Out_ HKEY* PreferredKey) 192 { 193 UNICODE_STRING KeyName; 194 LPWSTR SubKeyName; 195 LONG ErrorCode; 196 REGSAM SamDesired; 197 198 /* Get the key name */ 199 ErrorCode = GetKeyName(hKey, &KeyName); 200 if (ErrorCode != ERROR_SUCCESS) 201 { 202 *PreferredKey = hKey; 203 return ErrorCode; 204 } 205 206 /* See if we really need a conversion */ 207 if (!RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE)) 208 { 209 RtlFreeUnicodeString(&KeyName); 210 *PreferredKey = hKey; 211 return ERROR_SUCCESS; 212 } 213 214 /* 18 == wcslen(L"\\Registry\\Machine\\") */ 215 SubKeyName = KeyName.Buffer + 18; 216 217 /* Use the same access mask than the original key */ 218 ErrorCode = GetKeySam(hKey, &SamDesired); 219 if (ErrorCode != ERROR_SUCCESS) 220 { 221 RtlFreeUnicodeString(&KeyName); 222 *PreferredKey = hKey; 223 return ErrorCode; 224 } 225 226 /* Open the key. */ 227 ErrorCode = RegOpenKeyExW( 228 HKEY_CURRENT_USER, 229 SubKeyName, 230 0, 231 SamDesired, 232 PreferredKey); 233 234 RtlFreeUnicodeString(&KeyName); 235 236 return ErrorCode; 237 } 238 239 static 240 LONG 241 GetSubkeyInfoHelper( 242 _In_ HKEY hKey, 243 _Out_opt_ LPDWORD lpSubKeys, 244 _Out_opt_ LPDWORD lpMaxSubKeyLen) 245 { 246 LONG err = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, lpSubKeys, lpMaxSubKeyLen, 247 NULL, NULL, NULL, NULL, NULL, NULL); 248 if (err != ERROR_ACCESS_DENIED) 249 return err; 250 251 /* Windows RegEdit only uses KEY_ENUMERATE_SUB_KEYS when enumerating but 252 * KEY_QUERY_VALUE is required to get the info in EnumHKCRKey. 253 */ 254 if (RegOpenKeyExW(hKey, NULL, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) 255 { 256 err = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, lpSubKeys, lpMaxSubKeyLen, 257 NULL, NULL, NULL, NULL, NULL, NULL); 258 RegCloseKey(hKey); 259 } 260 return err; 261 } 262 263 /* HKCR version of RegCreateKeyExW. */ 264 LONG 265 WINAPI 266 CreateHKCRKey( 267 _In_ HKEY hKey, 268 _In_ LPCWSTR lpSubKey, 269 _In_ DWORD Reserved, 270 _In_opt_ LPWSTR lpClass, 271 _In_ DWORD dwOptions, 272 _In_ REGSAM samDesired, 273 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, 274 _Out_ PHKEY phkResult, 275 _Out_opt_ LPDWORD lpdwDisposition) 276 { 277 LONG ErrorCode; 278 HKEY QueriedKey, TestKey; 279 280 ASSERT(IsHKCRKey(hKey)); 281 282 /* Remove the HKCR flag while we're working */ 283 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 284 285 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 286 287 if (ErrorCode == ERROR_FILE_NOT_FOUND) 288 { 289 /* The current key doesn't exist on HKCU side, so we can only create it in HKLM */ 290 ErrorCode = RegCreateKeyExW( 291 hKey, 292 lpSubKey, 293 Reserved, 294 lpClass, 295 dwOptions, 296 samDesired, 297 lpSecurityAttributes, 298 phkResult, 299 lpdwDisposition); 300 if (ErrorCode == ERROR_SUCCESS) 301 MakeHKCRKey(phkResult); 302 return ErrorCode; 303 } 304 305 if (ErrorCode != ERROR_SUCCESS) 306 { 307 /* Somehow we failed for another reason (maybe deleted key or whatever) */ 308 return ErrorCode; 309 } 310 311 /* See if the subkey already exists in HKCU. */ 312 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, 0, READ_CONTROL, &TestKey); 313 if (ErrorCode != ERROR_FILE_NOT_FOUND) 314 { 315 if (ErrorCode == ERROR_SUCCESS) 316 { 317 /* Great. Close the test one and do the real create operation */ 318 RegCloseKey(TestKey); 319 ErrorCode = RegCreateKeyExW( 320 QueriedKey, 321 lpSubKey, 322 Reserved, 323 lpClass, 324 dwOptions, 325 samDesired, 326 lpSecurityAttributes, 327 phkResult, 328 lpdwDisposition); 329 if (ErrorCode == ERROR_SUCCESS) 330 MakeHKCRKey(phkResult); 331 } 332 if (QueriedKey != hKey) 333 RegCloseKey(QueriedKey); 334 335 return ERROR_SUCCESS; 336 } 337 338 if (QueriedKey != hKey) 339 RegCloseKey(QueriedKey); 340 341 /* So we must do the create operation in HKLM, creating the missing parent keys if needed. */ 342 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, TRUE); 343 if (ErrorCode != ERROR_SUCCESS) 344 return ErrorCode; 345 346 /* Do the key creation */ 347 ErrorCode = RegCreateKeyEx( 348 QueriedKey, 349 lpSubKey, 350 Reserved, 351 lpClass, 352 dwOptions, 353 samDesired, 354 lpSecurityAttributes, 355 phkResult, 356 lpdwDisposition); 357 358 if (QueriedKey != hKey) 359 RegCloseKey(QueriedKey); 360 361 if (ErrorCode == ERROR_SUCCESS) 362 MakeHKCRKey(phkResult); 363 364 return ErrorCode; 365 } 366 367 /* Same as RegOpenKeyExW, but for HKEY_CLASSES_ROOT subkeys */ 368 LONG 369 WINAPI 370 OpenHKCRKey( 371 _In_ HKEY hKey, 372 _In_ LPCWSTR lpSubKey, 373 _In_ DWORD ulOptions, 374 _In_ REGSAM samDesired, 375 _In_ PHKEY phkResult) 376 { 377 HKEY QueriedKey; 378 LONG ErrorCode; 379 380 ASSERT(IsHKCRKey(hKey)); 381 382 /* Remove the HKCR flag while we're working */ 383 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 384 385 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 386 387 if (ErrorCode == ERROR_FILE_NOT_FOUND) 388 { 389 /* The key doesn't exist on HKCU side, no chance for a subkey */ 390 ErrorCode = RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult); 391 if (ErrorCode == ERROR_SUCCESS) 392 MakeHKCRKey(phkResult); 393 return ErrorCode; 394 } 395 396 if (ErrorCode != ERROR_SUCCESS) 397 { 398 /* Somehow we failed for another reason (maybe deleted key or whatever) */ 399 return ErrorCode; 400 } 401 402 /* Try on the HKCU side */ 403 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult); 404 if (ErrorCode == ERROR_SUCCESS) 405 MakeHKCRKey(phkResult); 406 407 /* Close it if we must */ 408 if (QueriedKey != hKey) 409 { 410 RegCloseKey(QueriedKey); 411 } 412 413 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */ 414 if (ErrorCode != ERROR_FILE_NOT_FOUND) 415 return ErrorCode; 416 417 /* If we're here, we must open from HKLM key. */ 418 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE); 419 if (ErrorCode != ERROR_SUCCESS) 420 { 421 /* Maybe the key doesn't exist in the HKLM view */ 422 return ErrorCode; 423 } 424 425 ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult); 426 if (ErrorCode == ERROR_SUCCESS) 427 MakeHKCRKey(phkResult); 428 429 /* Close it if we must */ 430 if (QueriedKey != hKey) 431 { 432 RegCloseKey(QueriedKey); 433 } 434 435 return ErrorCode; 436 } 437 438 /* HKCR version of RegDeleteKeyExW */ 439 LONG 440 WINAPI 441 DeleteHKCRKey( 442 _In_ HKEY hKey, 443 _In_ LPCWSTR lpSubKey, 444 _In_ REGSAM RegSam, 445 _In_ DWORD Reserved) 446 { 447 HKEY QueriedKey; 448 LONG ErrorCode; 449 450 ASSERT(IsHKCRKey(hKey)); 451 452 /* Remove the HKCR flag while we're working */ 453 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 454 455 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 456 457 if (ErrorCode == ERROR_FILE_NOT_FOUND) 458 { 459 /* The key doesn't exist on HKCU side, no chance for a subkey */ 460 return RegDeleteKeyExW(hKey, lpSubKey, RegSam, Reserved); 461 } 462 463 if (ErrorCode != ERROR_SUCCESS) 464 { 465 /* Somehow we failed for another reason (maybe deleted key or whatever) */ 466 return ErrorCode; 467 } 468 469 ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved); 470 471 /* Close it if we must */ 472 if (QueriedKey != hKey) 473 { 474 /* The original key is on the machine view */ 475 RegCloseKey(QueriedKey); 476 } 477 478 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */ 479 if (ErrorCode != ERROR_FILE_NOT_FOUND) 480 return ErrorCode; 481 482 /* If we're here, we must open from HKLM key. */ 483 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE); 484 if (ErrorCode != ERROR_SUCCESS) 485 { 486 /* Maybe the key doesn't exist in the HKLM view */ 487 return ErrorCode; 488 } 489 490 ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved); 491 492 /* Close it if we must */ 493 if (QueriedKey != hKey) 494 { 495 RegCloseKey(QueriedKey); 496 } 497 498 return ErrorCode; 499 } 500 501 /* HKCR version of RegDeleteValueA+W */ 502 LONG 503 WINAPI 504 DeleteHKCRValue( 505 _In_ HKEY hKey, 506 _In_ PUNICODE_STRING ValueName) 507 { 508 HKEY hActualKey; 509 LONG ErrorCode; 510 511 ASSERT(IsHKCRKey(hKey)); 512 /* Remove the HKCR flag while we're working */ 513 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 514 515 /* Does the HKCU key and value exist? */ 516 ErrorCode = GetPreferredHKCRKey(hKey, &hActualKey); 517 if (ErrorCode == ERROR_SUCCESS) 518 { 519 if (!ValueExists(hActualKey, ValueName)) 520 { 521 if (hActualKey != hKey) 522 { 523 RegCloseKey(hActualKey); 524 } 525 ErrorCode = ERROR_FILE_NOT_FOUND; 526 } 527 } 528 if (ErrorCode == ERROR_FILE_NOT_FOUND) 529 { 530 ErrorCode = GetFallbackHKCRKey(hKey, &hActualKey, FALSE); 531 } 532 533 if (ErrorCode == ERROR_SUCCESS) 534 { 535 NTSTATUS Status = NtDeleteValueKey(hActualKey, ValueName); 536 ErrorCode = NT_SUCCESS(Status) ? ERROR_SUCCESS : RtlNtStatusToDosError(Status); 537 } 538 if (hActualKey != hKey) 539 { 540 RegCloseKey(hActualKey); 541 } 542 return ErrorCode; 543 } 544 545 /* HKCR version of RegQueryValueExW */ 546 LONG 547 WINAPI 548 QueryHKCRValue( 549 _In_ HKEY hKey, 550 _In_ LPCWSTR Name, 551 _In_ LPDWORD Reserved, 552 _In_ LPDWORD Type, 553 _In_ LPBYTE Data, 554 _In_ LPDWORD Count) 555 { 556 HKEY QueriedKey; 557 LONG ErrorCode; 558 559 ASSERT(IsHKCRKey(hKey)); 560 561 /* Remove the HKCR flag while we're working */ 562 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 563 564 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 565 566 if (ErrorCode == ERROR_FILE_NOT_FOUND) 567 { 568 /* The key doesn't exist on HKCU side, no chance for a value in it */ 569 return RegQueryValueExW(hKey, Name, Reserved, Type, Data, Count); 570 } 571 572 if (ErrorCode != ERROR_SUCCESS) 573 { 574 /* Somehow we failed for another reason (maybe deleted key or whatever) */ 575 return ErrorCode; 576 } 577 578 ErrorCode = RegQueryValueExW(QueriedKey, Name, Reserved, Type, Data, Count); 579 580 /* Close it if we must */ 581 if (QueriedKey != hKey) 582 { 583 /* The original key is on the machine view */ 584 RegCloseKey(QueriedKey); 585 } 586 587 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */ 588 if (ErrorCode != ERROR_FILE_NOT_FOUND) 589 return ErrorCode; 590 591 /* If we're here, we must open from HKLM key. */ 592 ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE); 593 if (ErrorCode != ERROR_SUCCESS) 594 { 595 /* Maybe the key doesn't exist in the HKLM view */ 596 return ErrorCode; 597 } 598 599 ErrorCode = RegQueryValueExW(QueriedKey, Name, Reserved, Type, Data, Count); 600 601 /* Close it if we must */ 602 if (QueriedKey != hKey) 603 { 604 RegCloseKey(QueriedKey); 605 } 606 607 return ErrorCode; 608 } 609 610 /* HKCR version of RegSetValueExW */ 611 LONG 612 WINAPI 613 SetHKCRValue( 614 _In_ HKEY hKey, 615 _In_ LPCWSTR Name, 616 _In_ DWORD Reserved, 617 _In_ DWORD Type, 618 _In_ CONST BYTE* Data, 619 _In_ DWORD DataSize) 620 { 621 HKEY QueriedKey; 622 LONG ErrorCode; 623 624 ASSERT(IsHKCRKey(hKey)); 625 626 /* Remove the HKCR flag while we're working */ 627 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 628 629 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 630 631 if (ErrorCode == ERROR_FILE_NOT_FOUND) 632 { 633 /* The key doesn't exist on HKCU side, no chance to put a value in it */ 634 return RegSetValueExW(hKey, Name, Reserved, Type, Data, DataSize); 635 } 636 637 if (ErrorCode != ERROR_SUCCESS) 638 { 639 /* Somehow we failed for another reason (maybe deleted key or whatever) */ 640 return ErrorCode; 641 } 642 643 /* Check if the value already exists in the preferred key */ 644 ErrorCode = RegQueryValueExW(QueriedKey, Name, NULL, NULL, NULL, NULL); 645 if (ErrorCode != ERROR_FILE_NOT_FOUND) 646 { 647 if (ErrorCode == ERROR_SUCCESS) 648 { 649 /* Yes, so we have the right to modify it */ 650 ErrorCode = RegSetValueExW(QueriedKey, Name, Reserved, Type, Data, DataSize); 651 } 652 if (QueriedKey != hKey) 653 RegCloseKey(QueriedKey); 654 return ErrorCode; 655 } 656 if (QueriedKey != hKey) 657 RegCloseKey(QueriedKey); 658 659 /* So we must set the value in the HKLM version */ 660 ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey); 661 if (ErrorCode == ERROR_FILE_NOT_FOUND) 662 { 663 /* No choice: put this in HKCU */ 664 return RegSetValueExW(hKey, Name, Reserved, Type, Data, DataSize); 665 } 666 else if (ErrorCode != ERROR_SUCCESS) 667 { 668 return ErrorCode; 669 } 670 671 ErrorCode = RegSetValueExW(QueriedKey, Name, Reserved, Type, Data, DataSize); 672 673 if (QueriedKey != hKey) 674 RegCloseKey(QueriedKey); 675 676 return ErrorCode; 677 } 678 679 /* HKCR version of RegEnumKeyExW */ 680 LONG 681 WINAPI 682 EnumHKCRKey( 683 _In_ HKEY hKey, 684 _In_ DWORD dwIndex, 685 _Out_ LPWSTR lpName, 686 _Inout_ LPDWORD lpcbName, 687 _Reserved_ LPDWORD lpReserved, 688 _Out_opt_ LPWSTR lpClass, 689 _Inout_opt_ LPDWORD lpcbClass, 690 _Out_opt_ PFILETIME lpftLastWriteTime) 691 { 692 HKEY PreferredKey, FallbackKey; 693 DWORD NumPreferredSubKeys; 694 DWORD MaxFallbackSubKeyLen; 695 DWORD FallbackIndex; 696 WCHAR* FallbackSubKeyName = NULL; 697 LONG ErrorCode; 698 699 ASSERT(IsHKCRKey(hKey)); 700 701 /* Remove the HKCR flag while we're working */ 702 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 703 704 /* Get the preferred key */ 705 ErrorCode = GetPreferredHKCRKey(hKey, &PreferredKey); 706 if (ErrorCode != ERROR_SUCCESS) 707 { 708 if (ErrorCode == ERROR_FILE_NOT_FOUND) 709 { 710 /* Only the HKLM key exists */ 711 return RegEnumKeyExW( 712 hKey, 713 dwIndex, 714 lpName, 715 lpcbName, 716 lpReserved, 717 lpClass, 718 lpcbClass, 719 lpftLastWriteTime); 720 } 721 return ErrorCode; 722 } 723 724 /* Get the fallback key */ 725 ErrorCode = GetFallbackHKCRKey(hKey, &FallbackKey, FALSE); 726 if (ErrorCode != ERROR_SUCCESS) 727 { 728 if (PreferredKey != hKey) 729 RegCloseKey(PreferredKey); 730 if (ErrorCode == ERROR_FILE_NOT_FOUND) 731 { 732 /* Only the HKCU key exists */ 733 return RegEnumKeyExW( 734 hKey, 735 dwIndex, 736 lpName, 737 lpcbName, 738 lpReserved, 739 lpClass, 740 lpcbClass, 741 lpftLastWriteTime); 742 } 743 return ErrorCode; 744 } 745 746 /* Get some info on the HKCU side */ 747 ErrorCode = GetSubkeyInfoHelper(PreferredKey, &NumPreferredSubKeys, NULL); 748 if (ErrorCode != ERROR_SUCCESS) 749 goto Exit; 750 751 if (dwIndex < NumPreferredSubKeys) 752 { 753 /* HKCU side takes precedence */ 754 ErrorCode = RegEnumKeyExW( 755 PreferredKey, 756 dwIndex, 757 lpName, 758 lpcbName, 759 lpReserved, 760 lpClass, 761 lpcbClass, 762 lpftLastWriteTime); 763 goto Exit; 764 } 765 766 /* Here it gets tricky. We must enumerate the values from the HKLM side, 767 * without reporting those which are present on the HKCU side */ 768 769 /* Squash out the indices from HKCU */ 770 dwIndex -= NumPreferredSubKeys; 771 772 /* Get some info */ 773 ErrorCode = GetSubkeyInfoHelper(FallbackKey, NULL, &MaxFallbackSubKeyLen); 774 if (ErrorCode != ERROR_SUCCESS) 775 { 776 ERR("Could not query info of key %p (Err: %d)\n", FallbackKey, ErrorCode); 777 goto Exit; 778 } 779 780 MaxFallbackSubKeyLen++; 781 TRACE("Maxfallbacksubkeylen: %d\n", MaxFallbackSubKeyLen); 782 783 /* Allocate our buffer */ 784 FallbackSubKeyName = RtlAllocateHeap( 785 RtlGetProcessHeap(), 0, MaxFallbackSubKeyLen * sizeof(WCHAR)); 786 if (!FallbackSubKeyName) 787 { 788 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 789 goto Exit; 790 } 791 792 /* We must begin at the very first subkey of the fallback key, 793 * and then see if we meet keys that already are in the preferred key. 794 * In that case, we must bump dwIndex, as otherwise we would enumerate a key we already 795 * saw in a previous call. 796 */ 797 FallbackIndex = 0; 798 while (TRUE) 799 { 800 HKEY PreferredSubKey; 801 DWORD FallbackSubkeyLen = MaxFallbackSubKeyLen; 802 803 /* Try enumerating */ 804 ErrorCode = RegEnumKeyExW( 805 FallbackKey, 806 FallbackIndex, 807 FallbackSubKeyName, 808 &FallbackSubkeyLen, 809 NULL, 810 NULL, 811 NULL, 812 NULL); 813 if (ErrorCode != ERROR_SUCCESS) 814 { 815 if (ErrorCode != ERROR_NO_MORE_ITEMS) 816 ERR("Returning %d.\n", ErrorCode); 817 goto Exit; 818 } 819 FallbackSubKeyName[FallbackSubkeyLen] = L'\0'; 820 821 /* See if there is such a value on HKCU side */ 822 ErrorCode = RegOpenKeyExW( 823 PreferredKey, 824 FallbackSubKeyName, 825 0, 826 READ_CONTROL, 827 &PreferredSubKey); 828 829 if (ErrorCode == ERROR_SUCCESS) 830 { 831 RegCloseKey(PreferredSubKey); 832 /* So we already enumerated it on HKCU side. */ 833 dwIndex++; 834 } 835 else if (ErrorCode != ERROR_FILE_NOT_FOUND) 836 { 837 ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode, FallbackSubKeyName); 838 goto Exit; 839 } 840 841 /* See if we caught up */ 842 if (FallbackIndex == dwIndex) 843 break; 844 845 FallbackIndex++; 846 } 847 848 /* We can finally enumerate on the fallback side */ 849 ErrorCode = RegEnumKeyExW( 850 FallbackKey, 851 dwIndex, 852 lpName, 853 lpcbName, 854 lpReserved, 855 lpClass, 856 lpcbClass, 857 lpftLastWriteTime); 858 859 Exit: 860 if (PreferredKey != hKey) 861 RegCloseKey(PreferredKey); 862 if (FallbackKey != hKey) 863 RegCloseKey(FallbackKey); 864 if (FallbackSubKeyName) 865 RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackSubKeyName); 866 867 return ErrorCode; 868 } 869 870 /* HKCR version of RegEnumValueW */ 871 LONG 872 WINAPI 873 EnumHKCRValue( 874 _In_ HKEY hKey, 875 _In_ DWORD dwIndex, 876 _Out_ LPWSTR lpName, 877 _Inout_ PDWORD lpcbName, 878 _Reserved_ PDWORD lpReserved, 879 _Out_opt_ PDWORD lpdwType, 880 _Out_opt_ LPBYTE lpData, 881 _Inout_opt_ PDWORD lpcbData) 882 { 883 HKEY PreferredKey, FallbackKey; 884 DWORD NumPreferredValues; 885 DWORD MaxFallbackValueNameLen; 886 DWORD FallbackIndex; 887 WCHAR* FallbackValueName = NULL; 888 LONG ErrorCode; 889 890 ASSERT(IsHKCRKey(hKey)); 891 892 /* Remove the HKCR flag while we're working */ 893 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 894 895 /* Get the preferred key */ 896 ErrorCode = GetPreferredHKCRKey(hKey, &PreferredKey); 897 if (ErrorCode != ERROR_SUCCESS) 898 { 899 if (ErrorCode == ERROR_FILE_NOT_FOUND) 900 { 901 /* Only the HKLM key exists */ 902 return RegEnumValueW( 903 hKey, 904 dwIndex, 905 lpName, 906 lpcbName, 907 lpReserved, 908 lpdwType, 909 lpData, 910 lpcbData); 911 } 912 return ErrorCode; 913 } 914 915 /* Get the fallback key */ 916 ErrorCode = GetFallbackHKCRKey(hKey, &FallbackKey, FALSE); 917 if (ErrorCode != ERROR_SUCCESS) 918 { 919 if (PreferredKey != hKey) 920 RegCloseKey(PreferredKey); 921 if (ErrorCode == ERROR_FILE_NOT_FOUND) 922 { 923 /* Only the HKCU key exists */ 924 return RegEnumValueW( 925 hKey, 926 dwIndex, 927 lpName, 928 lpcbName, 929 lpReserved, 930 lpdwType, 931 lpData, 932 lpcbData); 933 } 934 return ErrorCode; 935 } 936 937 /* Get some info on the HKCU side */ 938 ErrorCode = RegQueryInfoKeyW( 939 PreferredKey, 940 NULL, 941 NULL, 942 NULL, 943 NULL, 944 NULL, 945 NULL, 946 &NumPreferredValues, 947 NULL, 948 NULL, 949 NULL, 950 NULL); 951 if (ErrorCode != ERROR_SUCCESS) 952 goto Exit; 953 954 if (dwIndex < NumPreferredValues) 955 { 956 /* HKCU side takes precedence */ 957 return RegEnumValueW( 958 PreferredKey, 959 dwIndex, 960 lpName, 961 lpcbName, 962 lpReserved, 963 lpdwType, 964 lpData, 965 lpcbData); 966 goto Exit; 967 } 968 969 /* Here it gets tricky. We must enumerate the values from the HKLM side, 970 * without reporting those which are present on the HKCU side */ 971 972 /* Squash out the indices from HKCU */ 973 dwIndex -= NumPreferredValues; 974 975 /* Get some info */ 976 ErrorCode = RegQueryInfoKeyW( 977 FallbackKey, 978 NULL, 979 NULL, 980 NULL, 981 NULL, 982 NULL, 983 NULL, 984 NULL, 985 &MaxFallbackValueNameLen, 986 NULL, 987 NULL, 988 NULL); 989 if (ErrorCode != ERROR_SUCCESS) 990 { 991 ERR("Could not query info of key %p (Err: %d)\n", FallbackKey, ErrorCode); 992 goto Exit; 993 } 994 995 MaxFallbackValueNameLen++; 996 TRACE("Maxfallbacksubkeylen: %d\n", MaxFallbackValueNameLen); 997 998 /* Allocate our buffer */ 999 FallbackValueName = RtlAllocateHeap( 1000 RtlGetProcessHeap(), 0, MaxFallbackValueNameLen * sizeof(WCHAR)); 1001 if (!FallbackValueName) 1002 { 1003 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1004 goto Exit; 1005 } 1006 1007 /* We must begin at the very first subkey of the fallback key, 1008 * and then see if we meet keys that already are in the preferred key. 1009 * In that case, we must bump dwIndex, as otherwise we would enumerate a key we already 1010 * saw in a previous call. 1011 */ 1012 FallbackIndex = 0; 1013 while (TRUE) 1014 { 1015 DWORD FallbackValueNameLen = MaxFallbackValueNameLen; 1016 1017 /* Try enumerating */ 1018 ErrorCode = RegEnumValueW( 1019 FallbackKey, 1020 FallbackIndex, 1021 FallbackValueName, 1022 &FallbackValueNameLen, 1023 NULL, 1024 NULL, 1025 NULL, 1026 NULL); 1027 if (ErrorCode != ERROR_SUCCESS) 1028 { 1029 if (ErrorCode != ERROR_NO_MORE_ITEMS) 1030 ERR("Returning %d.\n", ErrorCode); 1031 goto Exit; 1032 } 1033 FallbackValueName[FallbackValueNameLen] = L'\0'; 1034 1035 /* See if there is such a value on HKCU side */ 1036 ErrorCode = RegQueryValueExW( 1037 PreferredKey, 1038 FallbackValueName, 1039 NULL, 1040 NULL, 1041 NULL, 1042 NULL); 1043 1044 if (ErrorCode == ERROR_SUCCESS) 1045 { 1046 /* So we already enumerated it on HKCU side. */ 1047 dwIndex++; 1048 } 1049 else if (ErrorCode != ERROR_FILE_NOT_FOUND) 1050 { 1051 ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode, FallbackValueName); 1052 goto Exit; 1053 } 1054 1055 /* See if we caught up */ 1056 if (FallbackIndex == dwIndex) 1057 break; 1058 1059 FallbackIndex++; 1060 } 1061 1062 /* We can finally enumerate on the fallback side */ 1063 ErrorCode = RegEnumValueW( 1064 FallbackKey, 1065 dwIndex, 1066 lpName, 1067 lpcbName, 1068 lpReserved, 1069 lpdwType, 1070 lpData, 1071 lpcbData); 1072 1073 Exit: 1074 if (PreferredKey != hKey) 1075 RegCloseKey(PreferredKey); 1076 if (FallbackKey != hKey) 1077 RegCloseKey(FallbackKey); 1078 if (FallbackValueName) 1079 RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackValueName); 1080 1081 return ErrorCode; 1082 } 1083 1084 /* HKCR version of RegQueryInfoKeyW */ 1085 LONG 1086 WINAPI 1087 QueryInfoHKCRKey( 1088 _In_ HKEY hKey, 1089 _Out_writes_to_opt_(*lpcchClass, *lpcchClass + 1) LPWSTR lpClass, 1090 _Inout_opt_ LPDWORD lpcchClass, 1091 _Reserved_ LPDWORD lpReserved, 1092 _Out_opt_ LPDWORD lpcSubKeys, 1093 _Out_opt_ LPDWORD lpcbMaxSubKeyLen, 1094 _Out_opt_ LPDWORD lpcbMaxClassLen, 1095 _Out_opt_ LPDWORD lpcValues, 1096 _Out_opt_ LPDWORD lpcbMaxValueNameLen, 1097 _Out_opt_ LPDWORD lpcbMaxValueLen, 1098 _Out_opt_ LPDWORD lpcbSecurityDescriptor, 1099 _Out_opt_ PFILETIME lpftLastWriteTime) 1100 { 1101 HKEY PreferredKey, FallbackKey; 1102 LONG retval, err; 1103 DWORD OtherSubKeys = 0, OtherMaxSub = 0, OtherMaxClass = 0; 1104 DWORD OtherValues = 0, OtherMaxName = 0, OtherMaxVal = 0; 1105 1106 ASSERT(IsHKCRKey(hKey)); 1107 1108 /* Remove the HKCR flag while we're working */ 1109 hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2); 1110 1111 retval = GetFallbackHKCRKey(hKey, &FallbackKey, FALSE); 1112 if (retval == ERROR_SUCCESS) 1113 { 1114 retval = RegQueryInfoKeyW(FallbackKey, lpClass, lpcchClass, lpReserved, 1115 &OtherSubKeys, &OtherMaxSub, &OtherMaxClass, 1116 &OtherValues, &OtherMaxName, &OtherMaxVal, 1117 lpcbSecurityDescriptor, lpftLastWriteTime); 1118 if (FallbackKey != hKey) 1119 RegCloseKey(FallbackKey); 1120 } 1121 1122 err = GetPreferredHKCRKey(hKey, &PreferredKey); 1123 if (err == ERROR_SUCCESS) 1124 { 1125 err = RegQueryInfoKeyW(PreferredKey, lpClass, lpcchClass, lpReserved, 1126 lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen, 1127 lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen, 1128 lpcbSecurityDescriptor, lpftLastWriteTime); 1129 if (PreferredKey != hKey) 1130 RegCloseKey(PreferredKey); 1131 } 1132 1133 if (lpcSubKeys) 1134 *lpcSubKeys = (err ? 0 : *lpcSubKeys) + OtherSubKeys; 1135 if (lpcValues) 1136 *lpcValues = (err ? 0 : *lpcValues) + OtherValues; 1137 if (lpcbMaxSubKeyLen) 1138 *lpcbMaxSubKeyLen = max((err ? 0 : *lpcbMaxSubKeyLen), OtherMaxSub); 1139 if (lpcbMaxClassLen) 1140 *lpcbMaxClassLen = max((err ? 0 : *lpcbMaxClassLen), OtherMaxClass); 1141 if (lpcbMaxValueNameLen) 1142 *lpcbMaxValueNameLen = max((err ? 0 : *lpcbMaxValueNameLen), OtherMaxName); 1143 if (lpcbMaxValueLen) 1144 *lpcbMaxValueLen = max((err ? 0 : *lpcbMaxValueLen), OtherMaxVal); 1145 1146 return (err == ERROR_SUCCESS) ? ERROR_SUCCESS : retval; 1147 } 1148