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