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