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