2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: lib/advapi32/reg/hkcr.c
5 * PURPOSE: Registry functions - HKEY_CLASSES_ROOT abstraction
6 * PROGRAMMER: Jerôme Gardou (jerome.gardou@reactos.org)
11 #include <ndk/cmfuncs.h>
12 #include <pseh/pseh2.h>
16 WINE_DEFAULT_DEBUG_CHANNEL(reg
);
18 static const UNICODE_STRING HKLM_ClassesPath
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\Software\\Classes");
22 GetKeyName(HKEY hKey
, PUNICODE_STRING KeyName
)
24 UNICODE_STRING InfoName
;
25 PKEY_NAME_INFORMATION NameInformation
;
31 Status
= NtQueryKey(hKey
, KeyNameInformation
, NULL
, 0, &InfoLength
);
32 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
34 ERR("NtQueryKey returned unexpected Status: 0x%08x\n", Status
);
35 return RtlNtStatusToDosError(Status
);
39 NameInformation
= RtlAllocateHeap(RtlGetProcessHeap(), 0, InfoLength
);
40 if (NameInformation
== NULL
)
42 ERR("Failed to allocate %lu bytes", InfoLength
);
43 return ERROR_NOT_ENOUGH_MEMORY
;
46 Status
= NtQueryKey(hKey
, KeyNameInformation
, NameInformation
, InfoLength
, &InfoLength
);
47 if (!NT_SUCCESS(Status
))
49 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation
);
50 ERR("NtQueryKey failed: 0x%08x\n", Status
);
51 return RtlNtStatusToDosError(Status
);
54 /* Make it a proper UNICODE_STRING */
55 InfoName
.Length
= NameInformation
->NameLength
;
56 InfoName
.MaximumLength
= NameInformation
->NameLength
;
57 InfoName
.Buffer
= NameInformation
->Name
;
59 Status
= RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE
, &InfoName
, KeyName
);
60 if (!NT_SUCCESS(Status
))
62 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation
);
63 ERR("RtlDuplicateUnicodeString failed: 0x%08x\n", Status
);
64 return RtlNtStatusToDosError(Status
);
67 RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation
);
79 OBJECT_BASIC_INFORMATION ObjectInfo
;
81 Status
= NtQueryObject(hKey
, ObjectBasicInformation
, &ObjectInfo
, sizeof(ObjectInfo
), NULL
);
82 if (!NT_SUCCESS(Status
))
84 ERR("NtQueryObject failed, Status %x08x\n", Status
);
85 return RtlNtStatusToDosError(Status
);
88 *RegSam
= ObjectInfo
.GrantedAccess
;
93 * Gets a HKLM key from an HKCU key.
99 _Out_ HKEY
* MachineKey
,
100 _In_ BOOL MustCreate
)
102 UNICODE_STRING KeyName
;
107 /* Get the key name */
108 ErrorCode
= GetKeyName(hKey
, &KeyName
);
109 if (ErrorCode
!= ERROR_SUCCESS
)
112 /* See if we really need a conversion */
113 if (RtlPrefixUnicodeString(&HKLM_ClassesPath
, &KeyName
, TRUE
))
115 RtlFreeUnicodeString(&KeyName
);
117 return ERROR_SUCCESS
;
120 SubKeyName
= KeyName
.Buffer
+ 15; /* 15 == wcslen(L"\\Registry\\User\\") */
121 /* Skip the user token */
122 while (*SubKeyName
++ != L
'\\')
126 ERR("Key name %S is invalid!\n", KeyName
.Buffer
);
127 return ERROR_INTERNAL_ERROR
;
131 /* Use the same access mask than the original key */
132 ErrorCode
= GetKeySam(hKey
, &SamDesired
);
133 if (ErrorCode
!= ERROR_SUCCESS
)
135 RtlFreeUnicodeString(&KeyName
);
141 ErrorCode
= RegCreateKeyExW(
155 ErrorCode
= RegOpenKeyExW(
163 RtlFreeUnicodeString(&KeyName
);
168 /* Get the HKCU key (if it exists) from an HKCR key */
173 _Out_ HKEY
* PreferredKey
)
175 UNICODE_STRING KeyName
;
180 /* Get the key name */
181 ErrorCode
= GetKeyName(hKey
, &KeyName
);
182 if (ErrorCode
!= ERROR_SUCCESS
)
185 /* See if we really need a conversion */
186 if (!RtlPrefixUnicodeString(&HKLM_ClassesPath
, &KeyName
, TRUE
))
188 RtlFreeUnicodeString(&KeyName
);
189 *PreferredKey
= hKey
;
190 return ERROR_SUCCESS
;
193 /* 18 == wcslen(L"\\Registry\\Machine\\") */
194 SubKeyName
= KeyName
.Buffer
+ 18;
196 /* Use the same access mask than the original key */
197 ErrorCode
= GetKeySam(hKey
, &SamDesired
);
198 if (ErrorCode
!= ERROR_SUCCESS
)
200 RtlFreeUnicodeString(&KeyName
);
205 ErrorCode
= RegOpenKeyExW(
212 RtlFreeUnicodeString(&KeyName
);
217 /* HKCR version of RegCreateKeyExW. */
222 _In_ LPCWSTR lpSubKey
,
224 _In_opt_ LPWSTR lpClass
,
225 _In_ DWORD dwOptions
,
226 _In_ REGSAM samDesired
,
227 _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes
,
228 _Out_ PHKEY phkResult
,
229 _Out_opt_ LPDWORD lpdwDisposition
)
232 HKEY QueriedKey
, TestKey
;
234 ASSERT(IsHKCRKey(hKey
));
236 /* Remove the HKCR flag while we're working */
237 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
239 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
241 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
243 /* The current key doesn't exist on HKCU side, so we can only create it in HKLM */
244 ErrorCode
= RegCreateKeyExW(
251 lpSecurityAttributes
,
254 if (ErrorCode
== ERROR_SUCCESS
)
255 MakeHKCRKey(phkResult
);
259 if (ErrorCode
!= ERROR_SUCCESS
)
261 /* Somehow we failed for another reason (maybe deleted key or whatever) */
265 /* See if the subkey already exists in HKCU. */
266 ErrorCode
= RegOpenKeyExW(QueriedKey
, lpSubKey
, 0, 0, &TestKey
);
267 if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
269 if (ErrorCode
== ERROR_SUCCESS
)
271 /* Great. Close the test one and do the real create operation */
272 RegCloseKey(TestKey
);
273 ErrorCode
= RegCreateKeyExW(
280 lpSecurityAttributes
,
283 if (ErrorCode
== ERROR_SUCCESS
)
284 MakeHKCRKey(phkResult
);
286 if (QueriedKey
!= hKey
)
287 RegCloseKey(QueriedKey
);
289 return ERROR_SUCCESS
;
292 if (QueriedKey
!= hKey
)
293 RegCloseKey(QueriedKey
);
295 /* So we must do the create operation in HKLM, creating the missing parent keys if needed. */
296 ErrorCode
= GetFallbackHKCRKey(hKey
, &QueriedKey
, TRUE
);
297 if (ErrorCode
!= ERROR_SUCCESS
)
300 /* Do the key creation */
301 ErrorCode
= RegCreateKeyEx(
308 lpSecurityAttributes
,
312 if (QueriedKey
!= hKey
)
313 RegCloseKey(QueriedKey
);
315 if (ErrorCode
== ERROR_SUCCESS
)
316 MakeHKCRKey(phkResult
);
321 /* Same as RegOpenKeyExW, but for HKEY_CLASSES_ROOT subkeys */
326 _In_ LPCWSTR lpSubKey
,
327 _In_ DWORD ulOptions
,
328 _In_ REGSAM samDesired
,
329 _In_ PHKEY phkResult
)
334 ASSERT(IsHKCRKey(hKey
));
336 /* Remove the HKCR flag while we're working */
337 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
339 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
341 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
343 /* The key doesn't exist on HKCU side, no chance for a subkey */
344 ErrorCode
= RegOpenKeyExW(hKey
, lpSubKey
, ulOptions
, samDesired
, phkResult
);
345 if (ErrorCode
== ERROR_SUCCESS
)
346 MakeHKCRKey(phkResult
);
350 if (ErrorCode
!= ERROR_SUCCESS
)
352 /* Somehow we failed for another reason (maybe deleted key or whatever) */
356 /* Try on the HKCU side */
357 ErrorCode
= RegOpenKeyExW(QueriedKey
, lpSubKey
, ulOptions
, samDesired
, phkResult
);
358 if (ErrorCode
== ERROR_SUCCESS
)
359 MakeHKCRKey(phkResult
);
361 /* Close it if we must */
362 if (QueriedKey
!= hKey
)
364 RegCloseKey(QueriedKey
);
367 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
368 if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
371 /* If we're here, we must open from HKLM key. */
372 ErrorCode
= GetFallbackHKCRKey(hKey
, &QueriedKey
, FALSE
);
373 if (ErrorCode
!= ERROR_SUCCESS
)
375 /* Maybe the key doesn't exist in the HKLM view */
379 ErrorCode
= RegOpenKeyExW(QueriedKey
, lpSubKey
, ulOptions
, samDesired
, phkResult
);
380 if (ErrorCode
== ERROR_SUCCESS
)
381 MakeHKCRKey(phkResult
);
383 /* Close it if we must */
384 if (QueriedKey
!= hKey
)
386 RegCloseKey(QueriedKey
);
392 /* HKCR version of RegDeleteKeyExW */
397 _In_ LPCWSTR lpSubKey
,
404 ASSERT(IsHKCRKey(hKey
));
406 /* Remove the HKCR flag while we're working */
407 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
409 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
411 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
413 /* The key doesn't exist on HKCU side, no chance for a subkey */
414 return RegDeleteKeyExW(hKey
, lpSubKey
, RegSam
, Reserved
);
417 if (ErrorCode
!= ERROR_SUCCESS
)
419 /* Somehow we failed for another reason (maybe deleted key or whatever) */
423 ErrorCode
= RegDeleteKeyExW(QueriedKey
, lpSubKey
, RegSam
, Reserved
);
425 /* Close it if we must */
426 if (QueriedKey
!= hKey
)
428 /* The original key is on the machine view */
429 RegCloseKey(QueriedKey
);
432 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
433 if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
436 /* If we're here, we must open from HKLM key. */
437 ErrorCode
= GetFallbackHKCRKey(hKey
, &QueriedKey
, FALSE
);
438 if (ErrorCode
!= ERROR_SUCCESS
)
440 /* Maybe the key doesn't exist in the HKLM view */
444 ErrorCode
= RegDeleteKeyExW(QueriedKey
, lpSubKey
, RegSam
, Reserved
);
446 /* Close it if we must */
447 if (QueriedKey
!= hKey
)
449 RegCloseKey(QueriedKey
);
455 /* HKCR version of RegQueryValueExW */
461 _In_ LPDWORD Reserved
,
469 ASSERT(IsHKCRKey(hKey
));
471 /* Remove the HKCR flag while we're working */
472 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
474 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
476 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
478 /* The key doesn't exist on HKCU side, no chance for a value in it */
479 return RegQueryValueExW(hKey
, Name
, Reserved
, Type
, Data
, Count
);
482 if (ErrorCode
!= ERROR_SUCCESS
)
484 /* Somehow we failed for another reason (maybe deleted key or whatever) */
488 ErrorCode
= RegQueryValueExW(QueriedKey
, Name
, Reserved
, Type
, Data
, Count
);
490 /* Close it if we must */
491 if (QueriedKey
!= hKey
)
493 /* The original key is on the machine view */
494 RegCloseKey(QueriedKey
);
497 /* Anything else than ERROR_FILE_NOT_FOUND means that we found it, even if it is with failures. */
498 if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
501 /* If we're here, we must open from HKLM key. */
502 ErrorCode
= GetFallbackHKCRKey(hKey
, &QueriedKey
, FALSE
);
503 if (ErrorCode
!= ERROR_SUCCESS
)
505 /* Maybe the key doesn't exist in the HKLM view */
509 ErrorCode
= RegQueryValueExW(QueriedKey
, Name
, Reserved
, Type
, Data
, Count
);
511 /* Close it if we must */
512 if (QueriedKey
!= hKey
)
514 RegCloseKey(QueriedKey
);
520 /* HKCR version of RegSetValueExW */
528 _In_ CONST BYTE
* Data
,
534 ASSERT(IsHKCRKey(hKey
));
536 /* Remove the HKCR flag while we're working */
537 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
539 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
541 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
543 /* The key doesn't exist on HKCU side, no chance to put a value in it */
544 return RegSetValueExW(hKey
, Name
, Reserved
, Type
, Data
, DataSize
);
547 if (ErrorCode
!= ERROR_SUCCESS
)
549 /* Somehow we failed for another reason (maybe deleted key or whatever) */
553 /* Check if the value already exists in the preferred key */
554 ErrorCode
= RegQueryValueExW(QueriedKey
, Name
, NULL
, NULL
, NULL
, NULL
);
555 if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
557 if (ErrorCode
== ERROR_SUCCESS
)
559 /* Yes, so we have the right to modify it */
560 ErrorCode
= RegSetValueExW(QueriedKey
, Name
, Reserved
, Type
, Data
, DataSize
);
562 if (QueriedKey
!= hKey
)
563 RegCloseKey(QueriedKey
);
566 if (QueriedKey
!= hKey
)
567 RegCloseKey(QueriedKey
);
569 /* So we must set the value in the HKLM version */
570 ErrorCode
= GetPreferredHKCRKey(hKey
, &QueriedKey
);
571 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
573 /* No choice: put this in HKCU */
574 return RegSetValueExW(hKey
, Name
, Reserved
, Type
, Data
, DataSize
);
576 else if (ErrorCode
!= ERROR_SUCCESS
)
581 ErrorCode
= RegSetValueExW(QueriedKey
, Name
, Reserved
, Type
, Data
, DataSize
);
583 if (QueriedKey
!= hKey
)
584 RegCloseKey(QueriedKey
);
589 /* HKCR version of RegEnumKeyExW */
596 _Inout_ LPDWORD lpcbName
,
597 _Reserved_ LPDWORD lpReserved
,
598 _Out_opt_ LPWSTR lpClass
,
599 _Inout_opt_ LPDWORD lpcbClass
,
600 _Out_opt_ PFILETIME lpftLastWriteTime
)
602 HKEY PreferredKey
, FallbackKey
;
603 DWORD NumPreferredSubKeys
;
604 DWORD MaxFallbackSubKeyLen
;
606 WCHAR
* FallbackSubKeyName
= NULL
;
609 ASSERT(IsHKCRKey(hKey
));
611 /* Remove the HKCR flag while we're working */
612 hKey
= (HKEY
)(((ULONG_PTR
)hKey
) & ~0x2);
614 /* Get the preferred key */
615 ErrorCode
= GetPreferredHKCRKey(hKey
, &PreferredKey
);
616 if (ErrorCode
!= ERROR_SUCCESS
)
618 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
620 /* Only the HKLM key exists */
621 return RegEnumKeyExW(
634 /* Get the fallback key */
635 ErrorCode
= GetFallbackHKCRKey(hKey
, &FallbackKey
, FALSE
);
636 if (ErrorCode
!= ERROR_SUCCESS
)
638 if (PreferredKey
!= hKey
)
639 RegCloseKey(PreferredKey
);
640 if (ErrorCode
== ERROR_FILE_NOT_FOUND
)
642 /* Only the HKCU key exists */
643 return RegEnumKeyExW(
656 /* Get some info on the HKCU side */
657 ErrorCode
= RegQueryInfoKeyW(
662 &NumPreferredSubKeys
,
670 if (ErrorCode
!= ERROR_SUCCESS
)
673 if (dwIndex
< NumPreferredSubKeys
)
675 /* HKCU side takes precedence */
676 ErrorCode
= RegEnumKeyExW(
688 /* Here it gets tricky. We must enumerate the values from the HKLM side,
689 * without reporting those which are present on the HKCU side */
691 /* Squash out the indices from HKCU */
692 dwIndex
-= NumPreferredSubKeys
;
695 ErrorCode
= RegQueryInfoKeyW(
701 &MaxFallbackSubKeyLen
,
708 if (ErrorCode
!= ERROR_SUCCESS
)
710 ERR("Could not query info of key %p (Err: %d)\n", FallbackKey
, ErrorCode
);
714 ERR("Maxfallbacksubkeylen: %d\n", MaxFallbackSubKeyLen
);
716 /* Allocate our buffer */
717 FallbackSubKeyName
= RtlAllocateHeap(
718 RtlGetProcessHeap(), 0, (MaxFallbackSubKeyLen
+ 1) * sizeof(WCHAR
));
719 if (!FallbackSubKeyName
)
721 ErrorCode
= ERROR_NOT_ENOUGH_MEMORY
;
725 /* We must begin at the very first subkey of the fallback key,
726 * and then see if we meet keys that already are in the preferred key.
727 * In that case, we must bump dwIndex, as otherwise we would enumerate a key we already
728 * saw in a previous call.
733 HKEY PreferredSubKey
;
734 DWORD FallbackSubkeyLen
= MaxFallbackSubKeyLen
;
736 /* Try enumerating */
737 ErrorCode
= RegEnumKeyExW(
746 if (ErrorCode
!= ERROR_SUCCESS
)
748 /* Most likely ERROR_NO_MORE_ITEMS */
749 ERR("Returning %d.\n", ErrorCode
);
752 FallbackSubKeyName
[FallbackSubkeyLen
] = L
'\0';
754 /* See if there is such a value on HKCU side */
755 ErrorCode
= RegOpenKeyExW(
762 if (ErrorCode
== ERROR_SUCCESS
)
764 RegCloseKey(PreferredSubKey
);
765 /* So we already enumerated it on HKCU side. */
768 else if (ErrorCode
!= ERROR_FILE_NOT_FOUND
)
770 ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode
, FallbackSubKeyName
);
774 /* See if we caught up */
775 if (FallbackIndex
== dwIndex
)
781 /* We can finally enumerate on the fallback side */
782 ErrorCode
= RegEnumKeyExW(
793 if (PreferredKey
!= hKey
)
794 RegCloseKey(PreferredKey
);
795 if (FallbackKey
!= hKey
)
796 RegCloseKey(FallbackKey
);
797 if (FallbackSubKeyName
)
798 RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackSubKeyName
);