1 /* 2 * COPYRIGHT: See COPYING in the top level directory 3 * PROJECT: ReactOS system libraries 4 * FILE: lib/advapi32/reg/reg.c 5 * PURPOSE: Registry functions 6 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl) 7 * Thomas Weidenmueller <w3seek@reactos.com> 8 * UPDATE HISTORY: 9 * Created 01/11/98 10 * 19990309 EA Stubs 11 * 20050502 Fireball imported some stuff from WINE 12 */ 13 14 /* INCLUDES *****************************************************************/ 15 16 #include <advapi32.h> 17 18 #include <ndk/cmfuncs.h> 19 #include <pseh/pseh2.h> 20 21 #include "reg.h" 22 23 WINE_DEFAULT_DEBUG_CHANNEL(reg); 24 25 /* DEFINES ******************************************************************/ 26 27 #define MAX_DEFAULT_HANDLES 6 28 #define REG_MAX_NAME_SIZE 256 29 #define REG_MAX_DATA_SIZE 2048 30 31 /* GLOBALS ******************************************************************/ 32 33 static RTL_CRITICAL_SECTION HandleTableCS; 34 static HANDLE DefaultHandleTable[MAX_DEFAULT_HANDLES]; 35 static HANDLE ProcessHeap; 36 static BOOLEAN DefaultHandlesDisabled = FALSE; 37 static BOOLEAN DefaultHandleHKUDisabled = FALSE; 38 static BOOLEAN DllInitialized = FALSE; /* HACK */ 39 40 /* PROTOTYPES ***************************************************************/ 41 42 static NTSTATUS MapDefaultKey (PHANDLE ParentKey, HKEY Key); 43 static VOID CloseDefaultKeys(VOID); 44 #define ClosePredefKey(Handle) \ 45 if ((ULONG_PTR)Handle & 0x1) { \ 46 NtClose(Handle); \ 47 } 48 #define IsPredefKey(HKey) \ 49 (((ULONG_PTR)(HKey) & 0xF0000000) == 0x80000000) 50 #define GetPredefKeyIndex(HKey) \ 51 ((ULONG_PTR)(HKey) & 0x0FFFFFFF) 52 53 static NTSTATUS OpenClassesRootKey(PHANDLE KeyHandle); 54 static NTSTATUS OpenLocalMachineKey (PHANDLE KeyHandle); 55 static NTSTATUS OpenUsersKey (PHANDLE KeyHandle); 56 static NTSTATUS OpenCurrentConfigKey(PHANDLE KeyHandle); 57 58 59 /* FUNCTIONS ****************************************************************/ 60 /* check if value type needs string conversion (Ansi<->Unicode) */ 61 __inline static int is_string( DWORD type ) 62 { 63 return (type == REG_SZ) || (type == REG_EXPAND_SZ) || (type == REG_MULTI_SZ); 64 } 65 66 /************************************************************************ 67 * RegInitDefaultHandles 68 */ 69 BOOL 70 RegInitialize(VOID) 71 { 72 TRACE("RegInitialize()\n"); 73 74 /* Lazy init hack */ 75 if (!DllInitialized) 76 { 77 ProcessHeap = RtlGetProcessHeap(); 78 RtlZeroMemory(DefaultHandleTable, 79 MAX_DEFAULT_HANDLES * sizeof(HANDLE)); 80 RtlInitializeCriticalSection(&HandleTableCS); 81 82 DllInitialized = TRUE; 83 } 84 85 return TRUE; 86 } 87 88 89 /************************************************************************ 90 * RegInit 91 */ 92 BOOL 93 RegCleanup(VOID) 94 { 95 TRACE("RegCleanup()\n"); 96 97 CloseDefaultKeys(); 98 RtlDeleteCriticalSection(&HandleTableCS); 99 100 return TRUE; 101 } 102 103 104 static NTSTATUS 105 OpenPredefinedKey(IN ULONG Index, 106 OUT HANDLE Handle) 107 { 108 NTSTATUS Status; 109 110 switch (Index) 111 { 112 case 0: /* HKEY_CLASSES_ROOT */ 113 Status = OpenClassesRootKey (Handle); 114 break; 115 116 case 1: /* HKEY_CURRENT_USER */ 117 Status = RtlOpenCurrentUser (MAXIMUM_ALLOWED, 118 Handle); 119 break; 120 121 case 2: /* HKEY_LOCAL_MACHINE */ 122 Status = OpenLocalMachineKey (Handle); 123 break; 124 125 case 3: /* HKEY_USERS */ 126 Status = OpenUsersKey (Handle); 127 break; 128 #if 0 129 case 4: /* HKEY_PERFORMANCE_DATA */ 130 Status = OpenPerformanceDataKey (Handle); 131 break; 132 #endif 133 134 case 5: /* HKEY_CURRENT_CONFIG */ 135 Status = OpenCurrentConfigKey (Handle); 136 break; 137 138 case 6: /* HKEY_DYN_DATA */ 139 Status = STATUS_NOT_IMPLEMENTED; 140 break; 141 142 default: 143 WARN("MapDefaultHandle() no handle creator\n"); 144 Status = STATUS_INVALID_PARAMETER; 145 break; 146 } 147 148 return Status; 149 } 150 151 152 static NTSTATUS 153 MapDefaultKey(OUT PHANDLE RealKey, 154 IN HKEY Key) 155 { 156 PHANDLE Handle; 157 ULONG Index; 158 BOOLEAN DoOpen, DefDisabled; 159 NTSTATUS Status = STATUS_SUCCESS; 160 161 TRACE("MapDefaultKey (Key %x)\n", Key); 162 163 if (!IsPredefKey(Key)) 164 { 165 *RealKey = (HANDLE)((ULONG_PTR)Key & ~0x1); 166 return STATUS_SUCCESS; 167 } 168 169 /* Handle special cases here */ 170 Index = GetPredefKeyIndex(Key); 171 if (Index >= MAX_DEFAULT_HANDLES) 172 { 173 return STATUS_INVALID_PARAMETER; 174 } 175 RegInitialize(); /* HACK until delay-loading is implemented */ 176 RtlEnterCriticalSection (&HandleTableCS); 177 178 if (Key == HKEY_CURRENT_USER) 179 DefDisabled = DefaultHandleHKUDisabled; 180 else 181 DefDisabled = DefaultHandlesDisabled; 182 183 if (!DefDisabled) 184 { 185 Handle = &DefaultHandleTable[Index]; 186 DoOpen = (*Handle == NULL); 187 } 188 else 189 { 190 Handle = RealKey; 191 DoOpen = TRUE; 192 } 193 194 if (DoOpen) 195 { 196 /* create/open the default handle */ 197 Status = OpenPredefinedKey(Index, 198 Handle); 199 } 200 201 if (NT_SUCCESS(Status)) 202 { 203 if (!DefDisabled) 204 *RealKey = *Handle; 205 else 206 *(PULONG_PTR)Handle |= 0x1; 207 } 208 209 RtlLeaveCriticalSection (&HandleTableCS); 210 211 return Status; 212 } 213 214 215 static VOID 216 CloseDefaultKeys(VOID) 217 { 218 ULONG i; 219 RegInitialize(); /* HACK until delay-loading is implemented */ 220 RtlEnterCriticalSection(&HandleTableCS); 221 222 for (i = 0; i < MAX_DEFAULT_HANDLES; i++) 223 { 224 if (DefaultHandleTable[i] != NULL) 225 { 226 NtClose(DefaultHandleTable[i]); 227 DefaultHandleTable[i] = NULL; 228 } 229 } 230 231 RtlLeaveCriticalSection(&HandleTableCS); 232 } 233 234 235 static NTSTATUS 236 OpenClassesRootKey(_Out_ PHANDLE KeyHandle) 237 { 238 OBJECT_ATTRIBUTES Attributes; 239 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\CLASSES"); 240 NTSTATUS Status; 241 242 TRACE("OpenClassesRootKey()\n"); 243 244 InitializeObjectAttributes(&Attributes, 245 &KeyName, 246 OBJ_CASE_INSENSITIVE, 247 NULL, 248 NULL); 249 Status = NtOpenKey(KeyHandle, 250 MAXIMUM_ALLOWED, 251 &Attributes); 252 253 if (!NT_SUCCESS(Status)) 254 return Status; 255 256 /* Mark it as HKCR */ 257 MakeHKCRKey((HKEY*)KeyHandle); 258 259 return Status; 260 } 261 262 263 static NTSTATUS 264 OpenLocalMachineKey(PHANDLE KeyHandle) 265 { 266 OBJECT_ATTRIBUTES Attributes; 267 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine"); 268 NTSTATUS Status; 269 270 TRACE("OpenLocalMachineKey()\n"); 271 272 InitializeObjectAttributes(&Attributes, 273 &KeyName, 274 OBJ_CASE_INSENSITIVE, 275 NULL, 276 NULL); 277 Status = NtOpenKey(KeyHandle, 278 MAXIMUM_ALLOWED, 279 &Attributes); 280 281 TRACE("NtOpenKey(%wZ) => %08x\n", &KeyName, Status); 282 283 return Status; 284 } 285 286 287 static NTSTATUS 288 OpenUsersKey(PHANDLE KeyHandle) 289 { 290 OBJECT_ATTRIBUTES Attributes; 291 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\User"); 292 293 TRACE("OpenUsersKey()\n"); 294 295 InitializeObjectAttributes(&Attributes, 296 &KeyName, 297 OBJ_CASE_INSENSITIVE, 298 NULL, 299 NULL); 300 return NtOpenKey(KeyHandle, 301 MAXIMUM_ALLOWED, 302 &Attributes); 303 } 304 305 306 static NTSTATUS 307 OpenCurrentConfigKey (PHANDLE KeyHandle) 308 { 309 OBJECT_ATTRIBUTES Attributes; 310 UNICODE_STRING KeyName = 311 RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current"); 312 313 TRACE("OpenCurrentConfigKey()\n"); 314 315 InitializeObjectAttributes(&Attributes, 316 &KeyName, 317 OBJ_CASE_INSENSITIVE, 318 NULL, 319 NULL); 320 return NtOpenKey(KeyHandle, 321 MAXIMUM_ALLOWED, 322 &Attributes); 323 } 324 325 326 /************************************************************************ 327 * RegDisablePredefinedCache 328 * 329 * @implemented 330 */ 331 LONG WINAPI 332 RegDisablePredefinedCache(VOID) 333 { 334 RegInitialize(); /* HACK until delay-loading is implemented */ 335 RtlEnterCriticalSection(&HandleTableCS); 336 DefaultHandleHKUDisabled = TRUE; 337 RtlLeaveCriticalSection(&HandleTableCS); 338 return ERROR_SUCCESS; 339 } 340 341 342 /************************************************************************ 343 * RegDisablePredefinedCacheEx 344 * 345 * @implemented 346 */ 347 LONG WINAPI 348 RegDisablePredefinedCacheEx(VOID) 349 { 350 RegInitialize(); /* HACK until delay-loading is implemented */ 351 RtlEnterCriticalSection(&HandleTableCS); 352 DefaultHandlesDisabled = TRUE; 353 DefaultHandleHKUDisabled = TRUE; 354 RtlLeaveCriticalSection(&HandleTableCS); 355 return ERROR_SUCCESS; 356 } 357 358 359 /************************************************************************ 360 * RegOverridePredefKey 361 * 362 * @implemented 363 */ 364 LONG WINAPI 365 RegOverridePredefKey(IN HKEY hKey, 366 IN HKEY hNewHKey OPTIONAL) 367 { 368 LONG ErrorCode = ERROR_SUCCESS; 369 370 if ((hKey == HKEY_CLASSES_ROOT || 371 hKey == HKEY_CURRENT_CONFIG || 372 hKey == HKEY_CURRENT_USER || 373 hKey == HKEY_LOCAL_MACHINE || 374 hKey == HKEY_PERFORMANCE_DATA || 375 hKey == HKEY_USERS) && 376 !IsPredefKey(hNewHKey)) 377 { 378 PHANDLE Handle; 379 ULONG Index; 380 381 Index = GetPredefKeyIndex(hKey); 382 Handle = &DefaultHandleTable[Index]; 383 384 if (hNewHKey == NULL) 385 { 386 /* restore the default mapping */ 387 NTSTATUS Status = OpenPredefinedKey(Index, 388 &hNewHKey); 389 if (!NT_SUCCESS(Status)) 390 { 391 return RtlNtStatusToDosError(Status); 392 } 393 394 ASSERT(hNewHKey != NULL); 395 } 396 RegInitialize(); /* HACK until delay-loading is implemented */ 397 RtlEnterCriticalSection(&HandleTableCS); 398 399 /* close the currently mapped handle if existing */ 400 if (*Handle != NULL) 401 { 402 NtClose(*Handle); 403 } 404 405 /* update the mapping */ 406 *Handle = hNewHKey; 407 408 RtlLeaveCriticalSection(&HandleTableCS); 409 } 410 else 411 ErrorCode = ERROR_INVALID_HANDLE; 412 413 return ErrorCode; 414 } 415 416 417 /************************************************************************ 418 * RegCloseKey 419 * 420 * @implemented 421 */ 422 LONG WINAPI 423 RegCloseKey(HKEY hKey) 424 { 425 NTSTATUS Status; 426 427 /* don't close null handle or a pseudo handle */ 428 if (!hKey) 429 { 430 return ERROR_INVALID_HANDLE; 431 } 432 433 if (((ULONG_PTR)hKey & 0xF0000000) == 0x80000000) 434 { 435 return ERROR_SUCCESS; 436 } 437 438 Status = NtClose(hKey); 439 if (!NT_SUCCESS(Status)) 440 { 441 return RtlNtStatusToDosError(Status); 442 } 443 444 return ERROR_SUCCESS; 445 } 446 447 448 static NTSTATUS 449 RegpCopyTree(IN HKEY hKeySrc, 450 IN HKEY hKeyDest) 451 { 452 typedef struct 453 { 454 LIST_ENTRY ListEntry; 455 HANDLE hKeySrc; 456 HANDLE hKeyDest; 457 } REGP_COPY_KEYS, *PREGP_COPY_KEYS; 458 459 LIST_ENTRY copyQueueHead; 460 PREGP_COPY_KEYS copyKeys, newCopyKeys; 461 union 462 { 463 KEY_VALUE_FULL_INFORMATION *KeyValue; 464 KEY_NODE_INFORMATION *KeyNode; 465 PVOID Buffer; 466 } Info; 467 ULONG Index, BufferSizeRequired, BufferSize = 0x200; 468 NTSTATUS Status = STATUS_SUCCESS; 469 NTSTATUS Status2 = STATUS_SUCCESS; 470 471 InitializeListHead(©QueueHead); 472 473 Info.Buffer = RtlAllocateHeap(ProcessHeap, 474 0, 475 BufferSize); 476 if (Info.Buffer == NULL) 477 { 478 return STATUS_INSUFFICIENT_RESOURCES; 479 } 480 481 copyKeys = RtlAllocateHeap(ProcessHeap, 482 0, 483 sizeof(REGP_COPY_KEYS)); 484 if (copyKeys != NULL) 485 { 486 copyKeys->hKeySrc = hKeySrc; 487 copyKeys->hKeyDest = hKeyDest; 488 InsertHeadList(©QueueHead, 489 ©Keys->ListEntry); 490 491 /* FIXME - copy security from hKeySrc to hKeyDest or just for the subkeys? */ 492 493 do 494 { 495 copyKeys = CONTAINING_RECORD(copyQueueHead.Flink, 496 REGP_COPY_KEYS, 497 ListEntry); 498 499 /* enumerate all values and copy them */ 500 Index = 0; 501 for (;;) 502 { 503 Status2 = NtEnumerateValueKey(copyKeys->hKeySrc, 504 Index, 505 KeyValueFullInformation, 506 Info.KeyValue, 507 BufferSize, 508 &BufferSizeRequired); 509 if (NT_SUCCESS(Status2)) 510 { 511 UNICODE_STRING ValueName; 512 PVOID Data; 513 514 /* don't use RtlInitUnicodeString as the string is not NULL-terminated! */ 515 ValueName.Length = Info.KeyValue->NameLength; 516 ValueName.MaximumLength = ValueName.Length; 517 ValueName.Buffer = Info.KeyValue->Name; 518 519 Data = (PVOID)((ULONG_PTR)Info.KeyValue + Info.KeyValue->DataOffset); 520 521 Status2 = NtSetValueKey(copyKeys->hKeyDest, 522 &ValueName, 523 Info.KeyValue->TitleIndex, 524 Info.KeyValue->Type, 525 Data, 526 Info.KeyValue->DataLength); 527 528 /* don't break, let's try to copy as many values as possible */ 529 if (!NT_SUCCESS(Status2) && NT_SUCCESS(Status)) 530 { 531 Status = Status2; 532 } 533 534 Index++; 535 } 536 else if (Status2 == STATUS_BUFFER_OVERFLOW) 537 { 538 PVOID Buffer; 539 540 ASSERT(BufferSize < BufferSizeRequired); 541 542 Buffer = RtlReAllocateHeap(ProcessHeap, 543 0, 544 Info.Buffer, 545 BufferSizeRequired); 546 if (Buffer != NULL) 547 { 548 Info.Buffer = Buffer; 549 /* try again */ 550 } 551 else 552 { 553 /* don't break, let's try to copy as many values as possible */ 554 Status2 = STATUS_INSUFFICIENT_RESOURCES; 555 Index++; 556 557 if (NT_SUCCESS(Status)) 558 { 559 Status = Status2; 560 } 561 } 562 } 563 else 564 { 565 /* break to avoid an infinite loop in case of denied access or 566 other errors! */ 567 if (Status2 != STATUS_NO_MORE_ENTRIES && NT_SUCCESS(Status)) 568 { 569 Status = Status2; 570 } 571 572 break; 573 } 574 } 575 576 /* enumerate all subkeys and open and enqueue them */ 577 Index = 0; 578 for (;;) 579 { 580 Status2 = NtEnumerateKey(copyKeys->hKeySrc, 581 Index, 582 KeyNodeInformation, 583 Info.KeyNode, 584 BufferSize, 585 &BufferSizeRequired); 586 if (NT_SUCCESS(Status2)) 587 { 588 HANDLE KeyHandle, NewKeyHandle; 589 OBJECT_ATTRIBUTES ObjectAttributes; 590 UNICODE_STRING SubKeyName, ClassName; 591 592 /* don't use RtlInitUnicodeString as the string is not NULL-terminated! */ 593 SubKeyName.Length = Info.KeyNode->NameLength; 594 SubKeyName.MaximumLength = SubKeyName.Length; 595 SubKeyName.Buffer = Info.KeyNode->Name; 596 ClassName.Length = Info.KeyNode->ClassLength; 597 ClassName.MaximumLength = ClassName.Length; 598 ClassName.Buffer = (PWSTR)((ULONG_PTR)Info.KeyNode + Info.KeyNode->ClassOffset); 599 600 /* open the subkey with sufficient rights */ 601 602 InitializeObjectAttributes(&ObjectAttributes, 603 &SubKeyName, 604 OBJ_CASE_INSENSITIVE, 605 copyKeys->hKeySrc, 606 NULL); 607 608 Status2 = NtOpenKey(&KeyHandle, 609 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 610 &ObjectAttributes); 611 if (NT_SUCCESS(Status2)) 612 { 613 /* FIXME - attempt to query the security information */ 614 615 InitializeObjectAttributes(&ObjectAttributes, 616 &SubKeyName, 617 OBJ_CASE_INSENSITIVE, 618 copyKeys->hKeyDest, 619 NULL); 620 621 Status2 = NtCreateKey(&NewKeyHandle, 622 KEY_ALL_ACCESS, 623 &ObjectAttributes, 624 Info.KeyNode->TitleIndex, 625 &ClassName, 626 0, 627 NULL); 628 if (NT_SUCCESS(Status2)) 629 { 630 newCopyKeys = RtlAllocateHeap(ProcessHeap, 631 0, 632 sizeof(REGP_COPY_KEYS)); 633 if (newCopyKeys != NULL) 634 { 635 /* save the handles and enqueue the subkey */ 636 newCopyKeys->hKeySrc = KeyHandle; 637 newCopyKeys->hKeyDest = NewKeyHandle; 638 InsertTailList(©QueueHead, 639 &newCopyKeys->ListEntry); 640 } 641 else 642 { 643 NtClose(KeyHandle); 644 NtClose(NewKeyHandle); 645 646 Status2 = STATUS_INSUFFICIENT_RESOURCES; 647 } 648 } 649 else 650 { 651 NtClose(KeyHandle); 652 } 653 } 654 655 if (!NT_SUCCESS(Status2) && NT_SUCCESS(Status)) 656 { 657 Status = Status2; 658 } 659 660 Index++; 661 } 662 else if (Status2 == STATUS_BUFFER_OVERFLOW) 663 { 664 PVOID Buffer; 665 666 ASSERT(BufferSize < BufferSizeRequired); 667 668 Buffer = RtlReAllocateHeap(ProcessHeap, 669 0, 670 Info.Buffer, 671 BufferSizeRequired); 672 if (Buffer != NULL) 673 { 674 Info.Buffer = Buffer; 675 /* try again */ 676 } 677 else 678 { 679 /* don't break, let's try to copy as many keys as possible */ 680 Status2 = STATUS_INSUFFICIENT_RESOURCES; 681 Index++; 682 683 if (NT_SUCCESS(Status)) 684 { 685 Status = Status2; 686 } 687 } 688 } 689 else 690 { 691 /* break to avoid an infinite loop in case of denied access or 692 other errors! */ 693 if (Status2 != STATUS_NO_MORE_ENTRIES && NT_SUCCESS(Status)) 694 { 695 Status = Status2; 696 } 697 698 break; 699 } 700 } 701 702 /* close the handles and remove the entry from the list */ 703 if (copyKeys->hKeySrc != hKeySrc) 704 { 705 NtClose(copyKeys->hKeySrc); 706 } 707 if (copyKeys->hKeyDest != hKeyDest) 708 { 709 NtClose(copyKeys->hKeyDest); 710 } 711 712 RemoveEntryList(©Keys->ListEntry); 713 714 RtlFreeHeap(ProcessHeap, 715 0, 716 copyKeys); 717 } while (!IsListEmpty(©QueueHead)); 718 } 719 else 720 Status = STATUS_INSUFFICIENT_RESOURCES; 721 722 RtlFreeHeap(ProcessHeap, 723 0, 724 Info.Buffer); 725 726 return Status; 727 } 728 729 730 /************************************************************************ 731 * RegCopyTreeW 732 * 733 * @implemented 734 */ 735 LONG WINAPI 736 RegCopyTreeW(IN HKEY hKeySrc, 737 IN LPCWSTR lpSubKey OPTIONAL, 738 IN HKEY hKeyDest) 739 { 740 HANDLE DestKeyHandle, KeyHandle, CurKey, SubKeyHandle = NULL; 741 NTSTATUS Status; 742 743 Status = MapDefaultKey(&KeyHandle, 744 hKeySrc); 745 if (!NT_SUCCESS(Status)) 746 { 747 return RtlNtStatusToDosError(Status); 748 } 749 750 Status = MapDefaultKey(&DestKeyHandle, 751 hKeyDest); 752 if (!NT_SUCCESS(Status)) 753 { 754 goto Cleanup2; 755 } 756 757 if (lpSubKey != NULL) 758 { 759 OBJECT_ATTRIBUTES ObjectAttributes; 760 UNICODE_STRING SubKeyName; 761 762 RtlInitUnicodeString(&SubKeyName, 763 (LPWSTR)lpSubKey); 764 765 InitializeObjectAttributes(&ObjectAttributes, 766 &SubKeyName, 767 OBJ_CASE_INSENSITIVE, 768 KeyHandle, 769 NULL); 770 771 Status = NtOpenKey(&SubKeyHandle, 772 KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, 773 &ObjectAttributes); 774 if (!NT_SUCCESS(Status)) 775 { 776 goto Cleanup; 777 } 778 779 CurKey = SubKeyHandle; 780 } 781 else 782 CurKey = KeyHandle; 783 784 Status = RegpCopyTree(CurKey, 785 hKeyDest); 786 787 if (SubKeyHandle != NULL) 788 { 789 NtClose(SubKeyHandle); 790 } 791 792 Cleanup: 793 ClosePredefKey(DestKeyHandle); 794 Cleanup2: 795 ClosePredefKey(KeyHandle); 796 797 if (!NT_SUCCESS(Status)) 798 { 799 return RtlNtStatusToDosError(Status); 800 } 801 802 return ERROR_SUCCESS; 803 } 804 805 806 /************************************************************************ 807 * RegCopyTreeA 808 * 809 * @implemented 810 */ 811 LONG WINAPI 812 RegCopyTreeA(IN HKEY hKeySrc, 813 IN LPCSTR lpSubKey OPTIONAL, 814 IN HKEY hKeyDest) 815 { 816 UNICODE_STRING SubKeyName = { 0, 0, NULL }; 817 LONG Ret; 818 819 if (lpSubKey != NULL && 820 !RtlCreateUnicodeStringFromAsciiz(&SubKeyName, 821 (LPSTR)lpSubKey)) 822 { 823 return ERROR_NOT_ENOUGH_MEMORY; 824 } 825 826 Ret = RegCopyTreeW(hKeySrc, 827 SubKeyName.Buffer, 828 hKeyDest); 829 830 RtlFreeUnicodeString(&SubKeyName); 831 832 return Ret; 833 } 834 835 836 /************************************************************************ 837 * RegConnectRegistryA 838 * 839 * @implemented 840 */ 841 LONG WINAPI 842 RegConnectRegistryA(IN LPCSTR lpMachineName, 843 IN HKEY hKey, 844 OUT PHKEY phkResult) 845 { 846 UNICODE_STRING MachineName = { 0, 0, NULL }; 847 LONG Ret; 848 849 if (lpMachineName != NULL && 850 !RtlCreateUnicodeStringFromAsciiz(&MachineName, 851 (LPSTR)lpMachineName)) 852 { 853 return ERROR_NOT_ENOUGH_MEMORY; 854 } 855 856 Ret = RegConnectRegistryW(MachineName.Buffer, 857 hKey, 858 phkResult); 859 860 RtlFreeUnicodeString(&MachineName); 861 862 return Ret; 863 } 864 865 866 /************************************************************************ 867 * RegConnectRegistryW 868 * 869 * @unimplemented 870 */ 871 LONG WINAPI 872 RegConnectRegistryW(LPCWSTR lpMachineName, 873 HKEY hKey, 874 PHKEY phkResult) 875 { 876 LONG ret; 877 878 TRACE("(%s,%p,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult); 879 880 if (!lpMachineName || !*lpMachineName) 881 { 882 /* Use the local machine name */ 883 ret = RegOpenKeyW( hKey, NULL, phkResult ); 884 } 885 else 886 { 887 WCHAR compName[MAX_COMPUTERNAME_LENGTH + 1]; 888 DWORD len = sizeof(compName) / sizeof(WCHAR); 889 890 /* MSDN says lpMachineName must start with \\ : not so */ 891 if( lpMachineName[0] == '\\' && lpMachineName[1] == '\\') 892 lpMachineName += 2; 893 894 if (GetComputerNameW(compName, &len)) 895 { 896 if (!_wcsicmp(lpMachineName, compName)) 897 ret = RegOpenKeyW(hKey, NULL, phkResult); 898 else 899 { 900 FIXME("Connect to %s is not supported.\n",debugstr_w(lpMachineName)); 901 ret = ERROR_BAD_NETPATH; 902 } 903 } 904 else 905 ret = GetLastError(); 906 } 907 908 return ret; 909 } 910 911 912 /************************************************************************ 913 * CreateNestedKey 914 * 915 * Create key and all necessary intermediate keys 916 */ 917 static NTSTATUS 918 CreateNestedKey(PHKEY KeyHandle, 919 POBJECT_ATTRIBUTES ObjectAttributes, 920 PUNICODE_STRING ClassString, 921 DWORD dwOptions, 922 REGSAM samDesired, 923 DWORD *lpdwDisposition) 924 { 925 OBJECT_ATTRIBUTES LocalObjectAttributes; 926 UNICODE_STRING LocalKeyName; 927 ULONG Disposition; 928 NTSTATUS Status; 929 ULONG FullNameLength; 930 ULONG Length; 931 PWCHAR Ptr; 932 HANDLE LocalKeyHandle; 933 934 Status = NtCreateKey((PHANDLE) KeyHandle, 935 samDesired, 936 ObjectAttributes, 937 0, 938 ClassString, 939 dwOptions, 940 (PULONG)lpdwDisposition); 941 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status); 942 if (Status != STATUS_OBJECT_NAME_NOT_FOUND) 943 return Status; 944 945 /* Copy object attributes */ 946 RtlCopyMemory(&LocalObjectAttributes, 947 ObjectAttributes, 948 sizeof(OBJECT_ATTRIBUTES)); 949 RtlCreateUnicodeString(&LocalKeyName, 950 ObjectAttributes->ObjectName->Buffer); 951 LocalObjectAttributes.ObjectName = &LocalKeyName; 952 FullNameLength = LocalKeyName.Length / sizeof(WCHAR); 953 954 LocalKeyHandle = NULL; 955 956 /* Remove the last part of the key name and try to create the key again. */ 957 while (Status == STATUS_OBJECT_NAME_NOT_FOUND) 958 { 959 Ptr = wcsrchr(LocalKeyName.Buffer, '\\'); 960 if (Ptr == NULL || Ptr == LocalKeyName.Buffer) 961 { 962 Status = STATUS_UNSUCCESSFUL; 963 break; 964 } 965 966 *Ptr = (WCHAR)0; 967 LocalKeyName.Length = wcslen(LocalKeyName.Buffer) * sizeof(WCHAR); 968 969 Status = NtCreateKey(&LocalKeyHandle, 970 KEY_CREATE_SUB_KEY, 971 &LocalObjectAttributes, 972 0, 973 NULL, 974 0, 975 &Disposition); 976 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status); 977 } 978 979 if (!NT_SUCCESS(Status)) 980 { 981 RtlFreeUnicodeString(&LocalKeyName); 982 return Status; 983 } 984 985 /* Add removed parts of the key name and create them too. */ 986 Length = wcslen(LocalKeyName.Buffer); 987 while (TRUE) 988 { 989 if (LocalKeyHandle) 990 NtClose (LocalKeyHandle); 991 992 LocalKeyName.Buffer[Length] = L'\\'; 993 Length = wcslen (LocalKeyName.Buffer); 994 LocalKeyName.Length = Length * sizeof(WCHAR); 995 996 if (Length == FullNameLength) 997 { 998 Status = NtCreateKey((PHANDLE) KeyHandle, 999 samDesired, 1000 ObjectAttributes, 1001 0, 1002 ClassString, 1003 dwOptions, 1004 (PULONG)lpdwDisposition); 1005 break; 1006 } 1007 1008 Status = NtCreateKey(&LocalKeyHandle, 1009 KEY_CREATE_SUB_KEY, 1010 &LocalObjectAttributes, 1011 0, 1012 NULL, 1013 0, 1014 &Disposition); 1015 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status); 1016 if (!NT_SUCCESS(Status)) 1017 break; 1018 } 1019 1020 RtlFreeUnicodeString(&LocalKeyName); 1021 1022 return Status; 1023 } 1024 1025 1026 /************************************************************************ 1027 * RegCreateKeyExA 1028 * 1029 * @implemented 1030 */ 1031 LONG WINAPI 1032 RegCreateKeyExA( 1033 _In_ HKEY hKey, 1034 _In_ LPCSTR lpSubKey, 1035 _In_ DWORD Reserved, 1036 _In_ LPSTR lpClass, 1037 _In_ DWORD dwOptions, 1038 _In_ REGSAM samDesired, 1039 _In_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, 1040 _Out_ PHKEY phkResult, 1041 _Out_ LPDWORD lpdwDisposition) 1042 { 1043 UNICODE_STRING SubKeyString; 1044 UNICODE_STRING ClassString; 1045 DWORD ErrorCode; 1046 1047 RtlInitEmptyUnicodeString(&ClassString, NULL, 0); 1048 RtlInitEmptyUnicodeString(&SubKeyString, NULL, 0); 1049 1050 if (lpClass) 1051 { 1052 if (!RtlCreateUnicodeStringFromAsciiz(&ClassString, lpClass)) 1053 { 1054 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1055 goto Exit; 1056 } 1057 } 1058 1059 if (lpSubKey) 1060 { 1061 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyString, lpSubKey)) 1062 { 1063 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1064 goto Exit; 1065 } 1066 } 1067 1068 ErrorCode = RegCreateKeyExW( 1069 hKey, 1070 SubKeyString.Buffer, 1071 Reserved, 1072 ClassString.Buffer, 1073 dwOptions, 1074 samDesired, 1075 lpSecurityAttributes, 1076 phkResult, 1077 lpdwDisposition); 1078 1079 Exit: 1080 RtlFreeUnicodeString(&SubKeyString); 1081 RtlFreeUnicodeString(&ClassString); 1082 1083 return ErrorCode; 1084 } 1085 1086 1087 /************************************************************************ 1088 * RegCreateKeyExW 1089 * 1090 * @implemented 1091 */ 1092 LONG 1093 WINAPI 1094 RegCreateKeyExW( 1095 _In_ HKEY hKey, 1096 _In_ LPCWSTR lpSubKey, 1097 _In_ DWORD Reserved, 1098 _In_opt_ LPWSTR lpClass, 1099 _In_ DWORD dwOptions, 1100 _In_ REGSAM samDesired, 1101 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, 1102 _Out_ PHKEY phkResult, 1103 _Out_opt_ LPDWORD lpdwDisposition) 1104 { 1105 UNICODE_STRING SubKeyString; 1106 UNICODE_STRING ClassString; 1107 OBJECT_ATTRIBUTES ObjectAttributes; 1108 HANDLE ParentKey; 1109 ULONG Attributes = OBJ_CASE_INSENSITIVE; 1110 NTSTATUS Status; 1111 1112 TRACE("RegCreateKeyExW() called\n"); 1113 1114 if (lpSecurityAttributes && lpSecurityAttributes->nLength != sizeof(SECURITY_ATTRIBUTES)) 1115 return ERROR_INVALID_USER_BUFFER; 1116 1117 /* get the real parent key */ 1118 Status = MapDefaultKey(&ParentKey, 1119 hKey); 1120 if (!NT_SUCCESS(Status)) 1121 { 1122 return RtlNtStatusToDosError(Status); 1123 } 1124 1125 TRACE("ParentKey %p\n", ParentKey); 1126 1127 if (IsHKCRKey(ParentKey)) 1128 { 1129 LONG ErrorCode = CreateHKCRKey( 1130 ParentKey, 1131 lpSubKey, 1132 Reserved, 1133 lpClass, 1134 dwOptions, 1135 samDesired, 1136 lpSecurityAttributes, 1137 phkResult, 1138 lpdwDisposition); 1139 ClosePredefKey(ParentKey); 1140 return ErrorCode; 1141 } 1142 1143 if (dwOptions & REG_OPTION_OPEN_LINK) 1144 Attributes |= OBJ_OPENLINK; 1145 1146 RtlInitUnicodeString(&ClassString, 1147 lpClass); 1148 RtlInitUnicodeString(&SubKeyString, 1149 lpSubKey); 1150 InitializeObjectAttributes(&ObjectAttributes, 1151 &SubKeyString, 1152 Attributes, 1153 (HANDLE)ParentKey, 1154 lpSecurityAttributes ? (PSECURITY_DESCRIPTOR)lpSecurityAttributes->lpSecurityDescriptor : NULL); 1155 Status = CreateNestedKey(phkResult, 1156 &ObjectAttributes, 1157 (lpClass == NULL)? NULL : &ClassString, 1158 dwOptions, 1159 samDesired, 1160 lpdwDisposition); 1161 1162 ClosePredefKey(ParentKey); 1163 1164 TRACE("Status %x\n", Status); 1165 if (!NT_SUCCESS(Status)) 1166 { 1167 return RtlNtStatusToDosError(Status); 1168 } 1169 1170 return ERROR_SUCCESS; 1171 } 1172 1173 1174 /************************************************************************ 1175 * RegCreateKeyA 1176 * 1177 * @implemented 1178 */ 1179 LONG WINAPI 1180 RegCreateKeyA(HKEY hKey, 1181 LPCSTR lpSubKey, 1182 PHKEY phkResult) 1183 { 1184 return RegCreateKeyExA(hKey, 1185 lpSubKey, 1186 0, 1187 NULL, 1188 0, 1189 MAXIMUM_ALLOWED, 1190 NULL, 1191 phkResult, 1192 NULL); 1193 } 1194 1195 1196 /************************************************************************ 1197 * RegCreateKeyW 1198 * 1199 * @implemented 1200 */ 1201 LONG WINAPI 1202 RegCreateKeyW(HKEY hKey, 1203 LPCWSTR lpSubKey, 1204 PHKEY phkResult) 1205 { 1206 return RegCreateKeyExW(hKey, 1207 lpSubKey, 1208 0, 1209 NULL, 1210 0, 1211 MAXIMUM_ALLOWED, 1212 NULL, 1213 phkResult, 1214 NULL); 1215 } 1216 1217 1218 /************************************************************************ 1219 * RegDeleteKeyA 1220 * 1221 * @implemented 1222 */ 1223 LONG 1224 WINAPI 1225 RegDeleteKeyA( 1226 _In_ HKEY hKey, 1227 _In_ LPCSTR lpSubKey) 1228 { 1229 return RegDeleteKeyExA(hKey, lpSubKey, 0, 0); 1230 } 1231 1232 1233 /************************************************************************ 1234 * RegDeleteKeyW 1235 * 1236 * @implemented 1237 */ 1238 LONG 1239 WINAPI 1240 RegDeleteKeyW( 1241 _In_ HKEY hKey, 1242 _In_ LPCWSTR lpSubKey) 1243 { 1244 return RegDeleteKeyExW(hKey, lpSubKey, 0, 0); 1245 } 1246 1247 1248 /************************************************************************ 1249 * RegDeleteKeyExA 1250 * 1251 * @implemented 1252 */ 1253 LONG 1254 WINAPI 1255 RegDeleteKeyExA( 1256 _In_ HKEY hKey, 1257 _In_ LPCSTR lpSubKey, 1258 _In_ REGSAM samDesired, 1259 _In_ DWORD Reserved) 1260 { 1261 LONG ErrorCode; 1262 UNICODE_STRING SubKeyName; 1263 1264 if (lpSubKey) 1265 { 1266 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyName, lpSubKey)) 1267 return ERROR_NOT_ENOUGH_MEMORY; 1268 } 1269 else 1270 RtlInitEmptyUnicodeString(&SubKeyName, NULL, 0); 1271 1272 ErrorCode = RegDeleteKeyExW(hKey, SubKeyName.Buffer, samDesired, Reserved); 1273 1274 RtlFreeUnicodeString(&SubKeyName); 1275 1276 return ErrorCode; 1277 } 1278 1279 1280 /************************************************************************ 1281 * RegDeleteKeyExW 1282 * 1283 * @implemented 1284 */ 1285 LONG 1286 WINAPI 1287 RegDeleteKeyExW( 1288 _In_ HKEY hKey, 1289 _In_ LPCWSTR lpSubKey, 1290 _In_ REGSAM samDesired, 1291 _In_ DWORD Reserved) 1292 { 1293 OBJECT_ATTRIBUTES ObjectAttributes; 1294 UNICODE_STRING SubKeyName; 1295 HANDLE ParentKey; 1296 HANDLE TargetKey; 1297 NTSTATUS Status; 1298 1299 /* Make sure we got a subkey */ 1300 if (!lpSubKey) 1301 { 1302 /* Fail */ 1303 return ERROR_INVALID_PARAMETER; 1304 } 1305 1306 Status = MapDefaultKey(&ParentKey, 1307 hKey); 1308 if (!NT_SUCCESS(Status)) 1309 { 1310 return RtlNtStatusToDosError(Status); 1311 } 1312 1313 if (IsHKCRKey(ParentKey)) 1314 { 1315 LONG ErrorCode = DeleteHKCRKey(ParentKey, lpSubKey, samDesired, Reserved); 1316 ClosePredefKey(ParentKey); 1317 return ErrorCode; 1318 } 1319 1320 if (samDesired & KEY_WOW64_32KEY) 1321 ERR("Wow64 not yet supported!\n"); 1322 1323 if (samDesired & KEY_WOW64_64KEY) 1324 ERR("Wow64 not yet supported!\n"); 1325 1326 1327 RtlInitUnicodeString(&SubKeyName, 1328 (LPWSTR)lpSubKey); 1329 InitializeObjectAttributes(&ObjectAttributes, 1330 &SubKeyName, 1331 OBJ_CASE_INSENSITIVE, 1332 ParentKey, 1333 NULL); 1334 Status = NtOpenKey(&TargetKey, 1335 DELETE, 1336 &ObjectAttributes); 1337 if (!NT_SUCCESS(Status)) 1338 { 1339 goto Cleanup; 1340 } 1341 1342 Status = NtDeleteKey(TargetKey); 1343 NtClose(TargetKey); 1344 1345 Cleanup: 1346 ClosePredefKey(ParentKey); 1347 1348 if (!NT_SUCCESS(Status)) 1349 { 1350 return RtlNtStatusToDosError(Status); 1351 } 1352 1353 return ERROR_SUCCESS; 1354 } 1355 1356 1357 /************************************************************************ 1358 * RegDeleteKeyValueW 1359 * 1360 * @implemented 1361 */ 1362 LONG WINAPI 1363 RegDeleteKeyValueW(IN HKEY hKey, 1364 IN LPCWSTR lpSubKey OPTIONAL, 1365 IN LPCWSTR lpValueName OPTIONAL) 1366 { 1367 UNICODE_STRING ValueName; 1368 HANDLE KeyHandle, CurKey, SubKeyHandle = NULL; 1369 NTSTATUS Status; 1370 1371 Status = MapDefaultKey(&KeyHandle, 1372 hKey); 1373 if (!NT_SUCCESS(Status)) 1374 { 1375 return RtlNtStatusToDosError(Status); 1376 } 1377 1378 if (lpSubKey != NULL) 1379 { 1380 OBJECT_ATTRIBUTES ObjectAttributes; 1381 UNICODE_STRING SubKeyName; 1382 1383 RtlInitUnicodeString(&SubKeyName, 1384 (LPWSTR)lpSubKey); 1385 1386 InitializeObjectAttributes(&ObjectAttributes, 1387 &SubKeyName, 1388 OBJ_CASE_INSENSITIVE, 1389 KeyHandle, 1390 NULL); 1391 1392 Status = NtOpenKey(&SubKeyHandle, 1393 KEY_SET_VALUE, 1394 &ObjectAttributes); 1395 if (!NT_SUCCESS(Status)) 1396 { 1397 goto Cleanup; 1398 } 1399 1400 CurKey = SubKeyHandle; 1401 } 1402 else 1403 CurKey = KeyHandle; 1404 1405 RtlInitUnicodeString(&ValueName, 1406 (LPWSTR)lpValueName); 1407 1408 Status = NtDeleteValueKey(CurKey, 1409 &ValueName); 1410 1411 if (SubKeyHandle != NULL) 1412 { 1413 NtClose(SubKeyHandle); 1414 } 1415 1416 Cleanup: 1417 ClosePredefKey(KeyHandle); 1418 1419 if (!NT_SUCCESS(Status)) 1420 { 1421 return RtlNtStatusToDosError(Status); 1422 } 1423 1424 return ERROR_SUCCESS; 1425 } 1426 1427 1428 /************************************************************************ 1429 * RegDeleteKeyValueA 1430 * 1431 * @implemented 1432 */ 1433 LONG WINAPI 1434 RegDeleteKeyValueA(IN HKEY hKey, 1435 IN LPCSTR lpSubKey OPTIONAL, 1436 IN LPCSTR lpValueName OPTIONAL) 1437 { 1438 UNICODE_STRING SubKey = { 0, 0, NULL }, ValueName = { 0, 0, NULL }; 1439 LONG Ret; 1440 1441 if (lpSubKey != NULL && 1442 !RtlCreateUnicodeStringFromAsciiz(&SubKey, 1443 (LPSTR)lpSubKey)) 1444 { 1445 return ERROR_NOT_ENOUGH_MEMORY; 1446 } 1447 1448 if (lpValueName != NULL && 1449 !RtlCreateUnicodeStringFromAsciiz(&ValueName, 1450 (LPSTR)lpValueName)) 1451 { 1452 RtlFreeUnicodeString(&SubKey); 1453 return ERROR_NOT_ENOUGH_MEMORY; 1454 } 1455 1456 Ret = RegDeleteKeyValueW(hKey, 1457 SubKey.Buffer, 1458 SubKey.Buffer); 1459 1460 RtlFreeUnicodeString(&SubKey); 1461 RtlFreeUnicodeString(&ValueName); 1462 1463 return Ret; 1464 } 1465 1466 #if 0 1467 // Non-recursive RegDeleteTreeW implementation by Thomas, however it needs bugfixing 1468 static NTSTATUS 1469 RegpDeleteTree(IN HKEY hKey) 1470 { 1471 typedef struct 1472 { 1473 LIST_ENTRY ListEntry; 1474 HANDLE KeyHandle; 1475 } REGP_DEL_KEYS, *PREG_DEL_KEYS; 1476 1477 LIST_ENTRY delQueueHead; 1478 PREG_DEL_KEYS delKeys, newDelKeys; 1479 HANDLE ProcessHeap; 1480 ULONG BufferSize; 1481 PKEY_BASIC_INFORMATION BasicInfo; 1482 PREG_DEL_KEYS KeyDelRoot; 1483 NTSTATUS Status = STATUS_SUCCESS; 1484 NTSTATUS Status2 = STATUS_SUCCESS; 1485 1486 InitializeListHead(&delQueueHead); 1487 1488 ProcessHeap = RtlGetProcessHeap(); 1489 1490 /* NOTE: no need to allocate enough memory for an additional KEY_BASIC_INFORMATION 1491 structure for the root key, we only do that for subkeys as we need to 1492 allocate REGP_DEL_KEYS structures anyway! */ 1493 KeyDelRoot = RtlAllocateHeap(ProcessHeap, 1494 0, 1495 sizeof(REGP_DEL_KEYS)); 1496 if (KeyDelRoot != NULL) 1497 { 1498 KeyDelRoot->KeyHandle = hKey; 1499 InsertTailList(&delQueueHead, 1500 &KeyDelRoot->ListEntry); 1501 1502 do 1503 { 1504 delKeys = CONTAINING_RECORD(delQueueHead.Flink, 1505 REGP_DEL_KEYS, 1506 ListEntry); 1507 1508 BufferSize = 0; 1509 BasicInfo = NULL; 1510 newDelKeys = NULL; 1511 1512 ReadFirstSubKey: 1513 /* check if this key contains subkeys and delete them first by queuing 1514 them at the head of the list */ 1515 Status2 = NtEnumerateKey(delKeys->KeyHandle, 1516 0, 1517 KeyBasicInformation, 1518 BasicInfo, 1519 BufferSize, 1520 &BufferSize); 1521 1522 if (NT_SUCCESS(Status2)) 1523 { 1524 OBJECT_ATTRIBUTES ObjectAttributes; 1525 UNICODE_STRING SubKeyName; 1526 1527 ASSERT(newDelKeys != NULL); 1528 ASSERT(BasicInfo != NULL); 1529 1530 /* don't use RtlInitUnicodeString as the string is not NULL-terminated! */ 1531 SubKeyName.Length = BasicInfo->NameLength; 1532 SubKeyName.MaximumLength = BasicInfo->NameLength; 1533 SubKeyName.Buffer = BasicInfo->Name; 1534 1535 InitializeObjectAttributes(&ObjectAttributes, 1536 &SubKeyName, 1537 OBJ_CASE_INSENSITIVE, 1538 delKeys->KeyHandle, 1539 NULL); 1540 1541 /* open the subkey */ 1542 Status2 = NtOpenKey(&newDelKeys->KeyHandle, 1543 DELETE | KEY_ENUMERATE_SUB_KEYS, 1544 &ObjectAttributes); 1545 if (!NT_SUCCESS(Status2)) 1546 { 1547 goto SubKeyFailure; 1548 } 1549 1550 /* enqueue this key to the head of the deletion queue */ 1551 InsertHeadList(&delQueueHead, 1552 &newDelKeys->ListEntry); 1553 1554 /* try again from the head of the list */ 1555 continue; 1556 } 1557 else 1558 { 1559 if (Status2 == STATUS_BUFFER_TOO_SMALL) 1560 { 1561 newDelKeys = RtlAllocateHeap(ProcessHeap, 1562 0, 1563 BufferSize + sizeof(REGP_DEL_KEYS)); 1564 if (newDelKeys != NULL) 1565 { 1566 BasicInfo = (PKEY_BASIC_INFORMATION)(newDelKeys + 1); 1567 1568 /* try again */ 1569 goto ReadFirstSubKey; 1570 } 1571 else 1572 { 1573 /* don't break, let's try to delete as many keys as possible */ 1574 Status2 = STATUS_INSUFFICIENT_RESOURCES; 1575 goto SubKeyFailureNoFree; 1576 } 1577 } 1578 else if (Status2 == STATUS_BUFFER_OVERFLOW) 1579 { 1580 PREG_DEL_KEYS newDelKeys2; 1581 1582 ASSERT(newDelKeys != NULL); 1583 1584 /* we need more memory to query the key name */ 1585 newDelKeys2 = RtlReAllocateHeap(ProcessHeap, 1586 0, 1587 newDelKeys, 1588 BufferSize + sizeof(REGP_DEL_KEYS)); 1589 if (newDelKeys2 != NULL) 1590 { 1591 newDelKeys = newDelKeys2; 1592 BasicInfo = (PKEY_BASIC_INFORMATION)(newDelKeys + 1); 1593 1594 /* try again */ 1595 goto ReadFirstSubKey; 1596 } 1597 else 1598 { 1599 /* don't break, let's try to delete as many keys as possible */ 1600 Status2 = STATUS_INSUFFICIENT_RESOURCES; 1601 } 1602 } 1603 else if (Status2 == STATUS_NO_MORE_ENTRIES) 1604 { 1605 /* in some race conditions where another thread would delete 1606 the same tree at the same time, newDelKeys could actually 1607 be != NULL! */ 1608 if (newDelKeys != NULL) 1609 { 1610 RtlFreeHeap(ProcessHeap, 1611 0, 1612 newDelKeys); 1613 } 1614 break; 1615 } 1616 1617 SubKeyFailure: 1618 /* newDelKeys can be NULL here when NtEnumerateKey returned an 1619 error other than STATUS_BUFFER_TOO_SMALL or STATUS_BUFFER_OVERFLOW! */ 1620 if (newDelKeys != NULL) 1621 { 1622 RtlFreeHeap(ProcessHeap, 1623 0, 1624 newDelKeys); 1625 } 1626 1627 SubKeyFailureNoFree: 1628 /* don't break, let's try to delete as many keys as possible */ 1629 if (NT_SUCCESS(Status)) 1630 { 1631 Status = Status2; 1632 } 1633 } 1634 1635 Status2 = NtDeleteKey(delKeys->KeyHandle); 1636 1637 /* NOTE: do NOT close the handle anymore, it's invalid already! */ 1638 1639 if (!NT_SUCCESS(Status2)) 1640 { 1641 /* close the key handle so we don't leak handles for keys we were 1642 unable to delete. But only do this for handles not supplied 1643 by the caller! */ 1644 1645 if (delKeys->KeyHandle != hKey) 1646 { 1647 NtClose(delKeys->KeyHandle); 1648 } 1649 1650 if (NT_SUCCESS(Status)) 1651 { 1652 /* don't break, let's try to delete as many keys as possible */ 1653 Status = Status2; 1654 } 1655 } 1656 1657 /* remove the entry from the list */ 1658 RemoveEntryList(&delKeys->ListEntry); 1659 1660 RtlFreeHeap(ProcessHeap, 1661 0, 1662 delKeys); 1663 } while (!IsListEmpty(&delQueueHead)); 1664 } 1665 else 1666 Status = STATUS_INSUFFICIENT_RESOURCES; 1667 1668 return Status; 1669 } 1670 1671 1672 /************************************************************************ 1673 * RegDeleteTreeW 1674 * 1675 * @implemented 1676 */ 1677 LONG WINAPI 1678 RegDeleteTreeW(IN HKEY hKey, 1679 IN LPCWSTR lpSubKey OPTIONAL) 1680 { 1681 HANDLE KeyHandle, CurKey, SubKeyHandle = NULL; 1682 NTSTATUS Status; 1683 1684 Status = MapDefaultKey(&KeyHandle, 1685 hKey); 1686 if (!NT_SUCCESS(Status)) 1687 { 1688 return RtlNtStatusToDosError(Status); 1689 } 1690 1691 if (lpSubKey != NULL) 1692 { 1693 OBJECT_ATTRIBUTES ObjectAttributes; 1694 UNICODE_STRING SubKeyName; 1695 1696 RtlInitUnicodeString(&SubKeyName, 1697 (LPWSTR)lpSubKey); 1698 1699 InitializeObjectAttributes(&ObjectAttributes, 1700 &SubKeyName, 1701 OBJ_CASE_INSENSITIVE, 1702 KeyHandle, 1703 NULL); 1704 1705 Status = NtOpenKey(&SubKeyHandle, 1706 DELETE | KEY_ENUMERATE_SUB_KEYS, 1707 &ObjectAttributes); 1708 if (!NT_SUCCESS(Status)) 1709 { 1710 goto Cleanup; 1711 } 1712 1713 CurKey = SubKeyHandle; 1714 } 1715 else 1716 CurKey = KeyHandle; 1717 1718 Status = RegpDeleteTree(CurKey); 1719 1720 if (NT_SUCCESS(Status)) 1721 { 1722 /* make sure we only close hKey (KeyHandle) when the caller specified a 1723 subkey, because the handle would be invalid already! */ 1724 if (CurKey != KeyHandle) 1725 { 1726 ClosePredefKey(KeyHandle); 1727 } 1728 1729 return ERROR_SUCCESS; 1730 } 1731 else 1732 { 1733 /* make sure we close all handles we created! */ 1734 if (SubKeyHandle != NULL) 1735 { 1736 NtClose(SubKeyHandle); 1737 } 1738 1739 Cleanup: 1740 ClosePredefKey(KeyHandle); 1741 1742 return RtlNtStatusToDosError(Status); 1743 } 1744 } 1745 #endif 1746 1747 1748 /************************************************************************ 1749 * RegDeleteTreeW 1750 * 1751 * @implemented 1752 */ 1753 LSTATUS 1754 WINAPI 1755 RegDeleteTreeW(HKEY hKey, 1756 LPCWSTR lpszSubKey) 1757 { 1758 LONG ret; 1759 DWORD dwMaxSubkeyLen, dwMaxValueLen; 1760 DWORD dwMaxLen, dwSize; 1761 NTSTATUS Status; 1762 HANDLE KeyHandle; 1763 HKEY hSubKey; 1764 WCHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf; 1765 1766 TRACE("(hkey=%p,%p %s)\n", hKey, lpszSubKey, debugstr_w(lpszSubKey)); 1767 1768 Status = MapDefaultKey(&KeyHandle, 1769 hKey); 1770 if (!NT_SUCCESS(Status)) 1771 { 1772 return RtlNtStatusToDosError(Status); 1773 } 1774 1775 hSubKey = KeyHandle; 1776 1777 if(lpszSubKey) 1778 { 1779 ret = RegOpenKeyExW(KeyHandle, lpszSubKey, 0, KEY_READ, &hSubKey); 1780 if (ret) 1781 { 1782 ClosePredefKey(KeyHandle); 1783 return ret; 1784 } 1785 } 1786 1787 /* Get highest length for keys, values */ 1788 ret = RegQueryInfoKeyW(hSubKey, NULL, NULL, NULL, NULL, 1789 &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL); 1790 if (ret) goto cleanup; 1791 1792 dwMaxSubkeyLen++; 1793 dwMaxValueLen++; 1794 dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen); 1795 if (dwMaxLen > sizeof(szNameBuf)/sizeof(WCHAR)) 1796 { 1797 /* Name too big: alloc a buffer for it */ 1798 if (!(lpszName = RtlAllocateHeap( RtlGetProcessHeap(), 0, dwMaxLen*sizeof(WCHAR)))) 1799 { 1800 ret = ERROR_NOT_ENOUGH_MEMORY; 1801 goto cleanup; 1802 } 1803 } 1804 1805 1806 /* Recursively delete all the subkeys */ 1807 while (TRUE) 1808 { 1809 dwSize = dwMaxLen; 1810 if (RegEnumKeyExW(hSubKey, 0, lpszName, &dwSize, NULL, 1811 NULL, NULL, NULL)) break; 1812 1813 ret = RegDeleteTreeW(hSubKey, lpszName); 1814 if (ret) goto cleanup; 1815 } 1816 1817 if (lpszSubKey) 1818 ret = RegDeleteKeyW(KeyHandle, lpszSubKey); 1819 else 1820 while (TRUE) 1821 { 1822 dwSize = dwMaxLen; 1823 if (RegEnumValueW(KeyHandle, 0, lpszName, &dwSize, 1824 NULL, NULL, NULL, NULL)) break; 1825 1826 ret = RegDeleteValueW(KeyHandle, lpszName); 1827 if (ret) goto cleanup; 1828 } 1829 1830 cleanup: 1831 /* Free buffer if allocated */ 1832 if (lpszName != szNameBuf) 1833 RtlFreeHeap( RtlGetProcessHeap(), 0, lpszName); 1834 if(lpszSubKey) 1835 RegCloseKey(hSubKey); 1836 1837 ClosePredefKey(KeyHandle); 1838 1839 return ret; 1840 } 1841 1842 1843 /************************************************************************ 1844 * RegDeleteTreeA 1845 * 1846 * @implemented 1847 */ 1848 LONG WINAPI 1849 RegDeleteTreeA(IN HKEY hKey, 1850 IN LPCSTR lpSubKey OPTIONAL) 1851 { 1852 UNICODE_STRING SubKeyName = { 0, 0, NULL }; 1853 LONG Ret; 1854 1855 if (lpSubKey != NULL && 1856 !RtlCreateUnicodeStringFromAsciiz(&SubKeyName, 1857 (LPSTR)lpSubKey)) 1858 { 1859 return ERROR_NOT_ENOUGH_MEMORY; 1860 } 1861 1862 Ret = RegDeleteTreeW(hKey, 1863 SubKeyName.Buffer); 1864 1865 RtlFreeUnicodeString(&SubKeyName); 1866 1867 return Ret; 1868 } 1869 1870 1871 /************************************************************************ 1872 * RegDisableReflectionKey 1873 * 1874 * @unimplemented 1875 */ 1876 LONG WINAPI 1877 RegDisableReflectionKey(IN HKEY hBase) 1878 { 1879 FIXME("RegDisableReflectionKey(0x%p) UNIMPLEMENTED!\n", hBase); 1880 return ERROR_CALL_NOT_IMPLEMENTED; 1881 } 1882 1883 1884 /************************************************************************ 1885 * RegEnableReflectionKey 1886 * 1887 * @unimplemented 1888 */ 1889 LONG WINAPI 1890 RegEnableReflectionKey(IN HKEY hBase) 1891 { 1892 FIXME("RegEnableReflectionKey(0x%p) UNIMPLEMENTED!\n", hBase); 1893 return ERROR_CALL_NOT_IMPLEMENTED; 1894 } 1895 1896 1897 /****************************************************************************** 1898 * RegpApplyRestrictions [internal] 1899 * 1900 * Helper function for RegGetValueA/W. 1901 */ 1902 static VOID 1903 RegpApplyRestrictions(DWORD dwFlags, 1904 DWORD dwType, 1905 DWORD cbData, 1906 PLONG ret) 1907 { 1908 /* Check if the type is restricted by the passed flags */ 1909 if (*ret == ERROR_SUCCESS || *ret == ERROR_MORE_DATA) 1910 { 1911 DWORD dwMask = 0; 1912 1913 switch (dwType) 1914 { 1915 case REG_NONE: dwMask = RRF_RT_REG_NONE; break; 1916 case REG_SZ: dwMask = RRF_RT_REG_SZ; break; 1917 case REG_EXPAND_SZ: dwMask = RRF_RT_REG_EXPAND_SZ; break; 1918 case REG_MULTI_SZ: dwMask = RRF_RT_REG_MULTI_SZ; break; 1919 case REG_BINARY: dwMask = RRF_RT_REG_BINARY; break; 1920 case REG_DWORD: dwMask = RRF_RT_REG_DWORD; break; 1921 case REG_QWORD: dwMask = RRF_RT_REG_QWORD; break; 1922 } 1923 1924 if (dwFlags & dwMask) 1925 { 1926 /* Type is not restricted, check for size mismatch */ 1927 if (dwType == REG_BINARY) 1928 { 1929 DWORD cbExpect = 0; 1930 1931 if ((dwFlags & RRF_RT_ANY) == RRF_RT_DWORD) 1932 cbExpect = 4; 1933 else if ((dwFlags & RRF_RT_ANY) == RRF_RT_QWORD) 1934 cbExpect = 8; 1935 1936 if (cbExpect && cbData != cbExpect) 1937 *ret = ERROR_DATATYPE_MISMATCH; 1938 } 1939 } 1940 else *ret = ERROR_UNSUPPORTED_TYPE; 1941 } 1942 } 1943 1944 1945 /****************************************************************************** 1946 * RegGetValueW [ADVAPI32.@] 1947 * 1948 * Retrieves the type and data for a value name associated with a key, 1949 * optionally expanding its content and restricting its type. 1950 * 1951 * PARAMS 1952 * hKey [I] Handle to an open key. 1953 * pszSubKey [I] Name of the subkey of hKey. 1954 * pszValue [I] Name of value under hKey/szSubKey to query. 1955 * dwFlags [I] Flags restricting the value type to retrieve. 1956 * pdwType [O] Destination for the values type, may be NULL. 1957 * pvData [O] Destination for the values content, may be NULL. 1958 * pcbData [I/O] Size of pvData, updated with the size in bytes required to 1959 * retrieve the whole content, including the trailing '\0' 1960 * for strings. 1961 * 1962 * RETURNS 1963 * Success: ERROR_SUCCESS 1964 * Failure: nonzero error code from Winerror.h 1965 * 1966 * NOTES 1967 * - Unless RRF_NOEXPAND is specified, REG_EXPAND_SZ values are automatically 1968 * expanded and pdwType is set to REG_SZ instead. 1969 * - Restrictions are applied after expanding, using RRF_RT_REG_EXPAND_SZ 1970 * without RRF_NOEXPAND is thus not allowed. 1971 * An exception is the case where RRF_RT_ANY is specified, because then 1972 * RRF_NOEXPAND is allowed. 1973 */ 1974 LSTATUS WINAPI 1975 RegGetValueW(HKEY hKey, 1976 LPCWSTR pszSubKey, 1977 LPCWSTR pszValue, 1978 DWORD dwFlags, 1979 LPDWORD pdwType, 1980 PVOID pvData, 1981 LPDWORD pcbData) 1982 { 1983 DWORD dwType, cbData = pcbData ? *pcbData : 0; 1984 PVOID pvBuf = NULL; 1985 LONG ret; 1986 1987 TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", 1988 hKey, debugstr_w(pszSubKey), debugstr_w(pszValue), dwFlags, pdwType, 1989 pvData, pcbData, cbData); 1990 1991 if (pvData && !pcbData) 1992 return ERROR_INVALID_PARAMETER; 1993 if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND) && 1994 ((dwFlags & RRF_RT_ANY) != RRF_RT_ANY)) 1995 return ERROR_INVALID_PARAMETER; 1996 1997 if (pszSubKey && pszSubKey[0]) 1998 { 1999 ret = RegOpenKeyExW(hKey, pszSubKey, 0, KEY_QUERY_VALUE, &hKey); 2000 if (ret != ERROR_SUCCESS) return ret; 2001 } 2002 2003 ret = RegQueryValueExW(hKey, pszValue, NULL, &dwType, pvData, &cbData); 2004 2005 /* If we are going to expand we need to read in the whole the value even 2006 * if the passed buffer was too small as the expanded string might be 2007 * smaller than the unexpanded one and could fit into cbData bytes. */ 2008 if ((ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && 2009 dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND)) 2010 { 2011 do 2012 { 2013 HeapFree(GetProcessHeap(), 0, pvBuf); 2014 2015 pvBuf = HeapAlloc(GetProcessHeap(), 0, cbData); 2016 if (!pvBuf) 2017 { 2018 ret = ERROR_NOT_ENOUGH_MEMORY; 2019 break; 2020 } 2021 2022 if (ret == ERROR_MORE_DATA || !pvData) 2023 ret = RegQueryValueExW(hKey, pszValue, NULL, 2024 &dwType, pvBuf, &cbData); 2025 else 2026 { 2027 /* Even if cbData was large enough we have to copy the 2028 * string since ExpandEnvironmentStrings can't handle 2029 * overlapping buffers. */ 2030 CopyMemory(pvBuf, pvData, cbData); 2031 } 2032 2033 /* Both the type or the value itself could have been modified in 2034 * between so we have to keep retrying until the buffer is large 2035 * enough or we no longer have to expand the value. */ 2036 } 2037 while (dwType == REG_EXPAND_SZ && ret == ERROR_MORE_DATA); 2038 2039 if (ret == ERROR_SUCCESS) 2040 { 2041 /* Recheck dwType in case it changed since the first call */ 2042 if (dwType == REG_EXPAND_SZ) 2043 { 2044 cbData = ExpandEnvironmentStringsW(pvBuf, pvData, 2045 pcbData ? *pcbData : 0) * sizeof(WCHAR); 2046 dwType = REG_SZ; 2047 if (pvData && pcbData && cbData > *pcbData) 2048 ret = ERROR_MORE_DATA; 2049 } 2050 else if (pvData) 2051 CopyMemory(pvData, pvBuf, *pcbData); 2052 } 2053 2054 HeapFree(GetProcessHeap(), 0, pvBuf); 2055 } 2056 2057 if (pszSubKey && pszSubKey[0]) 2058 RegCloseKey(hKey); 2059 2060 RegpApplyRestrictions(dwFlags, dwType, cbData, &ret); 2061 2062 if (pvData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE)) 2063 ZeroMemory(pvData, *pcbData); 2064 2065 if (pdwType) 2066 *pdwType = dwType; 2067 2068 if (pcbData) 2069 *pcbData = cbData; 2070 2071 return ret; 2072 } 2073 2074 2075 /****************************************************************************** 2076 * RegGetValueA [ADVAPI32.@] 2077 * 2078 * See RegGetValueW. 2079 */ 2080 LSTATUS WINAPI 2081 RegGetValueA(HKEY hKey, 2082 LPCSTR pszSubKey, 2083 LPCSTR pszValue, 2084 DWORD dwFlags, 2085 LPDWORD pdwType, 2086 PVOID pvData, 2087 LPDWORD pcbData) 2088 { 2089 DWORD dwType, cbData = pcbData ? *pcbData : 0; 2090 PVOID pvBuf = NULL; 2091 LONG ret; 2092 2093 TRACE("(%p,%s,%s,%ld,%p,%p,%p=%ld)\n", 2094 hKey, pszSubKey, pszValue, dwFlags, pdwType, pvData, pcbData, 2095 cbData); 2096 2097 if (pvData && !pcbData) 2098 return ERROR_INVALID_PARAMETER; 2099 if ((dwFlags & RRF_RT_REG_EXPAND_SZ) && !(dwFlags & RRF_NOEXPAND) && 2100 ((dwFlags & RRF_RT_ANY) != RRF_RT_ANY)) 2101 return ERROR_INVALID_PARAMETER; 2102 2103 if (pszSubKey && pszSubKey[0]) 2104 { 2105 ret = RegOpenKeyExA(hKey, pszSubKey, 0, KEY_QUERY_VALUE, &hKey); 2106 if (ret != ERROR_SUCCESS) return ret; 2107 } 2108 2109 ret = RegQueryValueExA(hKey, pszValue, NULL, &dwType, pvData, &cbData); 2110 2111 /* If we are going to expand we need to read in the whole the value even 2112 * if the passed buffer was too small as the expanded string might be 2113 * smaller than the unexpanded one and could fit into cbData bytes. */ 2114 if ((ret == ERROR_SUCCESS || ret == ERROR_MORE_DATA) && 2115 (dwType == REG_EXPAND_SZ && !(dwFlags & RRF_NOEXPAND))) 2116 { 2117 do { 2118 HeapFree(GetProcessHeap(), 0, pvBuf); 2119 2120 pvBuf = HeapAlloc(GetProcessHeap(), 0, cbData); 2121 if (!pvBuf) 2122 { 2123 ret = ERROR_NOT_ENOUGH_MEMORY; 2124 break; 2125 } 2126 2127 if (ret == ERROR_MORE_DATA || !pvData) 2128 ret = RegQueryValueExA(hKey, pszValue, NULL, 2129 &dwType, pvBuf, &cbData); 2130 else 2131 { 2132 /* Even if cbData was large enough we have to copy the 2133 * string since ExpandEnvironmentStrings can't handle 2134 * overlapping buffers. */ 2135 CopyMemory(pvBuf, pvData, cbData); 2136 } 2137 2138 /* Both the type or the value itself could have been modified in 2139 * between so we have to keep retrying until the buffer is large 2140 * enough or we no longer have to expand the value. */ 2141 } while (dwType == REG_EXPAND_SZ && ret == ERROR_MORE_DATA); 2142 2143 if (ret == ERROR_SUCCESS) 2144 { 2145 /* Recheck dwType in case it changed since the first call */ 2146 if (dwType == REG_EXPAND_SZ) 2147 { 2148 cbData = ExpandEnvironmentStringsA(pvBuf, pvData, 2149 pcbData ? *pcbData : 0); 2150 dwType = REG_SZ; 2151 if(pvData && pcbData && cbData > *pcbData) 2152 ret = ERROR_MORE_DATA; 2153 } 2154 else if (pvData) 2155 CopyMemory(pvData, pvBuf, *pcbData); 2156 } 2157 2158 HeapFree(GetProcessHeap(), 0, pvBuf); 2159 } 2160 2161 if (pszSubKey && pszSubKey[0]) 2162 RegCloseKey(hKey); 2163 2164 RegpApplyRestrictions(dwFlags, dwType, cbData, &ret); 2165 2166 if (pvData && ret != ERROR_SUCCESS && (dwFlags & RRF_ZEROONFAILURE)) 2167 ZeroMemory(pvData, *pcbData); 2168 2169 if (pdwType) *pdwType = dwType; 2170 if (pcbData) *pcbData = cbData; 2171 2172 return ret; 2173 } 2174 2175 2176 /************************************************************************ 2177 * RegSetKeyValueW 2178 * 2179 * @implemented 2180 */ 2181 LONG WINAPI 2182 RegSetKeyValueW(IN HKEY hKey, 2183 IN LPCWSTR lpSubKey OPTIONAL, 2184 IN LPCWSTR lpValueName OPTIONAL, 2185 IN DWORD dwType, 2186 IN LPCVOID lpData OPTIONAL, 2187 IN DWORD cbData) 2188 { 2189 HANDLE KeyHandle, CurKey, SubKeyHandle = NULL; 2190 NTSTATUS Status; 2191 LONG Ret; 2192 2193 Status = MapDefaultKey(&KeyHandle, 2194 hKey); 2195 if (!NT_SUCCESS(Status)) 2196 { 2197 return RtlNtStatusToDosError(Status); 2198 } 2199 2200 if (lpSubKey != NULL) 2201 { 2202 OBJECT_ATTRIBUTES ObjectAttributes; 2203 UNICODE_STRING SubKeyName; 2204 2205 RtlInitUnicodeString(&SubKeyName, 2206 (LPWSTR)lpSubKey); 2207 2208 InitializeObjectAttributes(&ObjectAttributes, 2209 &SubKeyName, 2210 OBJ_CASE_INSENSITIVE, 2211 KeyHandle, 2212 NULL); 2213 2214 Status = NtOpenKey(&SubKeyHandle, 2215 KEY_SET_VALUE, 2216 &ObjectAttributes); 2217 if (!NT_SUCCESS(Status)) 2218 { 2219 Ret = RtlNtStatusToDosError(Status); 2220 goto Cleanup; 2221 } 2222 2223 CurKey = SubKeyHandle; 2224 } 2225 else 2226 CurKey = KeyHandle; 2227 2228 Ret = RegSetValueExW(CurKey, 2229 lpValueName, 2230 0, 2231 dwType, 2232 lpData, 2233 cbData); 2234 2235 if (SubKeyHandle != NULL) 2236 { 2237 NtClose(SubKeyHandle); 2238 } 2239 2240 Cleanup: 2241 ClosePredefKey(KeyHandle); 2242 2243 return Ret; 2244 } 2245 2246 2247 /************************************************************************ 2248 * RegSetKeyValueA 2249 * 2250 * @implemented 2251 */ 2252 LONG WINAPI 2253 RegSetKeyValueA(IN HKEY hKey, 2254 IN LPCSTR lpSubKey OPTIONAL, 2255 IN LPCSTR lpValueName OPTIONAL, 2256 IN DWORD dwType, 2257 IN LPCVOID lpData OPTIONAL, 2258 IN DWORD cbData) 2259 { 2260 HANDLE KeyHandle, CurKey, SubKeyHandle = NULL; 2261 NTSTATUS Status; 2262 LONG Ret; 2263 2264 Status = MapDefaultKey(&KeyHandle, 2265 hKey); 2266 if (!NT_SUCCESS(Status)) 2267 { 2268 return RtlNtStatusToDosError(Status); 2269 } 2270 2271 if (lpSubKey != NULL) 2272 { 2273 OBJECT_ATTRIBUTES ObjectAttributes; 2274 UNICODE_STRING SubKeyName; 2275 2276 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyName, 2277 (LPSTR)lpSubKey)) 2278 { 2279 Ret = ERROR_NOT_ENOUGH_MEMORY; 2280 goto Cleanup; 2281 } 2282 2283 InitializeObjectAttributes(&ObjectAttributes, 2284 &SubKeyName, 2285 OBJ_CASE_INSENSITIVE, 2286 KeyHandle, 2287 NULL); 2288 2289 Status = NtOpenKey(&SubKeyHandle, 2290 KEY_SET_VALUE, 2291 &ObjectAttributes); 2292 2293 RtlFreeUnicodeString(&SubKeyName); 2294 2295 if (!NT_SUCCESS(Status)) 2296 { 2297 Ret = RtlNtStatusToDosError(Status); 2298 goto Cleanup; 2299 } 2300 2301 CurKey = SubKeyHandle; 2302 } 2303 else 2304 CurKey = KeyHandle; 2305 2306 Ret = RegSetValueExA(CurKey, 2307 lpValueName, 2308 0, 2309 dwType, 2310 lpData, 2311 cbData); 2312 2313 if (SubKeyHandle != NULL) 2314 { 2315 NtClose(SubKeyHandle); 2316 } 2317 2318 Cleanup: 2319 ClosePredefKey(KeyHandle); 2320 2321 return Ret; 2322 } 2323 2324 2325 /************************************************************************ 2326 * RegDeleteValueA 2327 * 2328 * @implemented 2329 */ 2330 LONG WINAPI 2331 RegDeleteValueA(HKEY hKey, 2332 LPCSTR lpValueName) 2333 { 2334 UNICODE_STRING ValueName; 2335 HANDLE KeyHandle; 2336 NTSTATUS Status; 2337 2338 Status = MapDefaultKey(&KeyHandle, 2339 hKey); 2340 if (!NT_SUCCESS(Status)) 2341 { 2342 return RtlNtStatusToDosError(Status); 2343 } 2344 2345 RtlCreateUnicodeStringFromAsciiz(&ValueName, 2346 (LPSTR)lpValueName); 2347 Status = NtDeleteValueKey(KeyHandle, 2348 &ValueName); 2349 RtlFreeUnicodeString (&ValueName); 2350 2351 ClosePredefKey(KeyHandle); 2352 2353 if (!NT_SUCCESS(Status)) 2354 { 2355 return RtlNtStatusToDosError(Status); 2356 } 2357 2358 return ERROR_SUCCESS; 2359 } 2360 2361 2362 /************************************************************************ 2363 * RegDeleteValueW 2364 * 2365 * @implemented 2366 */ 2367 LONG WINAPI 2368 RegDeleteValueW(HKEY hKey, 2369 LPCWSTR lpValueName) 2370 { 2371 UNICODE_STRING ValueName; 2372 NTSTATUS Status; 2373 HANDLE KeyHandle; 2374 2375 Status = MapDefaultKey(&KeyHandle, 2376 hKey); 2377 if (!NT_SUCCESS(Status)) 2378 { 2379 return RtlNtStatusToDosError(Status); 2380 } 2381 2382 RtlInitUnicodeString(&ValueName, 2383 (LPWSTR)lpValueName); 2384 2385 Status = NtDeleteValueKey(KeyHandle, 2386 &ValueName); 2387 2388 ClosePredefKey(KeyHandle); 2389 2390 if (!NT_SUCCESS(Status)) 2391 { 2392 return RtlNtStatusToDosError(Status); 2393 } 2394 2395 return ERROR_SUCCESS; 2396 } 2397 2398 2399 /************************************************************************ 2400 * RegEnumKeyA 2401 * 2402 * @implemented 2403 */ 2404 LONG WINAPI 2405 RegEnumKeyA(HKEY hKey, 2406 DWORD dwIndex, 2407 LPSTR lpName, 2408 DWORD cbName) 2409 { 2410 DWORD dwLength; 2411 2412 dwLength = cbName; 2413 return RegEnumKeyExA(hKey, 2414 dwIndex, 2415 lpName, 2416 &dwLength, 2417 NULL, 2418 NULL, 2419 NULL, 2420 NULL); 2421 } 2422 2423 2424 /************************************************************************ 2425 * RegEnumKeyW 2426 * 2427 * @implemented 2428 */ 2429 LONG WINAPI 2430 RegEnumKeyW(HKEY hKey, 2431 DWORD dwIndex, 2432 LPWSTR lpName, 2433 DWORD cbName) 2434 { 2435 DWORD dwLength; 2436 2437 dwLength = cbName; 2438 return RegEnumKeyExW(hKey, 2439 dwIndex, 2440 lpName, 2441 &dwLength, 2442 NULL, 2443 NULL, 2444 NULL, 2445 NULL); 2446 } 2447 2448 2449 /************************************************************************ 2450 * RegEnumKeyExA 2451 * 2452 * @implemented 2453 */ 2454 LONG 2455 WINAPI 2456 RegEnumKeyExA( 2457 _In_ HKEY hKey, 2458 _In_ DWORD dwIndex, 2459 _Out_ LPSTR lpName, 2460 _Inout_ LPDWORD lpcbName, 2461 _Reserved_ LPDWORD lpReserved, 2462 _Out_opt_ LPSTR lpClass, 2463 _Inout_opt_ LPDWORD lpcbClass, 2464 _Out_opt_ PFILETIME lpftLastWriteTime) 2465 { 2466 WCHAR* NameBuffer = NULL; 2467 WCHAR* ClassBuffer = NULL; 2468 DWORD NameLength, ClassLength; 2469 LONG ErrorCode; 2470 2471 /* Allocate our buffers */ 2472 if (*lpcbName > 0) 2473 { 2474 NameLength = *lpcbName; 2475 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, *lpcbName * sizeof(WCHAR)); 2476 if (NameBuffer == NULL) 2477 { 2478 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 2479 goto Exit; 2480 } 2481 } 2482 2483 if (lpClass) 2484 { 2485 if (*lpcbClass > 0) 2486 { 2487 ClassLength = *lpcbClass; 2488 ClassBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, *lpcbClass * sizeof(WCHAR)); 2489 if (ClassBuffer == NULL) 2490 { 2491 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 2492 goto Exit; 2493 } 2494 } 2495 } 2496 2497 /* Do the actual call */ 2498 ErrorCode = RegEnumKeyExW( 2499 hKey, 2500 dwIndex, 2501 NameBuffer, 2502 lpcbName, 2503 lpReserved, 2504 ClassBuffer, 2505 lpcbClass, 2506 lpftLastWriteTime); 2507 2508 if (ErrorCode != ERROR_SUCCESS) 2509 goto Exit; 2510 2511 /* Convert the strings */ 2512 RtlUnicodeToMultiByteN(lpName, *lpcbName, 0, NameBuffer, *lpcbName * sizeof(WCHAR)); 2513 /* NULL terminate if we can */ 2514 if (NameLength > *lpcbName) 2515 lpName[*lpcbName] = '\0'; 2516 2517 if (lpClass) 2518 { 2519 RtlUnicodeToMultiByteN(lpClass, *lpcbClass, 0, NameBuffer, *lpcbClass * sizeof(WCHAR)); 2520 if (ClassLength > *lpcbClass) 2521 lpClass[*lpcbClass] = '\0'; 2522 } 2523 2524 Exit: 2525 if (NameBuffer) 2526 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); 2527 if (ClassBuffer) 2528 RtlFreeHeap(RtlGetProcessHeap(), 0, ClassBuffer); 2529 2530 return ErrorCode; 2531 } 2532 2533 2534 /************************************************************************ 2535 * RegEnumKeyExW 2536 * 2537 * @implemented 2538 */ 2539 LONG 2540 WINAPI 2541 RegEnumKeyExW( 2542 _In_ HKEY hKey, 2543 _In_ DWORD dwIndex, 2544 _Out_ LPWSTR lpName, 2545 _Inout_ LPDWORD lpcbName, 2546 _Reserved_ LPDWORD lpReserved, 2547 _Out_opt_ LPWSTR lpClass, 2548 _Inout_opt_ LPDWORD lpcbClass, 2549 _Out_opt_ PFILETIME lpftLastWriteTime) 2550 { 2551 union 2552 { 2553 KEY_NODE_INFORMATION Node; 2554 KEY_BASIC_INFORMATION Basic; 2555 } *KeyInfo; 2556 2557 ULONG BufferSize; 2558 ULONG ResultSize; 2559 ULONG NameLength; 2560 ULONG ClassLength = 0; 2561 HANDLE KeyHandle; 2562 LONG ErrorCode = ERROR_SUCCESS; 2563 NTSTATUS Status; 2564 2565 Status = MapDefaultKey(&KeyHandle, 2566 hKey); 2567 if (!NT_SUCCESS(Status)) 2568 { 2569 return RtlNtStatusToDosError(Status); 2570 } 2571 2572 if (IsHKCRKey(KeyHandle)) 2573 { 2574 ErrorCode = EnumHKCRKey( 2575 KeyHandle, 2576 dwIndex, 2577 lpName, 2578 lpcbName, 2579 lpReserved, 2580 lpClass, 2581 lpcbClass, 2582 lpftLastWriteTime); 2583 ClosePredefKey(KeyHandle); 2584 return ErrorCode; 2585 } 2586 2587 if (*lpcbName > 0) 2588 { 2589 NameLength = min (*lpcbName - 1, REG_MAX_NAME_SIZE) * sizeof (WCHAR); 2590 } 2591 else 2592 { 2593 NameLength = 0; 2594 } 2595 2596 if (lpClass) 2597 { 2598 if (*lpcbClass > 0) 2599 { 2600 ClassLength = min (*lpcbClass - 1, REG_MAX_NAME_SIZE) * sizeof(WCHAR); 2601 } 2602 else 2603 { 2604 ClassLength = 0; 2605 } 2606 2607 BufferSize = ((sizeof(KEY_NODE_INFORMATION) + NameLength + 3) & ~3) + ClassLength; 2608 } 2609 else 2610 { 2611 BufferSize = sizeof(KEY_BASIC_INFORMATION) + NameLength; 2612 } 2613 2614 KeyInfo = RtlAllocateHeap(ProcessHeap, 2615 0, 2616 BufferSize); 2617 if (KeyInfo == NULL) 2618 { 2619 ErrorCode = ERROR_OUTOFMEMORY; 2620 goto Cleanup; 2621 } 2622 2623 Status = NtEnumerateKey(KeyHandle, 2624 (ULONG)dwIndex, 2625 lpClass ? KeyNodeInformation : KeyBasicInformation, 2626 KeyInfo, 2627 BufferSize, 2628 &ResultSize); 2629 TRACE("NtEnumerateKey() returned status 0x%X\n", Status); 2630 if (!NT_SUCCESS(Status)) 2631 { 2632 ErrorCode = RtlNtStatusToDosError (Status); 2633 } 2634 else 2635 { 2636 if (lpClass == NULL) 2637 { 2638 if (KeyInfo->Basic.NameLength > NameLength) 2639 { 2640 ErrorCode = ERROR_MORE_DATA; 2641 } 2642 else 2643 { 2644 RtlCopyMemory(lpName, 2645 KeyInfo->Basic.Name, 2646 KeyInfo->Basic.NameLength); 2647 *lpcbName = (DWORD)(KeyInfo->Basic.NameLength / sizeof(WCHAR)); 2648 lpName[*lpcbName] = 0; 2649 } 2650 } 2651 else 2652 { 2653 if (KeyInfo->Node.NameLength > NameLength || 2654 KeyInfo->Node.ClassLength > ClassLength) 2655 { 2656 ErrorCode = ERROR_MORE_DATA; 2657 } 2658 else 2659 { 2660 RtlCopyMemory(lpName, 2661 KeyInfo->Node.Name, 2662 KeyInfo->Node.NameLength); 2663 *lpcbName = KeyInfo->Node.NameLength / sizeof(WCHAR); 2664 lpName[*lpcbName] = 0; 2665 RtlCopyMemory(lpClass, 2666 (PVOID)((ULONG_PTR)KeyInfo->Node.Name + KeyInfo->Node.ClassOffset), 2667 KeyInfo->Node.ClassLength); 2668 *lpcbClass = (DWORD)(KeyInfo->Node.ClassLength / sizeof(WCHAR)); 2669 lpClass[*lpcbClass] = 0; 2670 } 2671 } 2672 2673 if (ErrorCode == ERROR_SUCCESS && lpftLastWriteTime != NULL) 2674 { 2675 if (lpClass == NULL) 2676 { 2677 lpftLastWriteTime->dwLowDateTime = KeyInfo->Basic.LastWriteTime.u.LowPart; 2678 lpftLastWriteTime->dwHighDateTime = KeyInfo->Basic.LastWriteTime.u.HighPart; 2679 } 2680 else 2681 { 2682 lpftLastWriteTime->dwLowDateTime = KeyInfo->Node.LastWriteTime.u.LowPart; 2683 lpftLastWriteTime->dwHighDateTime = KeyInfo->Node.LastWriteTime.u.HighPart; 2684 } 2685 } 2686 } 2687 2688 RtlFreeHeap(ProcessHeap, 2689 0, 2690 KeyInfo); 2691 2692 Cleanup: 2693 ClosePredefKey(KeyHandle); 2694 2695 return ErrorCode; 2696 } 2697 2698 2699 /************************************************************************ 2700 * RegEnumValueA 2701 * 2702 * @implemented 2703 */ 2704 LONG WINAPI 2705 RegEnumValueA( 2706 _In_ HKEY hKey, 2707 _In_ DWORD dwIndex, 2708 _Out_ LPSTR lpName, 2709 _Inout_ LPDWORD lpcbName, 2710 _Reserved_ LPDWORD lpdwReserved, 2711 _Out_opt_ LPDWORD lpdwType, 2712 _Out_opt_ LPBYTE lpData, 2713 _Inout_opt_ LPDWORD lpcbData) 2714 { 2715 WCHAR* NameBuffer; 2716 DWORD NameBufferSize, NameLength; 2717 LONG ErrorCode; 2718 DWORD LocalType = REG_NONE; 2719 BOOL NameOverflow = FALSE; 2720 2721 /* Do parameter checks now, once and for all. */ 2722 if (!lpName || !lpcbName) 2723 return ERROR_INVALID_PARAMETER; 2724 2725 if ((lpData && !lpcbData) || lpdwReserved) 2726 return ERROR_INVALID_PARAMETER; 2727 2728 /* Get the size of the buffer we must use for the first call to RegEnumValueW */ 2729 ErrorCode = RegQueryInfoKeyW( 2730 hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &NameBufferSize, NULL, NULL, NULL); 2731 if (ErrorCode != ERROR_SUCCESS) 2732 return ErrorCode; 2733 2734 /* Add space for the null terminator */ 2735 NameBufferSize++; 2736 2737 /* Allocate the buffer for the unicode name */ 2738 NameBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, NameBufferSize * sizeof(WCHAR)); 2739 if (NameBuffer == NULL) 2740 { 2741 return ERROR_NOT_ENOUGH_MEMORY; 2742 } 2743 2744 /* 2745 * This code calls RegEnumValueW twice, because we need to know the type of the enumerated value. 2746 * So for the first call, we check if we overflow on the name, as we have no way of knowing if this 2747 * is an overflow on the data or on the name during the the second call. So the first time, we make the 2748 * call with the supplied value. This is merdique, but this is how it is. 2749 */ 2750 NameLength = *lpcbName; 2751 ErrorCode = RegEnumValueW( 2752 hKey, 2753 dwIndex, 2754 NameBuffer, 2755 &NameLength, 2756 NULL, 2757 &LocalType, 2758 NULL, 2759 NULL); 2760 if (ErrorCode != ERROR_SUCCESS) 2761 { 2762 if (ErrorCode == ERROR_MORE_DATA) 2763 NameOverflow = TRUE; 2764 else 2765 goto Exit; 2766 } 2767 2768 if (is_string(LocalType) && lpcbData) 2769 { 2770 /* We must allocate a buffer to get the unicode data */ 2771 DWORD DataBufferSize = *lpcbData * sizeof(WCHAR); 2772 WCHAR* DataBuffer = NULL; 2773 DWORD DataLength = *lpcbData; 2774 LPSTR DataStr = (LPSTR)lpData; 2775 2776 if (lpData) 2777 DataBuffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, *lpcbData * sizeof(WCHAR)); 2778 2779 /* Do the real call */ 2780 ErrorCode = RegEnumValueW( 2781 hKey, 2782 dwIndex, 2783 NameBuffer, 2784 &NameBufferSize, 2785 lpdwReserved, 2786 lpdwType, 2787 (LPBYTE)DataBuffer, 2788 &DataBufferSize); 2789 2790 *lpcbData = DataBufferSize / sizeof(WCHAR); 2791 2792 if (ErrorCode != ERROR_SUCCESS) 2793 { 2794 RtlFreeHeap(RtlGetProcessHeap(), 0, DataBuffer); 2795 goto Exit; 2796 } 2797 2798 /* Copy the data whatever the error code is */ 2799 if (lpData) 2800 { 2801 /* Do the data conversion */ 2802 RtlUnicodeToMultiByteN(DataStr, DataLength, 0, DataBuffer, DataBufferSize); 2803 /* NULL-terminate if there is enough room */ 2804 if ((DataLength > *lpcbData) && (DataStr[*lpcbData - 1] != '\0')) 2805 DataStr[*lpcbData] = '\0'; 2806 } 2807 2808 RtlFreeHeap(RtlGetProcessHeap(), 0, DataBuffer); 2809 } 2810 else 2811 { 2812 /* No data conversion needed. Do the call with provided buffers */ 2813 ErrorCode = RegEnumValueW( 2814 hKey, 2815 dwIndex, 2816 NameBuffer, 2817 &NameBufferSize, 2818 lpdwReserved, 2819 lpdwType, 2820 lpData, 2821 lpcbData); 2822 2823 if (ErrorCode != ERROR_SUCCESS) 2824 { 2825 goto Exit; 2826 } 2827 } 2828 2829 if (NameOverflow) 2830 { 2831 ErrorCode = ERROR_MORE_DATA; 2832 goto Exit; 2833 } 2834 2835 /* Convert the name string */ 2836 RtlUnicodeToMultiByteN(lpName, *lpcbName, lpcbName, NameBuffer, NameBufferSize * sizeof(WCHAR)); 2837 ((PSTR)lpName)[*lpcbName] = '\0'; 2838 2839 Exit: 2840 if (NameBuffer) 2841 RtlFreeHeap(RtlGetProcessHeap(), 0, NameBuffer); 2842 2843 return ErrorCode; 2844 } 2845 2846 2847 /****************************************************************************** 2848 * RegEnumValueW [ADVAPI32.@] 2849 * @implemented 2850 * 2851 * PARAMS 2852 * hkey [I] Handle to key to query 2853 * index [I] Index of value to query 2854 * value [O] Value string 2855 * val_count [I/O] Size of value buffer (in wchars) 2856 * reserved [I] Reserved 2857 * type [O] Type code 2858 * data [O] Value data 2859 * count [I/O] Size of data buffer (in bytes) 2860 * 2861 * RETURNS 2862 * Success: ERROR_SUCCESS 2863 * Failure: nonzero error code from Winerror.h 2864 */ 2865 LONG 2866 WINAPI 2867 RegEnumValueW( 2868 _In_ HKEY hKey, 2869 _In_ DWORD index, 2870 _Out_ LPWSTR value, 2871 _Inout_ PDWORD val_count, 2872 _Reserved_ PDWORD reserved, 2873 _Out_opt_ PDWORD type, 2874 _Out_opt_ LPBYTE data, 2875 _Inout_opt_ PDWORD count) 2876 { 2877 HANDLE KeyHandle; 2878 NTSTATUS status; 2879 ULONG total_size; 2880 char buffer[256], *buf_ptr = buffer; 2881 KEY_VALUE_FULL_INFORMATION *info = (KEY_VALUE_FULL_INFORMATION *)buffer; 2882 static const int info_size = FIELD_OFFSET( KEY_VALUE_FULL_INFORMATION, Name ); 2883 2884 TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n", 2885 hKey, index, value, val_count, reserved, type, data, count ); 2886 2887 if (!value || !val_count) 2888 return ERROR_INVALID_PARAMETER; 2889 2890 if ((data && !count) || reserved) 2891 return ERROR_INVALID_PARAMETER; 2892 2893 status = MapDefaultKey(&KeyHandle, hKey); 2894 if (!NT_SUCCESS(status)) 2895 { 2896 return RtlNtStatusToDosError(status); 2897 } 2898 2899 if (IsHKCRKey(KeyHandle)) 2900 { 2901 LONG ErrorCode = EnumHKCRValue( 2902 KeyHandle, 2903 index, 2904 value, 2905 val_count, 2906 reserved, 2907 type, 2908 data, 2909 count); 2910 ClosePredefKey(KeyHandle); 2911 return ErrorCode; 2912 } 2913 2914 total_size = info_size + (MAX_PATH + 1) * sizeof(WCHAR); 2915 if (data) total_size += *count; 2916 total_size = min( sizeof(buffer), total_size ); 2917 2918 status = NtEnumerateValueKey( KeyHandle, index, KeyValueFullInformation, 2919 buffer, total_size, &total_size ); 2920 if (status && (status != STATUS_BUFFER_OVERFLOW) && (status != STATUS_BUFFER_TOO_SMALL)) goto done; 2921 2922 if (value || data) 2923 { 2924 /* retry with a dynamically allocated buffer */ 2925 while ((status == STATUS_BUFFER_OVERFLOW) || (status == STATUS_BUFFER_TOO_SMALL)) 2926 { 2927 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr ); 2928 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size ))) 2929 { 2930 status = ERROR_NOT_ENOUGH_MEMORY; 2931 goto done; 2932 } 2933 info = (KEY_VALUE_FULL_INFORMATION *)buf_ptr; 2934 status = NtEnumerateValueKey( KeyHandle, index, KeyValueFullInformation, 2935 buf_ptr, total_size, &total_size ); 2936 } 2937 2938 if (status) goto done; 2939 2940 if (value) 2941 { 2942 if (info->NameLength/sizeof(WCHAR) >= *val_count) 2943 { 2944 status = STATUS_BUFFER_OVERFLOW; 2945 goto overflow; 2946 } 2947 memcpy( value, info->Name, info->NameLength ); 2948 *val_count = info->NameLength / sizeof(WCHAR); 2949 value[*val_count] = 0; 2950 } 2951 2952 if (data) 2953 { 2954 if (info->DataLength > *count) 2955 { 2956 status = STATUS_BUFFER_OVERFLOW; 2957 goto overflow; 2958 } 2959 memcpy( data, buf_ptr + info->DataOffset, info->DataLength ); 2960 if (is_string(info->Type) && info->DataLength <= *count - sizeof(WCHAR)) 2961 { 2962 /* if the type is REG_SZ and data is not 0-terminated 2963 * and there is enough space in the buffer NT appends a \0 */ 2964 WCHAR *ptr = (WCHAR *)(data + info->DataLength); 2965 if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0; 2966 } 2967 } 2968 } 2969 else status = STATUS_SUCCESS; 2970 2971 overflow: 2972 if (type) *type = info->Type; 2973 if (count) *count = info->DataLength; 2974 2975 done: 2976 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr ); 2977 ClosePredefKey(KeyHandle); 2978 return RtlNtStatusToDosError(status); 2979 } 2980 2981 2982 /************************************************************************ 2983 * RegFlushKey 2984 * 2985 * @implemented 2986 */ 2987 LONG WINAPI 2988 RegFlushKey(HKEY hKey) 2989 { 2990 HANDLE KeyHandle; 2991 NTSTATUS Status; 2992 2993 if (hKey == HKEY_PERFORMANCE_DATA) 2994 { 2995 return ERROR_SUCCESS; 2996 } 2997 2998 Status = MapDefaultKey(&KeyHandle, 2999 hKey); 3000 if (!NT_SUCCESS(Status)) 3001 { 3002 return RtlNtStatusToDosError(Status); 3003 } 3004 3005 Status = NtFlushKey(KeyHandle); 3006 3007 ClosePredefKey(KeyHandle); 3008 3009 if (!NT_SUCCESS(Status)) 3010 { 3011 return RtlNtStatusToDosError(Status); 3012 } 3013 3014 return ERROR_SUCCESS; 3015 } 3016 3017 3018 /************************************************************************ 3019 * RegGetKeySecurity 3020 * 3021 * @implemented 3022 */ 3023 LONG WINAPI 3024 RegGetKeySecurity(HKEY hKey, 3025 SECURITY_INFORMATION SecurityInformation, 3026 PSECURITY_DESCRIPTOR pSecurityDescriptor, 3027 LPDWORD lpcbSecurityDescriptor) 3028 { 3029 HANDLE KeyHandle; 3030 NTSTATUS Status; 3031 3032 if (hKey == HKEY_PERFORMANCE_DATA) 3033 { 3034 return ERROR_INVALID_HANDLE; 3035 } 3036 3037 Status = MapDefaultKey(&KeyHandle, 3038 hKey); 3039 if (!NT_SUCCESS(Status)) 3040 { 3041 TRACE("MapDefaultKey() failed (Status %lx)\n", Status); 3042 return RtlNtStatusToDosError(Status); 3043 } 3044 3045 Status = NtQuerySecurityObject(KeyHandle, 3046 SecurityInformation, 3047 pSecurityDescriptor, 3048 *lpcbSecurityDescriptor, 3049 lpcbSecurityDescriptor); 3050 3051 ClosePredefKey(KeyHandle); 3052 3053 if (!NT_SUCCESS(Status)) 3054 { 3055 WARN("NtQuerySecurityObject() failed (Status %lx)\n", Status); 3056 return RtlNtStatusToDosError(Status); 3057 } 3058 3059 return ERROR_SUCCESS; 3060 } 3061 3062 3063 /************************************************************************ 3064 * RegLoadKeyA 3065 * 3066 * @implemented 3067 */ 3068 LONG WINAPI 3069 RegLoadKeyA(HKEY hKey, 3070 LPCSTR lpSubKey, 3071 LPCSTR lpFile) 3072 { 3073 UNICODE_STRING FileName; 3074 UNICODE_STRING KeyName; 3075 LONG ErrorCode; 3076 3077 RtlInitEmptyUnicodeString(&KeyName, NULL, 0); 3078 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 3079 3080 if (lpSubKey) 3081 { 3082 if (!RtlCreateUnicodeStringFromAsciiz(&KeyName, lpSubKey)) 3083 { 3084 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 3085 goto Exit; 3086 } 3087 } 3088 3089 if (lpFile) 3090 { 3091 if (!RtlCreateUnicodeStringFromAsciiz(&FileName, lpFile)) 3092 { 3093 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 3094 goto Exit; 3095 } 3096 } 3097 3098 ErrorCode = RegLoadKeyW(hKey, 3099 KeyName.Buffer, 3100 FileName.Buffer); 3101 3102 Exit: 3103 RtlFreeUnicodeString(&FileName); 3104 RtlFreeUnicodeString(&KeyName); 3105 3106 return ErrorCode; 3107 } 3108 3109 3110 /************************************************************************ 3111 * RegLoadKeyW 3112 * 3113 * @implemented 3114 */ 3115 LONG WINAPI 3116 RegLoadKeyW(HKEY hKey, 3117 LPCWSTR lpSubKey, 3118 LPCWSTR lpFile) 3119 { 3120 OBJECT_ATTRIBUTES FileObjectAttributes; 3121 OBJECT_ATTRIBUTES KeyObjectAttributes; 3122 UNICODE_STRING FileName; 3123 UNICODE_STRING KeyName; 3124 HANDLE KeyHandle; 3125 NTSTATUS Status; 3126 LONG ErrorCode = ERROR_SUCCESS; 3127 3128 if (hKey == HKEY_PERFORMANCE_DATA) 3129 { 3130 return ERROR_INVALID_HANDLE; 3131 } 3132 3133 Status = MapDefaultKey(&KeyHandle, 3134 hKey); 3135 if (!NT_SUCCESS(Status)) 3136 { 3137 return RtlNtStatusToDosError(Status); 3138 } 3139 3140 if (!RtlDosPathNameToNtPathName_U(lpFile, 3141 &FileName, 3142 NULL, 3143 NULL)) 3144 { 3145 ErrorCode = ERROR_BAD_PATHNAME; 3146 goto Cleanup; 3147 } 3148 3149 InitializeObjectAttributes(&FileObjectAttributes, 3150 &FileName, 3151 OBJ_CASE_INSENSITIVE, 3152 NULL, 3153 NULL); 3154 3155 RtlInitUnicodeString(&KeyName, 3156 (LPWSTR)lpSubKey); 3157 3158 InitializeObjectAttributes(&KeyObjectAttributes, 3159 &KeyName, 3160 OBJ_CASE_INSENSITIVE, 3161 KeyHandle, 3162 NULL); 3163 3164 Status = NtLoadKey(&KeyObjectAttributes, 3165 &FileObjectAttributes); 3166 3167 RtlFreeHeap(RtlGetProcessHeap(), 3168 0, 3169 FileName.Buffer); 3170 3171 if (!NT_SUCCESS(Status)) 3172 { 3173 ErrorCode = RtlNtStatusToDosError(Status); 3174 goto Cleanup; 3175 } 3176 3177 Cleanup: 3178 ClosePredefKey(KeyHandle); 3179 3180 return ErrorCode; 3181 } 3182 3183 3184 /************************************************************************ 3185 * RegNotifyChangeKeyValue 3186 * 3187 * @unimplemented 3188 */ 3189 LONG WINAPI 3190 RegNotifyChangeKeyValue(HKEY hKey, 3191 BOOL bWatchSubtree, 3192 DWORD dwNotifyFilter, 3193 HANDLE hEvent, 3194 BOOL fAsynchronous) 3195 { 3196 IO_STATUS_BLOCK IoStatusBlock; 3197 HANDLE KeyHandle; 3198 NTSTATUS Status; 3199 LONG ErrorCode = ERROR_SUCCESS; 3200 3201 if (hKey == HKEY_PERFORMANCE_DATA) 3202 { 3203 return ERROR_INVALID_HANDLE; 3204 } 3205 3206 if ((fAsynchronous != FALSE) && (hEvent == NULL)) 3207 { 3208 return ERROR_INVALID_PARAMETER; 3209 } 3210 3211 Status = MapDefaultKey(&KeyHandle, 3212 hKey); 3213 if (!NT_SUCCESS(Status)) 3214 { 3215 return RtlNtStatusToDosError(Status); 3216 } 3217 3218 /* FIXME: Remote key handles must fail */ 3219 3220 Status = NtNotifyChangeKey(KeyHandle, 3221 hEvent, 3222 0, 3223 0, 3224 &IoStatusBlock, 3225 dwNotifyFilter, 3226 bWatchSubtree, 3227 0, 3228 0, 3229 fAsynchronous); 3230 if (!NT_SUCCESS(Status) && Status != STATUS_TIMEOUT) 3231 { 3232 ErrorCode = RtlNtStatusToDosError(Status); 3233 } 3234 3235 ClosePredefKey(KeyHandle); 3236 3237 return ErrorCode; 3238 } 3239 3240 3241 /************************************************************************ 3242 * RegOpenCurrentUser 3243 * 3244 * @implemented 3245 */ 3246 LONG WINAPI 3247 RegOpenCurrentUser(IN REGSAM samDesired, 3248 OUT PHKEY phkResult) 3249 { 3250 NTSTATUS Status; 3251 3252 Status = RtlOpenCurrentUser((ACCESS_MASK)samDesired, 3253 (PHANDLE)phkResult); 3254 if (!NT_SUCCESS(Status)) 3255 { 3256 /* NOTE - don't set the last error code! just return the error! */ 3257 return RtlNtStatusToDosError(Status); 3258 } 3259 3260 return ERROR_SUCCESS; 3261 } 3262 3263 3264 /************************************************************************ 3265 * RegOpenKeyA 3266 * 3267 * 20050503 Fireball - imported from WINE 3268 * 3269 * @implemented 3270 */ 3271 LONG WINAPI 3272 RegOpenKeyA(HKEY hKey, 3273 LPCSTR lpSubKey, 3274 PHKEY phkResult) 3275 { 3276 TRACE("RegOpenKeyA hKey 0x%x lpSubKey %s phkResult %p\n", 3277 hKey, lpSubKey, phkResult); 3278 3279 if (!phkResult) 3280 return ERROR_INVALID_PARAMETER; 3281 3282 if (!hKey && lpSubKey && phkResult) 3283 { 3284 return ERROR_INVALID_HANDLE; 3285 } 3286 3287 if (!lpSubKey || !*lpSubKey) 3288 { 3289 *phkResult = hKey; 3290 return ERROR_SUCCESS; 3291 } 3292 3293 return RegOpenKeyExA(hKey, 3294 lpSubKey, 3295 0, 3296 MAXIMUM_ALLOWED, 3297 phkResult); 3298 } 3299 3300 3301 /************************************************************************ 3302 * RegOpenKeyW 3303 * 3304 * 19981101 Ariadne 3305 * 19990525 EA 3306 * 20050503 Fireball - imported from WINE 3307 * 3308 * @implemented 3309 */ 3310 LONG WINAPI 3311 RegOpenKeyW(HKEY hKey, 3312 LPCWSTR lpSubKey, 3313 PHKEY phkResult) 3314 { 3315 TRACE("RegOpenKeyW hKey 0x%x lpSubKey %S phkResult %p\n", 3316 hKey, lpSubKey, phkResult); 3317 3318 if (!phkResult) 3319 return ERROR_INVALID_PARAMETER; 3320 3321 if (!hKey && lpSubKey && phkResult) 3322 { 3323 return ERROR_INVALID_HANDLE; 3324 } 3325 3326 if (!lpSubKey || !*lpSubKey) 3327 { 3328 *phkResult = hKey; 3329 return ERROR_SUCCESS; 3330 } 3331 3332 return RegOpenKeyExW(hKey, 3333 lpSubKey, 3334 0, 3335 MAXIMUM_ALLOWED, 3336 phkResult); 3337 } 3338 3339 3340 /************************************************************************ 3341 * RegOpenKeyExA 3342 * 3343 * @implemented 3344 */ 3345 LONG WINAPI 3346 RegOpenKeyExA( 3347 _In_ HKEY hKey, 3348 _In_ LPCSTR lpSubKey, 3349 _In_ DWORD ulOptions, 3350 _In_ REGSAM samDesired, 3351 _Out_ PHKEY phkResult) 3352 { 3353 UNICODE_STRING SubKeyString; 3354 LONG ErrorCode; 3355 3356 TRACE("RegOpenKeyExA hKey 0x%x lpSubKey %s ulOptions 0x%x samDesired 0x%x phkResult %p\n", 3357 hKey, lpSubKey, ulOptions, samDesired, phkResult); 3358 3359 if (lpSubKey) 3360 { 3361 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyString, lpSubKey)) 3362 return ERROR_NOT_ENOUGH_MEMORY; 3363 } 3364 else 3365 RtlInitEmptyUnicodeString(&SubKeyString, NULL, 0); 3366 3367 ErrorCode = RegOpenKeyExW(hKey, SubKeyString.Buffer, ulOptions, samDesired, phkResult); 3368 3369 RtlFreeUnicodeString(&SubKeyString); 3370 3371 return ErrorCode; 3372 } 3373 3374 3375 /************************************************************************ 3376 * RegOpenKeyExW 3377 * 3378 * @implemented 3379 */ 3380 LONG WINAPI 3381 RegOpenKeyExW(HKEY hKey, 3382 LPCWSTR lpSubKey, 3383 DWORD ulOptions, 3384 REGSAM samDesired, 3385 PHKEY phkResult) 3386 { 3387 OBJECT_ATTRIBUTES ObjectAttributes; 3388 UNICODE_STRING SubKeyString; 3389 HANDLE KeyHandle; 3390 NTSTATUS Status; 3391 ULONG Attributes = OBJ_CASE_INSENSITIVE; 3392 LONG ErrorCode = ERROR_SUCCESS; 3393 3394 TRACE("RegOpenKeyExW hKey 0x%x lpSubKey %S ulOptions 0x%x samDesired 0x%x phkResult %p\n", 3395 hKey, lpSubKey, ulOptions, samDesired, phkResult); 3396 if (!phkResult) 3397 { 3398 return ERROR_INVALID_PARAMETER; 3399 } 3400 3401 Status = MapDefaultKey(&KeyHandle, hKey); 3402 if (!NT_SUCCESS(Status)) 3403 { 3404 return RtlNtStatusToDosError(Status); 3405 } 3406 3407 if (IsHKCRKey(KeyHandle)) 3408 { 3409 ErrorCode = OpenHKCRKey(KeyHandle, lpSubKey, ulOptions, samDesired, phkResult); 3410 ClosePredefKey(KeyHandle); 3411 return ErrorCode; 3412 } 3413 3414 if (ulOptions & REG_OPTION_OPEN_LINK) 3415 Attributes |= OBJ_OPENLINK; 3416 3417 if (lpSubKey != NULL) 3418 RtlInitUnicodeString(&SubKeyString, (LPWSTR)lpSubKey); 3419 else 3420 RtlInitUnicodeString(&SubKeyString, (LPWSTR)L""); 3421 3422 InitializeObjectAttributes(&ObjectAttributes, 3423 &SubKeyString, 3424 Attributes, 3425 KeyHandle, 3426 NULL); 3427 3428 Status = NtOpenKey((PHANDLE)phkResult, 3429 samDesired, 3430 &ObjectAttributes); 3431 3432 if (Status == STATUS_DATATYPE_MISALIGNMENT) 3433 { 3434 HANDLE hAligned; 3435 UNICODE_STRING AlignedString; 3436 3437 Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, 3438 &SubKeyString, 3439 &AlignedString); 3440 if (NT_SUCCESS(Status)) 3441 { 3442 /* Try again with aligned parameters */ 3443 InitializeObjectAttributes(&ObjectAttributes, 3444 &AlignedString, 3445 Attributes, 3446 KeyHandle, 3447 NULL); 3448 3449 Status = NtOpenKey(&hAligned, 3450 samDesired, 3451 &ObjectAttributes); 3452 3453 RtlFreeUnicodeString(&AlignedString); 3454 3455 if (NT_SUCCESS(Status)) 3456 *phkResult = hAligned; 3457 } 3458 else 3459 { 3460 /* Restore the original error */ 3461 Status = STATUS_DATATYPE_MISALIGNMENT; 3462 } 3463 } 3464 3465 if (!NT_SUCCESS(Status)) 3466 { 3467 ErrorCode = RtlNtStatusToDosError(Status); 3468 } 3469 3470 3471 ClosePredefKey(KeyHandle); 3472 3473 return ErrorCode; 3474 } 3475 3476 3477 /************************************************************************ 3478 * RegOpenUserClassesRoot 3479 * 3480 * @implemented 3481 */ 3482 LONG WINAPI 3483 RegOpenUserClassesRoot(IN HANDLE hToken, 3484 IN DWORD dwOptions, 3485 IN REGSAM samDesired, 3486 OUT PHKEY phkResult) 3487 { 3488 const WCHAR UserClassesKeyPrefix[] = L"\\Registry\\User\\"; 3489 const WCHAR UserClassesKeySuffix[] = L"_Classes"; 3490 PTOKEN_USER TokenUserData; 3491 ULONG RequiredLength; 3492 UNICODE_STRING UserSidString, UserClassesKeyRoot; 3493 OBJECT_ATTRIBUTES ObjectAttributes; 3494 NTSTATUS Status; 3495 3496 /* check parameters */ 3497 if (hToken == NULL || dwOptions != 0 || phkResult == NULL) 3498 { 3499 return ERROR_INVALID_PARAMETER; 3500 } 3501 3502 /* 3503 * Get the user sid from the token 3504 */ 3505 3506 ReadTokenSid: 3507 /* determine how much memory we need */ 3508 Status = NtQueryInformationToken(hToken, 3509 TokenUser, 3510 NULL, 3511 0, 3512 &RequiredLength); 3513 if (!NT_SUCCESS(Status) && (Status != STATUS_BUFFER_TOO_SMALL)) 3514 { 3515 /* NOTE - as opposed to all other registry functions windows does indeed 3516 change the last error code in case the caller supplied a invalid 3517 handle for example! */ 3518 return RtlNtStatusToDosError(Status); 3519 } 3520 RegInitialize(); /* HACK until delay-loading is implemented */ 3521 TokenUserData = RtlAllocateHeap(ProcessHeap, 3522 0, 3523 RequiredLength); 3524 if (TokenUserData == NULL) 3525 { 3526 return ERROR_NOT_ENOUGH_MEMORY; 3527 } 3528 3529 /* attempt to read the information */ 3530 Status = NtQueryInformationToken(hToken, 3531 TokenUser, 3532 TokenUserData, 3533 RequiredLength, 3534 &RequiredLength); 3535 if (!NT_SUCCESS(Status)) 3536 { 3537 RtlFreeHeap(ProcessHeap, 3538 0, 3539 TokenUserData); 3540 if (Status == STATUS_BUFFER_TOO_SMALL) 3541 { 3542 /* the information appears to have changed?! try again */ 3543 goto ReadTokenSid; 3544 } 3545 3546 /* NOTE - as opposed to all other registry functions windows does indeed 3547 change the last error code in case the caller supplied a invalid 3548 handle for example! */ 3549 return RtlNtStatusToDosError(Status); 3550 } 3551 3552 /* 3553 * Build the absolute path for the user's registry in the form 3554 * "\Registry\User\<SID>_Classes" 3555 */ 3556 Status = RtlConvertSidToUnicodeString(&UserSidString, 3557 TokenUserData->User.Sid, 3558 TRUE); 3559 3560 /* we don't need the user data anymore, free it */ 3561 RtlFreeHeap(ProcessHeap, 3562 0, 3563 TokenUserData); 3564 3565 if (!NT_SUCCESS(Status)) 3566 { 3567 return RtlNtStatusToDosError(Status); 3568 } 3569 3570 /* allocate enough memory for the entire key string */ 3571 UserClassesKeyRoot.Length = 0; 3572 UserClassesKeyRoot.MaximumLength = UserSidString.Length + 3573 sizeof(UserClassesKeyPrefix) + 3574 sizeof(UserClassesKeySuffix); 3575 UserClassesKeyRoot.Buffer = RtlAllocateHeap(ProcessHeap, 3576 0, 3577 UserClassesKeyRoot.MaximumLength); 3578 if (UserClassesKeyRoot.Buffer == NULL) 3579 { 3580 RtlFreeUnicodeString(&UserSidString); 3581 return RtlNtStatusToDosError(Status); 3582 } 3583 3584 /* build the string */ 3585 RtlAppendUnicodeToString(&UserClassesKeyRoot, 3586 UserClassesKeyPrefix); 3587 RtlAppendUnicodeStringToString(&UserClassesKeyRoot, 3588 &UserSidString); 3589 RtlAppendUnicodeToString(&UserClassesKeyRoot, 3590 UserClassesKeySuffix); 3591 3592 TRACE("RegOpenUserClassesRoot: Absolute path: %wZ\n", &UserClassesKeyRoot); 3593 3594 /* 3595 * Open the key 3596 */ 3597 InitializeObjectAttributes(&ObjectAttributes, 3598 &UserClassesKeyRoot, 3599 OBJ_CASE_INSENSITIVE, 3600 NULL, 3601 NULL); 3602 3603 Status = NtOpenKey((PHANDLE)phkResult, 3604 samDesired, 3605 &ObjectAttributes); 3606 3607 RtlFreeUnicodeString(&UserSidString); 3608 RtlFreeUnicodeString(&UserClassesKeyRoot); 3609 3610 if (!NT_SUCCESS(Status)) 3611 { 3612 return RtlNtStatusToDosError(Status); 3613 } 3614 3615 return ERROR_SUCCESS; 3616 } 3617 3618 3619 /************************************************************************ 3620 * RegQueryInfoKeyA 3621 * 3622 * @implemented 3623 */ 3624 LONG WINAPI 3625 RegQueryInfoKeyA(HKEY hKey, 3626 LPSTR lpClass, 3627 LPDWORD lpcClass, 3628 LPDWORD lpReserved, 3629 LPDWORD lpcSubKeys, 3630 LPDWORD lpcMaxSubKeyLen, 3631 LPDWORD lpcMaxClassLen, 3632 LPDWORD lpcValues, 3633 LPDWORD lpcMaxValueNameLen, 3634 LPDWORD lpcMaxValueLen, 3635 LPDWORD lpcbSecurityDescriptor, 3636 PFILETIME lpftLastWriteTime) 3637 { 3638 WCHAR ClassName[MAX_PATH]; 3639 UNICODE_STRING UnicodeString; 3640 ANSI_STRING AnsiString; 3641 LONG ErrorCode; 3642 NTSTATUS Status; 3643 DWORD cClass = 0; 3644 3645 if ((lpClass) && (!lpcClass)) 3646 { 3647 return ERROR_INVALID_PARAMETER; 3648 } 3649 3650 RtlInitUnicodeString(&UnicodeString, 3651 NULL); 3652 if (lpClass != NULL) 3653 { 3654 RtlInitEmptyUnicodeString(&UnicodeString, 3655 ClassName, 3656 sizeof(ClassName)); 3657 cClass = sizeof(ClassName) / sizeof(WCHAR); 3658 } 3659 3660 ErrorCode = RegQueryInfoKeyW(hKey, 3661 UnicodeString.Buffer, 3662 &cClass, 3663 lpReserved, 3664 lpcSubKeys, 3665 lpcMaxSubKeyLen, 3666 lpcMaxClassLen, 3667 lpcValues, 3668 lpcMaxValueNameLen, 3669 lpcMaxValueLen, 3670 lpcbSecurityDescriptor, 3671 lpftLastWriteTime); 3672 if ((ErrorCode == ERROR_SUCCESS) && (lpClass != NULL)) 3673 { 3674 if (*lpcClass == 0) 3675 { 3676 return ErrorCode; 3677 } 3678 3679 RtlInitEmptyAnsiString(&AnsiString, lpClass, *lpcClass); 3680 UnicodeString.Length = cClass * sizeof(WCHAR); 3681 Status = RtlUnicodeStringToAnsiString(&AnsiString, 3682 &UnicodeString, 3683 FALSE); 3684 ErrorCode = RtlNtStatusToDosError(Status); 3685 cClass = AnsiString.Length; 3686 lpClass[cClass] = ANSI_NULL; 3687 } 3688 3689 if (lpcClass != NULL) 3690 { 3691 *lpcClass = cClass; 3692 } 3693 3694 return ErrorCode; 3695 } 3696 3697 3698 /************************************************************************ 3699 * RegQueryInfoKeyW 3700 * 3701 * @implemented 3702 */ 3703 LONG WINAPI 3704 RegQueryInfoKeyW(HKEY hKey, 3705 LPWSTR lpClass, 3706 LPDWORD lpcClass, 3707 LPDWORD lpReserved, 3708 LPDWORD lpcSubKeys, 3709 LPDWORD lpcMaxSubKeyLen, 3710 LPDWORD lpcMaxClassLen, 3711 LPDWORD lpcValues, 3712 LPDWORD lpcMaxValueNameLen, 3713 LPDWORD lpcMaxValueLen, 3714 LPDWORD lpcbSecurityDescriptor, 3715 PFILETIME lpftLastWriteTime) 3716 { 3717 KEY_FULL_INFORMATION FullInfoBuffer; 3718 PKEY_FULL_INFORMATION FullInfo; 3719 ULONG FullInfoSize; 3720 ULONG ClassLength = 0; 3721 HANDLE KeyHandle; 3722 NTSTATUS Status; 3723 ULONG Length; 3724 LONG ErrorCode = ERROR_SUCCESS; 3725 3726 if ((lpClass) && (!lpcClass)) 3727 { 3728 return ERROR_INVALID_PARAMETER; 3729 } 3730 3731 Status = MapDefaultKey(&KeyHandle, 3732 hKey); 3733 if (!NT_SUCCESS(Status)) 3734 { 3735 return RtlNtStatusToDosError(Status); 3736 } 3737 3738 if (lpClass != NULL) 3739 { 3740 if (*lpcClass > 0) 3741 { 3742 ClassLength = min(*lpcClass - 1, REG_MAX_NAME_SIZE) * sizeof(WCHAR); 3743 } 3744 else 3745 { 3746 ClassLength = 0; 3747 } 3748 3749 FullInfoSize = sizeof(KEY_FULL_INFORMATION) + ((ClassLength + 3) & ~3); 3750 FullInfo = RtlAllocateHeap(ProcessHeap, 3751 0, 3752 FullInfoSize); 3753 if (FullInfo == NULL) 3754 { 3755 ErrorCode = ERROR_OUTOFMEMORY; 3756 goto Cleanup; 3757 } 3758 } 3759 else 3760 { 3761 FullInfoSize = sizeof(KEY_FULL_INFORMATION); 3762 FullInfo = &FullInfoBuffer; 3763 } 3764 3765 if (lpcbSecurityDescriptor != NULL) 3766 *lpcbSecurityDescriptor = 0; 3767 3768 Status = NtQueryKey(KeyHandle, 3769 KeyFullInformation, 3770 FullInfo, 3771 FullInfoSize, 3772 &Length); 3773 TRACE("NtQueryKey() returned status 0x%X\n", Status); 3774 if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW) 3775 { 3776 ErrorCode = RtlNtStatusToDosError(Status); 3777 goto Cleanup; 3778 } 3779 3780 TRACE("SubKeys %d\n", FullInfo->SubKeys); 3781 if (lpcSubKeys != NULL) 3782 { 3783 *lpcSubKeys = FullInfo->SubKeys; 3784 } 3785 3786 TRACE("MaxNameLen %lu\n", FullInfo->MaxNameLen); 3787 if (lpcMaxSubKeyLen != NULL) 3788 { 3789 *lpcMaxSubKeyLen = FullInfo->MaxNameLen / sizeof(WCHAR); 3790 } 3791 3792 TRACE("MaxClassLen %lu\n", FullInfo->MaxClassLen); 3793 if (lpcMaxClassLen != NULL) 3794 { 3795 *lpcMaxClassLen = FullInfo->MaxClassLen / sizeof(WCHAR); 3796 } 3797 3798 TRACE("Values %lu\n", FullInfo->Values); 3799 if (lpcValues != NULL) 3800 { 3801 *lpcValues = FullInfo->Values; 3802 } 3803 3804 TRACE("MaxValueNameLen %lu\n", FullInfo->MaxValueNameLen); 3805 if (lpcMaxValueNameLen != NULL) 3806 { 3807 *lpcMaxValueNameLen = FullInfo->MaxValueNameLen / sizeof(WCHAR); 3808 } 3809 3810 TRACE("MaxValueDataLen %lu\n", FullInfo->MaxValueDataLen); 3811 if (lpcMaxValueLen != NULL) 3812 { 3813 *lpcMaxValueLen = FullInfo->MaxValueDataLen; 3814 } 3815 3816 if (lpcbSecurityDescriptor != NULL) 3817 { 3818 Status = NtQuerySecurityObject(KeyHandle, 3819 OWNER_SECURITY_INFORMATION | 3820 GROUP_SECURITY_INFORMATION | 3821 DACL_SECURITY_INFORMATION, 3822 NULL, 3823 0, 3824 lpcbSecurityDescriptor); 3825 TRACE("NtQuerySecurityObject() returned status 0x%X\n", Status); 3826 } 3827 3828 if (lpftLastWriteTime != NULL) 3829 { 3830 lpftLastWriteTime->dwLowDateTime = FullInfo->LastWriteTime.u.LowPart; 3831 lpftLastWriteTime->dwHighDateTime = FullInfo->LastWriteTime.u.HighPart; 3832 } 3833 3834 if (lpClass != NULL) 3835 { 3836 if (*lpcClass == 0) 3837 { 3838 goto Cleanup; 3839 } 3840 3841 if (FullInfo->ClassLength > ClassLength) 3842 { 3843 ErrorCode = ERROR_INSUFFICIENT_BUFFER; 3844 } 3845 else 3846 { 3847 RtlCopyMemory(lpClass, 3848 FullInfo->Class, 3849 FullInfo->ClassLength); 3850 lpClass[FullInfo->ClassLength / sizeof(WCHAR)] = UNICODE_NULL; 3851 } 3852 } 3853 3854 if (lpcClass != NULL) 3855 { 3856 *lpcClass = FullInfo->ClassLength / sizeof(WCHAR); 3857 } 3858 3859 Cleanup: 3860 if (lpClass != NULL) 3861 { 3862 RtlFreeHeap(ProcessHeap, 3863 0, 3864 FullInfo); 3865 } 3866 3867 ClosePredefKey(KeyHandle); 3868 3869 return ErrorCode; 3870 } 3871 3872 3873 /************************************************************************ 3874 * RegQueryMultipleValuesA 3875 * 3876 * @implemented 3877 */ 3878 LONG WINAPI 3879 RegQueryMultipleValuesA(HKEY hKey, 3880 PVALENTA val_list, 3881 DWORD num_vals, 3882 LPSTR lpValueBuf, 3883 LPDWORD ldwTotsize) 3884 { 3885 ULONG i; 3886 DWORD maxBytes = *ldwTotsize; 3887 LPSTR bufptr = (LPSTR)lpValueBuf; 3888 LONG ErrorCode; 3889 3890 if (maxBytes >= (1024*1024)) 3891 return ERROR_MORE_DATA; 3892 3893 *ldwTotsize = 0; 3894 3895 TRACE("RegQueryMultipleValuesA(%p,%p,%ld,%p,%p=%ld)\n", 3896 hKey, val_list, num_vals, lpValueBuf, ldwTotsize, *ldwTotsize); 3897 3898 for (i = 0; i < num_vals; i++) 3899 { 3900 val_list[i].ve_valuelen = 0; 3901 ErrorCode = RegQueryValueExA(hKey, 3902 val_list[i].ve_valuename, 3903 NULL, 3904 NULL, 3905 NULL, 3906 &val_list[i].ve_valuelen); 3907 if (ErrorCode != ERROR_SUCCESS) 3908 { 3909 return ErrorCode; 3910 } 3911 3912 if (lpValueBuf != NULL && *ldwTotsize + val_list[i].ve_valuelen <= maxBytes) 3913 { 3914 ErrorCode = RegQueryValueExA(hKey, 3915 val_list[i].ve_valuename, 3916 NULL, 3917 &val_list[i].ve_type, 3918 (LPBYTE)bufptr, 3919 &val_list[i].ve_valuelen); 3920 if (ErrorCode != ERROR_SUCCESS) 3921 { 3922 return ErrorCode; 3923 } 3924 3925 val_list[i].ve_valueptr = (DWORD_PTR)bufptr; 3926 3927 bufptr += val_list[i].ve_valuelen; 3928 } 3929 3930 *ldwTotsize += val_list[i].ve_valuelen; 3931 } 3932 3933 return (lpValueBuf != NULL && *ldwTotsize <= maxBytes) ? ERROR_SUCCESS : ERROR_MORE_DATA; 3934 } 3935 3936 3937 /************************************************************************ 3938 * RegQueryMultipleValuesW 3939 * 3940 * @implemented 3941 */ 3942 LONG WINAPI 3943 RegQueryMultipleValuesW(HKEY hKey, 3944 PVALENTW val_list, 3945 DWORD num_vals, 3946 LPWSTR lpValueBuf, 3947 LPDWORD ldwTotsize) 3948 { 3949 ULONG i; 3950 DWORD maxBytes = *ldwTotsize; 3951 LPSTR bufptr = (LPSTR)lpValueBuf; 3952 LONG ErrorCode; 3953 3954 if (maxBytes >= (1024*1024)) 3955 return ERROR_MORE_DATA; 3956 3957 *ldwTotsize = 0; 3958 3959 TRACE("RegQueryMultipleValuesW(%p,%p,%ld,%p,%p=%ld)\n", 3960 hKey, val_list, num_vals, lpValueBuf, ldwTotsize, *ldwTotsize); 3961 3962 for (i = 0; i < num_vals; i++) 3963 { 3964 val_list[i].ve_valuelen = 0; 3965 ErrorCode = RegQueryValueExW(hKey, 3966 val_list[i].ve_valuename, 3967 NULL, 3968 NULL, 3969 NULL, 3970 &val_list[i].ve_valuelen); 3971 if (ErrorCode != ERROR_SUCCESS) 3972 { 3973 return ErrorCode; 3974 } 3975 3976 if (lpValueBuf != NULL && *ldwTotsize + val_list[i].ve_valuelen <= maxBytes) 3977 { 3978 ErrorCode = RegQueryValueExW(hKey, 3979 val_list[i].ve_valuename, 3980 NULL, 3981 &val_list[i].ve_type, 3982 (LPBYTE)bufptr, 3983 &val_list[i].ve_valuelen); 3984 if (ErrorCode != ERROR_SUCCESS) 3985 { 3986 return ErrorCode; 3987 } 3988 3989 val_list[i].ve_valueptr = (DWORD_PTR)bufptr; 3990 3991 bufptr += val_list[i].ve_valuelen; 3992 } 3993 3994 *ldwTotsize += val_list[i].ve_valuelen; 3995 } 3996 3997 return (lpValueBuf != NULL && *ldwTotsize <= maxBytes) ? ERROR_SUCCESS : ERROR_MORE_DATA; 3998 } 3999 4000 4001 /************************************************************************ 4002 * RegQueryReflectionKey 4003 * 4004 * @unimplemented 4005 */ 4006 LONG WINAPI 4007 RegQueryReflectionKey(IN HKEY hBase, 4008 OUT BOOL* bIsReflectionDisabled) 4009 { 4010 FIXME("RegQueryReflectionKey(0x%p, 0x%p) UNIMPLEMENTED!\n", 4011 hBase, bIsReflectionDisabled); 4012 return ERROR_CALL_NOT_IMPLEMENTED; 4013 } 4014 4015 4016 /****************************************************************************** 4017 * RegQueryValueExA [ADVAPI32.@] 4018 * 4019 * Get the type and contents of a specified value under with a key. 4020 * 4021 * PARAMS 4022 * hkey [I] Handle of the key to query 4023 * name [I] Name of value under hkey to query 4024 * reserved [I] Reserved - must be NULL 4025 * type [O] Destination for the value type, or NULL if not required 4026 * data [O] Destination for the values contents, or NULL if not required 4027 * count [I/O] Size of data, updated with the number of bytes returned 4028 * 4029 * RETURNS 4030 * Success: ERROR_SUCCESS. *count is updated with the number of bytes copied to data. 4031 * Failure: ERROR_INVALID_HANDLE, if hkey is invalid. 4032 * ERROR_INVALID_PARAMETER, if any other parameter is invalid. 4033 * ERROR_MORE_DATA, if on input *count is too small to hold the contents. 4034 * 4035 * NOTES 4036 * MSDN states that if data is too small it is partially filled. In reality 4037 * it remains untouched. 4038 */ 4039 LONG 4040 WINAPI 4041 RegQueryValueExA( 4042 _In_ HKEY hkeyorg, 4043 _In_ LPCSTR name, 4044 _In_ LPDWORD reserved, 4045 _Out_opt_ LPDWORD type, 4046 _Out_opt_ LPBYTE data, 4047 _Inout_opt_ LPDWORD count) 4048 { 4049 UNICODE_STRING nameW; 4050 DWORD DataLength; 4051 DWORD ErrorCode; 4052 DWORD BufferSize = 0; 4053 WCHAR* Buffer; 4054 CHAR* DataStr = (CHAR*)data; 4055 DWORD LocalType; 4056 4057 /* Validate those parameters, the rest will be done with the first RegQueryValueExW call */ 4058 if ((data && !count) || reserved) 4059 return ERROR_INVALID_PARAMETER; 4060 4061 if (name) 4062 { 4063 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name)) 4064 return ERROR_NOT_ENOUGH_MEMORY; 4065 } 4066 else 4067 RtlInitEmptyUnicodeString(&nameW, NULL, 0); 4068 4069 ErrorCode = RegQueryValueExW(hkeyorg, nameW.Buffer, NULL, &LocalType, NULL, &BufferSize); 4070 if (ErrorCode != ERROR_SUCCESS) 4071 { 4072 if ((!data) && count) 4073 *count = 0; 4074 RtlFreeUnicodeString(&nameW); 4075 return ErrorCode; 4076 } 4077 4078 /* See if we can directly handle the call without caring for conversion */ 4079 if (!is_string(LocalType) || !count) 4080 { 4081 ErrorCode = RegQueryValueExW(hkeyorg, nameW.Buffer, reserved, type, data, count); 4082 RtlFreeUnicodeString(&nameW); 4083 return ErrorCode; 4084 } 4085 4086 /* Allocate a unicode string to get the data */ 4087 Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, BufferSize); 4088 if (!Buffer) 4089 { 4090 RtlFreeUnicodeString(&nameW); 4091 return ERROR_NOT_ENOUGH_MEMORY; 4092 } 4093 4094 ErrorCode = RegQueryValueExW(hkeyorg, nameW.Buffer, reserved, type, (LPBYTE)Buffer, &BufferSize); 4095 if (ErrorCode != ERROR_SUCCESS) 4096 { 4097 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 4098 RtlFreeUnicodeString(&nameW); 4099 return ErrorCode; 4100 } 4101 4102 /* We don't need this anymore */ 4103 RtlFreeUnicodeString(&nameW); 4104 4105 DataLength = *count; 4106 RtlUnicodeToMultiByteSize(count, Buffer, BufferSize); 4107 4108 if ((!data) || (DataLength < *count)) 4109 { 4110 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 4111 return data ? ERROR_MORE_DATA : ERROR_SUCCESS; 4112 } 4113 4114 /* We can finally do the conversion */ 4115 RtlUnicodeToMultiByteN(DataStr, DataLength, NULL, Buffer, BufferSize); 4116 4117 /* NULL-terminate if there is enough room */ 4118 if ((DataLength > *count) && (DataStr[*count - 1] != '\0')) 4119 DataStr[*count] = '\0'; 4120 4121 RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer); 4122 4123 return ERROR_SUCCESS; 4124 } 4125 4126 4127 /************************************************************************ 4128 * RegQueryValueExW 4129 * 4130 * @implemented 4131 */ 4132 LONG 4133 WINAPI 4134 RegQueryValueExW( 4135 _In_ HKEY hkeyorg, 4136 _In_ LPCWSTR name, 4137 _In_ LPDWORD reserved, 4138 _In_ LPDWORD type, 4139 _In_ LPBYTE data, 4140 _In_ LPDWORD count) 4141 { 4142 HANDLE hkey; 4143 NTSTATUS status; 4144 UNICODE_STRING name_str; 4145 DWORD total_size; 4146 char buffer[256], *buf_ptr = buffer; 4147 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer; 4148 static const int info_size = offsetof( KEY_VALUE_PARTIAL_INFORMATION, Data ); 4149 4150 TRACE("(%p,%s,%p,%p,%p,%p=%d)\n", 4151 hkeyorg, debugstr_w(name), reserved, type, data, count, 4152 (count && data) ? *count : 0 ); 4153 4154 if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER; 4155 4156 status = MapDefaultKey(&hkey, hkeyorg); 4157 if (!NT_SUCCESS(status)) 4158 { 4159 return RtlNtStatusToDosError(status); 4160 } 4161 4162 if (IsHKCRKey(hkey)) 4163 { 4164 LONG ErrorCode = QueryHKCRValue(hkey, name, reserved, type, data, count); 4165 ClosePredefKey(hkey); 4166 return ErrorCode; 4167 } 4168 4169 RtlInitUnicodeString( &name_str, name ); 4170 4171 if (data) 4172 total_size = min( sizeof(buffer), *count + info_size ); 4173 else 4174 total_size = info_size; 4175 4176 4177 status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation, 4178 buffer, total_size, &total_size ); 4179 4180 if (!NT_SUCCESS(status) && status != STATUS_BUFFER_OVERFLOW) 4181 { 4182 // NT: Valid handles with inexistant/null values or invalid (but not NULL) handles sets type to REG_NONE 4183 // On windows these conditions are likely to be side effects of the implementation... 4184 if (status == STATUS_INVALID_HANDLE && hkey) 4185 { 4186 if (type) *type = REG_NONE; 4187 if (count) *count = 0; 4188 } 4189 else if (status == STATUS_OBJECT_NAME_NOT_FOUND) 4190 { 4191 if (type) *type = REG_NONE; 4192 if (data == NULL && count) *count = 0; 4193 } 4194 goto done; 4195 } 4196 4197 if (data) 4198 { 4199 /* retry with a dynamically allocated buffer */ 4200 while (status == STATUS_BUFFER_OVERFLOW && total_size - info_size <= *count) 4201 { 4202 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr ); 4203 if (!(buf_ptr = HeapAlloc( GetProcessHeap(), 0, total_size ))) 4204 { 4205 ClosePredefKey(hkey); 4206 return ERROR_NOT_ENOUGH_MEMORY; 4207 } 4208 info = (KEY_VALUE_PARTIAL_INFORMATION *)buf_ptr; 4209 status = NtQueryValueKey( hkey, &name_str, KeyValuePartialInformation, 4210 buf_ptr, total_size, &total_size ); 4211 } 4212 4213 if (NT_SUCCESS(status)) 4214 { 4215 memcpy( data, buf_ptr + info_size, total_size - info_size ); 4216 /* if the type is REG_SZ and data is not 0-terminated 4217 * and there is enough space in the buffer NT appends a \0 */ 4218 if (is_string(info->Type) && total_size - info_size <= *count-sizeof(WCHAR)) 4219 { 4220 WCHAR *ptr = (WCHAR *)(data + total_size - info_size); 4221 if (ptr > (WCHAR *)data && ptr[-1]) *ptr = 0; 4222 } 4223 } 4224 else if (status != STATUS_BUFFER_OVERFLOW) goto done; 4225 } 4226 else status = STATUS_SUCCESS; 4227 4228 if (type) *type = info->Type; 4229 if (count) *count = total_size - info_size; 4230 4231 done: 4232 if (buf_ptr != buffer) HeapFree( GetProcessHeap(), 0, buf_ptr ); 4233 ClosePredefKey(hkey); 4234 return RtlNtStatusToDosError(status); 4235 } 4236 4237 4238 /************************************************************************ 4239 * RegQueryValueA 4240 * 4241 * @implemented 4242 */ 4243 LSTATUS WINAPI RegQueryValueA( HKEY hkey, LPCSTR name, LPSTR data, LPLONG count ) 4244 { 4245 DWORD ret; 4246 HKEY subkey = hkey; 4247 4248 TRACE("(%p,%s,%p,%d)\n", hkey, debugstr_a(name), data, count ? *count : 0 ); 4249 4250 if (name && name[0]) 4251 { 4252 if ((ret = RegOpenKeyA( hkey, name, &subkey )) != ERROR_SUCCESS) return ret; 4253 } 4254 ret = RegQueryValueExA( subkey, NULL, NULL, NULL, (LPBYTE)data, (LPDWORD)count ); 4255 if (subkey != hkey) RegCloseKey( subkey ); 4256 if (ret == ERROR_FILE_NOT_FOUND) 4257 { 4258 /* return empty string if default value not found */ 4259 if (data) *data = 0; 4260 if (count) *count = 1; 4261 ret = ERROR_SUCCESS; 4262 } 4263 return ret; 4264 } 4265 4266 4267 /************************************************************************ 4268 * RegQueryValueW 4269 * 4270 * @implemented 4271 */ 4272 LSTATUS WINAPI RegQueryValueW( HKEY hkey, LPCWSTR name, LPWSTR data, LPLONG count ) 4273 { 4274 DWORD ret; 4275 HKEY subkey = hkey; 4276 4277 TRACE("(%p,%s,%p,%d)\n", hkey, debugstr_w(name), data, count ? *count : 0 ); 4278 if (hkey == NULL) 4279 { 4280 return ERROR_INVALID_HANDLE; 4281 } 4282 if (name && name[0]) 4283 { 4284 ret = RegOpenKeyW( hkey, name, &subkey); 4285 if (ret != ERROR_SUCCESS) 4286 { 4287 return ret; 4288 } 4289 } 4290 4291 ret = RegQueryValueExW( subkey, NULL, NULL, NULL, (LPBYTE)data, (LPDWORD)count ); 4292 4293 if (subkey != hkey) 4294 { 4295 RegCloseKey( subkey ); 4296 } 4297 4298 if (ret == ERROR_FILE_NOT_FOUND) 4299 { 4300 /* return empty string if default value not found */ 4301 if (data) 4302 *data = 0; 4303 if (count) 4304 *count = sizeof(WCHAR); 4305 ret = ERROR_SUCCESS; 4306 } 4307 return ret; 4308 } 4309 4310 4311 /************************************************************************ 4312 * RegReplaceKeyA 4313 * 4314 * @implemented 4315 */ 4316 LONG WINAPI 4317 RegReplaceKeyA(HKEY hKey, 4318 LPCSTR lpSubKey, 4319 LPCSTR lpNewFile, 4320 LPCSTR lpOldFile) 4321 { 4322 UNICODE_STRING SubKey; 4323 UNICODE_STRING NewFile; 4324 UNICODE_STRING OldFile; 4325 LONG ErrorCode; 4326 4327 RtlInitEmptyUnicodeString(&SubKey, NULL, 0); 4328 RtlInitEmptyUnicodeString(&OldFile, NULL, 0); 4329 RtlInitEmptyUnicodeString(&NewFile, NULL, 0); 4330 4331 if (lpSubKey) 4332 { 4333 if (!RtlCreateUnicodeStringFromAsciiz(&SubKey, lpSubKey)) 4334 { 4335 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 4336 goto Exit; 4337 } 4338 } 4339 4340 if (lpOldFile) 4341 { 4342 if (!RtlCreateUnicodeStringFromAsciiz(&OldFile, lpOldFile)) 4343 { 4344 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 4345 goto Exit; 4346 } 4347 } 4348 4349 if (lpNewFile) 4350 { 4351 if (!RtlCreateUnicodeStringFromAsciiz(&NewFile, lpNewFile)) 4352 { 4353 ErrorCode = ERROR_NOT_ENOUGH_MEMORY; 4354 goto Exit; 4355 } 4356 } 4357 4358 ErrorCode = RegReplaceKeyW(hKey, 4359 SubKey.Buffer, 4360 NewFile.Buffer, 4361 OldFile.Buffer); 4362 4363 Exit: 4364 RtlFreeUnicodeString(&OldFile); 4365 RtlFreeUnicodeString(&NewFile); 4366 RtlFreeUnicodeString(&SubKey); 4367 4368 return ErrorCode; 4369 } 4370 4371 4372 /************************************************************************ 4373 * RegReplaceKeyW 4374 * 4375 * @unimplemented 4376 */ 4377 LONG WINAPI 4378 RegReplaceKeyW(HKEY hKey, 4379 LPCWSTR lpSubKey, 4380 LPCWSTR lpNewFile, 4381 LPCWSTR lpOldFile) 4382 { 4383 OBJECT_ATTRIBUTES KeyObjectAttributes; 4384 OBJECT_ATTRIBUTES NewObjectAttributes; 4385 OBJECT_ATTRIBUTES OldObjectAttributes; 4386 UNICODE_STRING SubKeyName; 4387 UNICODE_STRING NewFileName; 4388 UNICODE_STRING OldFileName; 4389 BOOLEAN CloseRealKey; 4390 HANDLE RealKeyHandle; 4391 HANDLE KeyHandle; 4392 NTSTATUS Status; 4393 LONG ErrorCode = ERROR_SUCCESS; 4394 4395 if (hKey == HKEY_PERFORMANCE_DATA) 4396 { 4397 return ERROR_INVALID_HANDLE; 4398 } 4399 4400 Status = MapDefaultKey(&KeyHandle, 4401 hKey); 4402 if (!NT_SUCCESS(Status)) 4403 { 4404 return RtlNtStatusToDosError(Status); 4405 } 4406 4407 /* Open the real key */ 4408 if (lpSubKey != NULL && *lpSubKey != (WCHAR)0) 4409 { 4410 RtlInitUnicodeString(&SubKeyName, 4411 (PWSTR)lpSubKey); 4412 InitializeObjectAttributes(&KeyObjectAttributes, 4413 &SubKeyName, 4414 OBJ_CASE_INSENSITIVE, 4415 KeyHandle, 4416 NULL); 4417 Status = NtOpenKey(&RealKeyHandle, 4418 MAXIMUM_ALLOWED, 4419 &KeyObjectAttributes); 4420 if (!NT_SUCCESS(Status)) 4421 { 4422 ErrorCode = RtlNtStatusToDosError(Status); 4423 goto Cleanup; 4424 } 4425 4426 CloseRealKey = TRUE; 4427 } 4428 else 4429 { 4430 RealKeyHandle = KeyHandle; 4431 CloseRealKey = FALSE; 4432 } 4433 4434 /* Convert new file name */ 4435 if (!RtlDosPathNameToNtPathName_U(lpNewFile, 4436 &NewFileName, 4437 NULL, 4438 NULL)) 4439 { 4440 if (CloseRealKey) 4441 { 4442 NtClose(RealKeyHandle); 4443 } 4444 4445 ErrorCode = ERROR_INVALID_PARAMETER; 4446 goto Cleanup; 4447 } 4448 4449 InitializeObjectAttributes(&NewObjectAttributes, 4450 &NewFileName, 4451 OBJ_CASE_INSENSITIVE, 4452 NULL, 4453 NULL); 4454 4455 /* Convert old file name */ 4456 if (!RtlDosPathNameToNtPathName_U(lpOldFile, 4457 &OldFileName, 4458 NULL, 4459 NULL)) 4460 { 4461 RtlFreeHeap(RtlGetProcessHeap (), 4462 0, 4463 NewFileName.Buffer); 4464 if (CloseRealKey) 4465 { 4466 NtClose(RealKeyHandle); 4467 } 4468 4469 ErrorCode = ERROR_INVALID_PARAMETER; 4470 goto Cleanup; 4471 } 4472 4473 InitializeObjectAttributes(&OldObjectAttributes, 4474 &OldFileName, 4475 OBJ_CASE_INSENSITIVE, 4476 NULL, 4477 NULL); 4478 4479 Status = NtReplaceKey(&NewObjectAttributes, 4480 RealKeyHandle, 4481 &OldObjectAttributes); 4482 4483 RtlFreeHeap(RtlGetProcessHeap(), 4484 0, 4485 OldFileName.Buffer); 4486 RtlFreeHeap(RtlGetProcessHeap(), 4487 0, 4488 NewFileName.Buffer); 4489 4490 if (CloseRealKey) 4491 { 4492 NtClose(RealKeyHandle); 4493 } 4494 4495 if (!NT_SUCCESS(Status)) 4496 { 4497 return RtlNtStatusToDosError(Status); 4498 } 4499 4500 Cleanup: 4501 ClosePredefKey(KeyHandle); 4502 4503 return ErrorCode; 4504 } 4505 4506 4507 /************************************************************************ 4508 * RegRestoreKeyA 4509 * 4510 * @implemented 4511 */ 4512 LONG WINAPI 4513 RegRestoreKeyA(HKEY hKey, 4514 LPCSTR lpFile, 4515 DWORD dwFlags) 4516 { 4517 UNICODE_STRING FileName; 4518 LONG ErrorCode; 4519 4520 if (lpFile) 4521 { 4522 if (!RtlCreateUnicodeStringFromAsciiz(&FileName, lpFile)) 4523 return ERROR_NOT_ENOUGH_MEMORY; 4524 } 4525 else 4526 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 4527 4528 ErrorCode = RegRestoreKeyW(hKey, 4529 FileName.Buffer, 4530 dwFlags); 4531 4532 RtlFreeUnicodeString(&FileName); 4533 4534 return ErrorCode; 4535 } 4536 4537 4538 /************************************************************************ 4539 * RegRestoreKeyW 4540 * 4541 * @implemented 4542 */ 4543 LONG WINAPI 4544 RegRestoreKeyW(HKEY hKey, 4545 LPCWSTR lpFile, 4546 DWORD dwFlags) 4547 { 4548 OBJECT_ATTRIBUTES ObjectAttributes; 4549 IO_STATUS_BLOCK IoStatusBlock; 4550 UNICODE_STRING FileName; 4551 HANDLE FileHandle; 4552 HANDLE KeyHandle; 4553 NTSTATUS Status; 4554 4555 if (hKey == HKEY_PERFORMANCE_DATA) 4556 { 4557 return ERROR_INVALID_HANDLE; 4558 } 4559 4560 Status = MapDefaultKey(&KeyHandle, 4561 hKey); 4562 if (!NT_SUCCESS(Status)) 4563 { 4564 return RtlNtStatusToDosError(Status); 4565 } 4566 4567 if (!RtlDosPathNameToNtPathName_U(lpFile, 4568 &FileName, 4569 NULL, 4570 NULL)) 4571 { 4572 Status = STATUS_INVALID_PARAMETER; 4573 goto Cleanup; 4574 } 4575 4576 InitializeObjectAttributes(&ObjectAttributes, 4577 &FileName, 4578 OBJ_CASE_INSENSITIVE, 4579 NULL, 4580 NULL); 4581 4582 Status = NtOpenFile(&FileHandle, 4583 FILE_GENERIC_READ, 4584 &ObjectAttributes, 4585 &IoStatusBlock, 4586 FILE_SHARE_READ, 4587 FILE_SYNCHRONOUS_IO_NONALERT); 4588 RtlFreeHeap(RtlGetProcessHeap(), 4589 0, 4590 FileName.Buffer); 4591 if (!NT_SUCCESS(Status)) 4592 { 4593 goto Cleanup; 4594 } 4595 4596 Status = NtRestoreKey(KeyHandle, 4597 FileHandle, 4598 (ULONG)dwFlags); 4599 NtClose (FileHandle); 4600 4601 Cleanup: 4602 ClosePredefKey(KeyHandle); 4603 4604 if (!NT_SUCCESS(Status)) 4605 { 4606 return RtlNtStatusToDosError(Status); 4607 } 4608 4609 return ERROR_SUCCESS; 4610 } 4611 4612 4613 /************************************************************************ 4614 * RegSaveKeyA 4615 * 4616 * @implemented 4617 */ 4618 LONG WINAPI 4619 RegSaveKeyA(HKEY hKey, 4620 LPCSTR lpFile, 4621 LPSECURITY_ATTRIBUTES lpSecurityAttributes) 4622 { 4623 UNICODE_STRING FileName; 4624 LONG ErrorCode; 4625 4626 if (lpFile) 4627 { 4628 if (!RtlCreateUnicodeStringFromAsciiz(&FileName, lpFile)) 4629 return ERROR_NOT_ENOUGH_MEMORY; 4630 } 4631 else 4632 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 4633 4634 ErrorCode = RegSaveKeyW(hKey, 4635 FileName.Buffer, 4636 lpSecurityAttributes); 4637 RtlFreeUnicodeString(&FileName); 4638 4639 return ErrorCode; 4640 } 4641 4642 4643 /************************************************************************ 4644 * RegSaveKeyW 4645 * 4646 * @implemented 4647 */ 4648 LONG WINAPI 4649 RegSaveKeyW(HKEY hKey, 4650 LPCWSTR lpFile, 4651 LPSECURITY_ATTRIBUTES lpSecurityAttributes) 4652 { 4653 PSECURITY_DESCRIPTOR SecurityDescriptor = NULL; 4654 OBJECT_ATTRIBUTES ObjectAttributes; 4655 UNICODE_STRING FileName; 4656 IO_STATUS_BLOCK IoStatusBlock; 4657 HANDLE FileHandle; 4658 HANDLE KeyHandle; 4659 NTSTATUS Status; 4660 4661 Status = MapDefaultKey(&KeyHandle, 4662 hKey); 4663 if (!NT_SUCCESS(Status)) 4664 { 4665 return RtlNtStatusToDosError(Status); 4666 } 4667 4668 if (!RtlDosPathNameToNtPathName_U(lpFile, 4669 &FileName, 4670 NULL, 4671 NULL)) 4672 { 4673 Status = STATUS_INVALID_PARAMETER; 4674 goto Cleanup; 4675 } 4676 4677 if (lpSecurityAttributes != NULL) 4678 { 4679 SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor; 4680 } 4681 4682 InitializeObjectAttributes(&ObjectAttributes, 4683 &FileName, 4684 OBJ_CASE_INSENSITIVE, 4685 NULL, 4686 SecurityDescriptor); 4687 Status = NtCreateFile(&FileHandle, 4688 GENERIC_WRITE | SYNCHRONIZE, 4689 &ObjectAttributes, 4690 &IoStatusBlock, 4691 NULL, 4692 FILE_ATTRIBUTE_NORMAL, 4693 FILE_SHARE_READ, 4694 FILE_CREATE, 4695 FILE_OPEN_FOR_BACKUP_INTENT | FILE_SYNCHRONOUS_IO_NONALERT, 4696 NULL, 4697 0); 4698 RtlFreeHeap(RtlGetProcessHeap(), 4699 0, 4700 FileName.Buffer); 4701 if (!NT_SUCCESS(Status)) 4702 { 4703 goto Cleanup; 4704 } 4705 4706 Status = NtSaveKey(KeyHandle, 4707 FileHandle); 4708 NtClose (FileHandle); 4709 4710 Cleanup: 4711 ClosePredefKey(KeyHandle); 4712 4713 if (!NT_SUCCESS(Status)) 4714 { 4715 return RtlNtStatusToDosError(Status); 4716 } 4717 4718 return ERROR_SUCCESS; 4719 } 4720 4721 4722 /************************************************************************ 4723 * RegSaveKeyExA 4724 * 4725 * @implemented 4726 */ 4727 LONG 4728 WINAPI 4729 RegSaveKeyExA(HKEY hKey, 4730 LPCSTR lpFile, 4731 LPSECURITY_ATTRIBUTES lpSecurityAttributes, 4732 DWORD Flags) 4733 { 4734 UNICODE_STRING FileName; 4735 LONG ErrorCode; 4736 4737 if (lpFile) 4738 { 4739 if (!RtlCreateUnicodeStringFromAsciiz(&FileName, lpFile)) 4740 return ERROR_NOT_ENOUGH_MEMORY; 4741 } 4742 else 4743 RtlInitEmptyUnicodeString(&FileName, NULL, 0); 4744 4745 ErrorCode = RegSaveKeyExW(hKey, 4746 FileName.Buffer, 4747 lpSecurityAttributes, 4748 Flags); 4749 RtlFreeUnicodeString(&FileName); 4750 4751 return ErrorCode; 4752 } 4753 4754 4755 /************************************************************************ 4756 * RegSaveKeyExW 4757 * 4758 * @unimplemented 4759 */ 4760 LONG 4761 WINAPI 4762 RegSaveKeyExW(HKEY hKey, 4763 LPCWSTR lpFile, 4764 LPSECURITY_ATTRIBUTES lpSecurityAttributes, 4765 DWORD Flags) 4766 { 4767 switch (Flags) 4768 { 4769 case REG_STANDARD_FORMAT: 4770 case REG_LATEST_FORMAT: 4771 case REG_NO_COMPRESSION: 4772 break; 4773 default: 4774 return ERROR_INVALID_PARAMETER; 4775 } 4776 4777 FIXME("RegSaveKeyExW(): Flags ignored!\n"); 4778 4779 return RegSaveKeyW(hKey, 4780 lpFile, 4781 lpSecurityAttributes); 4782 } 4783 4784 4785 /************************************************************************ 4786 * RegSetKeySecurity 4787 * 4788 * @implemented 4789 */ 4790 LONG WINAPI 4791 RegSetKeySecurity(HKEY hKey, 4792 SECURITY_INFORMATION SecurityInformation, 4793 PSECURITY_DESCRIPTOR pSecurityDescriptor) 4794 { 4795 HANDLE KeyHandle; 4796 NTSTATUS Status; 4797 4798 if (hKey == HKEY_PERFORMANCE_DATA) 4799 { 4800 return ERROR_INVALID_HANDLE; 4801 } 4802 4803 Status = MapDefaultKey(&KeyHandle, 4804 hKey); 4805 if (!NT_SUCCESS(Status)) 4806 { 4807 return RtlNtStatusToDosError(Status); 4808 } 4809 4810 Status = NtSetSecurityObject(KeyHandle, 4811 SecurityInformation, 4812 pSecurityDescriptor); 4813 4814 ClosePredefKey(KeyHandle); 4815 4816 if (!NT_SUCCESS(Status)) 4817 { 4818 return RtlNtStatusToDosError(Status); 4819 } 4820 4821 return ERROR_SUCCESS; 4822 } 4823 4824 4825 /************************************************************************ 4826 * RegSetValueExA 4827 * 4828 * @implemented 4829 */ 4830 LONG WINAPI 4831 RegSetValueExA(HKEY hKey, 4832 LPCSTR lpValueName, 4833 DWORD Reserved, 4834 DWORD dwType, 4835 CONST BYTE* lpData, 4836 DWORD cbData) 4837 { 4838 UNICODE_STRING ValueName; 4839 LPWSTR pValueName; 4840 ANSI_STRING AnsiString; 4841 UNICODE_STRING Data; 4842 LONG ErrorCode; 4843 LPBYTE pData; 4844 DWORD DataSize; 4845 NTSTATUS Status; 4846 4847 /* Convert SubKey name to Unicode */ 4848 if (lpValueName != NULL && lpValueName[0] != '\0') 4849 { 4850 BOOL bConverted; 4851 bConverted = RtlCreateUnicodeStringFromAsciiz(&ValueName, 4852 (PSTR)lpValueName); 4853 if(!bConverted) 4854 return ERROR_NOT_ENOUGH_MEMORY; 4855 } 4856 else 4857 { 4858 ValueName.Buffer = NULL; 4859 } 4860 4861 pValueName = (LPWSTR)ValueName.Buffer; 4862 4863 4864 if (is_string(dwType) && (cbData != 0)) 4865 { 4866 /* Convert ANSI string Data to Unicode */ 4867 /* If last character NOT zero then increment length */ 4868 LONG bNoNulledStr = ((lpData[cbData-1] != '\0') ? 1 : 0); 4869 AnsiString.Buffer = (PSTR)lpData; 4870 AnsiString.Length = cbData + bNoNulledStr; 4871 AnsiString.MaximumLength = cbData + bNoNulledStr; 4872 Status = RtlAnsiStringToUnicodeString(&Data, 4873 &AnsiString, 4874 TRUE); 4875 4876 if (!NT_SUCCESS(Status)) 4877 { 4878 if (pValueName != NULL) 4879 RtlFreeUnicodeString(&ValueName); 4880 4881 return RtlNtStatusToDosError(Status); 4882 } 4883 pData = (LPBYTE)Data.Buffer; 4884 DataSize = cbData * sizeof(WCHAR); 4885 } 4886 else 4887 { 4888 Data.Buffer = NULL; 4889 pData = (LPBYTE)lpData; 4890 DataSize = cbData; 4891 } 4892 4893 ErrorCode = RegSetValueExW(hKey, 4894 pValueName, 4895 Reserved, 4896 dwType, 4897 pData, 4898 DataSize); 4899 4900 if (pValueName != NULL) 4901 RtlFreeUnicodeString(&ValueName); 4902 4903 if (Data.Buffer != NULL) 4904 RtlFreeUnicodeString(&Data); 4905 4906 return ErrorCode; 4907 } 4908 4909 4910 /************************************************************************ 4911 * RegSetValueExW 4912 * 4913 * @implemented 4914 */ 4915 LONG 4916 WINAPI 4917 RegSetValueExW( 4918 _In_ HKEY hKey, 4919 _In_ LPCWSTR lpValueName, 4920 _In_ DWORD Reserved, 4921 _In_ DWORD dwType, 4922 _In_ CONST BYTE* lpData, 4923 _In_ DWORD cbData) 4924 { 4925 UNICODE_STRING ValueName; 4926 HANDLE KeyHandle; 4927 NTSTATUS Status; 4928 4929 Status = MapDefaultKey(&KeyHandle, 4930 hKey); 4931 if (!NT_SUCCESS(Status)) 4932 { 4933 return RtlNtStatusToDosError(Status); 4934 } 4935 4936 if (IsHKCRKey(KeyHandle)) 4937 { 4938 LONG ErrorCode = SetHKCRValue(KeyHandle, lpValueName, Reserved, dwType, lpData, cbData); 4939 ClosePredefKey(KeyHandle); 4940 return ErrorCode; 4941 } 4942 4943 if (is_string(dwType) && (cbData != 0)) 4944 { 4945 PWSTR pwsData = (PWSTR)lpData; 4946 4947 _SEH2_TRY 4948 { 4949 if((pwsData[cbData / sizeof(WCHAR) - 1] != L'\0') && 4950 (pwsData[cbData / sizeof(WCHAR)] == L'\0')) 4951 { 4952 /* Increment length if last character is not zero and next is zero */ 4953 cbData += sizeof(WCHAR); 4954 } 4955 } 4956 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 4957 { 4958 ClosePredefKey(KeyHandle); 4959 return ERROR_NOACCESS; 4960 } 4961 _SEH2_END; 4962 } 4963 4964 RtlInitUnicodeString(&ValueName, lpValueName); 4965 4966 Status = NtSetValueKey(KeyHandle, 4967 &ValueName, 4968 0, 4969 dwType, 4970 (PVOID)lpData, 4971 (ULONG)cbData); 4972 4973 ClosePredefKey(KeyHandle); 4974 4975 if (!NT_SUCCESS(Status)) 4976 { 4977 return RtlNtStatusToDosError(Status); 4978 } 4979 4980 return ERROR_SUCCESS; 4981 } 4982 4983 4984 /************************************************************************ 4985 * RegSetValueA 4986 * 4987 * @implemented 4988 */ 4989 LONG WINAPI 4990 RegSetValueA(HKEY hKeyOriginal, 4991 LPCSTR lpSubKey, 4992 DWORD dwType, 4993 LPCSTR lpData, 4994 DWORD cbData) 4995 { 4996 HKEY subkey; 4997 HANDLE hKey; 4998 DWORD ret; 4999 NTSTATUS Status; 5000 5001 TRACE("(%p,%s,%d,%s,%d)\n", hKeyOriginal, debugstr_a(lpSubKey), dwType, debugstr_a(lpData), cbData ); 5002 5003 if (dwType != REG_SZ || !lpData) return ERROR_INVALID_PARAMETER; 5004 5005 Status = MapDefaultKey(&hKey, hKeyOriginal); 5006 if (!NT_SUCCESS(Status)) 5007 { 5008 return RtlNtStatusToDosError (Status); 5009 } 5010 subkey = hKey; 5011 5012 if (lpSubKey && lpSubKey[0]) /* need to create the subkey */ 5013 { 5014 ret = RegCreateKeyA(hKey, lpSubKey, &subkey); 5015 if (ret != ERROR_SUCCESS) 5016 goto Cleanup; 5017 } 5018 5019 ret = RegSetValueExA( subkey, NULL, 0, REG_SZ, (const BYTE*)lpData, strlen(lpData)+1 ); 5020 if (subkey != hKey) 5021 RegCloseKey(subkey); 5022 5023 Cleanup: 5024 ClosePredefKey(hKey); 5025 5026 return ret; 5027 } 5028 5029 5030 /************************************************************************ 5031 * RegSetValueW 5032 * 5033 * @implemented 5034 */ 5035 LONG WINAPI 5036 RegSetValueW(HKEY hKeyOriginal, 5037 LPCWSTR lpSubKey, 5038 DWORD dwType, 5039 LPCWSTR lpData, 5040 DWORD cbData) 5041 { 5042 HKEY subkey; 5043 HANDLE hKey; 5044 DWORD ret; 5045 NTSTATUS Status; 5046 5047 TRACE("(%p,%s,%d,%s,%d)\n", hKeyOriginal, debugstr_w(lpSubKey), dwType, debugstr_w(lpData), cbData ); 5048 5049 if (dwType != REG_SZ || !lpData) 5050 return ERROR_INVALID_PARAMETER; 5051 5052 Status = MapDefaultKey(&hKey, 5053 hKeyOriginal); 5054 if (!NT_SUCCESS(Status)) 5055 { 5056 return RtlNtStatusToDosError(Status); 5057 } 5058 subkey = hKey; 5059 5060 if (lpSubKey && lpSubKey[0]) /* need to create the subkey */ 5061 { 5062 ret = RegCreateKeyW(hKey, lpSubKey, &subkey); 5063 if (ret != ERROR_SUCCESS) 5064 goto Cleanup; 5065 } 5066 5067 ret = RegSetValueExW( subkey, NULL, 0, REG_SZ, (const BYTE*)lpData, 5068 (wcslen( lpData ) + 1) * sizeof(WCHAR) ); 5069 if (subkey != hKey) 5070 RegCloseKey(subkey); 5071 5072 Cleanup: 5073 ClosePredefKey(hKey); 5074 5075 return ret; 5076 } 5077 5078 5079 /************************************************************************ 5080 * RegUnLoadKeyA 5081 * 5082 * @implemented 5083 */ 5084 LONG WINAPI 5085 RegUnLoadKeyA(HKEY hKey, 5086 LPCSTR lpSubKey) 5087 { 5088 UNICODE_STRING KeyName; 5089 DWORD ErrorCode; 5090 5091 if (lpSubKey) 5092 { 5093 if (!RtlCreateUnicodeStringFromAsciiz(&KeyName, lpSubKey)) 5094 return ERROR_NOT_ENOUGH_MEMORY; 5095 } 5096 else 5097 RtlInitEmptyUnicodeString(&KeyName, NULL, 0); 5098 5099 ErrorCode = RegUnLoadKeyW(hKey, 5100 KeyName.Buffer); 5101 5102 RtlFreeUnicodeString (&KeyName); 5103 5104 return ErrorCode; 5105 } 5106 5107 5108 /************************************************************************ 5109 * RegUnLoadKeyW 5110 * 5111 * @implemented 5112 */ 5113 LONG WINAPI 5114 RegUnLoadKeyW(HKEY hKey, 5115 LPCWSTR lpSubKey) 5116 { 5117 OBJECT_ATTRIBUTES ObjectAttributes; 5118 UNICODE_STRING KeyName; 5119 HANDLE KeyHandle; 5120 NTSTATUS Status; 5121 5122 if (hKey == HKEY_PERFORMANCE_DATA) 5123 { 5124 return ERROR_INVALID_HANDLE; 5125 } 5126 5127 Status = MapDefaultKey(&KeyHandle, hKey); 5128 if (!NT_SUCCESS(Status)) 5129 { 5130 return RtlNtStatusToDosError(Status); 5131 } 5132 5133 RtlInitUnicodeString(&KeyName, 5134 (LPWSTR)lpSubKey); 5135 5136 InitializeObjectAttributes(&ObjectAttributes, 5137 &KeyName, 5138 OBJ_CASE_INSENSITIVE, 5139 KeyHandle, 5140 NULL); 5141 5142 Status = NtUnloadKey(&ObjectAttributes); 5143 5144 ClosePredefKey(KeyHandle); 5145 5146 if (!NT_SUCCESS(Status)) 5147 { 5148 return RtlNtStatusToDosError(Status); 5149 } 5150 5151 return ERROR_SUCCESS; 5152 } 5153 5154 /* EOF */ 5155