Sync with trunk r64509.
[reactos.git] / dll / win32 / advapi32 / reg / hkcr.c
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", 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, 0, &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 ERR("Maxfallbacksubkeylen: %d\n", MaxFallbackSubKeyLen);
715
716 /* Allocate our buffer */
717 FallbackSubKeyName = RtlAllocateHeap(
718 RtlGetProcessHeap(), 0, (MaxFallbackSubKeyLen + 1) * sizeof(WCHAR));
719 if (!FallbackSubKeyName)
720 {
721 ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
722 goto Exit;
723 }
724
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.
729 */
730 FallbackIndex = 0;
731 while (TRUE)
732 {
733 HKEY PreferredSubKey;
734 DWORD FallbackSubkeyLen = MaxFallbackSubKeyLen;
735
736 /* Try enumerating */
737 ErrorCode = RegEnumKeyExW(
738 FallbackKey,
739 FallbackIndex,
740 FallbackSubKeyName,
741 &FallbackSubkeyLen,
742 NULL,
743 NULL,
744 NULL,
745 NULL);
746 if (ErrorCode != ERROR_SUCCESS)
747 {
748 /* Most likely ERROR_NO_MORE_ITEMS */
749 ERR("Returning %d.\n", ErrorCode);
750 goto Exit;
751 }
752 FallbackSubKeyName[FallbackSubkeyLen] = L'\0';
753
754 /* See if there is such a value on HKCU side */
755 ErrorCode = RegOpenKeyExW(
756 PreferredKey,
757 FallbackSubKeyName,
758 0,
759 0,
760 &PreferredSubKey);
761
762 if (ErrorCode == ERROR_SUCCESS)
763 {
764 RegCloseKey(PreferredSubKey);
765 /* So we already enumerated it on HKCU side. */
766 dwIndex++;
767 }
768 else if (ErrorCode != ERROR_FILE_NOT_FOUND)
769 {
770 ERR("Got error %d while querying for %s on HKCU side.\n", ErrorCode, FallbackSubKeyName);
771 goto Exit;
772 }
773
774 /* See if we caught up */
775 if (FallbackIndex == dwIndex)
776 break;
777
778 FallbackIndex++;
779 }
780
781 /* We can finally enumerate on the fallback side */
782 ErrorCode = RegEnumKeyExW(
783 FallbackKey,
784 dwIndex,
785 lpName,
786 lpcbName,
787 lpReserved,
788 lpClass,
789 lpcbClass,
790 lpftLastWriteTime);
791
792 Exit:
793 if (PreferredKey != hKey)
794 RegCloseKey(PreferredKey);
795 if (FallbackKey != hKey)
796 RegCloseKey(FallbackKey);
797 if (FallbackSubKeyName)
798 RtlFreeHeap(RtlGetProcessHeap(), 0, FallbackSubKeyName);
799
800 return ErrorCode;
801 }