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