xref: /reactos/dll/win32/advapi32/reg/hkcr.c (revision 305aae75)
1 /*
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)
7  */
8 
9 #include <advapi32.h>
10 
11 #include <ndk/cmfuncs.h>
12 #include <pseh/pseh2.h>
13 
14 #include "reg.h"
15 
16 WINE_DEFAULT_DEBUG_CHANNEL(reg);
17 
18 static const UNICODE_STRING HKLM_ClassesPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\Software\\Classes");
19 
20 static
21 LONG
22 GetKeyName(HKEY hKey, PUNICODE_STRING KeyName)
23 {
24     UNICODE_STRING InfoName;
25     PKEY_NAME_INFORMATION NameInformation;
26     ULONG InfoLength;
27     NTSTATUS Status;
28 
29     /* Get info length */
30     InfoLength = 0;
31     Status = NtQueryKey(hKey, KeyNameInformation, NULL, 0, &InfoLength);
32     if (Status != STATUS_BUFFER_TOO_SMALL)
33     {
34         ERR("NtQueryKey returned unexpected Status: 0x%08x\n", Status);
35         return RtlNtStatusToDosError(Status);
36     }
37 
38     /* Get it for real */
39     NameInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, InfoLength);
40     if (NameInformation == NULL)
41     {
42         ERR("Failed to allocate %lu bytes\n", InfoLength);
43         return ERROR_NOT_ENOUGH_MEMORY;
44     }
45 
46     Status = NtQueryKey(hKey, KeyNameInformation, NameInformation, InfoLength, &InfoLength);
47     if (!NT_SUCCESS(Status))
48     {
49         RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
50         ERR("NtQueryKey failed: 0x%08x\n", Status);
51         return RtlNtStatusToDosError(Status);
52     }
53 
54     /* Make it a proper UNICODE_STRING */
55     InfoName.Length = NameInformation->NameLength;
56     InfoName.MaximumLength = NameInformation->NameLength;
57     InfoName.Buffer = NameInformation->Name;
58 
59     Status = RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, &InfoName, KeyName);
60     if (!NT_SUCCESS(Status))
61     {
62         RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
63         ERR("RtlDuplicateUnicodeString failed: 0x%08x\n", Status);
64         return RtlNtStatusToDosError(Status);
65     }
66 
67     RtlFreeHeap(RtlGetProcessHeap(), 0, NameInformation);
68 
69     return ERROR_SUCCESS;
70 }
71 
72 static
73 LONG
74 GetKeySam(
75     _In_ HKEY hKey,
76     _Out_ REGSAM* RegSam)
77 {
78     NTSTATUS Status;
79     OBJECT_BASIC_INFORMATION ObjectInfo;
80 
81     Status = NtQueryObject(hKey, ObjectBasicInformation, &ObjectInfo, sizeof(ObjectInfo), NULL);
82     if (!NT_SUCCESS(Status))
83     {
84         ERR("NtQueryObject failed, Status %x08x\n", Status);
85         return RtlNtStatusToDosError(Status);
86     }
87 
88     *RegSam = ObjectInfo.GrantedAccess;
89     return ERROR_SUCCESS;
90 }
91 
92 /*
93  * Gets a HKLM key from an HKCU key.
94  */
95 static
96 LONG
97 GetFallbackHKCRKey(
98     _In_ HKEY hKey,
99     _Out_ HKEY* MachineKey,
100     _In_ BOOL MustCreate)
101 {
102     UNICODE_STRING KeyName;
103     LPWSTR SubKeyName;
104     LONG ErrorCode;
105     REGSAM SamDesired;
106 
107     /* Get the key name */
108     ErrorCode = GetKeyName(hKey, &KeyName);
109     if (ErrorCode != ERROR_SUCCESS)
110         return ErrorCode;
111 
112     /* See if we really need a conversion */
113     if (RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
114     {
115         RtlFreeUnicodeString(&KeyName);
116         *MachineKey = hKey;
117         return ERROR_SUCCESS;
118     }
119 
120     SubKeyName = KeyName.Buffer + 15; /* 15 == wcslen(L"\\Registry\\User\\") */
121     /* Skip the user token */
122     while (*SubKeyName++ != L'\\')
123     {
124         if (!*SubKeyName)
125         {
126             ERR("Key name %S is invalid!\n", KeyName.Buffer);
127             return ERROR_INTERNAL_ERROR;
128         }
129     }
130 
131     /* Use the same access mask than the original key */
132     ErrorCode = GetKeySam(hKey, &SamDesired);
133     if (ErrorCode != ERROR_SUCCESS)
134     {
135         RtlFreeUnicodeString(&KeyName);
136         return ErrorCode;
137     }
138 
139     if (MustCreate)
140     {
141         ErrorCode = RegCreateKeyExW(
142             HKEY_LOCAL_MACHINE,
143             SubKeyName,
144             0,
145             NULL,
146             0,
147             SamDesired,
148             NULL,
149             MachineKey,
150             NULL);
151     }
152     else
153     {
154         /* Open the key. */
155         ErrorCode = RegOpenKeyExW(
156             HKEY_LOCAL_MACHINE,
157             SubKeyName,
158             0,
159             SamDesired,
160             MachineKey);
161     }
162 
163     RtlFreeUnicodeString(&KeyName);
164 
165     return ErrorCode;
166 }
167 
168 /* Get the HKCU key (if it exists) from an HKCR key */
169 static
170 LONG
171 GetPreferredHKCRKey(
172     _In_ HKEY hKey,
173     _Out_ HKEY* PreferredKey)
174 {
175     UNICODE_STRING KeyName;
176     LPWSTR SubKeyName;
177     LONG ErrorCode;
178     REGSAM SamDesired;
179 
180     /* Get the key name */
181     ErrorCode = GetKeyName(hKey, &KeyName);
182     if (ErrorCode != ERROR_SUCCESS)
183         return ErrorCode;
184 
185     /* See if we really need a conversion */
186     if (!RtlPrefixUnicodeString(&HKLM_ClassesPath, &KeyName, TRUE))
187     {
188         RtlFreeUnicodeString(&KeyName);
189         *PreferredKey = hKey;
190         return ERROR_SUCCESS;
191     }
192 
193     /* 18 == wcslen(L"\\Registry\\Machine\\") */
194     SubKeyName = KeyName.Buffer + 18;
195 
196     /* Use the same access mask than the original key */
197     ErrorCode = GetKeySam(hKey, &SamDesired);
198     if (ErrorCode != ERROR_SUCCESS)
199     {
200         RtlFreeUnicodeString(&KeyName);
201         return ErrorCode;
202     }
203 
204     /* Open the key. */
205     ErrorCode = RegOpenKeyExW(
206         HKEY_CURRENT_USER,
207         SubKeyName,
208         0,
209         SamDesired,
210         PreferredKey);
211 
212     RtlFreeUnicodeString(&KeyName);
213 
214     return ErrorCode;
215 }
216 
217 /* HKCR version of RegCreateKeyExW. */
218 LONG
219 WINAPI
220 CreateHKCRKey(
221     _In_ HKEY hKey,
222     _In_ LPCWSTR lpSubKey,
223     _In_ DWORD Reserved,
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)
230 {
231     LONG ErrorCode;
232     HKEY QueriedKey, TestKey;
233 
234     ASSERT(IsHKCRKey(hKey));
235 
236     /* Remove the HKCR flag while we're working */
237     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
238 
239     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
240 
241     if (ErrorCode == ERROR_FILE_NOT_FOUND)
242     {
243         /* The current key doesn't exist on HKCU side, so we can only create it in HKLM */
244         ErrorCode = RegCreateKeyExW(
245             hKey,
246             lpSubKey,
247             Reserved,
248             lpClass,
249             dwOptions,
250             samDesired,
251             lpSecurityAttributes,
252             phkResult,
253             lpdwDisposition);
254         if (ErrorCode == ERROR_SUCCESS)
255             MakeHKCRKey(phkResult);
256         return ErrorCode;
257     }
258 
259     if (ErrorCode != ERROR_SUCCESS)
260     {
261         /* Somehow we failed for another reason (maybe deleted key or whatever) */
262         return ErrorCode;
263     }
264 
265     /* See if the subkey already exists in HKCU. */
266     ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, 0, READ_CONTROL, &TestKey);
267     if (ErrorCode != ERROR_FILE_NOT_FOUND)
268     {
269         if (ErrorCode == ERROR_SUCCESS)
270         {
271             /* Great. Close the test one and do the real create operation */
272             RegCloseKey(TestKey);
273             ErrorCode = RegCreateKeyExW(
274                 QueriedKey,
275                 lpSubKey,
276                 Reserved,
277                 lpClass,
278                 dwOptions,
279                 samDesired,
280                 lpSecurityAttributes,
281                 phkResult,
282                 lpdwDisposition);
283             if (ErrorCode == ERROR_SUCCESS)
284                 MakeHKCRKey(phkResult);
285         }
286         if (QueriedKey != hKey)
287             RegCloseKey(QueriedKey);
288 
289         return ERROR_SUCCESS;
290     }
291 
292     if (QueriedKey != hKey)
293         RegCloseKey(QueriedKey);
294 
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)
298         return ErrorCode;
299 
300     /* Do the key creation */
301     ErrorCode = RegCreateKeyEx(
302         QueriedKey,
303         lpSubKey,
304         Reserved,
305         lpClass,
306         dwOptions,
307         samDesired,
308         lpSecurityAttributes,
309         phkResult,
310         lpdwDisposition);
311 
312     if (QueriedKey != hKey)
313         RegCloseKey(QueriedKey);
314 
315     if (ErrorCode == ERROR_SUCCESS)
316         MakeHKCRKey(phkResult);
317 
318     return ErrorCode;
319 }
320 
321 /* Same as RegOpenKeyExW, but for HKEY_CLASSES_ROOT subkeys */
322 LONG
323 WINAPI
324 OpenHKCRKey(
325     _In_ HKEY hKey,
326     _In_ LPCWSTR lpSubKey,
327     _In_ DWORD ulOptions,
328     _In_ REGSAM samDesired,
329     _In_ PHKEY phkResult)
330 {
331     HKEY QueriedKey;
332     LONG ErrorCode;
333 
334     ASSERT(IsHKCRKey(hKey));
335 
336     /* Remove the HKCR flag while we're working */
337     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
338 
339     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
340 
341     if (ErrorCode == ERROR_FILE_NOT_FOUND)
342     {
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);
347         return ErrorCode;
348     }
349 
350     if (ErrorCode != ERROR_SUCCESS)
351     {
352         /* Somehow we failed for another reason (maybe deleted key or whatever) */
353         return ErrorCode;
354     }
355 
356     /* Try on the HKCU side */
357     ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
358     if (ErrorCode == ERROR_SUCCESS)
359         MakeHKCRKey(phkResult);
360 
361     /* Close it if we must */
362     if (QueriedKey != hKey)
363     {
364         RegCloseKey(QueriedKey);
365     }
366 
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)
369         return ErrorCode;
370 
371     /* If we're here, we must open from HKLM key. */
372     ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE);
373     if (ErrorCode != ERROR_SUCCESS)
374     {
375         /* Maybe the key doesn't exist in the HKLM view */
376         return ErrorCode;
377     }
378 
379     ErrorCode = RegOpenKeyExW(QueriedKey, lpSubKey, ulOptions, samDesired, phkResult);
380     if (ErrorCode == ERROR_SUCCESS)
381         MakeHKCRKey(phkResult);
382 
383     /* Close it if we must */
384     if (QueriedKey != hKey)
385     {
386         RegCloseKey(QueriedKey);
387     }
388 
389     return ErrorCode;
390 }
391 
392 /* HKCR version of RegDeleteKeyExW */
393 LONG
394 WINAPI
395 DeleteHKCRKey(
396     _In_ HKEY hKey,
397     _In_ LPCWSTR lpSubKey,
398     _In_ REGSAM RegSam,
399     _In_ DWORD Reserved)
400 {
401     HKEY QueriedKey;
402     LONG ErrorCode;
403 
404     ASSERT(IsHKCRKey(hKey));
405 
406     /* Remove the HKCR flag while we're working */
407     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
408 
409     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
410 
411     if (ErrorCode == ERROR_FILE_NOT_FOUND)
412     {
413         /* The key doesn't exist on HKCU side, no chance for a subkey */
414         return RegDeleteKeyExW(hKey, lpSubKey, RegSam, Reserved);
415     }
416 
417     if (ErrorCode != ERROR_SUCCESS)
418     {
419         /* Somehow we failed for another reason (maybe deleted key or whatever) */
420         return ErrorCode;
421     }
422 
423     ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved);
424 
425     /* Close it if we must */
426     if (QueriedKey != hKey)
427     {
428         /* The original key is on the machine view */
429         RegCloseKey(QueriedKey);
430     }
431 
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)
434         return ErrorCode;
435 
436     /* If we're here, we must open from HKLM key. */
437     ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE);
438     if (ErrorCode != ERROR_SUCCESS)
439     {
440         /* Maybe the key doesn't exist in the HKLM view */
441         return ErrorCode;
442     }
443 
444     ErrorCode = RegDeleteKeyExW(QueriedKey, lpSubKey, RegSam, Reserved);
445 
446     /* Close it if we must */
447     if (QueriedKey != hKey)
448     {
449         RegCloseKey(QueriedKey);
450     }
451 
452     return ErrorCode;
453 }
454 
455 /* HKCR version of RegQueryValueExW */
456 LONG
457 WINAPI
458 QueryHKCRValue(
459     _In_ HKEY hKey,
460     _In_ LPCWSTR Name,
461     _In_ LPDWORD Reserved,
462     _In_ LPDWORD Type,
463     _In_ LPBYTE Data,
464     _In_ LPDWORD Count)
465 {
466     HKEY QueriedKey;
467     LONG ErrorCode;
468 
469     ASSERT(IsHKCRKey(hKey));
470 
471     /* Remove the HKCR flag while we're working */
472     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
473 
474     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
475 
476     if (ErrorCode == ERROR_FILE_NOT_FOUND)
477     {
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);
480     }
481 
482     if (ErrorCode != ERROR_SUCCESS)
483     {
484         /* Somehow we failed for another reason (maybe deleted key or whatever) */
485         return ErrorCode;
486     }
487 
488     ErrorCode = RegQueryValueExW(QueriedKey, Name, Reserved, Type, Data, Count);
489 
490     /* Close it if we must */
491     if (QueriedKey != hKey)
492     {
493         /* The original key is on the machine view */
494         RegCloseKey(QueriedKey);
495     }
496 
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)
499         return ErrorCode;
500 
501     /* If we're here, we must open from HKLM key. */
502     ErrorCode = GetFallbackHKCRKey(hKey, &QueriedKey, FALSE);
503     if (ErrorCode != ERROR_SUCCESS)
504     {
505         /* Maybe the key doesn't exist in the HKLM view */
506         return ErrorCode;
507     }
508 
509     ErrorCode = RegQueryValueExW(QueriedKey, Name, Reserved, Type, Data, Count);
510 
511     /* Close it if we must */
512     if (QueriedKey != hKey)
513     {
514         RegCloseKey(QueriedKey);
515     }
516 
517     return ErrorCode;
518 }
519 
520 /* HKCR version of RegSetValueExW */
521 LONG
522 WINAPI
523 SetHKCRValue(
524     _In_ HKEY hKey,
525     _In_ LPCWSTR Name,
526     _In_ DWORD Reserved,
527     _In_ DWORD Type,
528     _In_ CONST BYTE* Data,
529     _In_ DWORD DataSize)
530 {
531     HKEY QueriedKey;
532     LONG ErrorCode;
533 
534     ASSERT(IsHKCRKey(hKey));
535 
536     /* Remove the HKCR flag while we're working */
537     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
538 
539     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
540 
541     if (ErrorCode == ERROR_FILE_NOT_FOUND)
542     {
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);
545     }
546 
547     if (ErrorCode != ERROR_SUCCESS)
548     {
549         /* Somehow we failed for another reason (maybe deleted key or whatever) */
550         return ErrorCode;
551     }
552 
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)
556     {
557         if (ErrorCode == ERROR_SUCCESS)
558         {
559             /* Yes, so we have the right to modify it */
560             ErrorCode = RegSetValueExW(QueriedKey, Name, Reserved, Type, Data, DataSize);
561         }
562         if (QueriedKey != hKey)
563             RegCloseKey(QueriedKey);
564         return ErrorCode;
565     }
566     if (QueriedKey != hKey)
567         RegCloseKey(QueriedKey);
568 
569     /* So we must set the value in the HKLM version */
570     ErrorCode = GetPreferredHKCRKey(hKey, &QueriedKey);
571     if (ErrorCode == ERROR_FILE_NOT_FOUND)
572     {
573         /* No choice: put this in HKCU */
574         return RegSetValueExW(hKey, Name, Reserved, Type, Data, DataSize);
575     }
576     else if (ErrorCode != ERROR_SUCCESS)
577     {
578         return ErrorCode;
579     }
580 
581     ErrorCode = RegSetValueExW(QueriedKey, Name, Reserved, Type, Data, DataSize);
582 
583     if (QueriedKey != hKey)
584         RegCloseKey(QueriedKey);
585 
586     return ErrorCode;
587 }
588 
589 /* HKCR version of RegEnumKeyExW */
590 LONG
591 WINAPI
592 EnumHKCRKey(
593     _In_ HKEY hKey,
594     _In_ DWORD dwIndex,
595     _Out_ LPWSTR lpName,
596     _Inout_ LPDWORD lpcbName,
597     _Reserved_ LPDWORD lpReserved,
598     _Out_opt_ LPWSTR lpClass,
599     _Inout_opt_ LPDWORD lpcbClass,
600     _Out_opt_ PFILETIME lpftLastWriteTime)
601 {
602     HKEY PreferredKey, FallbackKey;
603     DWORD NumPreferredSubKeys;
604     DWORD MaxFallbackSubKeyLen;
605     DWORD FallbackIndex;
606     WCHAR* FallbackSubKeyName = NULL;
607     LONG ErrorCode;
608 
609     ASSERT(IsHKCRKey(hKey));
610 
611     /* Remove the HKCR flag while we're working */
612     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
613 
614     /* Get the preferred key */
615     ErrorCode = GetPreferredHKCRKey(hKey, &PreferredKey);
616     if (ErrorCode != ERROR_SUCCESS)
617     {
618         if (ErrorCode == ERROR_FILE_NOT_FOUND)
619         {
620             /* Only the HKLM key exists */
621             return RegEnumKeyExW(
622                 hKey,
623                 dwIndex,
624                 lpName,
625                 lpcbName,
626                 lpReserved,
627                 lpClass,
628                 lpcbClass,
629                 lpftLastWriteTime);
630         }
631         return ErrorCode;
632     }
633 
634     /* Get the fallback key */
635     ErrorCode = GetFallbackHKCRKey(hKey, &FallbackKey, FALSE);
636     if (ErrorCode != ERROR_SUCCESS)
637     {
638         if (PreferredKey != hKey)
639             RegCloseKey(PreferredKey);
640         if (ErrorCode == ERROR_FILE_NOT_FOUND)
641         {
642             /* Only the HKCU key exists */
643             return RegEnumKeyExW(
644                 hKey,
645                 dwIndex,
646                 lpName,
647                 lpcbName,
648                 lpReserved,
649                 lpClass,
650                 lpcbClass,
651                 lpftLastWriteTime);
652         }
653         return ErrorCode;
654     }
655 
656     /* Get some info on the HKCU side */
657     ErrorCode = RegQueryInfoKeyW(
658         PreferredKey,
659         NULL,
660         NULL,
661         NULL,
662         &NumPreferredSubKeys,
663         NULL,
664         NULL,
665         NULL,
666         NULL,
667         NULL,
668         NULL,
669         NULL);
670     if (ErrorCode != ERROR_SUCCESS)
671         goto Exit;
672 
673     if (dwIndex < NumPreferredSubKeys)
674     {
675         /* HKCU side takes precedence */
676         ErrorCode = RegEnumKeyExW(
677             PreferredKey,
678             dwIndex,
679             lpName,
680             lpcbName,
681             lpReserved,
682             lpClass,
683             lpcbClass,
684             lpftLastWriteTime);
685         goto Exit;
686     }
687 
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 */
690 
691     /* Squash out the indices from HKCU */
692     dwIndex -= NumPreferredSubKeys;
693 
694     /* Get some info */
695     ErrorCode = RegQueryInfoKeyW(
696         FallbackKey,
697         NULL,
698         NULL,
699         NULL,
700         NULL,
701         &MaxFallbackSubKeyLen,
702         NULL,
703         NULL,
704         NULL,
705         NULL,
706         NULL,
707         NULL);
708     if (ErrorCode != ERROR_SUCCESS)
709     {
710         ERR("Could not query info of key %p (Err: %d)\n", FallbackKey, ErrorCode);
711         goto Exit;
712     }
713 
714     MaxFallbackSubKeyLen++;
715     TRACE("Maxfallbacksubkeylen: %d\n", MaxFallbackSubKeyLen);
716 
717     /* Allocate our buffer */
718     FallbackSubKeyName = RtlAllocateHeap(
719         RtlGetProcessHeap(), 0, MaxFallbackSubKeyLen * sizeof(WCHAR));
720     if (!FallbackSubKeyName)
721     {
722         ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
723         goto Exit;
724     }
725 
726     /* We must begin at the very first subkey of the fallback key,
727      * and then see if we meet keys that already are in the preferred key.
728      * In that case, we must bump dwIndex, as otherwise we would enumerate a key we already
729      * saw in a previous call.
730      */
731     FallbackIndex = 0;
732     while (TRUE)
733     {
734         HKEY PreferredSubKey;
735         DWORD FallbackSubkeyLen = MaxFallbackSubKeyLen;
736 
737         /* Try enumerating */
738         ErrorCode = RegEnumKeyExW(
739             FallbackKey,
740             FallbackIndex,
741             FallbackSubKeyName,
742             &FallbackSubkeyLen,
743             NULL,
744             NULL,
745             NULL,
746             NULL);
747         if (ErrorCode != ERROR_SUCCESS)
748         {
749             if (ErrorCode != ERROR_NO_MORE_ITEMS)
750                 ERR("Returning %d.\n", ErrorCode);
751             goto Exit;
752         }
753         FallbackSubKeyName[FallbackSubkeyLen] = L'\0';
754 
755         /* See if there is such a value on HKCU side */
756         ErrorCode = RegOpenKeyExW(
757             PreferredKey,
758             FallbackSubKeyName,
759             0,
760             READ_CONTROL,
761             &PreferredSubKey);
762 
763         if (ErrorCode == ERROR_SUCCESS)
764         {
765             RegCloseKey(PreferredSubKey);
766             /* So we already enumerated it on HKCU side. */
767             dwIndex++;
768         }
769         else if (ErrorCode != ERROR_FILE_NOT_FOUND)
770         {
771             ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode, FallbackSubKeyName);
772             goto Exit;
773         }
774 
775         /* See if we caught up */
776         if (FallbackIndex == dwIndex)
777             break;
778 
779         FallbackIndex++;
780     }
781 
782     /* We can finally enumerate on the fallback side */
783     ErrorCode = RegEnumKeyExW(
784         FallbackKey,
785         dwIndex,
786         lpName,
787         lpcbName,
788         lpReserved,
789         lpClass,
790         lpcbClass,
791         lpftLastWriteTime);
792 
793 Exit:
794     if (PreferredKey != hKey)
795         RegCloseKey(PreferredKey);
796     if (FallbackKey != hKey)
797         RegCloseKey(FallbackKey);
798     if (FallbackSubKeyName)
799         RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackSubKeyName);
800 
801     return ErrorCode;
802 }
803 
804 /* HKCR version of RegEnumValueW */
805 LONG
806 WINAPI
807 EnumHKCRValue(
808     _In_ HKEY hKey,
809     _In_ DWORD dwIndex,
810     _Out_ LPWSTR lpName,
811     _Inout_ PDWORD lpcbName,
812     _Reserved_ PDWORD lpReserved,
813     _Out_opt_ PDWORD lpdwType,
814     _Out_opt_ LPBYTE lpData,
815     _Inout_opt_ PDWORD lpcbData)
816 {
817     HKEY PreferredKey, FallbackKey;
818     DWORD NumPreferredValues;
819     DWORD MaxFallbackValueNameLen;
820     DWORD FallbackIndex;
821     WCHAR* FallbackValueName = NULL;
822     LONG ErrorCode;
823 
824     ASSERT(IsHKCRKey(hKey));
825 
826     /* Remove the HKCR flag while we're working */
827     hKey = (HKEY)(((ULONG_PTR)hKey) & ~0x2);
828 
829     /* Get the preferred key */
830     ErrorCode = GetPreferredHKCRKey(hKey, &PreferredKey);
831     if (ErrorCode != ERROR_SUCCESS)
832     {
833         if (ErrorCode == ERROR_FILE_NOT_FOUND)
834         {
835             /* Only the HKLM key exists */
836             return RegEnumValueW(
837                 hKey,
838                 dwIndex,
839                 lpName,
840                 lpcbName,
841                 lpReserved,
842                 lpdwType,
843                 lpData,
844                 lpcbData);
845         }
846         return ErrorCode;
847     }
848 
849     /* Get the fallback key */
850     ErrorCode = GetFallbackHKCRKey(hKey, &FallbackKey, FALSE);
851     if (ErrorCode != ERROR_SUCCESS)
852     {
853         if (PreferredKey != hKey)
854             RegCloseKey(PreferredKey);
855         if (ErrorCode == ERROR_FILE_NOT_FOUND)
856         {
857             /* Only the HKCU key exists */
858             return RegEnumValueW(
859                 hKey,
860                 dwIndex,
861                 lpName,
862                 lpcbName,
863                 lpReserved,
864                 lpdwType,
865                 lpData,
866                 lpcbData);
867         }
868         return ErrorCode;
869     }
870 
871     /* Get some info on the HKCU side */
872     ErrorCode = RegQueryInfoKeyW(
873         PreferredKey,
874         NULL,
875         NULL,
876         NULL,
877         NULL,
878         NULL,
879         NULL,
880         &NumPreferredValues,
881         NULL,
882         NULL,
883         NULL,
884         NULL);
885     if (ErrorCode != ERROR_SUCCESS)
886         goto Exit;
887 
888     if (dwIndex < NumPreferredValues)
889     {
890         /* HKCU side takes precedence */
891         return RegEnumValueW(
892             PreferredKey,
893             dwIndex,
894             lpName,
895             lpcbName,
896             lpReserved,
897             lpdwType,
898             lpData,
899             lpcbData);
900         goto Exit;
901     }
902 
903     /* Here it gets tricky. We must enumerate the values from the HKLM side,
904      * without reporting those which are present on the HKCU side */
905 
906     /* Squash out the indices from HKCU */
907     dwIndex -= NumPreferredValues;
908 
909     /* Get some info */
910     ErrorCode = RegQueryInfoKeyW(
911         FallbackKey,
912         NULL,
913         NULL,
914         NULL,
915         NULL,
916         NULL,
917         NULL,
918         NULL,
919         &MaxFallbackValueNameLen,
920         NULL,
921         NULL,
922         NULL);
923     if (ErrorCode != ERROR_SUCCESS)
924     {
925         ERR("Could not query info of key %p (Err: %d)\n", FallbackKey, ErrorCode);
926         goto Exit;
927     }
928 
929     MaxFallbackValueNameLen++;
930     TRACE("Maxfallbacksubkeylen: %d\n", MaxFallbackValueNameLen);
931 
932     /* Allocate our buffer */
933     FallbackValueName = RtlAllocateHeap(
934         RtlGetProcessHeap(), 0, MaxFallbackValueNameLen * sizeof(WCHAR));
935     if (!FallbackValueName)
936     {
937         ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
938         goto Exit;
939     }
940 
941     /* We must begin at the very first subkey of the fallback key,
942      * and then see if we meet keys that already are in the preferred key.
943      * In that case, we must bump dwIndex, as otherwise we would enumerate a key we already
944      * saw in a previous call.
945      */
946     FallbackIndex = 0;
947     while (TRUE)
948     {
949         DWORD FallbackValueNameLen = MaxFallbackValueNameLen;
950 
951         /* Try enumerating */
952         ErrorCode = RegEnumValueW(
953             FallbackKey,
954             FallbackIndex,
955             FallbackValueName,
956             &FallbackValueNameLen,
957             NULL,
958             NULL,
959             NULL,
960             NULL);
961         if (ErrorCode != ERROR_SUCCESS)
962         {
963             if (ErrorCode != ERROR_NO_MORE_ITEMS)
964                 ERR("Returning %d.\n", ErrorCode);
965             goto Exit;
966         }
967         FallbackValueName[FallbackValueNameLen] = L'\0';
968 
969         /* See if there is such a value on HKCU side */
970         ErrorCode = RegQueryValueExW(
971             PreferredKey,
972             FallbackValueName,
973             NULL,
974             NULL,
975             NULL,
976             NULL);
977 
978         if (ErrorCode == ERROR_SUCCESS)
979         {
980             /* So we already enumerated it on HKCU side. */
981             dwIndex++;
982         }
983         else if (ErrorCode != ERROR_FILE_NOT_FOUND)
984         {
985             ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode, FallbackValueName);
986             goto Exit;
987         }
988 
989         /* See if we caught up */
990         if (FallbackIndex == dwIndex)
991             break;
992 
993         FallbackIndex++;
994     }
995 
996     /* We can finally enumerate on the fallback side */
997     ErrorCode = RegEnumValueW(
998         FallbackKey,
999         dwIndex,
1000         lpName,
1001         lpcbName,
1002         lpReserved,
1003         lpdwType,
1004         lpData,
1005         lpcbData);
1006 
1007 Exit:
1008     if (PreferredKey != hKey)
1009         RegCloseKey(PreferredKey);
1010     if (FallbackKey != hKey)
1011         RegCloseKey(FallbackKey);
1012     if (FallbackValueName)
1013         RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackValueName);
1014 
1015     return ErrorCode;
1016 }
1017