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