13063dfa08327ac4b99aea449a1362c93064353a
[reactos.git] / reactos / ntoskrnl / cm / rtlfunc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/cm/rtlfunc.c
5 * PURPOSE: Rtlxxx function for registry access
6 * UPDATE HISTORY:
7 */
8
9 #ifdef WIN32_REGDBG
10 #include "cm_win32.h"
11 #else
12 #include <ddk/ntddk.h>
13 #include <roscfg.h>
14 #include <internal/ob.h>
15 #include <limits.h>
16 #include <string.h>
17 #include <internal/pool.h>
18 #include <internal/registry.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 #include "cm.h"
24 #endif
25
26 NTSTATUS STDCALL
27 RtlCheckRegistryKey(IN ULONG RelativeTo,
28 IN PWSTR Path)
29 {
30 HANDLE KeyHandle;
31 NTSTATUS Status;
32
33 Status = RtlpGetRegistryHandle(RelativeTo,
34 Path,
35 FALSE,
36 &KeyHandle);
37 if (!NT_SUCCESS(Status))
38 return(Status);
39
40 NtClose(KeyHandle);
41
42 return(STATUS_SUCCESS);
43 }
44
45
46 NTSTATUS STDCALL
47 RtlCreateRegistryKey(IN ULONG RelativeTo,
48 IN PWSTR Path)
49 {
50 HANDLE KeyHandle;
51 NTSTATUS Status;
52
53 Status = RtlpGetRegistryHandle(RelativeTo,
54 Path,
55 TRUE,
56 &KeyHandle);
57 if (!NT_SUCCESS(Status))
58 return(Status);
59
60 NtClose(KeyHandle);
61
62 return(STATUS_SUCCESS);
63 }
64
65
66 NTSTATUS STDCALL
67 RtlDeleteRegistryValue(IN ULONG RelativeTo,
68 IN PWSTR Path,
69 IN PWSTR ValueName)
70 {
71 HANDLE KeyHandle;
72 NTSTATUS Status;
73 UNICODE_STRING Name;
74
75 Status = RtlpGetRegistryHandle(RelativeTo,
76 Path,
77 TRUE,
78 &KeyHandle);
79 if (!NT_SUCCESS(Status))
80 return(Status);
81
82 RtlInitUnicodeString(&Name,
83 ValueName);
84
85 NtDeleteValueKey(KeyHandle,
86 &Name);
87
88 NtClose(KeyHandle);
89
90 return(STATUS_SUCCESS);
91 }
92
93
94 NTSTATUS STDCALL
95 RtlOpenCurrentUser(IN ACCESS_MASK DesiredAccess,
96 OUT PHANDLE KeyHandle)
97 {
98 OBJECT_ATTRIBUTES ObjectAttributes;
99 UNICODE_STRING KeyPath = UNICODE_STRING_INITIALIZER(L"\\Registry\\User\\.Default");
100 NTSTATUS Status;
101
102 Status = RtlFormatCurrentUserKeyPath(&KeyPath);
103 if (NT_SUCCESS(Status))
104 {
105 InitializeObjectAttributes(&ObjectAttributes,
106 &KeyPath,
107 OBJ_CASE_INSENSITIVE,
108 NULL,
109 NULL);
110 Status = NtOpenKey(KeyHandle,
111 DesiredAccess,
112 &ObjectAttributes);
113 RtlFreeUnicodeString(&KeyPath);
114 if (NT_SUCCESS(Status))
115 return(STATUS_SUCCESS);
116 }
117
118 InitializeObjectAttributes(&ObjectAttributes,
119 &KeyPath,
120 OBJ_CASE_INSENSITIVE,
121 NULL,
122 NULL);
123 Status = NtOpenKey(KeyHandle,
124 DesiredAccess,
125 &ObjectAttributes);
126 return(Status);
127 }
128
129
130 NTSTATUS STDCALL
131 RtlQueryRegistryValues(IN ULONG RelativeTo,
132 IN PWSTR Path,
133 IN PRTL_QUERY_REGISTRY_TABLE QueryTable,
134 IN PVOID Context,
135 IN PVOID Environment)
136 {
137 NTSTATUS Status;
138 HANDLE BaseKeyHandle;
139 HANDLE CurrentKeyHandle;
140 PRTL_QUERY_REGISTRY_TABLE QueryEntry;
141 OBJECT_ATTRIBUTES ObjectAttributes;
142 UNICODE_STRING KeyName;
143 PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
144 PKEY_VALUE_FULL_INFORMATION FullValueInfo;
145 ULONG BufferSize;
146 ULONG ResultSize;
147 ULONG Index;
148 ULONG StringLen;
149 PWSTR StringPtr;
150
151 DPRINT("RtlQueryRegistryValues() called\n");
152 #ifdef WIN32_REGDBG
153 BaseKeyHandle = NULL;
154 CurrentKeyHandle = NULL;
155 #endif
156
157 Status = RtlpGetRegistryHandle(RelativeTo,
158 Path,
159 FALSE,
160 &BaseKeyHandle);
161 if (!NT_SUCCESS(Status))
162 {
163 DPRINT("RtlpGetRegistryHandle() failed with status %x\n", Status);
164 return(Status);
165 }
166
167 CurrentKeyHandle = BaseKeyHandle;
168 QueryEntry = QueryTable;
169 while ((QueryEntry->QueryRoutine != NULL) ||
170 (QueryEntry->Name != NULL))
171 {
172 /* TODO: (from RobD)
173
174 packet.sys has this code which calls this (and fails here) with:
175
176 RtlZeroMemory(ParamTable, sizeof(ParamTable));
177 //
178 // change to the linkage key
179 //
180 ParamTable[0].QueryRoutine = NULL; // NOTE: QueryRoutine is set to NULL
181 ParamTable[0].Flags = RTL_QUERY_REGISTRY_SUBKEY;
182 ParamTable[0].Name = L"Linkage";
183 //
184 // Get the name of the mac driver we should bind to
185 //
186 ParamTable[1].QueryRoutine = PacketQueryRegistryRoutine;
187 ParamTable[1].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_NOEXPAND;
188 ParamTable[1].Name = L"Bind";
189 ParamTable[1].EntryContext = (PVOID)MacDriverName;
190 ParamTable[1].DefaultType = REG_MULTI_SZ;
191
192 Status = RtlQueryRegistryValues(
193 IN ULONG RelativeTo = RTL_REGISTRY_ABSOLUTE,
194 IN PWSTR Path = Path,
195 IN PRTL_QUERY_REGISTRY_TABLE QueryTable = ParamTable,
196 IN PVOID Context = NULL,
197 IN PVOID Environment = NULL);
198
199 */
200 //CSH: Was:
201 //if ((QueryEntry->QueryRoutine == NULL) &&
202 // ((QueryEntry->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_DIRECT)) != 0))
203 // Which is more correct?
204 if ((QueryEntry->QueryRoutine == NULL) &&
205 ((QueryEntry->Flags & RTL_QUERY_REGISTRY_SUBKEY) != 0))
206 {
207 DPRINT("Bad parameters\n");
208 Status = STATUS_INVALID_PARAMETER;
209 break;
210 }
211
212 DPRINT("Name: %S\n", QueryEntry->Name);
213
214 if (((QueryEntry->Flags & (RTL_QUERY_REGISTRY_SUBKEY | RTL_QUERY_REGISTRY_TOPKEY)) != 0) &&
215 (BaseKeyHandle != CurrentKeyHandle))
216 {
217 NtClose(CurrentKeyHandle);
218 CurrentKeyHandle = BaseKeyHandle;
219 }
220
221 if (QueryEntry->Flags & RTL_QUERY_REGISTRY_SUBKEY)
222 {
223 DPRINT("Open new subkey: %S\n", QueryEntry->Name);
224
225 RtlInitUnicodeString(&KeyName,
226 QueryEntry->Name);
227 InitializeObjectAttributes(&ObjectAttributes,
228 &KeyName,
229 OBJ_CASE_INSENSITIVE,
230 BaseKeyHandle,
231 NULL);
232 Status = NtOpenKey(&CurrentKeyHandle,
233 KEY_ALL_ACCESS,
234 &ObjectAttributes);
235 if (!NT_SUCCESS(Status))
236 break;
237 }
238 else if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DIRECT)
239 {
240 DPRINT("Query value directly: %S\n", QueryEntry->Name);
241
242 RtlInitUnicodeString(&KeyName,
243 QueryEntry->Name);
244
245 BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
246 ValueInfo = ExAllocatePool(PagedPool, BufferSize);
247 if (ValueInfo == NULL)
248 {
249 Status = STATUS_NO_MEMORY;
250 break;
251 }
252 #ifdef WIN32_REGDBG
253 memset(ValueInfo, 0, BufferSize);
254 #endif
255 Status = ZwQueryValueKey(CurrentKeyHandle,
256 &KeyName,
257 KeyValuePartialInformation,
258 ValueInfo,
259 BufferSize,
260 &ResultSize);
261 if (!NT_SUCCESS(Status))
262 {
263 if (QueryEntry->Flags & RTL_QUERY_REGISTRY_REQUIRED)
264 {
265 ExFreePool(ValueInfo);
266 Status = STATUS_OBJECT_NAME_NOT_FOUND;
267 goto ByeBye;
268 }
269
270 if (QueryEntry->DefaultType == REG_SZ)
271 {
272 PUNICODE_STRING ValueString;
273 PUNICODE_STRING SourceString;
274
275 SourceString = (PUNICODE_STRING)QueryEntry->DefaultData;
276 ValueString = (PUNICODE_STRING)QueryEntry->EntryContext;
277 if (ValueString->Buffer == 0)
278 {
279 ValueString->Length = SourceString->Length;
280 ValueString->MaximumLength = SourceString->MaximumLength;
281 ValueString->Buffer = ExAllocatePool(PagedPool,
282 ValueString->MaximumLength);
283 if (!ValueString->Buffer)
284 break;
285 ValueString->Buffer[0] = 0;
286 memcpy(ValueString->Buffer,
287 SourceString->Buffer,
288 SourceString->MaximumLength);
289 }
290 else
291 {
292 ValueString->Length = RtlMin(SourceString->Length,
293 ValueString->MaximumLength - sizeof(WCHAR));
294 memcpy(ValueString->Buffer,
295 SourceString->Buffer,
296 ValueString->Length);
297 ((PWSTR)ValueString->Buffer)[ValueString->Length / sizeof(WCHAR)] = 0;
298 }
299 }
300 else
301 {
302 memcpy(QueryEntry->EntryContext,
303 QueryEntry->DefaultData,
304 QueryEntry->DefaultLength);
305 }
306 Status = STATUS_SUCCESS;
307 }
308 else
309 {
310 if (ValueInfo->Type == REG_SZ ||
311 ValueInfo->Type == REG_MULTI_SZ ||
312 ValueInfo->Type == REG_EXPAND_SZ)
313 {
314 PUNICODE_STRING ValueString;
315
316 ValueString = (PUNICODE_STRING)QueryEntry->EntryContext;
317 if (ValueString->Buffer == 0)
318 {
319 RtlInitUnicodeString(ValueString,
320 NULL);
321 ValueString->MaximumLength = ValueInfo->DataLength + sizeof(WCHAR); //256 * sizeof(WCHAR);
322 ValueString->Buffer = ExAllocatePool(PagedPool,
323 ValueString->MaximumLength);
324 if (!ValueString->Buffer)
325 break;
326 ValueString->Buffer[0] = 0;
327 }
328 ValueString->Length = RtlMin(ValueInfo->DataLength,
329 ValueString->MaximumLength - sizeof(WCHAR));
330 memcpy(ValueString->Buffer,
331 ValueInfo->Data,
332 ValueString->Length);
333 ((PWSTR)ValueString->Buffer)[ValueString->Length / sizeof(WCHAR)] = 0;
334 }
335 else
336 {
337 memcpy(QueryEntry->EntryContext,
338 ValueInfo->Data,
339 ValueInfo->DataLength);
340 }
341 }
342
343 if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DELETE)
344 {
345 DPRINT("FIXME: Delete value: %S\n", QueryEntry->Name);
346
347 }
348
349 ExFreePool(ValueInfo);
350 }
351 else
352 {
353 DPRINT("Query value via query routine: %S\n", QueryEntry->Name);
354
355 if (QueryEntry->Name != NULL)
356 {
357 DPRINT("Callback\n");
358
359 RtlInitUnicodeString(&KeyName,
360 QueryEntry->Name);
361
362 BufferSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 4096;
363 ValueInfo = ExAllocatePool(PagedPool,
364 BufferSize);
365 if (ValueInfo == NULL)
366 {
367 Status = STATUS_NO_MEMORY;
368 break;
369 }
370
371 Status = NtQueryValueKey(CurrentKeyHandle,
372 &KeyName,
373 KeyValuePartialInformation,
374 ValueInfo,
375 BufferSize,
376 &ResultSize);
377 if (!NT_SUCCESS(Status))
378 {
379 Status = QueryEntry->QueryRoutine(QueryEntry->Name,
380 QueryEntry->DefaultType,
381 QueryEntry->DefaultData,
382 QueryEntry->DefaultLength,
383 Context,
384 QueryEntry->EntryContext);
385 }
386 else if ((ValueInfo->Type == REG_MULTI_SZ) &&
387 !(QueryEntry->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
388 {
389 DPRINT("Expand REG_MULTI_SZ type\n");
390 StringPtr = (PWSTR)ValueInfo->Data;
391 while (*StringPtr != 0)
392 {
393 StringLen = (wcslen(StringPtr) + 1) * sizeof(WCHAR);
394 Status = QueryEntry->QueryRoutine(QueryEntry->Name,
395 REG_SZ,
396 (PVOID)StringPtr,
397 StringLen,
398 Context,
399 QueryEntry->EntryContext);
400 if(!NT_SUCCESS(Status))
401 break;
402 StringPtr = (PWSTR)((PUCHAR)StringPtr + StringLen);
403 }
404 }
405 else
406 {
407 Status = QueryEntry->QueryRoutine(QueryEntry->Name,
408 ValueInfo->Type,
409 ValueInfo->Data,
410 ValueInfo->DataLength,
411 Context,
412 QueryEntry->EntryContext);
413 }
414
415 if (QueryEntry->Flags & RTL_QUERY_REGISTRY_DELETE)
416 {
417 DPRINT("FIXME: Delete value: %S\n", QueryEntry->Name);
418
419 }
420
421 ExFreePool(ValueInfo);
422
423 if (!NT_SUCCESS(Status))
424 break;
425 }
426 else if (QueryEntry->Flags & RTL_QUERY_REGISTRY_NOVALUE)
427 {
428 DPRINT("Simple callback\n");
429 Status = QueryEntry->QueryRoutine(NULL,
430 REG_NONE,
431 NULL,
432 0,
433 Context,
434 QueryEntry->EntryContext);
435 if (!NT_SUCCESS(Status))
436 break;
437 }
438 else
439 {
440 DPRINT("Enumerate values\n");
441
442 BufferSize = sizeof(KEY_VALUE_FULL_INFORMATION) + 4096;
443 FullValueInfo = ExAllocatePool(PagedPool,
444 BufferSize);
445 if (FullValueInfo == NULL)
446 {
447 Status = STATUS_NO_MEMORY;
448 break;
449 }
450
451 Index = 0;
452 while (TRUE)
453 {
454 Status = NtEnumerateValueKey(CurrentKeyHandle,
455 Index,
456 KeyValueFullInformation,
457 FullValueInfo,
458 BufferSize,
459 &ResultSize);
460 if (!NT_SUCCESS(Status))
461 {
462 if ((Status == STATUS_NO_MORE_ENTRIES) &&
463 (Index == 0) &&
464 (QueryEntry->Flags & RTL_QUERY_REGISTRY_REQUIRED))
465 {
466 Status = STATUS_OBJECT_NAME_NOT_FOUND;
467 }
468 else if (Status == STATUS_NO_MORE_ENTRIES)
469 {
470 Status = STATUS_SUCCESS;
471 }
472 break;
473 }
474
475 if ((FullValueInfo->Type == REG_MULTI_SZ) &&
476 !(QueryEntry->Flags & RTL_QUERY_REGISTRY_NOEXPAND))
477 {
478 DPRINT("Expand REG_MULTI_SZ type\n");
479 #ifdef WIN32_REGDBG
480 StringPtr = (PWSTR)(FullValueInfo + FullValueInfo->DataOffset);
481 #else
482 StringPtr = (PWSTR)((PVOID)FullValueInfo + FullValueInfo->DataOffset);
483 #endif
484 while (*StringPtr != 0)
485 {
486 StringLen = (wcslen(StringPtr) + 1) * sizeof(WCHAR);
487 Status = QueryEntry->QueryRoutine(QueryEntry->Name,
488 REG_SZ,
489 (PVOID)StringPtr,
490 StringLen,
491 Context,
492 QueryEntry->EntryContext);
493 if(!NT_SUCCESS(Status))
494 break;
495 StringPtr = (PWSTR)((PUCHAR)StringPtr + StringLen);
496 }
497 }
498 else
499 {
500 Status = QueryEntry->QueryRoutine(FullValueInfo->Name,
501 FullValueInfo->Type,
502 #ifdef WIN32_REGDBG
503 FullValueInfo + FullValueInfo->DataOffset,
504 #else
505 (PVOID)FullValueInfo + FullValueInfo->DataOffset,
506 #endif
507 FullValueInfo->DataLength,
508 Context,
509 QueryEntry->EntryContext);
510 }
511
512 if (!NT_SUCCESS(Status))
513 break;
514
515 /* FIXME: How will these be deleted? */
516
517 Index++;
518 }
519
520 ExFreePool(FullValueInfo);
521
522 if (!NT_SUCCESS(Status))
523 break;
524 }
525 }
526
527 QueryEntry++;
528 }
529
530 ByeBye:
531
532 if (CurrentKeyHandle != BaseKeyHandle)
533 NtClose(CurrentKeyHandle);
534
535 NtClose(BaseKeyHandle);
536
537 return(Status);
538 }
539
540
541 NTSTATUS STDCALL
542 RtlWriteRegistryValue(IN ULONG RelativeTo,
543 IN PWSTR Path,
544 IN PWSTR ValueName,
545 IN ULONG ValueType,
546 IN PVOID ValueData,
547 IN ULONG ValueLength)
548 {
549 HANDLE KeyHandle;
550 NTSTATUS Status;
551 UNICODE_STRING Name;
552
553 Status = RtlpGetRegistryHandle(RelativeTo,
554 Path,
555 TRUE,
556 &KeyHandle);
557 if (!NT_SUCCESS(Status))
558 return(Status);
559
560 RtlInitUnicodeString(&Name,
561 ValueName);
562
563 NtSetValueKey(KeyHandle,
564 &Name,
565 0,
566 ValueType,
567 ValueData,
568 ValueLength);
569
570 NtClose(KeyHandle);
571
572 return(STATUS_SUCCESS);
573 }
574
575
576 NTSTATUS STDCALL
577 RtlFormatCurrentUserKeyPath(IN OUT PUNICODE_STRING KeyPath)
578 {
579 /* FIXME: !!! */
580 RtlCreateUnicodeString(KeyPath,
581 L"\\Registry\\User\\.Default");
582
583 return(STATUS_SUCCESS);
584 }
585
586 /* ------------------------------------------ Private Implementation */
587
588
589 NTSTATUS
590 RtlpGetRegistryHandle(ULONG RelativeTo,
591 PWSTR Path,
592 BOOLEAN Create,
593 PHANDLE KeyHandle)
594 {
595 UNICODE_STRING KeyName;
596 WCHAR KeyBuffer[MAX_PATH];
597 OBJECT_ATTRIBUTES ObjectAttributes;
598 NTSTATUS Status;
599
600 if (RelativeTo & RTL_REGISTRY_HANDLE)
601 {
602 Status = NtDuplicateObject(PsGetCurrentProcessId(),
603 (HANDLE)Path,
604 PsGetCurrentProcessId(),
605 KeyHandle,
606 0,
607 FALSE,
608 DUPLICATE_SAME_ACCESS);
609 return(Status);
610 }
611
612 if (RelativeTo & RTL_REGISTRY_OPTIONAL)
613 RelativeTo &= ~RTL_REGISTRY_OPTIONAL;
614
615 if (RelativeTo >= RTL_REGISTRY_MAXIMUM)
616 return STATUS_INVALID_PARAMETER;
617
618 KeyName.Length = 0;
619 KeyName.MaximumLength = MAX_PATH;
620 KeyName.Buffer = KeyBuffer;
621 KeyBuffer[0] = 0;
622
623 switch (RelativeTo)
624 {
625 case RTL_REGISTRY_SERVICES:
626 RtlAppendUnicodeToString(&KeyName,
627 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
628 break;
629
630 case RTL_REGISTRY_CONTROL:
631 RtlAppendUnicodeToString(&KeyName,
632 L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\");
633 break;
634
635 case RTL_REGISTRY_WINDOWS_NT:
636 RtlAppendUnicodeToString(&KeyName,
637 L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\");
638 break;
639
640 case RTL_REGISTRY_DEVICEMAP:
641 RtlAppendUnicodeToString(&KeyName,
642 L"\\Registry\\Machine\\Hardware\\DeviceMap\\");
643 break;
644
645 case RTL_REGISTRY_USER:
646 Status = RtlFormatCurrentUserKeyPath(&KeyName);
647 if (!NT_SUCCESS(Status))
648 return(Status);
649 break;
650
651 /* ReactOS specific */
652 case RTL_REGISTRY_ENUM:
653 RtlAppendUnicodeToString(&KeyName,
654 L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
655 break;
656 }
657
658 if (Path[0] == L'\\' && RelativeTo != RTL_REGISTRY_ABSOLUTE)
659 {
660 Path++;
661 }
662 RtlAppendUnicodeToString(&KeyName,
663 Path);
664
665 DPRINT("KeyName '%wZ'\n", &KeyName);
666
667 InitializeObjectAttributes(&ObjectAttributes,
668 &KeyName,
669 OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
670 NULL,
671 NULL);
672
673 if (Create == TRUE)
674 {
675 Status = NtCreateKey(KeyHandle,
676 KEY_ALL_ACCESS,
677 &ObjectAttributes,
678 0,
679 NULL,
680 0,
681 NULL);
682 }
683 else
684 {
685 Status = NtOpenKey(KeyHandle,
686 KEY_ALL_ACCESS,
687 &ObjectAttributes);
688 }
689
690 return(Status);
691 }
692
693
694 NTSTATUS
695 RtlpCreateRegistryKeyPath(PWSTR Path)
696 {
697 OBJECT_ATTRIBUTES ObjectAttributes;
698 WCHAR KeyBuffer[MAX_PATH];
699 UNICODE_STRING KeyName;
700 HANDLE KeyHandle;
701 NTSTATUS Status;
702 PWCHAR Current;
703 PWCHAR Next;
704
705 if (_wcsnicmp(Path, L"\\Registry\\", 10) != 0)
706 {
707 return(STATUS_INVALID_PARAMETER);
708 }
709
710 wcsncpy(KeyBuffer, Path, MAX_PATH-1);
711 RtlInitUnicodeString(&KeyName, KeyBuffer);
712
713 /* Skip \\Registry\\ */
714 Current = KeyName.Buffer;
715 Current = wcschr(Current, '\\') + 1;
716 Current = wcschr(Current, '\\') + 1;
717
718 do {
719 Next = wcschr(Current, '\\');
720 if (Next == NULL)
721 {
722 /* The end */
723 }
724 else
725 {
726 *Next = 0;
727 }
728
729 InitializeObjectAttributes(
730 &ObjectAttributes,
731 &KeyName,
732 OBJ_CASE_INSENSITIVE,
733 NULL,
734 NULL);
735
736 DPRINT("Create '%S'\n", KeyName.Buffer);
737
738 Status = NtCreateKey(
739 &KeyHandle,
740 KEY_ALL_ACCESS,
741 &ObjectAttributes,
742 0,
743 NULL,
744 0,
745 NULL);
746 if (!NT_SUCCESS(Status))
747 {
748 DPRINT("NtCreateKey() failed with status %x\n", Status);
749 return Status;
750 }
751
752 NtClose(KeyHandle);
753
754 if (Next != NULL)
755 {
756 *Next = L'\\';
757 }
758
759 Current = Next + 1;
760 } while (Next != NULL);
761
762 return STATUS_SUCCESS;
763 }
764
765 /* EOF */