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)
11 * 20050502 Fireball imported some stuff from WINE
14 /* INCLUDES *****************************************************************/
18 #include <wine/debug.h>
20 /* DEFINES ******************************************************************/
22 #define MAX_DEFAULT_HANDLES 6
23 #define REG_MAX_NAME_SIZE 256
24 #define REG_MAX_DATA_SIZE 2048
26 /* FIXME: should go into msvcrt.h header? */
27 #define offsetof(s,m) (size_t)&(((s*)NULL)->m)
29 /* GLOBALS ******************************************************************/
31 static RTL_CRITICAL_SECTION HandleTableCS
;
32 static HANDLE DefaultHandleTable
[MAX_DEFAULT_HANDLES
];
33 static HANDLE ProcessHeap
;
34 static BOOLEAN DefaultHandlesDisabled
= FALSE
;
36 /* PROTOTYPES ***************************************************************/
38 static NTSTATUS
MapDefaultKey (PHANDLE ParentKey
, HKEY Key
);
39 static VOID
CloseDefaultKeys(VOID
);
40 #define CloseDefaultKey(Handle) \
41 if ((ULONG_PTR)Handle & 0x1) { \
45 static NTSTATUS
OpenClassesRootKey(PHANDLE KeyHandle
);
46 static NTSTATUS
OpenLocalMachineKey (PHANDLE KeyHandle
);
47 static NTSTATUS
OpenUsersKey (PHANDLE KeyHandle
);
48 static NTSTATUS
OpenCurrentConfigKey(PHANDLE KeyHandle
);
51 /* FUNCTIONS ****************************************************************/
52 /* check if value type needs string conversion (Ansi<->Unicode) */
53 inline static int is_string( DWORD type
)
55 return (type
== REG_SZ
) || (type
== REG_EXPAND_SZ
) || (type
== REG_MULTI_SZ
);
58 /************************************************************************
59 * RegInitDefaultHandles
64 TRACE("RegInitialize()\n");
66 ProcessHeap
= RtlGetProcessHeap();
67 RtlZeroMemory (DefaultHandleTable
,
68 MAX_DEFAULT_HANDLES
* sizeof(HANDLE
));
69 RtlInitializeCriticalSection (&HandleTableCS
);
75 /************************************************************************
81 TRACE("RegCleanup()\n");
84 RtlDeleteCriticalSection (&HandleTableCS
);
91 MapDefaultKey (OUT PHANDLE RealKey
,
97 NTSTATUS Status
= STATUS_SUCCESS
;
99 TRACE("MapDefaultKey (Key %x)\n", Key
);
101 if (((ULONG
)Key
& 0xF0000000) != 0x80000000)
103 *RealKey
= (HANDLE
)((ULONG_PTR
)Key
& ~0x1);
104 return STATUS_SUCCESS
;
107 /* Handle special cases here */
108 Index
= (ULONG
)Key
& 0x0FFFFFFF;
109 if (Index
>= MAX_DEFAULT_HANDLES
)
111 return STATUS_INVALID_PARAMETER
;
114 RtlEnterCriticalSection (&HandleTableCS
);
116 if (!DefaultHandlesDisabled
)
118 Handle
= &DefaultHandleTable
[Index
];
119 DoOpen
= (*Handle
== NULL
);
129 /* create/open the default handle */
132 case 0: /* HKEY_CLASSES_ROOT */
133 Status
= OpenClassesRootKey (Handle
);
136 case 1: /* HKEY_CURRENT_USER */
137 Status
= RtlOpenCurrentUser (MAXIMUM_ALLOWED
,
141 case 2: /* HKEY_LOCAL_MACHINE */
142 Status
= OpenLocalMachineKey (Handle
);
145 case 3: /* HKEY_USERS */
146 Status
= OpenUsersKey (Handle
);
149 case 4: /* HKEY_PERFORMANCE_DATA */
150 Status
= OpenPerformanceDataKey (Handle
);
153 case 5: /* HKEY_CURRENT_CONFIG */
154 Status
= OpenCurrentConfigKey (Handle
);
157 case 6: /* HKEY_DYN_DATA */
158 Status
= STATUS_NOT_IMPLEMENTED
;
162 WARN("MapDefaultHandle() no handle creator\n");
163 Status
= STATUS_INVALID_PARAMETER
;
168 if (NT_SUCCESS(Status
))
170 if (!DefaultHandlesDisabled
)
173 *(PULONG_PTR
)Handle
|= 0x1;
176 RtlLeaveCriticalSection (&HandleTableCS
);
183 CloseDefaultKeys (VOID
)
187 RtlEnterCriticalSection (&HandleTableCS
);
188 for (i
= 0; i
< MAX_DEFAULT_HANDLES
; i
++)
190 if (DefaultHandleTable
[i
] != NULL
)
192 NtClose (DefaultHandleTable
[i
]);
193 DefaultHandleTable
[i
] = NULL
;
196 RtlLeaveCriticalSection (&HandleTableCS
);
201 OpenClassesRootKey (PHANDLE KeyHandle
)
203 OBJECT_ATTRIBUTES Attributes
;
204 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\Software\\CLASSES");
206 TRACE("OpenClassesRootKey()\n");
208 InitializeObjectAttributes (&Attributes
,
210 OBJ_CASE_INSENSITIVE
,
213 return NtOpenKey (KeyHandle
,
220 OpenLocalMachineKey (PHANDLE KeyHandle
)
222 OBJECT_ATTRIBUTES Attributes
;
223 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine");
226 TRACE("OpenLocalMachineKey()\n");
228 InitializeObjectAttributes (&Attributes
,
230 OBJ_CASE_INSENSITIVE
,
233 Status
= NtOpenKey (KeyHandle
,
237 TRACE("NtOpenKey(%wZ) => %08x\n", &KeyName
, Status
);
243 OpenUsersKey (PHANDLE KeyHandle
)
245 OBJECT_ATTRIBUTES Attributes
;
246 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\User");
248 TRACE("OpenUsersKey()\n");
250 InitializeObjectAttributes (&Attributes
,
252 OBJ_CASE_INSENSITIVE
,
255 return NtOpenKey (KeyHandle
,
262 OpenCurrentConfigKey (PHANDLE KeyHandle
)
264 OBJECT_ATTRIBUTES Attributes
;
265 UNICODE_STRING KeyName
=
266 RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\System\\CurrentControlSet\\Hardware Profiles\\Current");
268 TRACE("OpenCurrentConfigKey()\n");
270 InitializeObjectAttributes (&Attributes
,
272 OBJ_CASE_INSENSITIVE
,
275 return NtOpenKey (KeyHandle
,
281 /************************************************************************
282 * RegDisablePredefinedCacheEx
287 RegDisablePredefinedCacheEx(VOID
)
289 RtlEnterCriticalSection (&HandleTableCS
);
290 DefaultHandlesDisabled
= TRUE
;
291 RtlLeaveCriticalSection (&HandleTableCS
);
292 return ERROR_SUCCESS
;
296 /************************************************************************
302 RegCloseKey (HKEY hKey
)
306 /* don't close null handle or a pseudo handle */
307 if ((!hKey
) || (((ULONG
)hKey
& 0xF0000000) == 0x80000000))
309 return ERROR_INVALID_HANDLE
;
312 Status
= NtClose (hKey
);
313 if (!NT_SUCCESS(Status
))
315 return RtlNtStatusToDosError (Status
);
318 return ERROR_SUCCESS
;
322 /************************************************************************
328 RegCopyTreeW(IN HKEY hKeySrc
,
329 IN LPCWSTR lpSubKey OPTIONAL
,
332 HANDLE DestKeyHandle
, KeyHandle
, CurKey
, SubKeyHandle
= NULL
;
335 Status
= MapDefaultKey(&KeyHandle
,
337 if (!NT_SUCCESS(Status
))
339 return RtlNtStatusToDosError(Status
);
342 Status
= MapDefaultKey(&DestKeyHandle
,
344 if (!NT_SUCCESS(Status
))
349 if (lpSubKey
!= NULL
)
351 OBJECT_ATTRIBUTES ObjectAttributes
;
352 UNICODE_STRING SubKeyName
;
354 RtlInitUnicodeString(&SubKeyName
,
357 InitializeObjectAttributes(&ObjectAttributes
,
359 OBJ_CASE_INSENSITIVE
,
363 Status
= NtOpenKey(&SubKeyHandle
,
366 if (!NT_SUCCESS(Status
))
371 CurKey
= SubKeyHandle
;
376 /* FIXME - copy all keys and values recursively */
377 Status
= STATUS_NOT_IMPLEMENTED
;
379 if (SubKeyHandle
!= NULL
)
381 NtClose(SubKeyHandle
);
385 CloseDefaultKey(DestKeyHandle
);
387 CloseDefaultKey(KeyHandle
);
389 if (!NT_SUCCESS(Status
))
391 return RtlNtStatusToDosError(Status
);
394 return ERROR_SUCCESS
;
398 /************************************************************************
404 RegCopyTreeA(IN HKEY hKeySrc
,
405 IN LPCSTR lpSubKey OPTIONAL
,
408 UNICODE_STRING SubKeyName
;
411 if (lpSubKey
!= NULL
)
413 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyName
,
416 return ERROR_NOT_ENOUGH_MEMORY
;
420 RtlInitUnicodeString(&SubKeyName
,
423 Ret
= RegCopyTreeW(hKeySrc
,
427 RtlFreeUnicodeString(&SubKeyName
);
433 /************************************************************************
434 * RegConnectRegistryA
439 RegConnectRegistryA (IN LPCSTR lpMachineName
,
443 UNICODE_STRING MachineName
;
446 if (lpMachineName
!= NULL
)
448 if (!RtlCreateUnicodeStringFromAsciiz(&MachineName
,
449 (LPSTR
)lpMachineName
))
451 return ERROR_NOT_ENOUGH_MEMORY
;
455 RtlInitUnicodeString(&MachineName
,
458 Ret
= RegConnectRegistryW(MachineName
.Buffer
,
462 RtlFreeUnicodeString(&MachineName
);
468 /************************************************************************
469 * RegConnectRegistryW
474 RegConnectRegistryW (LPCWSTR lpMachineName
,
478 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
479 return ERROR_CALL_NOT_IMPLEMENTED
;
483 /************************************************************************
486 * Create key and all necessary intermediate keys
489 CreateNestedKey(PHKEY KeyHandle
,
490 POBJECT_ATTRIBUTES ObjectAttributes
,
491 PUNICODE_STRING ClassString
,
494 DWORD
*lpdwDisposition
)
496 OBJECT_ATTRIBUTES LocalObjectAttributes
;
497 UNICODE_STRING LocalKeyName
;
500 ULONG FullNameLength
;
503 HANDLE LocalKeyHandle
;
505 Status
= NtCreateKey((PHANDLE
) KeyHandle
,
511 (PULONG
)lpdwDisposition
);
512 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes
->ObjectName
, Status
);
513 if (Status
!= STATUS_OBJECT_NAME_NOT_FOUND
)
516 /* Copy object attributes */
517 RtlCopyMemory (&LocalObjectAttributes
,
519 sizeof(OBJECT_ATTRIBUTES
));
520 RtlCreateUnicodeString (&LocalKeyName
,
521 ObjectAttributes
->ObjectName
->Buffer
);
522 LocalObjectAttributes
.ObjectName
= &LocalKeyName
;
523 FullNameLength
= LocalKeyName
.Length
/ sizeof(WCHAR
);
525 /* Remove the last part of the key name and try to create the key again. */
526 while (Status
== STATUS_OBJECT_NAME_NOT_FOUND
)
528 Ptr
= wcsrchr (LocalKeyName
.Buffer
, '\\');
529 if (Ptr
== NULL
|| Ptr
== LocalKeyName
.Buffer
)
531 Status
= STATUS_UNSUCCESSFUL
;
535 LocalKeyName
.Length
= wcslen (LocalKeyName
.Buffer
) * sizeof(WCHAR
);
537 Status
= NtCreateKey (&LocalKeyHandle
,
539 &LocalObjectAttributes
,
544 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName
, Status
);
547 if (!NT_SUCCESS(Status
))
549 RtlFreeUnicodeString (&LocalKeyName
);
553 /* Add removed parts of the key name and create them too. */
554 Length
= wcslen (LocalKeyName
.Buffer
);
557 NtClose (LocalKeyHandle
);
559 LocalKeyName
.Buffer
[Length
] = L
'\\';
560 Length
= wcslen (LocalKeyName
.Buffer
);
561 LocalKeyName
.Length
= Length
* sizeof(WCHAR
);
563 if (Length
== FullNameLength
)
565 Status
= NtCreateKey((PHANDLE
) KeyHandle
,
571 (PULONG
)lpdwDisposition
);
574 Status
= NtCreateKey (&LocalKeyHandle
,
576 &LocalObjectAttributes
,
581 TRACE("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName
, Status
);
582 if (!NT_SUCCESS(Status
))
586 RtlFreeUnicodeString (&LocalKeyName
);
592 /************************************************************************
598 RegCreateKeyExA (HKEY hKey
,
604 LPSECURITY_ATTRIBUTES lpSecurityAttributes
,
606 LPDWORD lpdwDisposition
)
608 UNICODE_STRING SubKeyString
;
609 UNICODE_STRING ClassString
;
610 OBJECT_ATTRIBUTES Attributes
;
614 TRACE("RegCreateKeyExA() called\n");
616 /* get the real parent key */
617 Status
= MapDefaultKey (&ParentKey
,
619 if (!NT_SUCCESS(Status
))
621 return RtlNtStatusToDosError (Status
);
623 TRACE("ParentKey %x\n", (ULONG
)ParentKey
);
627 RtlCreateUnicodeStringFromAsciiz (&ClassString
,
631 RtlCreateUnicodeStringFromAsciiz(&SubKeyString
,
633 InitializeObjectAttributes (&Attributes
,
635 OBJ_CASE_INSENSITIVE
,
637 (PSECURITY_DESCRIPTOR
)lpSecurityAttributes
);
638 Status
= CreateNestedKey(phkResult
,
640 (lpClass
== NULL
)? NULL
: &ClassString
,
644 RtlFreeUnicodeString (&SubKeyString
);
647 RtlFreeUnicodeString (&ClassString
);
650 CloseDefaultKey(ParentKey
);
652 TRACE("Status %x\n", Status
);
653 if (!NT_SUCCESS(Status
))
655 return RtlNtStatusToDosError (Status
);
658 return ERROR_SUCCESS
;
662 /************************************************************************
668 RegCreateKeyExW (HKEY hKey
,
674 LPSECURITY_ATTRIBUTES lpSecurityAttributes
,
676 LPDWORD lpdwDisposition
)
678 UNICODE_STRING SubKeyString
;
679 UNICODE_STRING ClassString
;
680 OBJECT_ATTRIBUTES Attributes
;
684 TRACE("RegCreateKeyExW() called\n");
686 /* get the real parent key */
687 Status
= MapDefaultKey (&ParentKey
,
689 if (!NT_SUCCESS(Status
))
691 return RtlNtStatusToDosError(Status
);
693 TRACE("ParentKey %x\n", (ULONG
)ParentKey
);
695 RtlInitUnicodeString (&ClassString
,
697 RtlInitUnicodeString (&SubKeyString
,
699 InitializeObjectAttributes (&Attributes
,
701 OBJ_CASE_INSENSITIVE
,
703 (PSECURITY_DESCRIPTOR
)lpSecurityAttributes
);
704 Status
= CreateNestedKey(phkResult
,
706 (lpClass
== NULL
)? NULL
: &ClassString
,
711 CloseDefaultKey(ParentKey
);
713 TRACE("Status %x\n", Status
);
714 if (!NT_SUCCESS(Status
))
716 return RtlNtStatusToDosError (Status
);
719 return ERROR_SUCCESS
;
723 /************************************************************************
729 RegCreateKeyA (HKEY hKey
,
733 return RegCreateKeyExA (hKey
,
745 /************************************************************************
751 RegCreateKeyW (HKEY hKey
,
755 return RegCreateKeyExW (hKey
,
767 /************************************************************************
773 RegDeleteKeyA (HKEY hKey
,
776 OBJECT_ATTRIBUTES ObjectAttributes
;
777 UNICODE_STRING SubKeyName
;
782 Status
= MapDefaultKey (&ParentKey
,
784 if (!NT_SUCCESS(Status
))
786 return RtlNtStatusToDosError (Status
);
789 RtlCreateUnicodeStringFromAsciiz (&SubKeyName
,
791 InitializeObjectAttributes(&ObjectAttributes
,
793 OBJ_CASE_INSENSITIVE
,
797 Status
= NtOpenKey (&TargetKey
,
800 RtlFreeUnicodeString (&SubKeyName
);
801 if (!NT_SUCCESS(Status
))
806 Status
= NtDeleteKey (TargetKey
);
810 CloseDefaultKey(ParentKey
);
812 if (!NT_SUCCESS(Status
))
814 return RtlNtStatusToDosError(Status
);
817 return ERROR_SUCCESS
;
821 /************************************************************************
827 RegDeleteKeyW (HKEY hKey
,
830 OBJECT_ATTRIBUTES ObjectAttributes
;
831 UNICODE_STRING SubKeyName
;
836 Status
= MapDefaultKey (&ParentKey
,
838 if (!NT_SUCCESS(Status
))
840 return RtlNtStatusToDosError (Status
);
843 RtlInitUnicodeString (&SubKeyName
,
845 InitializeObjectAttributes (&ObjectAttributes
,
847 OBJ_CASE_INSENSITIVE
,
850 Status
= NtOpenKey (&TargetKey
,
853 if (!NT_SUCCESS(Status
))
858 Status
= NtDeleteKey (TargetKey
);
862 CloseDefaultKey(ParentKey
);
864 if (!NT_SUCCESS(Status
))
866 return RtlNtStatusToDosError (Status
);
869 return ERROR_SUCCESS
;
873 /************************************************************************
879 RegDeleteKeyValueW(IN HKEY hKey
,
880 IN LPCWSTR lpSubKey OPTIONAL
,
881 IN LPCWSTR lpValueName OPTIONAL
)
883 UNICODE_STRING ValueName
;
884 HANDLE KeyHandle
, CurKey
, SubKeyHandle
= NULL
;
887 Status
= MapDefaultKey(&KeyHandle
,
889 if (!NT_SUCCESS(Status
))
891 return RtlNtStatusToDosError(Status
);
894 if (lpSubKey
!= NULL
)
896 OBJECT_ATTRIBUTES ObjectAttributes
;
897 UNICODE_STRING SubKeyName
;
899 RtlInitUnicodeString(&SubKeyName
,
902 InitializeObjectAttributes(&ObjectAttributes
,
904 OBJ_CASE_INSENSITIVE
,
908 Status
= NtOpenKey(&SubKeyHandle
,
911 if (!NT_SUCCESS(Status
))
916 CurKey
= SubKeyHandle
;
921 RtlInitUnicodeString(&ValueName
,
922 (LPWSTR
)lpValueName
);
924 Status
= NtDeleteValueKey(CurKey
,
927 if (SubKeyHandle
!= NULL
)
929 NtClose(SubKeyHandle
);
933 CloseDefaultKey(KeyHandle
);
935 if (!NT_SUCCESS(Status
))
937 return RtlNtStatusToDosError(Status
);
940 return ERROR_SUCCESS
;
944 /************************************************************************
950 RegDeleteKeyValueA(IN HKEY hKey
,
951 IN LPCSTR lpSubKey OPTIONAL
,
952 IN LPCSTR lpValueName OPTIONAL
)
954 UNICODE_STRING SubKey
, ValueName
;
957 if (lpSubKey
!= NULL
)
959 if (!RtlCreateUnicodeStringFromAsciiz(&SubKey
,
962 return ERROR_NOT_ENOUGH_MEMORY
;
966 RtlInitUnicodeString(&SubKey
,
969 if (lpValueName
!= NULL
)
971 if (!RtlCreateUnicodeStringFromAsciiz(&ValueName
,
974 RtlFreeUnicodeString(&SubKey
);
975 return ERROR_NOT_ENOUGH_MEMORY
;
979 RtlInitUnicodeString(&ValueName
,
982 Ret
= RegDeleteKeyValueW(hKey
,
986 RtlFreeUnicodeString(&SubKey
);
987 RtlFreeUnicodeString(&ValueName
);
994 RegpDeleteTree(IN HKEY hKey
)
998 LIST_ENTRY ListEntry
;
1000 } REGP_DEL_KEYS
, *PREG_DEL_KEYS
;
1002 LIST_ENTRY delQueueHead
;
1003 PREG_DEL_KEYS delKeys
, newDelKeys
;
1006 PKEY_BASIC_INFORMATION BasicInfo
;
1007 PREG_DEL_KEYS KeyDelRoot
;
1008 NTSTATUS Status
= STATUS_SUCCESS
;
1009 NTSTATUS Status2
= STATUS_SUCCESS
;
1011 InitializeListHead(&delQueueHead
);
1013 ProcessHeap
= RtlGetProcessHeap();
1015 /* NOTE: no need to allocate enough memory for an additional KEY_BASIC_INFORMATION
1016 structure for the root key, we only do that for subkeys as we need to
1017 allocate REGP_DEL_KEYS structures anyway! */
1018 KeyDelRoot
= RtlAllocateHeap(ProcessHeap
,
1020 sizeof(REGP_DEL_KEYS
));
1021 if (KeyDelRoot
!= NULL
)
1023 KeyDelRoot
->KeyHandle
= hKey
;
1024 InsertTailList(&delQueueHead
,
1025 &KeyDelRoot
->ListEntry
);
1029 delKeys
= CONTAINING_RECORD(delQueueHead
.Flink
,
1038 /* check if this key contains subkeys and delete them first by queuing
1039 them at the head of the list */
1040 Status2
= NtEnumerateKey(delKeys
->KeyHandle
,
1042 KeyBasicInformation
,
1047 if (NT_SUCCESS(Status2
))
1049 OBJECT_ATTRIBUTES ObjectAttributes
;
1050 UNICODE_STRING SubKeyName
;
1052 ASSERT(newDelKeys
!= NULL
);
1053 ASSERT(BasicInfo
!= NULL
);
1055 /* don't use RtlInitUnicodeString as the string is not NULL-terminated! */
1056 SubKeyName
.Length
= BasicInfo
->NameLength
;
1057 SubKeyName
.MaximumLength
= BasicInfo
->NameLength
;
1058 SubKeyName
.Buffer
= BasicInfo
->Name
;
1060 InitializeObjectAttributes(&ObjectAttributes
,
1062 OBJ_CASE_INSENSITIVE
,
1066 /* open the subkey */
1067 Status2
= NtOpenKey(&newDelKeys
->KeyHandle
,
1068 DELETE
| KEY_ENUMERATE_SUB_KEYS
| KEY_QUERY_VALUE
,
1070 if (!NT_SUCCESS(Status2
))
1075 /* enqueue this key to the head of the deletion queue */
1076 InsertHeadList(&delQueueHead
,
1077 &newDelKeys
->ListEntry
);
1079 /* try again from the head of the list */
1084 if (Status2
== STATUS_BUFFER_TOO_SMALL
)
1086 newDelKeys
= RtlAllocateHeap(ProcessHeap
,
1088 BufferSize
+ sizeof(REGP_DEL_KEYS
));
1089 if (newDelKeys
!= NULL
)
1091 BasicInfo
= (PKEY_BASIC_INFORMATION
)(newDelKeys
+ 1);
1094 goto ReadFirstSubKey
;
1098 /* don't break, let's try to delete as many keys as possible */
1099 Status2
= STATUS_INSUFFICIENT_RESOURCES
;
1100 goto SubKeyFailureNoFree
;
1103 else if (Status2
== STATUS_BUFFER_OVERFLOW
)
1105 PREG_DEL_KEYS newDelKeys2
;
1107 ASSERT(newDelKeys
!= NULL
);
1109 /* we need more memory to query the key name */
1110 newDelKeys2
= RtlReAllocateHeap(ProcessHeap
,
1113 BufferSize
+ sizeof(REGP_DEL_KEYS
));
1114 if (newDelKeys2
!= NULL
)
1116 newDelKeys
= newDelKeys2
;
1117 BasicInfo
= (PKEY_BASIC_INFORMATION
)(newDelKeys
+ 1);
1120 goto ReadFirstSubKey
;
1124 /* don't break, let's try to delete as many keys as possible */
1125 Status2
= STATUS_INSUFFICIENT_RESOURCES
;
1130 ASSERT(newDelKeys
!= NULL
);
1131 RtlFreeHeap(ProcessHeap
,
1135 SubKeyFailureNoFree
:
1136 /* don't break, let's try to delete as many keys as possible */
1137 if (Status2
!= STATUS_NO_MORE_ENTRIES
&& NT_SUCCESS(Status
))
1143 Status2
= NtDeleteKey(delKeys
->KeyHandle
);
1145 /* NOTE: do NOT close the handle anymore, it's invalid already! */
1147 if (!NT_SUCCESS(Status2
))
1149 /* close the key handle so we don't leak handles for keys we were
1150 unable to delete. But only do this for handles not supplied
1153 if (delKeys
->KeyHandle
!= hKey
)
1155 NtClose(delKeys
->KeyHandle
);
1158 if (!NT_SUCCESS(Status
))
1160 /* don't break, let's try to delete as many keys as possible */
1165 /* remove the entry from the list */
1166 RemoveEntryList(&delKeys
->ListEntry
);
1168 RtlFreeHeap(ProcessHeap
,
1171 } while (!IsListEmpty(&delQueueHead
));
1174 Status
= STATUS_INSUFFICIENT_RESOURCES
;
1180 /************************************************************************
1186 RegDeleteTreeW(IN HKEY hKey
,
1187 IN LPCWSTR lpSubKey OPTIONAL
)
1189 HANDLE KeyHandle
, CurKey
, SubKeyHandle
= NULL
;
1192 Status
= MapDefaultKey(&KeyHandle
,
1194 if (!NT_SUCCESS(Status
))
1196 return RtlNtStatusToDosError(Status
);
1199 if (lpSubKey
!= NULL
)
1201 OBJECT_ATTRIBUTES ObjectAttributes
;
1202 UNICODE_STRING SubKeyName
;
1204 RtlInitUnicodeString(&SubKeyName
,
1207 InitializeObjectAttributes(&ObjectAttributes
,
1209 OBJ_CASE_INSENSITIVE
,
1213 Status
= NtOpenKey(&SubKeyHandle
,
1214 DELETE
| KEY_ENUMERATE_SUB_KEYS
| KEY_QUERY_VALUE
,
1216 if (!NT_SUCCESS(Status
))
1221 CurKey
= SubKeyHandle
;
1226 Status
= RegpDeleteTree(CurKey
);
1228 if (NT_SUCCESS(Status
))
1230 /* make sure we only close hKey (KeyHandle) when the caller specified a
1231 subkey, because the handle would be invalid already! */
1232 if (CurKey
!= KeyHandle
)
1234 CloseDefaultKey(KeyHandle
);
1237 return ERROR_SUCCESS
;
1241 /* make sure we close all handles we created! */
1242 if (SubKeyHandle
!= NULL
)
1244 NtClose(SubKeyHandle
);
1248 CloseDefaultKey(KeyHandle
);
1250 return RtlNtStatusToDosError(Status
);
1255 /************************************************************************
1261 RegDeleteTreeA(IN HKEY hKey
,
1262 IN LPCSTR lpSubKey OPTIONAL
)
1264 UNICODE_STRING SubKeyName
;
1267 if (lpSubKey
!= NULL
)
1269 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyName
,
1272 return ERROR_NOT_ENOUGH_MEMORY
;
1276 RtlInitUnicodeString(&SubKeyName
,
1279 Ret
= RegDeleteTreeW(hKey
,
1282 RtlFreeUnicodeString(&SubKeyName
);
1288 /************************************************************************
1294 RegSetKeyValueW(IN HKEY hKey
,
1295 IN LPCWSTR lpSubKey OPTIONAL
,
1296 IN LPCWSTR lpValueName OPTIONAL
,
1298 IN LPCVOID lpData OPTIONAL
,
1301 HANDLE KeyHandle
, CurKey
, SubKeyHandle
= NULL
;
1305 Status
= MapDefaultKey(&KeyHandle
,
1307 if (!NT_SUCCESS(Status
))
1309 return RtlNtStatusToDosError(Status
);
1312 if (lpSubKey
!= NULL
)
1314 OBJECT_ATTRIBUTES ObjectAttributes
;
1315 UNICODE_STRING SubKeyName
;
1317 RtlInitUnicodeString(&SubKeyName
,
1320 InitializeObjectAttributes(&ObjectAttributes
,
1322 OBJ_CASE_INSENSITIVE
,
1326 Status
= NtOpenKey(&SubKeyHandle
,
1329 if (!NT_SUCCESS(Status
))
1331 Ret
= RtlNtStatusToDosError(Status
);
1335 CurKey
= SubKeyHandle
;
1340 Ret
= RegSetValueExW(CurKey
,
1347 if (SubKeyHandle
!= NULL
)
1349 NtClose(SubKeyHandle
);
1353 CloseDefaultKey(KeyHandle
);
1359 /************************************************************************
1365 RegSetKeyValueA(IN HKEY hKey
,
1366 IN LPCSTR lpSubKey OPTIONAL
,
1367 IN LPCSTR lpValueName OPTIONAL
,
1369 IN LPCVOID lpData OPTIONAL
,
1372 HANDLE KeyHandle
, CurKey
, SubKeyHandle
= NULL
;
1376 Status
= MapDefaultKey(&KeyHandle
,
1378 if (!NT_SUCCESS(Status
))
1380 return RtlNtStatusToDosError(Status
);
1383 if (lpSubKey
!= NULL
)
1385 OBJECT_ATTRIBUTES ObjectAttributes
;
1386 UNICODE_STRING SubKeyName
;
1388 if (!RtlCreateUnicodeStringFromAsciiz(&SubKeyName
,
1391 Ret
= ERROR_NOT_ENOUGH_MEMORY
;
1395 InitializeObjectAttributes(&ObjectAttributes
,
1397 OBJ_CASE_INSENSITIVE
,
1401 Status
= NtOpenKey(&SubKeyHandle
,
1405 RtlFreeUnicodeString(&SubKeyName
);
1407 if (!NT_SUCCESS(Status
))
1409 Ret
= RtlNtStatusToDosError(Status
);
1413 CurKey
= SubKeyHandle
;
1418 Ret
= RegSetValueExA(CurKey
,
1425 if (SubKeyHandle
!= NULL
)
1427 NtClose(SubKeyHandle
);
1431 CloseDefaultKey(KeyHandle
);
1437 /************************************************************************
1443 RegDeleteValueA (HKEY hKey
,
1446 UNICODE_STRING ValueName
;
1450 Status
= MapDefaultKey (&KeyHandle
,
1452 if (!NT_SUCCESS(Status
))
1454 return RtlNtStatusToDosError (Status
);
1457 RtlCreateUnicodeStringFromAsciiz (&ValueName
,
1458 (LPSTR
)lpValueName
);
1459 Status
= NtDeleteValueKey (KeyHandle
,
1461 RtlFreeUnicodeString (&ValueName
);
1463 CloseDefaultKey(KeyHandle
);
1465 if (!NT_SUCCESS(Status
))
1467 return RtlNtStatusToDosError (Status
);
1470 return ERROR_SUCCESS
;
1474 /************************************************************************
1480 RegDeleteValueW (HKEY hKey
,
1481 LPCWSTR lpValueName
)
1483 UNICODE_STRING ValueName
;
1487 Status
= MapDefaultKey (&KeyHandle
,
1489 if (!NT_SUCCESS(Status
))
1491 return RtlNtStatusToDosError (Status
);
1494 RtlInitUnicodeString (&ValueName
,
1495 (LPWSTR
)lpValueName
);
1497 Status
= NtDeleteValueKey (KeyHandle
,
1500 CloseDefaultKey(KeyHandle
);
1502 if (!NT_SUCCESS(Status
))
1504 return RtlNtStatusToDosError (Status
);
1507 return ERROR_SUCCESS
;
1511 /************************************************************************
1517 RegEnumKeyA (HKEY hKey
,
1525 return RegEnumKeyExA (hKey
,
1536 /************************************************************************
1542 RegEnumKeyW (HKEY hKey
,
1550 return RegEnumKeyExW (hKey
,
1561 /************************************************************************
1567 RegEnumKeyExA (HKEY hKey
,
1574 PFILETIME lpftLastWriteTime
)
1578 KEY_NODE_INFORMATION Node
;
1579 KEY_BASIC_INFORMATION Basic
;
1582 UNICODE_STRING StringU
;
1583 ANSI_STRING StringA
;
1584 LONG ErrorCode
= ERROR_SUCCESS
;
1586 DWORD ClassLength
= 0;
1592 TRACE("RegEnumKeyExA(hKey 0x%x, dwIndex %d, lpName 0x%x, *lpcbName %d, lpClass 0x%x, lpcbClass %d)\n",
1593 hKey
, dwIndex
, lpName
, *lpcbName
, lpClass
, lpcbClass
? *lpcbClass
: 0);
1595 if ((lpClass
) && (!lpcbClass
))
1597 return ERROR_INVALID_PARAMETER
;
1600 Status
= MapDefaultKey(&KeyHandle
, hKey
);
1601 if (!NT_SUCCESS(Status
))
1603 return RtlNtStatusToDosError (Status
);
1608 NameLength
= min (*lpcbName
- 1 , REG_MAX_NAME_SIZE
) * sizeof (WCHAR
);
1619 ClassLength
= min (*lpcbClass
-1, REG_MAX_NAME_SIZE
) * sizeof(WCHAR
);
1626 /* The class name should start at a dword boundary */
1627 BufferSize
= ((sizeof(KEY_NODE_INFORMATION
) + NameLength
+ 3) & ~3) + ClassLength
;
1631 BufferSize
= sizeof(KEY_BASIC_INFORMATION
) + NameLength
;
1634 KeyInfo
= RtlAllocateHeap (ProcessHeap
, 0, BufferSize
);
1635 if (KeyInfo
== NULL
)
1637 ErrorCode
= ERROR_OUTOFMEMORY
;
1641 Status
= NtEnumerateKey (KeyHandle
,
1643 lpClass
== NULL
? KeyBasicInformation
: KeyNodeInformation
,
1647 TRACE("NtEnumerateKey() returned status 0x%X\n", Status
);
1648 if (!NT_SUCCESS(Status
))
1650 ErrorCode
= RtlNtStatusToDosError (Status
);
1654 if (lpClass
== NULL
)
1656 if (KeyInfo
->Basic
.NameLength
> NameLength
)
1658 ErrorCode
= ERROR_BUFFER_OVERFLOW
;
1662 StringU
.Buffer
= KeyInfo
->Basic
.Name
;
1663 StringU
.Length
= KeyInfo
->Basic
.NameLength
;
1664 StringU
.MaximumLength
= KeyInfo
->Basic
.NameLength
;
1669 if (KeyInfo
->Node
.NameLength
> NameLength
||
1670 KeyInfo
->Node
.ClassLength
> ClassLength
)
1672 ErrorCode
= ERROR_BUFFER_OVERFLOW
;
1676 StringA
.Buffer
= lpClass
;
1678 StringA
.MaximumLength
= *lpcbClass
;
1679 StringU
.Buffer
= (PWCHAR
)((ULONG_PTR
)KeyInfo
->Node
.Name
+ KeyInfo
->Node
.ClassOffset
);
1680 StringU
.Length
= KeyInfo
->Node
.ClassLength
;
1681 StringU
.MaximumLength
= KeyInfo
->Node
.ClassLength
;
1682 RtlUnicodeStringToAnsiString (&StringA
, &StringU
, FALSE
);
1683 lpClass
[StringA
.Length
] = 0;
1684 *lpcbClass
= StringA
.Length
;
1685 StringU
.Buffer
= KeyInfo
->Node
.Name
;
1686 StringU
.Length
= KeyInfo
->Node
.NameLength
;
1687 StringU
.MaximumLength
= KeyInfo
->Node
.NameLength
;
1691 if (ErrorCode
== ERROR_SUCCESS
)
1693 StringA
.Buffer
= lpName
;
1695 StringA
.MaximumLength
= *lpcbName
;
1696 RtlUnicodeStringToAnsiString (&StringA
, &StringU
, FALSE
);
1697 lpName
[StringA
.Length
] = 0;
1698 *lpcbName
= StringA
.Length
;
1699 if (lpftLastWriteTime
!= NULL
)
1701 if (lpClass
== NULL
)
1703 lpftLastWriteTime
->dwLowDateTime
= KeyInfo
->Basic
.LastWriteTime
.u
.LowPart
;
1704 lpftLastWriteTime
->dwHighDateTime
= KeyInfo
->Basic
.LastWriteTime
.u
.HighPart
;
1708 lpftLastWriteTime
->dwLowDateTime
= KeyInfo
->Node
.LastWriteTime
.u
.LowPart
;
1709 lpftLastWriteTime
->dwHighDateTime
= KeyInfo
->Node
.LastWriteTime
.u
.HighPart
;
1715 TRACE("Key Namea0 Length %d\n", StringU
.Length
);
1716 TRACE("Key Namea1 Length %d\n", NameLength
);
1717 TRACE("Key Namea Length %d\n", *lpcbName
);
1718 TRACE("Key Namea %s\n", lpName
);
1720 RtlFreeHeap (ProcessHeap
,
1725 CloseDefaultKey(KeyHandle
);
1731 /************************************************************************
1737 RegEnumKeyExW (HKEY hKey
,
1744 PFILETIME lpftLastWriteTime
)
1748 KEY_NODE_INFORMATION Node
;
1749 KEY_BASIC_INFORMATION Basic
;
1755 ULONG ClassLength
= 0;
1757 LONG ErrorCode
= ERROR_SUCCESS
;
1760 Status
= MapDefaultKey(&KeyHandle
,
1762 if (!NT_SUCCESS(Status
))
1764 return RtlNtStatusToDosError (Status
);
1769 NameLength
= min (*lpcbName
- 1, REG_MAX_NAME_SIZE
) * sizeof (WCHAR
);
1780 ClassLength
= min (*lpcbClass
- 1, REG_MAX_NAME_SIZE
) * sizeof(WCHAR
);
1787 BufferSize
= ((sizeof(KEY_NODE_INFORMATION
) + NameLength
+ 3) & ~3) + ClassLength
;
1791 BufferSize
= sizeof(KEY_BASIC_INFORMATION
) + NameLength
;
1794 KeyInfo
= RtlAllocateHeap (ProcessHeap
,
1797 if (KeyInfo
== NULL
)
1799 ErrorCode
= ERROR_OUTOFMEMORY
;
1803 Status
= NtEnumerateKey (KeyHandle
,
1805 lpClass
? KeyNodeInformation
: KeyBasicInformation
,
1809 TRACE("NtEnumerateKey() returned status 0x%X\n", Status
);
1810 if (!NT_SUCCESS(Status
))
1812 ErrorCode
= RtlNtStatusToDosError (Status
);
1816 if (lpClass
== NULL
)
1818 if (KeyInfo
->Basic
.NameLength
> NameLength
)
1820 ErrorCode
= ERROR_BUFFER_OVERFLOW
;
1824 RtlCopyMemory (lpName
,
1825 KeyInfo
->Basic
.Name
,
1826 KeyInfo
->Basic
.NameLength
);
1827 *lpcbName
= (DWORD
)(KeyInfo
->Basic
.NameLength
/ sizeof(WCHAR
));
1828 lpName
[*lpcbName
] = 0;
1833 if (KeyInfo
->Node
.NameLength
> NameLength
||
1834 KeyInfo
->Node
.ClassLength
> ClassLength
)
1836 ErrorCode
= ERROR_BUFFER_OVERFLOW
;
1840 RtlCopyMemory (lpName
,
1842 KeyInfo
->Node
.NameLength
);
1843 *lpcbName
= KeyInfo
->Node
.NameLength
/ sizeof(WCHAR
);
1844 lpName
[*lpcbName
] = 0;
1845 RtlCopyMemory (lpClass
,
1846 (PVOID
)((ULONG_PTR
)KeyInfo
->Node
.Name
+ KeyInfo
->Node
.ClassOffset
),
1847 KeyInfo
->Node
.ClassLength
);
1848 *lpcbClass
= (DWORD
)(KeyInfo
->Node
.ClassLength
/ sizeof(WCHAR
));
1849 lpClass
[*lpcbClass
] = 0;
1853 if (ErrorCode
== ERROR_SUCCESS
&& lpftLastWriteTime
!= NULL
)
1855 if (lpClass
== NULL
)
1857 lpftLastWriteTime
->dwLowDateTime
= KeyInfo
->Basic
.LastWriteTime
.u
.LowPart
;
1858 lpftLastWriteTime
->dwHighDateTime
= KeyInfo
->Basic
.LastWriteTime
.u
.HighPart
;
1862 lpftLastWriteTime
->dwLowDateTime
= KeyInfo
->Node
.LastWriteTime
.u
.LowPart
;
1863 lpftLastWriteTime
->dwHighDateTime
= KeyInfo
->Node
.LastWriteTime
.u
.HighPart
;
1868 RtlFreeHeap (ProcessHeap
,
1873 CloseDefaultKey(KeyHandle
);
1878 /************************************************************************
1884 RegEnumValueA( HKEY hKey
, DWORD index
, LPSTR value
, LPDWORD val_count
,
1885 LPDWORD reserved
, LPDWORD type
, LPBYTE data
, LPDWORD count
)
1890 char buffer
[256], *buf_ptr
= buffer
;
1891 KEY_VALUE_FULL_INFORMATION
*info
= (KEY_VALUE_FULL_INFORMATION
*)buffer
;
1892 static const int info_size
= offsetof( KEY_VALUE_FULL_INFORMATION
, Name
);
1894 //TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
1895 // hkey, index, value, val_count, reserved, type, data, count );
1897 /* NT only checks count, not val_count */
1898 if ((data
&& !count
) || reserved
) return ERROR_INVALID_PARAMETER
;
1899 status
= MapDefaultKey (&KeyHandle
, hKey
);
1900 if (!NT_SUCCESS(status
))
1902 return RtlNtStatusToDosError (status
);
1905 total_size
= info_size
+ (MAX_PATH
+ 1) * sizeof(WCHAR
);
1906 if (data
) total_size
+= *count
;
1907 total_size
= min( sizeof(buffer
), total_size
);
1909 status
= NtEnumerateValueKey( KeyHandle
, index
, KeyValueFullInformation
,
1910 buffer
, total_size
, &total_size
);
1911 if (status
&& status
!= STATUS_BUFFER_OVERFLOW
) goto done
;
1913 /* we need to fetch the contents for a string type even if not requested,
1914 * because we need to compute the length of the ASCII string. */
1915 if (value
|| data
|| is_string(info
->Type
))
1917 /* retry with a dynamically allocated buffer */
1918 while (status
== STATUS_BUFFER_OVERFLOW
)
1920 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
1921 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
1923 status
= STATUS_INSUFFICIENT_RESOURCES
;
1926 info
= (KEY_VALUE_FULL_INFORMATION
*)buf_ptr
;
1927 status
= NtEnumerateValueKey( KeyHandle
, index
, KeyValueFullInformation
,
1928 buf_ptr
, total_size
, &total_size
);
1931 if (status
) goto done
;
1933 if (is_string(info
->Type
))
1936 RtlUnicodeToMultiByteSize( &len
, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
1937 total_size
- info
->DataOffset
);
1940 if (len
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
1943 RtlUnicodeToMultiByteN( (PCHAR
)data
, len
, NULL
, (WCHAR
*)(buf_ptr
+ info
->DataOffset
),
1944 total_size
- info
->DataOffset
);
1945 /* if the type is REG_SZ and data is not 0-terminated
1946 * and there is enough space in the buffer NT appends a \0 */
1947 if (len
< *count
&& data
[len
-1]) data
[len
] = 0;
1950 info
->DataLength
= len
;
1954 if (total_size
- info
->DataOffset
> *count
) status
= STATUS_BUFFER_OVERFLOW
;
1955 else memcpy( data
, buf_ptr
+ info
->DataOffset
, total_size
- info
->DataOffset
);
1958 if (value
&& !status
)
1962 RtlUnicodeToMultiByteSize( &len
, info
->Name
, info
->NameLength
);
1963 if (len
>= *val_count
)
1965 status
= STATUS_BUFFER_OVERFLOW
;
1968 len
= *val_count
- 1;
1969 RtlUnicodeToMultiByteN( value
, len
, NULL
, info
->Name
, info
->NameLength
);
1975 RtlUnicodeToMultiByteN( value
, len
, NULL
, info
->Name
, info
->NameLength
);
1981 else status
= STATUS_SUCCESS
;
1983 if (type
) *type
= info
->Type
;
1984 if (count
) *count
= info
->DataLength
;
1987 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
1988 CloseDefaultKey(KeyHandle
);
1989 return RtlNtStatusToDosError(status
);
1992 /******************************************************************************
1993 * RegEnumValueW [ADVAPI32.@]
1997 * hkey [I] Handle to key to query
1998 * index [I] Index of value to query
1999 * value [O] Value string
2000 * val_count [I/O] Size of value buffer (in wchars)
2001 * reserved [I] Reserved
2002 * type [O] Type code
2003 * data [O] Value data
2004 * count [I/O] Size of data buffer (in bytes)
2007 * Success: ERROR_SUCCESS
2008 * Failure: nonzero error code from Winerror.h
2011 RegEnumValueW( HKEY hKey
, DWORD index
, LPWSTR value
, PDWORD val_count
,
2012 PDWORD reserved
, PDWORD type
, LPBYTE data
, PDWORD count
)
2017 char buffer
[256], *buf_ptr
= buffer
;
2018 KEY_VALUE_FULL_INFORMATION
*info
= (KEY_VALUE_FULL_INFORMATION
*)buffer
;
2019 static const int info_size
= offsetof( KEY_VALUE_FULL_INFORMATION
, Name
);
2021 //TRACE("(%p,%ld,%p,%p,%p,%p,%p,%p)\n",
2022 // hkey, index, value, val_count, reserved, type, data, count );
2024 /* NT only checks count, not val_count */
2025 if ((data
&& !count
) || reserved
) return ERROR_INVALID_PARAMETER
;
2027 status
= MapDefaultKey (&KeyHandle
, hKey
);
2028 if (!NT_SUCCESS(status
))
2030 return RtlNtStatusToDosError (status
);
2033 total_size
= info_size
+ (MAX_PATH
+ 1) * sizeof(WCHAR
);
2034 if (data
) total_size
+= *count
;
2035 total_size
= min( sizeof(buffer
), total_size
);
2037 status
= NtEnumerateValueKey( KeyHandle
, index
, KeyValueFullInformation
,
2038 buffer
, total_size
, &total_size
);
2039 if (status
&& status
!= STATUS_BUFFER_OVERFLOW
) goto done
;
2043 /* retry with a dynamically allocated buffer */
2044 while (status
== STATUS_BUFFER_OVERFLOW
)
2046 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
2047 if (!(buf_ptr
= HeapAlloc( GetProcessHeap(), 0, total_size
)))
2049 status
= ERROR_NOT_ENOUGH_MEMORY
;
2052 info
= (KEY_VALUE_FULL_INFORMATION
*)buf_ptr
;
2053 status
= NtEnumerateValueKey( KeyHandle
, index
, KeyValueFullInformation
,
2054 buf_ptr
, total_size
, &total_size
);
2057 if (status
) goto done
;
2061 if (info
->NameLength
/sizeof(WCHAR
) >= *val_count
)
2063 status
= STATUS_BUFFER_OVERFLOW
;
2066 memcpy( value
, info
->Name
, info
->NameLength
);
2067 *val_count
= info
->NameLength
/ sizeof(WCHAR
);
2068 value
[*val_count
] = 0;
2073 if (total_size
- info
->DataOffset
> *count
)
2075 status
= STATUS_BUFFER_OVERFLOW
;
2078 memcpy( data
, buf_ptr
+ info
->DataOffset
, total_size
- info
->DataOffset
);
2079 if (total_size
- info
->DataOffset
<= *count
-sizeof(WCHAR
) && is_string(info
->Type
))
2081 /* if the type is REG_SZ and data is not 0-terminated
2082 * and there is enough space in the buffer NT appends a \0 */
2083 WCHAR
*ptr
= (WCHAR
*)(data
+ total_size
- info
->DataOffset
);
2084 if (ptr
> (WCHAR
*)data
&& ptr
[-1]) *ptr
= 0;
2088 else status
= STATUS_SUCCESS
;
2091 if (type
) *type
= info
->Type
;
2092 if (count
) *count
= info
->DataLength
;
2095 if (buf_ptr
!= buffer
) HeapFree( GetProcessHeap(), 0, buf_ptr
);
2096 CloseDefaultKey(KeyHandle
);
2097 return RtlNtStatusToDosError(status
);
2100 /************************************************************************
2106 RegFlushKey(HKEY hKey
)
2111 if (hKey
== HKEY_PERFORMANCE_DATA
)
2113 return ERROR_SUCCESS
;
2116 Status
= MapDefaultKey (&KeyHandle
,
2118 if (!NT_SUCCESS(Status
))
2120 return RtlNtStatusToDosError (Status
);
2123 Status
= NtFlushKey (KeyHandle
);
2125 CloseDefaultKey(KeyHandle
);
2127 if (!NT_SUCCESS(Status
))
2129 return RtlNtStatusToDosError (Status
);
2132 return ERROR_SUCCESS
;
2136 /************************************************************************
2142 RegGetKeySecurity(HKEY hKey
,
2143 SECURITY_INFORMATION SecurityInformation
,
2144 PSECURITY_DESCRIPTOR pSecurityDescriptor
,
2145 LPDWORD lpcbSecurityDescriptor
)
2150 if (hKey
== HKEY_PERFORMANCE_DATA
)
2152 return ERROR_INVALID_HANDLE
;
2155 Status
= MapDefaultKey(&KeyHandle
,
2157 if (!NT_SUCCESS(Status
))
2159 TRACE("MapDefaultKey() failed (Status %lx)\n", Status
);
2160 return RtlNtStatusToDosError (Status
);
2163 Status
= NtQuerySecurityObject(KeyHandle
,
2164 SecurityInformation
,
2165 pSecurityDescriptor
,
2166 *lpcbSecurityDescriptor
,
2167 lpcbSecurityDescriptor
);
2169 CloseDefaultKey(KeyHandle
);
2171 if (!NT_SUCCESS(Status
))
2173 WARN("NtQuerySecurityObject() failed (Status %lx)\n", Status
);
2174 return RtlNtStatusToDosError (Status
);
2177 return ERROR_SUCCESS
;
2181 /************************************************************************
2187 RegLoadKeyA (HKEY hKey
,
2191 UNICODE_STRING FileName
;
2192 UNICODE_STRING KeyName
;
2195 RtlCreateUnicodeStringFromAsciiz (&KeyName
,
2197 RtlCreateUnicodeStringFromAsciiz (&FileName
,
2200 ErrorCode
= RegLoadKeyW (hKey
,
2204 RtlFreeUnicodeString (&FileName
);
2205 RtlFreeUnicodeString (&KeyName
);
2211 /************************************************************************
2217 RegLoadKeyW (HKEY hKey
,
2221 OBJECT_ATTRIBUTES FileObjectAttributes
;
2222 OBJECT_ATTRIBUTES KeyObjectAttributes
;
2223 UNICODE_STRING FileName
;
2224 UNICODE_STRING KeyName
;
2227 LONG ErrorCode
= ERROR_SUCCESS
;
2229 if (hKey
== HKEY_PERFORMANCE_DATA
)
2231 return ERROR_INVALID_HANDLE
;
2234 Status
= MapDefaultKey (&KeyHandle
,
2236 if (!NT_SUCCESS(Status
))
2238 return RtlNtStatusToDosError (Status
);
2241 if (!RtlDosPathNameToNtPathName_U ((LPWSTR
)lpFile
,
2246 ErrorCode
= ERROR_BAD_PATHNAME
;
2250 InitializeObjectAttributes (&FileObjectAttributes
,
2252 OBJ_CASE_INSENSITIVE
,
2256 RtlInitUnicodeString (&KeyName
,
2259 InitializeObjectAttributes (&KeyObjectAttributes
,
2261 OBJ_CASE_INSENSITIVE
,
2265 Status
= NtLoadKey (&KeyObjectAttributes
,
2266 &FileObjectAttributes
);
2268 RtlFreeUnicodeString (&FileName
);
2270 if (!NT_SUCCESS(Status
))
2272 ErrorCode
= RtlNtStatusToDosError (Status
);
2277 CloseDefaultKey(KeyHandle
);
2283 /************************************************************************
2284 * RegNotifyChangeKeyValue
2289 RegNotifyChangeKeyValue (HKEY hKey
,
2291 DWORD dwNotifyFilter
,
2295 IO_STATUS_BLOCK IoStatusBlock
;
2298 LONG ErrorCode
= ERROR_SUCCESS
;
2300 if (hKey
== HKEY_PERFORMANCE_DATA
)
2302 return ERROR_INVALID_HANDLE
;
2305 if (fAsynchronous
== TRUE
&& hEvent
== NULL
)
2307 return ERROR_INVALID_PARAMETER
;
2310 Status
= MapDefaultKey (&KeyHandle
,
2312 if (!NT_SUCCESS(Status
))
2314 return RtlNtStatusToDosError (Status
);
2317 /* FIXME: Remote key handles must fail */
2319 Status
= NtNotifyChangeKey (KeyHandle
,
2329 if (!NT_SUCCESS(Status
) && Status
!= STATUS_TIMEOUT
)
2331 ErrorCode
= RtlNtStatusToDosError (Status
);
2334 CloseDefaultKey(KeyHandle
);
2340 /************************************************************************
2341 * RegOpenCurrentUser
2346 RegOpenCurrentUser (IN REGSAM samDesired
,
2347 OUT PHKEY phkResult
)
2351 Status
= RtlOpenCurrentUser((ACCESS_MASK
)samDesired
,
2352 (PHANDLE
)phkResult
);
2353 if (!NT_SUCCESS(Status
))
2355 /* NOTE - don't set the last error code! just return the error! */
2356 return RtlNtStatusToDosError(Status
);
2359 return ERROR_SUCCESS
;
2363 /************************************************************************
2366 * 20050503 Fireball - imported from WINE
2371 RegOpenKeyA (HKEY hKey
,
2375 TRACE("RegOpenKeyA hKey 0x%x lpSubKey %s phkResult %p\n", hKey
, lpSubKey
, phkResult
);
2377 if (!lpSubKey
|| !*lpSubKey
)
2380 return ERROR_SUCCESS
;
2383 return RegOpenKeyExA( hKey
, lpSubKey
, 0, MAXIMUM_ALLOWED
, phkResult
);
2387 /************************************************************************
2392 * 20050503 Fireball - imported from WINE
2397 RegOpenKeyW (HKEY hKey
,
2401 TRACE("RegOpenKeyW hKey 0x%x lpSubKey %S phkResult %p\n", hKey
, lpSubKey
, phkResult
);
2403 if (!lpSubKey
|| !*lpSubKey
)
2406 return ERROR_SUCCESS
;
2408 return RegOpenKeyExW(hKey
, lpSubKey
, 0, MAXIMUM_ALLOWED
, phkResult
);
2412 /************************************************************************
2418 RegOpenKeyExA (HKEY hKey
,
2424 OBJECT_ATTRIBUTES ObjectAttributes
;
2425 UNICODE_STRING SubKeyString
;
2428 LONG ErrorCode
= ERROR_SUCCESS
;
2430 TRACE("RegOpenKeyExA hKey 0x%x lpSubKey %s ulOptions 0x%x samDesired 0x%x phkResult %p\n",
2431 hKey
, lpSubKey
, ulOptions
, samDesired
, phkResult
);
2433 Status
= MapDefaultKey (&KeyHandle
, hKey
);
2434 if (!NT_SUCCESS(Status
))
2436 return RtlNtStatusToDosError (Status
);
2439 RtlCreateUnicodeStringFromAsciiz (&SubKeyString
, (LPSTR
)lpSubKey
);
2440 InitializeObjectAttributes (&ObjectAttributes
,
2442 OBJ_CASE_INSENSITIVE
,
2446 Status
= NtOpenKey ((PHANDLE
)phkResult
, samDesired
, &ObjectAttributes
);
2447 RtlFreeUnicodeString (&SubKeyString
);
2448 if (!NT_SUCCESS(Status
))
2450 ErrorCode
= RtlNtStatusToDosError (Status
);
2453 CloseDefaultKey(KeyHandle
);
2459 /************************************************************************
2465 RegOpenKeyExW (HKEY hKey
,
2471 OBJECT_ATTRIBUTES ObjectAttributes
;
2472 UNICODE_STRING SubKeyString
;
2475 LONG ErrorCode
= ERROR_SUCCESS
;
2477 TRACE("RegOpenKeyExW hKey 0x%x lpSubKey %S ulOptions 0x%x samDesired 0x%x phkResult %p\n",
2478 hKey
, lpSubKey
, ulOptions
, samDesired
, phkResult
);
2480 Status
= MapDefaultKey (&KeyHandle
, hKey
);
2481 if (!NT_SUCCESS(Status
))
2483 return RtlNtStatusToDosError (Status
);
2486 if (lpSubKey
!= NULL
)
2487 RtlInitUnicodeString (&SubKeyString
, (LPWSTR
)lpSubKey
);
2489 RtlInitUnicodeString (&SubKeyString
, (LPWSTR
)L
"");
2491 InitializeObjectAttributes (&ObjectAttributes
,
2493 OBJ_CASE_INSENSITIVE
,
2497 Status
= NtOpenKey ((PHANDLE
)phkResult
, samDesired
, &ObjectAttributes
);
2499 if (!NT_SUCCESS(Status
))
2501 ErrorCode
= RtlNtStatusToDosError (Status
);
2504 CloseDefaultKey(KeyHandle
);
2510 /************************************************************************
2511 * RegOpenUserClassesRoot
2516 RegOpenUserClassesRoot (IN HANDLE hToken
,
2518 IN REGSAM samDesired
,
2519 OUT PHKEY phkResult
)
2521 const WCHAR UserClassesKeyPrefix
[] = L
"\\Registry\\User\\";
2522 const WCHAR UserClassesKeySuffix
[] = L
"_Classes";
2523 PTOKEN_USER TokenUserData
;
2524 ULONG RequiredLength
;
2525 UNICODE_STRING UserSidString
, UserClassesKeyRoot
;
2526 OBJECT_ATTRIBUTES ObjectAttributes
;
2529 /* check parameters */
2530 if (hToken
== NULL
|| dwOptions
!= 0 || phkResult
== NULL
)
2532 return ERROR_INVALID_PARAMETER
;
2536 * Get the user sid from the token
2540 /* determine how much memory we need */
2541 Status
= NtQueryInformationToken(hToken
,
2546 if (!NT_SUCCESS(Status
) && (Status
!= STATUS_BUFFER_TOO_SMALL
))
2548 /* NOTE - as opposed to all other registry functions windows does indeed
2549 change the last error code in case the caller supplied a invalid
2550 handle for example! */
2551 return RtlNtStatusToDosError (Status
);
2554 TokenUserData
= RtlAllocateHeap(ProcessHeap
,
2557 if (TokenUserData
== NULL
)
2559 return ERROR_NOT_ENOUGH_MEMORY
;
2562 /* attempt to read the information */
2563 Status
= NtQueryInformationToken(hToken
,
2568 if (!NT_SUCCESS(Status
))
2570 RtlFreeHeap(ProcessHeap
,
2573 if (Status
== STATUS_BUFFER_TOO_SMALL
)
2575 /* the information appears to have changed?! try again */
2579 /* NOTE - as opposed to all other registry functions windows does indeed
2580 change the last error code in case the caller supplied a invalid
2581 handle for example! */
2582 return RtlNtStatusToDosError (Status
);
2586 * Build the absolute path for the user's registry in the form
2587 * "\Registry\User\<SID>_Classes"
2589 Status
= RtlConvertSidToUnicodeString(&UserSidString
,
2590 TokenUserData
->User
.Sid
,
2593 /* we don't need the user data anymore, free it */
2594 RtlFreeHeap(ProcessHeap
,
2598 if (!NT_SUCCESS(Status
))
2600 return RtlNtStatusToDosError (Status
);
2603 /* allocate enough memory for the entire key string */
2604 UserClassesKeyRoot
.Length
= 0;
2605 UserClassesKeyRoot
.MaximumLength
= UserSidString
.Length
+
2606 sizeof(UserClassesKeyPrefix
) +
2607 sizeof(UserClassesKeySuffix
);
2608 UserClassesKeyRoot
.Buffer
= RtlAllocateHeap(ProcessHeap
,
2610 UserClassesKeyRoot
.MaximumLength
);
2611 if (UserClassesKeyRoot
.Buffer
== NULL
)
2613 RtlFreeUnicodeString(&UserSidString
);
2614 return RtlNtStatusToDosError (Status
);
2617 /* build the string */
2618 RtlAppendUnicodeToString(&UserClassesKeyRoot
,
2619 UserClassesKeyPrefix
);
2620 RtlAppendUnicodeStringToString(&UserClassesKeyRoot
,
2622 RtlAppendUnicodeToString(&UserClassesKeyRoot
,
2623 UserClassesKeySuffix
);
2625 TRACE("RegOpenUserClassesRoot: Absolute path: %wZ\n", &UserClassesKeyRoot
);
2631 InitializeObjectAttributes (&ObjectAttributes
,
2632 &UserClassesKeyRoot
,
2633 OBJ_CASE_INSENSITIVE
,
2637 Status
= NtOpenKey((PHANDLE
)phkResult
,
2641 RtlFreeUnicodeString(&UserSidString
);
2642 RtlFreeUnicodeString(&UserClassesKeyRoot
);
2644 if (!NT_SUCCESS(Status
))
2646 return RtlNtStatusToDosError (Status
);
2649 return ERROR_SUCCESS
;
2653 /************************************************************************
2659 RegQueryInfoKeyA (HKEY hKey
,
2664 LPDWORD lpcbMaxSubKeyLen
,
2665 LPDWORD lpcbMaxClassLen
,
2667 LPDWORD lpcbMaxValueNameLen
,
2668 LPDWORD lpcbMaxValueLen
,
2669 LPDWORD lpcbSecurityDescriptor
,
2670 PFILETIME lpftLastWriteTime
)
2672 WCHAR ClassName
[MAX_PATH
];
2673 UNICODE_STRING UnicodeString
;
2674 ANSI_STRING AnsiString
;
2677 RtlInitUnicodeString (&UnicodeString
,
2679 if (lpClass
!= NULL
)
2681 UnicodeString
.Buffer
= &ClassName
[0];
2682 UnicodeString
.MaximumLength
= sizeof(ClassName
);
2683 AnsiString
.MaximumLength
= *lpcbClass
;
2686 ErrorCode
= RegQueryInfoKeyW (hKey
,
2687 UnicodeString
.Buffer
,
2694 lpcbMaxValueNameLen
,
2696 lpcbSecurityDescriptor
,
2698 if ((ErrorCode
== ERROR_SUCCESS
) && (lpClass
!= NULL
))
2700 AnsiString
.Buffer
= lpClass
;
2701 AnsiString
.Length
= 0;
2702 UnicodeString
.Length
= *lpcbClass
* sizeof(WCHAR
);
2703 RtlUnicodeStringToAnsiString (&AnsiString
,
2706 *lpcbClass
= AnsiString
.Length
;
2707 lpClass
[AnsiString
.Length
] = 0;
2714 /************************************************************************
2720 RegQueryInfoKeyW (HKEY hKey
,
2725 LPDWORD lpcbMaxSubKeyLen
,
2726 LPDWORD lpcbMaxClassLen
,
2728 LPDWORD lpcbMaxValueNameLen
,
2729 LPDWORD lpcbMaxValueLen
,
2730 LPDWORD lpcbSecurityDescriptor
,
2731 PFILETIME lpftLastWriteTime
)
2733 KEY_FULL_INFORMATION FullInfoBuffer
;
2734 PKEY_FULL_INFORMATION FullInfo
;
2736 ULONG ClassLength
= 0;
2740 LONG ErrorCode
= ERROR_SUCCESS
;
2742 if ((lpClass
) && (!lpcbClass
))
2744 return ERROR_INVALID_PARAMETER
;
2747 Status
= MapDefaultKey (&KeyHandle
,
2749 if (!NT_SUCCESS(Status
))
2751 return RtlNtStatusToDosError (Status
);
2754 if (lpClass
!= NULL
)
2758 ClassLength
= min(*lpcbClass
- 1, REG_MAX_NAME_SIZE
) * sizeof(WCHAR
);
2765 FullInfoSize
= sizeof(KEY_FULL_INFORMATION
) + ((ClassLength
+ 3) & ~3);
2766 FullInfo
= RtlAllocateHeap (ProcessHeap
,
2769 if (FullInfo
== NULL
)
2771 ErrorCode
= ERROR_OUTOFMEMORY
;
2775 FullInfo
->ClassLength
= ClassLength
;
2779 FullInfoSize
= sizeof(KEY_FULL_INFORMATION
);
2780 FullInfo
= &FullInfoBuffer
;
2781 FullInfo
->ClassLength
= 0;
2783 FullInfo
->ClassOffset
= FIELD_OFFSET(KEY_FULL_INFORMATION
, Class
);
2785 Status
= NtQueryKey (KeyHandle
,
2790 TRACE("NtQueryKey() returned status 0x%X\n", Status
);
2791 if (!NT_SUCCESS(Status
))
2793 if (lpClass
!= NULL
)
2795 RtlFreeHeap (ProcessHeap
,
2800 ErrorCode
= RtlNtStatusToDosError (Status
);
2804 TRACE("SubKeys %d\n", FullInfo
->SubKeys
);
2805 if (lpcSubKeys
!= NULL
)
2807 *lpcSubKeys
= FullInfo
->SubKeys
;
2810 TRACE("MaxNameLen %lu\n", FullInfo
->MaxNameLen
);
2811 if (lpcbMaxSubKeyLen
!= NULL
)
2813 *lpcbMaxSubKeyLen
= FullInfo
->MaxNameLen
/ sizeof(WCHAR
) + 1;
2816 TRACE("MaxClassLen %lu\n", FullInfo
->MaxClassLen
);
2817 if (lpcbMaxClassLen
!= NULL
)
2819 *lpcbMaxClassLen
= FullInfo
->MaxClassLen
/ sizeof(WCHAR
) + 1;
2822 TRACE("Values %lu\n", FullInfo
->Values
);
2823 if (lpcValues
!= NULL
)
2825 *lpcValues
= FullInfo
->Values
;
2828 TRACE("MaxValueNameLen %lu\n", FullInfo
->MaxValueNameLen
);
2829 if (lpcbMaxValueNameLen
!= NULL
)
2831 *lpcbMaxValueNameLen
= FullInfo
->MaxValueNameLen
/ sizeof(WCHAR
) + 1;
2834 TRACE("MaxValueDataLen %lu\n", FullInfo
->MaxValueDataLen
);
2835 if (lpcbMaxValueLen
!= NULL
)
2837 *lpcbMaxValueLen
= FullInfo
->MaxValueDataLen
;
2840 if (lpcbSecurityDescriptor
!= NULL
)
2842 Status
= NtQuerySecurityObject(KeyHandle
,
2843 OWNER_SECURITY_INFORMATION
|
2844 GROUP_SECURITY_INFORMATION
|
2845 DACL_SECURITY_INFORMATION
,
2848 lpcbSecurityDescriptor
);
2849 if (!NT_SUCCESS(Status
))
2851 if (lpClass
!= NULL
)
2853 RtlFreeHeap(ProcessHeap
,
2858 ErrorCode
= RtlNtStatusToDosError (Status
);
2863 if (lpftLastWriteTime
!= NULL
)
2865 lpftLastWriteTime
->dwLowDateTime
= FullInfo
->LastWriteTime
.u
.LowPart
;
2866 lpftLastWriteTime
->dwHighDateTime
= FullInfo
->LastWriteTime
.u
.HighPart
;
2869 if (lpClass
!= NULL
)
2871 if (FullInfo
->ClassLength
> ClassLength
)
2873 ErrorCode
= ERROR_BUFFER_OVERFLOW
;
2877 RtlCopyMemory (lpClass
,
2879 FullInfo
->ClassLength
);
2880 *lpcbClass
= FullInfo
->ClassLength
/ sizeof(WCHAR
);
2881 lpClass
[*lpcbClass
] = 0;
2884 RtlFreeHeap (ProcessHeap
,
2890 CloseDefaultKey(KeyHandle
);
2896 /************************************************************************
2897 * RegQueryMultipleValuesA
2902 RegQueryMultipleValuesA (HKEY hKey
,
2909 DWORD maxBytes
= *ldwTotsize
;
2910 LPSTR bufptr
= (LPSTR
)lpValueBuf
;
2913 if (maxBytes
>= (1024*1024))
2914 return ERROR_TRANSFER_TOO_LONG
;
2918 TRACE("RegQueryMultipleValuesA(%p,%p,%ld,%p,%p=%ld)\n",
2919 hKey
, val_list
, num_vals
, lpValueBuf
, ldwTotsize
, *ldwTotsize
);
2921 for (i
= 0; i
< num_vals
; i
++)
2923 val_list
[i
].ve_valuelen
= 0;
2924 ErrorCode
= RegQueryValueExA (hKey
,
2925 val_list
[i
].ve_valuename
,
2929 &val_list
[i
].ve_valuelen
);
2930 if (ErrorCode
!= ERROR_SUCCESS
)
2935 if (lpValueBuf
!= NULL
&& *ldwTotsize
+ val_list
[i
].ve_valuelen
<= maxBytes
)
2937 ErrorCode
= RegQueryValueExA (hKey
,
2938 val_list
[i
].ve_valuename
,
2940 &val_list
[i
].ve_type
,
2942 &val_list
[i
].ve_valuelen
);
2943 if (ErrorCode
!= ERROR_SUCCESS
)
2948 val_list
[i
].ve_valueptr
= (DWORD_PTR
)bufptr
;
2950 bufptr
+= val_list
[i
].ve_valuelen
;
2953 *ldwTotsize
+= val_list
[i
].ve_valuelen
;
2956 return (lpValueBuf
!= NULL
&& *ldwTotsize
<= maxBytes
) ? ERROR_SUCCESS
: ERROR_MORE_DATA
;
2960 /************************************************************************
2961 * RegQueryMultipleValuesW
2966 RegQueryMultipleValuesW (HKEY hKey
,
2973 DWORD maxBytes
= *ldwTotsize
;
2974 LPSTR bufptr
= (LPSTR
)lpValueBuf
;
2977 if (maxBytes
>= (1024*1024))
2978 return ERROR_TRANSFER_TOO_LONG
;
2982 TRACE ("RegQueryMultipleValuesW(%p,%p,%ld,%p,%p=%ld)\n",
2983 hKey
, val_list
, num_vals
, lpValueBuf
, ldwTotsize
, *ldwTotsize
);
2985 for (i
= 0; i
< num_vals
; i
++)
2987 val_list
[i
].ve_valuelen
= 0;
2988 ErrorCode
= RegQueryValueExW (hKey
,
2989 val_list
[i
].ve_valuename
,
2993 &val_list
[i
].ve_valuelen
);
2994 if (ErrorCode
!= ERROR_SUCCESS
)
2999 if (lpValueBuf
!= NULL
&& *ldwTotsize
+ val_list
[i
].ve_valuelen
<= maxBytes
)
3001 ErrorCode
= RegQueryValueExW (hKey
,
3002 val_list
[i
].ve_valuename
,
3004 &val_list
[i
].ve_type
,
3006 &val_list
[i
].ve_valuelen
);
3007 if (ErrorCode
!= ERROR_SUCCESS
)
3012 val_list
[i
].ve_valueptr
= (DWORD_PTR
)bufptr
;
3014 bufptr
+= val_list
[i
].ve_valuelen
;
3017 *ldwTotsize
+= val_list
[i
].ve_valuelen
;
3020 return (lpValueBuf
!= NULL
&& *ldwTotsize
<= maxBytes
) ? ERROR_SUCCESS
: ERROR_MORE_DATA
;
3024 /************************************************************************
3030 RegQueryValueExW (HKEY hKey
,
3031 LPCWSTR lpValueName
,
3037 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo
;
3038 UNICODE_STRING ValueName
;
3043 LONG ErrorCode
= ERROR_SUCCESS
;
3044 ULONG MaxCopy
= lpcbData
!= NULL
&& lpData
!= NULL
? *lpcbData
: 0;
3046 TRACE("hKey 0x%X lpValueName %S lpData 0x%X lpcbData %d\n",
3047 hKey
, lpValueName
, lpData
, lpcbData
? *lpcbData
: 0);
3049 Status
= MapDefaultKey (&KeyHandle
,
3051 if (!NT_SUCCESS(Status
))
3053 return RtlNtStatusToDosError (Status
);
3056 if (lpData
!= NULL
&& lpcbData
== NULL
)
3058 ErrorCode
= ERROR_INVALID_PARAMETER
;
3062 RtlInitUnicodeString (&ValueName
,
3064 BufferSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[0]) + MaxCopy
;
3065 ValueInfo
= RtlAllocateHeap (ProcessHeap
,
3068 if (ValueInfo
== NULL
)
3070 ErrorCode
= ERROR_OUTOFMEMORY
;
3074 Status
= NtQueryValueKey (KeyHandle
,
3076 KeyValuePartialInformation
,
3080 TRACE("Status 0x%X\n", Status
);
3081 if (Status
== STATUS_BUFFER_OVERFLOW
)
3083 /* Return ERROR_SUCCESS and the buffer space needed for a successful call */
3085 ErrorCode
= lpData
? ERROR_MORE_DATA
: ERROR_SUCCESS
;
3087 else if (!NT_SUCCESS(Status
))
3089 ErrorCode
= RtlNtStatusToDosError (Status
);
3091 if (lpcbData
!= NULL
)
3093 ResultSize
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[0]) + *lpcbData
;
3099 *lpType
= ValueInfo
->Type
;
3102 if (NT_SUCCESS(Status
) && lpData
!= NULL
)
3104 RtlMoveMemory (lpData
,
3106 min(ValueInfo
->DataLength
, MaxCopy
));
3109 if ((ValueInfo
->Type
== REG_SZ
) ||
3110 (ValueInfo
->Type
== REG_MULTI_SZ
) ||
3111 (ValueInfo
->Type
== REG_EXPAND_SZ
))
3113 if (lpData
!= NULL
&& MaxCopy
> ValueInfo
->DataLength
)
3115 ((PWSTR
)lpData
)[ValueInfo
->DataLength
/ sizeof(WCHAR
)] = 0;
3118 if (lpcbData
!= NULL
)
3120 *lpcbData
= (ResultSize
- FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[0]));
3121 TRACE("(string) Returning Size: %lu\n", *lpcbData
);
3126 if (lpcbData
!= NULL
)
3128 *lpcbData
= ResultSize
- FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
, Data
[0]);
3129 TRACE("(other) Returning Size: %lu\n", *lpcbData
);
3133 TRACE("Type %d Size %d\n", ValueInfo
->Type
, ValueInfo
->DataLength
);
3135 RtlFreeHeap (ProcessHeap
,
3140 CloseDefaultKey(KeyHandle
);
3146 /************************************************************************
3152 RegQueryValueExA (HKEY hKey
,
3159 UNICODE_STRING ValueName
;
3160 UNICODE_STRING ValueData
;
3161 ANSI_STRING AnsiString
;
3166 TRACE("hKey 0x%X lpValueName %s lpData 0x%X lpcbData %d\n",
3167 hKey
, lpValueName
, lpData
, lpcbData
? *lpcbData
: 0);
3169 if (lpData
!= NULL
&& lpcbData
== NULL
)
3171 return ERROR_INVALID_PARAMETER
;
3176 ValueData
.Length
= 0;
3177 ValueData
.MaximumLength
= (*lpcbData
+ 1) * sizeof(WCHAR
);
3178 ValueData
.Buffer
= RtlAllocateHeap (ProcessHeap
,
3180 ValueData
.MaximumLength
);
3181 if (!ValueData
.Buffer
)
3183 return ERROR_OUTOFMEMORY
;
3188 ValueData
.Buffer
= NULL
;
3189 ValueData
.Length
= 0;
3190 ValueData
.MaximumLength
= 0;
3193 RtlCreateUnicodeStringFromAsciiz (&ValueName
,
3194 (LPSTR
)lpValueName
);
3196 Length
= (lpcbData
== NULL
) ? 0 : *lpcbData
* sizeof(WCHAR
);
3197 ErrorCode
= RegQueryValueExW (hKey
,
3201 (lpData
== NULL
) ? NULL
: (LPBYTE
)ValueData
.Buffer
,
3203 TRACE("ErrorCode %lu\n", ErrorCode
);
3204 RtlFreeUnicodeString(&ValueName
);
3206 if (ErrorCode
== ERROR_SUCCESS
||
3207 ErrorCode
== ERROR_MORE_DATA
)
3214 if ((Type
== REG_SZ
) || (Type
== REG_MULTI_SZ
) || (Type
== REG_EXPAND_SZ
))
3216 if (ErrorCode
== ERROR_SUCCESS
&& ValueData
.Buffer
!= NULL
)
3218 RtlInitAnsiString(&AnsiString
, NULL
);
3219 AnsiString
.Buffer
= (LPSTR
)lpData
;
3220 AnsiString
.MaximumLength
= *lpcbData
;
3221 ValueData
.Length
= Length
;
3222 ValueData
.MaximumLength
= ValueData
.Length
+ sizeof(WCHAR
);
3223 RtlUnicodeStringToAnsiString(&AnsiString
, &ValueData
, FALSE
);
3225 Length
= Length
/ sizeof(WCHAR
);
3227 else if (ErrorCode
== ERROR_SUCCESS
&& ValueData
.Buffer
!= NULL
)
3229 if (*lpcbData
< Length
)
3231 ErrorCode
= ERROR_MORE_DATA
;
3235 RtlMoveMemory(lpData
, ValueData
.Buffer
, Length
);
3239 if (lpcbData
!= NULL
)
3245 if (ValueData
.Buffer
!= NULL
)
3247 RtlFreeHeap(ProcessHeap
, 0, ValueData
.Buffer
);
3254 /************************************************************************
3260 RegQueryValueA (HKEY hKey
,
3265 WCHAR SubKeyNameBuffer
[MAX_PATH
+1];
3266 UNICODE_STRING SubKeyName
;
3267 UNICODE_STRING Value
;
3268 ANSI_STRING AnsiString
;
3272 TRACE("hKey 0x%X lpSubKey %s lpValue %p lpcbValue %d\n",
3273 hKey
, lpSubKey
, lpValue
, lpcbValue
? *lpcbValue
: 0);
3275 if (lpValue
!= NULL
&&
3278 return ERROR_INVALID_PARAMETER
;
3281 RtlInitUnicodeString (&SubKeyName
,
3283 RtlInitUnicodeString (&Value
,
3285 if (lpSubKey
!= NULL
&&
3286 strlen(lpSubKey
) != 0)
3288 RtlInitAnsiString (&AnsiString
,
3290 SubKeyName
.Buffer
= &SubKeyNameBuffer
[0];
3291 SubKeyName
.MaximumLength
= sizeof(SubKeyNameBuffer
);
3292 RtlAnsiStringToUnicodeString (&SubKeyName
,
3297 if (lpValue
!= NULL
)
3299 ValueSize
= *lpcbValue
* sizeof(WCHAR
);
3300 Value
.MaximumLength
= ValueSize
;
3301 Value
.Buffer
= RtlAllocateHeap (ProcessHeap
,
3304 if (Value
.Buffer
== NULL
)
3306 return ERROR_OUTOFMEMORY
;
3314 ErrorCode
= RegQueryValueW (hKey
,
3315 (LPCWSTR
)SubKeyName
.Buffer
,
3318 if (ErrorCode
== ERROR_SUCCESS
)
3320 Value
.Length
= ValueSize
;
3321 RtlInitAnsiString (&AnsiString
,
3323 AnsiString
.Buffer
= lpValue
;
3324 AnsiString
.MaximumLength
= *lpcbValue
;
3325 RtlUnicodeStringToAnsiString (&AnsiString
,
3330 *lpcbValue
= ValueSize
;
3331 if (Value
.Buffer
!= NULL
)
3333 RtlFreeHeap (ProcessHeap
,
3342 /************************************************************************
3348 RegQueryValueW (HKEY hKey
,
3353 OBJECT_ATTRIBUTES ObjectAttributes
;
3354 UNICODE_STRING SubKeyString
;
3361 TRACE("hKey 0x%X lpSubKey %S lpValue %p lpcbValue %d\n",
3362 hKey
, lpSubKey
, lpValue
, lpcbValue
? *lpcbValue
: 0);
3364 Status
= MapDefaultKey (&KeyHandle
,
3366 if (!NT_SUCCESS(Status
))
3368 return RtlNtStatusToDosError (Status
);
3371 if (lpSubKey
!= NULL
&&
3372 wcslen(lpSubKey
) != 0)
3374 RtlInitUnicodeString (&SubKeyString
,
3376 InitializeObjectAttributes (&ObjectAttributes
,
3378 OBJ_CASE_INSENSITIVE
,
3381 Status
= NtOpenKey (&RealKey
,
3384 if (!NT_SUCCESS(Status
))
3386 ErrorCode
= RtlNtStatusToDosError (Status
);
3389 CloseRealKey
= TRUE
;
3394 CloseRealKey
= FALSE
;
3397 ErrorCode
= RegQueryValueExW (RealKey
,
3402 (LPDWORD
)lpcbValue
);
3409 CloseDefaultKey(KeyHandle
);
3415 /************************************************************************
3421 RegReplaceKeyA (HKEY hKey
,
3426 UNICODE_STRING SubKey
;
3427 UNICODE_STRING NewFile
;
3428 UNICODE_STRING OldFile
;
3431 RtlCreateUnicodeStringFromAsciiz (&SubKey
,
3433 RtlCreateUnicodeStringFromAsciiz (&OldFile
,
3435 RtlCreateUnicodeStringFromAsciiz (&NewFile
,
3438 ErrorCode
= RegReplaceKeyW (hKey
,
3443 RtlFreeUnicodeString (&OldFile
);
3444 RtlFreeUnicodeString (&NewFile
);
3445 RtlFreeUnicodeString (&SubKey
);
3451 /************************************************************************
3457 RegReplaceKeyW (HKEY hKey
,
3462 OBJECT_ATTRIBUTES KeyObjectAttributes
;
3463 OBJECT_ATTRIBUTES NewObjectAttributes
;
3464 OBJECT_ATTRIBUTES OldObjectAttributes
;
3465 UNICODE_STRING SubKeyName
;
3466 UNICODE_STRING NewFileName
;
3467 UNICODE_STRING OldFileName
;
3468 BOOLEAN CloseRealKey
;
3469 HANDLE RealKeyHandle
;
3472 LONG ErrorCode
= ERROR_SUCCESS
;
3474 if (hKey
== HKEY_PERFORMANCE_DATA
)
3476 return ERROR_INVALID_HANDLE
;
3479 Status
= MapDefaultKey (&KeyHandle
,
3481 if (!NT_SUCCESS(Status
))
3483 return RtlNtStatusToDosError (Status
);
3486 /* Open the real key */
3487 if (lpSubKey
!= NULL
&& *lpSubKey
!= (WCHAR
)0)
3489 RtlInitUnicodeString (&SubKeyName
,
3491 InitializeObjectAttributes (&KeyObjectAttributes
,
3493 OBJ_CASE_INSENSITIVE
,
3496 Status
= NtOpenKey (&RealKeyHandle
,
3498 &KeyObjectAttributes
);
3499 if (!NT_SUCCESS(Status
))
3501 ErrorCode
= RtlNtStatusToDosError (Status
);
3504 CloseRealKey
= TRUE
;
3508 RealKeyHandle
= KeyHandle
;
3509 CloseRealKey
= FALSE
;
3512 /* Convert new file name */
3513 if (!RtlDosPathNameToNtPathName_U ((LPWSTR
)lpNewFile
,
3520 NtClose (RealKeyHandle
);
3522 ErrorCode
= ERROR_INVALID_PARAMETER
;
3526 InitializeObjectAttributes (&NewObjectAttributes
,
3528 OBJ_CASE_INSENSITIVE
,
3532 /* Convert old file name */
3533 if (!RtlDosPathNameToNtPathName_U ((LPWSTR
)lpOldFile
,
3538 RtlFreeUnicodeString (&NewFileName
);
3541 NtClose (RealKeyHandle
);
3543 ErrorCode
= ERROR_INVALID_PARAMETER
;
3547 InitializeObjectAttributes (&OldObjectAttributes
,
3549 OBJ_CASE_INSENSITIVE
,
3553 Status
= NtReplaceKey (&NewObjectAttributes
,
3555 &OldObjectAttributes
);
3557 RtlFreeUnicodeString (&OldFileName
);
3558 RtlFreeUnicodeString (&NewFileName
);
3562 NtClose (RealKeyHandle
);
3565 if (!NT_SUCCESS(Status
))
3567 return RtlNtStatusToDosError (Status
);
3571 CloseDefaultKey(KeyHandle
);
3577 /************************************************************************
3583 RegRestoreKeyA (HKEY hKey
,
3587 UNICODE_STRING FileName
;
3590 RtlCreateUnicodeStringFromAsciiz (&FileName
,
3593 ErrorCode
= RegRestoreKeyW (hKey
,
3597 RtlFreeUnicodeString (&FileName
);
3603 /************************************************************************
3609 RegRestoreKeyW (HKEY hKey
,
3613 OBJECT_ATTRIBUTES ObjectAttributes
;
3614 IO_STATUS_BLOCK IoStatusBlock
;
3615 UNICODE_STRING FileName
;
3620 if (hKey
== HKEY_PERFORMANCE_DATA
)
3622 return ERROR_INVALID_HANDLE
;
3625 Status
= MapDefaultKey (&KeyHandle
,
3627 if (!NT_SUCCESS(Status
))
3629 return RtlNtStatusToDosError (Status
);
3632 if (!RtlDosPathNameToNtPathName_U ((LPWSTR
)lpFile
,
3637 Status
= STATUS_INVALID_PARAMETER
;
3641 InitializeObjectAttributes (&ObjectAttributes
,
3643 OBJ_CASE_INSENSITIVE
,
3647 Status
= NtOpenFile (&FileHandle
,
3652 FILE_SYNCHRONOUS_IO_NONALERT
);
3653 RtlFreeUnicodeString (&FileName
);
3654 if (!NT_SUCCESS(Status
))
3659 Status
= NtRestoreKey (KeyHandle
,
3662 NtClose (FileHandle
);
3665 CloseDefaultKey(KeyHandle
);
3667 if (!NT_SUCCESS(Status
))
3669 return RtlNtStatusToDosError (Status
);
3672 return ERROR_SUCCESS
;
3676 /************************************************************************
3682 RegSaveKeyA (HKEY hKey
,
3684 LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
3686 UNICODE_STRING FileName
;
3689 RtlCreateUnicodeStringFromAsciiz (&FileName
,
3691 ErrorCode
= RegSaveKeyW (hKey
,
3693 lpSecurityAttributes
);
3694 RtlFreeUnicodeString (&FileName
);
3700 /************************************************************************
3706 RegSaveKeyW (HKEY hKey
,
3708 LPSECURITY_ATTRIBUTES lpSecurityAttributes
)
3710 PSECURITY_DESCRIPTOR SecurityDescriptor
= NULL
;
3711 OBJECT_ATTRIBUTES ObjectAttributes
;
3712 UNICODE_STRING FileName
;
3713 IO_STATUS_BLOCK IoStatusBlock
;
3718 Status
= MapDefaultKey (&KeyHandle
,
3720 if (!NT_SUCCESS(Status
))
3722 return RtlNtStatusToDosError (Status
);
3725 if (!RtlDosPathNameToNtPathName_U ((PWSTR
)lpFile
,
3730 Status
= STATUS_INVALID_PARAMETER
;
3734 if (lpSecurityAttributes
!= NULL
)
3736 SecurityDescriptor
= lpSecurityAttributes
->lpSecurityDescriptor
;
3739 InitializeObjectAttributes (&ObjectAttributes
,
3741 OBJ_CASE_INSENSITIVE
,
3743 SecurityDescriptor
);
3744 Status
= NtCreateFile (&FileHandle
,
3745 GENERIC_WRITE
| SYNCHRONIZE
,
3749 FILE_ATTRIBUTE_NORMAL
,
3752 FILE_OPEN_FOR_BACKUP_INTENT
| FILE_SYNCHRONOUS_IO_NONALERT
,
3755 RtlFreeUnicodeString (&FileName
);
3756 if (!NT_SUCCESS(Status
))
3761 Status
= NtSaveKey (KeyHandle
,
3763 NtClose (FileHandle
);
3766 CloseDefaultKey(KeyHandle
);
3768 if (!NT_SUCCESS(Status
))
3770 return RtlNtStatusToDosError (Status
);
3773 return ERROR_SUCCESS
;
3777 /************************************************************************
3783 RegSetKeySecurity (HKEY hKey
,
3784 SECURITY_INFORMATION SecurityInformation
,
3785 PSECURITY_DESCRIPTOR pSecurityDescriptor
)
3790 if (hKey
== HKEY_PERFORMANCE_DATA
)
3792 return ERROR_INVALID_HANDLE
;
3795 Status
= MapDefaultKey (&KeyHandle
,
3797 if (!NT_SUCCESS(Status
))
3799 return RtlNtStatusToDosError (Status
);
3802 Status
= NtSetSecurityObject (KeyHandle
,
3803 SecurityInformation
,
3804 pSecurityDescriptor
);
3806 CloseDefaultKey(KeyHandle
);
3808 if (!NT_SUCCESS(Status
))
3810 return RtlNtStatusToDosError (Status
);
3813 return ERROR_SUCCESS
;
3817 /************************************************************************
3823 RegSetValueExA (HKEY hKey
,
3830 UNICODE_STRING ValueName
;
3832 ANSI_STRING AnsiString
;
3833 UNICODE_STRING Data
;
3838 if (lpValueName
!= NULL
&&
3839 strlen(lpValueName
) != 0)
3841 RtlCreateUnicodeStringFromAsciiz (&ValueName
,
3843 pValueName
= (LPWSTR
)ValueName
.Buffer
;
3850 if (((dwType
== REG_SZ
) ||
3851 (dwType
== REG_MULTI_SZ
) ||
3852 (dwType
== REG_EXPAND_SZ
)) &&
3855 /* NT adds one if the caller forgot the NULL-termination character */
3856 if (lpData
[cbData
- 1] != '\0')
3861 RtlInitAnsiString (&AnsiString
,
3863 AnsiString
.Buffer
= (PSTR
)lpData
;
3864 AnsiString
.Length
= cbData
- 1;
3865 AnsiString
.MaximumLength
= cbData
;
3866 RtlAnsiStringToUnicodeString (&Data
,
3869 pData
= (LPBYTE
)Data
.Buffer
;
3870 DataSize
= cbData
* sizeof(WCHAR
);
3874 RtlInitUnicodeString (&Data
,
3876 pData
= (LPBYTE
)lpData
;
3880 ErrorCode
= RegSetValueExW (hKey
,
3886 if (pValueName
!= NULL
)
3888 RtlFreeHeap (ProcessHeap
,
3893 if (Data
.Buffer
!= NULL
)
3895 RtlFreeHeap (ProcessHeap
,
3904 /************************************************************************
3910 RegSetValueExW (HKEY hKey
,
3911 LPCWSTR lpValueName
,
3917 UNICODE_STRING ValueName
;
3918 PUNICODE_STRING pValueName
;
3922 Status
= MapDefaultKey (&KeyHandle
,
3924 if (!NT_SUCCESS(Status
))
3926 return RtlNtStatusToDosError (Status
);
3929 if (lpValueName
!= NULL
)
3931 RtlInitUnicodeString (&ValueName
,
3936 RtlInitUnicodeString (&ValueName
, L
"");
3938 pValueName
= &ValueName
;
3940 if (((dwType
== REG_SZ
) ||
3941 (dwType
== REG_MULTI_SZ
) ||
3942 (dwType
== REG_EXPAND_SZ
)) &&
3943 (cbData
!= 0) && (*(((PWCHAR
)lpData
) + (cbData
/ sizeof(WCHAR
)) - 1) != L
'\0'))
3945 /* NT adds one if the caller forgot the NULL-termination character */
3946 cbData
+= sizeof(WCHAR
);
3949 Status
= NtSetValueKey (KeyHandle
,
3956 CloseDefaultKey(KeyHandle
);
3958 if (!NT_SUCCESS(Status
))
3960 return RtlNtStatusToDosError (Status
);
3963 return ERROR_SUCCESS
;
3967 /************************************************************************
3973 RegSetValueA (HKEY hKey
,
3982 if (dwType
!= REG_SZ
)
3984 return ERROR_INVALID_PARAMETER
;
3987 if (lpSubKey
!= NULL
&& lpSubKey
[0] != '\0')
3989 ret
= RegCreateKeyA(hKey
,
3993 if (ret
!= ERROR_SUCCESS
)
4001 ret
= RegSetValueExA(hSubKey
,
4005 (CONST BYTE
*)lpData
,
4006 strlen(lpData
) + 1);
4008 if (hSubKey
!= hKey
)
4010 RegCloseKey(hSubKey
);
4017 /************************************************************************
4023 RegSetValueW (HKEY hKey
,
4029 OBJECT_ATTRIBUTES ObjectAttributes
;
4030 UNICODE_STRING SubKeyString
;
4037 Status
= MapDefaultKey (&KeyHandle
,
4039 if (!NT_SUCCESS(Status
))
4041 return RtlNtStatusToDosError (Status
);
4044 if ((lpSubKey
) && (wcslen(lpSubKey
) != 0))
4046 RtlInitUnicodeString (&SubKeyString
,
4048 InitializeObjectAttributes (&ObjectAttributes
,
4050 OBJ_CASE_INSENSITIVE
,
4053 Status
= NtOpenKey (&RealKey
,
4056 if (!NT_SUCCESS(Status
))
4058 ErrorCode
= RtlNtStatusToDosError (Status
);
4061 CloseRealKey
= TRUE
;
4066 CloseRealKey
= FALSE
;
4069 ErrorCode
= RegSetValueExW (RealKey
,
4075 if (CloseRealKey
== TRUE
)
4081 CloseDefaultKey(KeyHandle
);
4087 /************************************************************************
4093 RegUnLoadKeyA (HKEY hKey
,
4096 UNICODE_STRING KeyName
;
4099 RtlCreateUnicodeStringFromAsciiz (&KeyName
,
4102 ErrorCode
= RegUnLoadKeyW (hKey
,
4105 RtlFreeUnicodeString (&KeyName
);
4111 /************************************************************************
4117 RegUnLoadKeyW (HKEY hKey
,
4120 OBJECT_ATTRIBUTES ObjectAttributes
;
4121 UNICODE_STRING KeyName
;
4125 if (hKey
== HKEY_PERFORMANCE_DATA
)
4127 return ERROR_INVALID_HANDLE
;
4130 Status
= MapDefaultKey (&KeyHandle
, hKey
);
4131 if (!NT_SUCCESS(Status
))
4133 return RtlNtStatusToDosError (Status
);
4136 RtlInitUnicodeString (&KeyName
,
4139 InitializeObjectAttributes (&ObjectAttributes
,
4141 OBJ_CASE_INSENSITIVE
,
4145 Status
= NtUnloadKey (&ObjectAttributes
);
4147 CloseDefaultKey(KeyHandle
);
4149 if (!NT_SUCCESS(Status
))
4151 return RtlNtStatusToDosError (Status
);
4154 return ERROR_SUCCESS
;
4158 /************************************************************************
4164 RegLoadMUIStringW(IN HKEY hKey
,
4165 IN LPCWSTR pszValue OPTIONAL
,
4166 OUT LPWSTR pszOutBuf
,
4169 IN LPCWSTR pszDirectory OPTIONAL
)
4171 DPRINT1("RegLoadMUIStringW(0x%p, 0x%p, 0x%p, 0x%x, 0x%x, 0x%p) UNIMPLEMENTED!\n",
4172 hKey
, pszValue
, pszOutBuf
, cbOutBuf
, Reserved
, pszDirectory
);
4173 return ERROR_CALL_NOT_IMPLEMENTED
;
4177 /************************************************************************
4183 RegLoadMUIStringA(IN HKEY hKey
,
4184 IN LPCSTR pszValue OPTIONAL
,
4185 OUT LPSTR pszOutBuf
,
4188 IN LPCSTR pszDirectory OPTIONAL
)
4190 DPRINT1("RegLoadMUIStringA(0x%p, 0x%p, 0x%p, 0x%x, 0x%x, 0x%p) UNIMPLEMENTED!\n",
4191 hKey
, pszValue
, pszOutBuf
, cbOutBuf
, Reserved
, pszDirectory
);
4192 return ERROR_CALL_NOT_IMPLEMENTED
;