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