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