[ADVAPI32]
[reactos.git] / reactos / 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, KEY_READ, &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 }