The real, definitive, Visual C++ support branch. Accept no substitutes
[reactos.git] / base / setup / usetup / registry.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * COPYRIGHT: See COPYING in the top level directory
21 * PROJECT: ReactOS text-mode setup
22 * FILE: subsys/system/usetup/registry.c
23 * PURPOSE: Registry creation functions
24 * PROGRAMMER: Eric Kohl
25 */
26
27 /* INCLUDES *****************************************************************/
28
29 #include "usetup.h"
30
31 #define NDEBUG
32 #include <debug.h>
33
34 #ifdef __REACTOS__
35 #define FLG_ADDREG_BINVALUETYPE 0x00000001
36 #define FLG_ADDREG_NOCLOBBER 0x00000002
37 #define FLG_ADDREG_DELVAL 0x00000004
38 #define FLG_ADDREG_APPEND 0x00000008
39 #define FLG_ADDREG_KEYONLY 0x00000010
40 #define FLG_ADDREG_OVERWRITEONLY 0x00000020
41 #define FLG_ADDREG_TYPE_SZ 0x00000000
42 #define FLG_ADDREG_TYPE_MULTI_SZ 0x00010000
43 #define FLG_ADDREG_TYPE_EXPAND_SZ 0x00020000
44 #define FLG_ADDREG_TYPE_BINARY (0x00000000 | FLG_ADDREG_BINVALUETYPE)
45 #define FLG_ADDREG_TYPE_DWORD (0x00010000 | FLG_ADDREG_BINVALUETYPE)
46 #define FLG_ADDREG_TYPE_NONE (0x00020000 | FLG_ADDREG_BINVALUETYPE)
47 #define FLG_ADDREG_TYPE_MASK (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
48 #endif
49
50 #include <pshpack1.h>
51
52 typedef struct _REG_DISK_MOUNT_INFO
53 {
54 ULONG Signature;
55 LARGE_INTEGER StartingOffset;
56 } REG_DISK_MOUNT_INFO, *PREG_DISK_MOUNT_INFO;
57
58 #include <poppack.h>
59
60 /* FUNCTIONS ****************************************************************/
61
62 static BOOLEAN
63 GetRootKey (PWCHAR Name)
64 {
65 if (!_wcsicmp (Name, L"HKCR"))
66 {
67 wcscpy (Name, L"\\Registry\\Machine\\SOFTWARE\\Classes\\");
68 return TRUE;
69 }
70
71 if (!_wcsicmp (Name, L"HKCU"))
72 {
73 wcscpy (Name, L"\\Registry\\User\\.DEFAULT\\");
74 return TRUE;
75 }
76
77 if (!_wcsicmp (Name, L"HKLM"))
78 {
79 wcscpy (Name, L"\\Registry\\Machine\\");
80 return TRUE;
81 }
82
83 if (!_wcsicmp (Name, L"HKU"))
84 {
85 wcscpy (Name, L"\\Registry\\User\\");
86 return TRUE;
87 }
88
89 #if 0
90 if (!_wcsicmp (Name, L"HKR"))
91 return FALSE;
92 #endif
93
94 return FALSE;
95 }
96
97
98 /***********************************************************************
99 * append_multi_sz_value
100 *
101 * Append a multisz string to a multisz registry value.
102 */
103 #if 0
104 static void
105 append_multi_sz_value (HANDLE hkey,
106 const WCHAR *value,
107 const WCHAR *strings,
108 DWORD str_size )
109 {
110 DWORD size, type, total;
111 WCHAR *buffer, *p;
112
113 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
114 if (type != REG_MULTI_SZ) return;
115
116 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
117 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
118
119 /* compare each string against all the existing ones */
120 total = size;
121 while (*strings)
122 {
123 int len = strlenW(strings) + 1;
124
125 for (p = buffer; *p; p += strlenW(p) + 1)
126 if (!strcmpiW( p, strings )) break;
127
128 if (!*p) /* not found, need to append it */
129 {
130 memcpy( p, strings, len * sizeof(WCHAR) );
131 p[len] = 0;
132 total += len;
133 }
134 strings += len;
135 }
136 if (total != size)
137 {
138 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
139 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
140 }
141 done:
142 HeapFree( GetProcessHeap(), 0, buffer );
143 }
144 #endif
145
146 /***********************************************************************
147 * delete_multi_sz_value
148 *
149 * Remove a string from a multisz registry value.
150 */
151 #if 0
152 static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
153 {
154 DWORD size, type;
155 WCHAR *buffer, *src, *dst;
156
157 if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
158 if (type != REG_MULTI_SZ) return;
159 /* allocate double the size, one for value before and one for after */
160 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
161 if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
162 src = buffer;
163 dst = buffer + size;
164 while (*src)
165 {
166 int len = strlenW(src) + 1;
167 if (strcmpiW( src, string ))
168 {
169 memcpy( dst, src, len * sizeof(WCHAR) );
170 dst += len;
171 }
172 src += len;
173 }
174 *dst++ = 0;
175 if (dst != buffer + 2*size) /* did we remove something? */
176 {
177 TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
178 RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
179 (BYTE *)(buffer + size), dst - (buffer + size) );
180 }
181 done:
182 HeapFree( GetProcessHeap(), 0, buffer );
183 }
184 #endif
185
186 /***********************************************************************
187 * do_reg_operation
188 *
189 * Perform an add/delete registry operation depending on the flags.
190 */
191 static BOOLEAN
192 do_reg_operation(HANDLE KeyHandle,
193 PUNICODE_STRING ValueName,
194 PINFCONTEXT Context,
195 ULONG Flags)
196 {
197 WCHAR EmptyStr = (WCHAR)0;
198 ULONG Type;
199 ULONG Size;
200
201 if (Flags & FLG_ADDREG_DELVAL) /* deletion */
202 {
203 #if 0
204 if (ValueName)
205 {
206 RegDeleteValueW( KeyHandle, ValueName );
207 }
208 else
209 {
210 RegDeleteKeyW( KeyHandle, NULL );
211 }
212 #endif
213 return TRUE;
214 }
215
216 if (Flags & FLG_ADDREG_KEYONLY)
217 return TRUE;
218
219 #if 0
220 if (Flags & (FLG_ADDREG_NOCLOBBER | FLG_ADDREG_OVERWRITEONLY))
221 {
222 BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
223 if (exists && (flags & FLG_ADDREG_NOCLOBBER))
224 return TRUE;
225 if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY))
226 return TRUE;
227 }
228 #endif
229
230 switch (Flags & FLG_ADDREG_TYPE_MASK)
231 {
232 case FLG_ADDREG_TYPE_SZ:
233 Type = REG_SZ;
234 break;
235
236 case FLG_ADDREG_TYPE_MULTI_SZ:
237 Type = REG_MULTI_SZ;
238 break;
239
240 case FLG_ADDREG_TYPE_EXPAND_SZ:
241 Type = REG_EXPAND_SZ;
242 break;
243
244 case FLG_ADDREG_TYPE_BINARY:
245 Type = REG_BINARY;
246 break;
247
248 case FLG_ADDREG_TYPE_DWORD:
249 Type = REG_DWORD;
250 break;
251
252 case FLG_ADDREG_TYPE_NONE:
253 Type = REG_NONE;
254 break;
255
256 default:
257 Type = Flags >> 16;
258 break;
259 }
260
261 if (!(Flags & FLG_ADDREG_BINVALUETYPE) ||
262 (Type == REG_DWORD && SetupGetFieldCount (Context) == 5))
263 {
264 PWCHAR Str = NULL;
265
266 if (Type == REG_MULTI_SZ)
267 {
268 if (!SetupGetMultiSzFieldW (Context, 5, NULL, 0, &Size))
269 Size = 0;
270
271 if (Size)
272 {
273 Str = (WCHAR*) RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
274 if (Str == NULL)
275 return FALSE;
276
277 SetupGetMultiSzFieldW (Context, 5, Str, Size, NULL);
278 }
279
280 if (Flags & FLG_ADDREG_APPEND)
281 {
282 if (Str == NULL)
283 return TRUE;
284
285 // append_multi_sz_value( hkey, value, str, size );
286
287 RtlFreeHeap (ProcessHeap, 0, Str);
288 return TRUE;
289 }
290 /* else fall through to normal string handling */
291 }
292 else
293 {
294 if (!SetupGetStringFieldW (Context, 5, NULL, 0, &Size))
295 Size = 0;
296
297 if (Size)
298 {
299 Str = (WCHAR*) RtlAllocateHeap (ProcessHeap, 0, Size * sizeof(WCHAR));
300 if (Str == NULL)
301 return FALSE;
302
303 SetupGetStringFieldW (Context, 5, Str, Size, NULL);
304 }
305 }
306
307 if (Type == REG_DWORD)
308 {
309 ULONG dw = Str ? wcstol (Str, NULL, 0) : 0;
310
311 DPRINT("setting dword %wZ to %lx\n", ValueName, dw);
312
313 #ifdef __REACTOS__
314 NtSetValueKey (KeyHandle,
315 ValueName,
316 0,
317 Type,
318 (PVOID)&dw,
319 sizeof(ULONG));
320 #else
321 RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)&dw, sizeof(ULONG));
322 #endif
323 }
324 else
325 {
326 DPRINT("setting value %wZ to %S\n", ValueName, Str);
327
328 if (Str)
329 {
330 #ifdef __REACTOS__
331 NtSetValueKey (KeyHandle,
332 ValueName,
333 0,
334 Type,
335 (PVOID)Str,
336 Size * sizeof(WCHAR));
337 #else
338 RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)Str, Size * sizeof(WCHAR));
339 #endif
340 }
341 else
342 {
343 #ifdef __REACTOS__
344 NtSetValueKey (KeyHandle,
345 ValueName,
346 0,
347 Type,
348 (PVOID)&EmptyStr,
349 sizeof(WCHAR));
350 #else
351 RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)&EmptyStr, sizeof(WCHAR));
352 #endif
353 }
354 }
355 RtlFreeHeap (ProcessHeap, 0, Str);
356 }
357 else /* get the binary data */
358 {
359 PUCHAR Data = NULL;
360
361 if (!SetupGetBinaryField (Context, 5, NULL, 0, &Size))
362 Size = 0;
363
364 if (Size)
365 {
366 Data = (unsigned char*) RtlAllocateHeap (ProcessHeap, 0, Size);
367 if (Data == NULL)
368 return FALSE;
369
370 DPRINT("setting binary data %wZ len %lu\n", ValueName, Size);
371 SetupGetBinaryField (Context, 5, Data, Size, NULL);
372 }
373
374 #ifdef __REACTOS__
375 NtSetValueKey (KeyHandle,
376 ValueName,
377 0,
378 Type,
379 (PVOID)Data,
380 Size);
381 #else
382 RegSetValueExW(KeyHandle, ValueName, 0, Type, (const UCHAR*)Data, Size);
383 #endif
384
385 RtlFreeHeap (ProcessHeap, 0, Data);
386 }
387
388 return TRUE;
389 }
390
391 #ifdef __REACTOS__
392 NTSTATUS
393 CreateNestedKey (PHANDLE KeyHandle,
394 ACCESS_MASK DesiredAccess,
395 POBJECT_ATTRIBUTES ObjectAttributes)
396 {
397 OBJECT_ATTRIBUTES LocalObjectAttributes;
398 UNICODE_STRING LocalKeyName;
399 ULONG Disposition;
400 NTSTATUS Status;
401 ULONG FullNameLength;
402 ULONG Length;
403 PWCHAR Ptr;
404 HANDLE LocalKeyHandle;
405
406 Status = NtCreateKey (KeyHandle,
407 KEY_ALL_ACCESS,
408 ObjectAttributes,
409 0,
410 NULL,
411 0,
412 &Disposition);
413 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", ObjectAttributes->ObjectName, Status);
414 if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
415 return Status;
416
417 /* Copy object attributes */
418 RtlCopyMemory (&LocalObjectAttributes,
419 ObjectAttributes,
420 sizeof(OBJECT_ATTRIBUTES));
421 RtlCreateUnicodeString (&LocalKeyName,
422 ObjectAttributes->ObjectName->Buffer);
423 LocalObjectAttributes.ObjectName = &LocalKeyName;
424 FullNameLength = LocalKeyName.Length / sizeof(WCHAR);
425
426 /* Remove the last part of the key name and try to create the key again. */
427 while (Status == STATUS_OBJECT_NAME_NOT_FOUND)
428 {
429 Ptr = wcsrchr (LocalKeyName.Buffer, '\\');
430 if (Ptr == NULL || Ptr == LocalKeyName.Buffer)
431 {
432 Status = STATUS_UNSUCCESSFUL;
433 break;
434 }
435 *Ptr = (WCHAR)0;
436 LocalKeyName.Length = wcslen (LocalKeyName.Buffer) * sizeof(WCHAR);
437
438 Status = NtCreateKey (&LocalKeyHandle,
439 KEY_ALL_ACCESS,
440 &LocalObjectAttributes,
441 0,
442 NULL,
443 0,
444 &Disposition);
445 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
446 }
447
448 if (!NT_SUCCESS(Status))
449 {
450 RtlFreeUnicodeString (&LocalKeyName);
451 return Status;
452 }
453
454 /* Add removed parts of the key name and create them too. */
455 Length = wcslen (LocalKeyName.Buffer);
456 while (TRUE)
457 {
458 if (Length == FullNameLength)
459 {
460 Status = STATUS_SUCCESS;
461 *KeyHandle = LocalKeyHandle;
462 break;
463 }
464 NtClose (LocalKeyHandle);
465
466 LocalKeyName.Buffer[Length] = L'\\';
467 Length = wcslen (LocalKeyName.Buffer);
468 LocalKeyName.Length = Length * sizeof(WCHAR);
469
470 Status = NtCreateKey (&LocalKeyHandle,
471 KEY_ALL_ACCESS,
472 &LocalObjectAttributes,
473 0,
474 NULL,
475 0,
476 &Disposition);
477 DPRINT("NtCreateKey(%wZ) called (Status %lx)\n", &LocalKeyName, Status);
478 if (!NT_SUCCESS(Status))
479 break;
480 }
481
482 RtlFreeUnicodeString (&LocalKeyName);
483
484 return Status;
485 }
486 #endif
487
488 /***********************************************************************
489 * registry_callback
490 *
491 * Called once for each AddReg and DelReg entry in a given section.
492 */
493 static BOOLEAN
494 registry_callback (HINF hInf, PCWSTR Section, BOOLEAN Delete)
495 {
496 OBJECT_ATTRIBUTES ObjectAttributes;
497 WCHAR Buffer[MAX_INF_STRING_LENGTH];
498 UNICODE_STRING Name;
499 UNICODE_STRING Value;
500 PUNICODE_STRING ValuePtr;
501 NTSTATUS Status;
502 UINT Flags;
503 ULONG Length;
504
505 INFCONTEXT Context;
506 HANDLE KeyHandle;
507 BOOLEAN Ok;
508
509
510 Ok = SetupFindFirstLineW (hInf, Section, NULL, &Context);
511
512 if (Ok)
513 {
514 for (;Ok; Ok = SetupFindNextLine (&Context, &Context))
515 {
516 /* get root */
517 if (!SetupGetStringFieldW (&Context, 1, Buffer, MAX_INF_STRING_LENGTH, NULL))
518 continue;
519 if (!GetRootKey (Buffer))
520 continue;
521
522 /* get key */
523 Length = wcslen (Buffer);
524 if (!SetupGetStringFieldW (&Context, 2, Buffer + Length, MAX_INF_STRING_LENGTH - Length, NULL))
525 *Buffer = 0;
526
527 DPRINT("KeyName: <%S>\n", Buffer);
528
529 /* get flags */
530 if (!SetupGetIntField (&Context, 4, (PINT)&Flags))
531 Flags = 0;
532
533 DPRINT("Flags: %lx\n", Flags);
534
535 #ifdef __REACTOS__
536 RtlInitUnicodeString (&Name,
537 Buffer);
538
539 InitializeObjectAttributes (&ObjectAttributes,
540 &Name,
541 OBJ_CASE_INSENSITIVE,
542 NULL,
543 NULL);
544
545 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
546 {
547 Status = NtOpenKey (&KeyHandle,
548 KEY_ALL_ACCESS,
549 &ObjectAttributes);
550 if (!NT_SUCCESS(Status))
551 {
552 DPRINT("NtOpenKey(%wZ) failed (Status %lx)\n", &Name, Status);
553 continue; /* ignore if it doesn't exist */
554 }
555 }
556 else
557 {
558 Status = CreateNestedKey (&KeyHandle,
559 KEY_ALL_ACCESS,
560 &ObjectAttributes);
561 if (!NT_SUCCESS(Status))
562 {
563 DPRINT("CreateNestedKey(%wZ) failed (Status %lx)\n", &Name, Status);
564 continue;
565 }
566 }
567 #else
568 if (Delete || (Flags & FLG_ADDREG_OVERWRITEONLY))
569 {
570 LONG rc = RegOpenKeyW(NULL, Buffer, &KeyHandle);
571 if (rc != ERROR_SUCCESS)
572 {
573 DPRINT("RegOpenKeyW(%S) failed (error %lu)\n", Buffer, rc);
574 continue; /* ignore if it doesn't exist */
575 }
576 }
577 else
578 {
579 LONG rc = RegCreateKeyW(NULL, Buffer, &KeyHandle);
580 if (rc != ERROR_SUCCESS)
581 {
582 DPRINT("RegCreateKeyW(%S) failed (error %lu)\n", Buffer, rc);
583 continue;
584 }
585 }
586 #endif
587
588 /* get value name */
589 if (SetupGetStringFieldW (&Context, 3, Buffer, MAX_INF_STRING_LENGTH, NULL))
590 {
591 RtlInitUnicodeString (&Value,
592 Buffer);
593 ValuePtr = &Value;
594 }
595 else
596 {
597 ValuePtr = NULL;
598 }
599
600 /* and now do it */
601 if (!do_reg_operation (KeyHandle, ValuePtr, &Context, Flags))
602 {
603 NtClose (KeyHandle);
604 return FALSE;
605 }
606
607 #ifdef __REACTOS__
608 NtClose (KeyHandle);
609 #endif
610 }
611 }
612
613 return TRUE;
614 }
615
616
617 BOOLEAN
618 ImportRegistryFile(PWSTR Filename,
619 PWSTR Section,
620 BOOLEAN Delete)
621 {
622 WCHAR FileNameBuffer[MAX_PATH];
623 HINF hInf;
624 UINT ErrorLine;
625
626 /* Load inf file from install media. */
627 wcscpy(FileNameBuffer, SourcePath.Buffer);
628 wcscat(FileNameBuffer, L"\\");
629 wcscat(FileNameBuffer, Filename);
630
631 hInf = SetupOpenInfFileW(
632 FileNameBuffer,
633 NULL,
634 INF_STYLE_WIN4,
635 &ErrorLine);
636 if (hInf == INVALID_HANDLE_VALUE)
637 {
638 DPRINT1("SetupOpenInfFile() failed\n");
639 return FALSE;
640 }
641
642 if (!registry_callback (hInf, L"AddReg", FALSE))
643 {
644 DPRINT1("registry_callback() failed\n");
645 }
646
647 SetupCloseInfFile (hInf);
648
649 return TRUE;
650 }
651
652
653 BOOLEAN
654 SetInstallPathValue(PUNICODE_STRING InstallPath)
655 {
656 OBJECT_ATTRIBUTES ObjectAttributes;
657 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE");
658 UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"InstallPath");
659 HANDLE KeyHandle;
660 NTSTATUS Status;
661
662 /* Create the 'secret' InstallPath key */
663 InitializeObjectAttributes (&ObjectAttributes,
664 &KeyName,
665 OBJ_CASE_INSENSITIVE,
666 NULL,
667 NULL);
668 Status = NtOpenKey (&KeyHandle,
669 KEY_ALL_ACCESS,
670 &ObjectAttributes);
671 if (!NT_SUCCESS(Status))
672 {
673 DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
674 return FALSE;
675 }
676
677 Status = NtSetValueKey (KeyHandle,
678 &ValueName,
679 0,
680 REG_SZ,
681 (PVOID)InstallPath->Buffer,
682 InstallPath->Length);
683 NtClose(KeyHandle);
684 if (!NT_SUCCESS(Status))
685 {
686 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
687 return FALSE;
688 }
689
690 return TRUE;
691 }
692
693 BOOLEAN
694 SetMountedDeviceValue(CHAR Letter, ULONG Signature, LARGE_INTEGER StartingOffset)
695 {
696 OBJECT_ATTRIBUTES ObjectAttributes;
697 WCHAR ValueNameBuffer[16];
698 UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\SYSTEM\\MountedDevices");
699 UNICODE_STRING ValueName;
700 REG_DISK_MOUNT_INFO MountInfo;
701 NTSTATUS Status;
702 HANDLE KeyHandle;
703
704 swprintf(ValueNameBuffer, L"\\DosDevices\\%C:", Letter);
705 RtlInitUnicodeString(&ValueName, ValueNameBuffer);
706
707 InitializeObjectAttributes (&ObjectAttributes,
708 &KeyName,
709 OBJ_CASE_INSENSITIVE,
710 NULL,
711 NULL);
712 Status = NtOpenKey (&KeyHandle,
713 KEY_ALL_ACCESS,
714 &ObjectAttributes);
715 if (!NT_SUCCESS(Status))
716 {
717 Status = NtCreateKey(&KeyHandle,
718 KEY_ALL_ACCESS,
719 &ObjectAttributes,
720 0,
721 NULL,
722 REG_OPTION_NON_VOLATILE,
723 NULL);
724 }
725
726 if (!NT_SUCCESS(Status))
727 {
728 DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
729 return FALSE;
730 }
731
732 MountInfo.Signature = Signature;
733 MountInfo.StartingOffset = StartingOffset;
734 Status = NtSetValueKey (KeyHandle,
735 &ValueName,
736 0,
737 REG_BINARY,
738 (PVOID)&MountInfo,
739 sizeof(MountInfo));
740 NtClose(KeyHandle);
741 if (!NT_SUCCESS(Status))
742 {
743 DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
744 return FALSE;
745 }
746
747 return TRUE;
748 }
749
750 /* EOF */