xref: /reactos/base/setup/lib/settings.c (revision 8a978a17)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2004 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 along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /* COPYRIGHT:       See COPYING in the top level directory
20  * PROJECT:         ReactOS text-mode setup
21  * FILE:            base/setup/usetup/settings.c
22  * PURPOSE:         Device settings support functions
23  * PROGRAMMERS:     Colin Finck
24  */
25 
26 /* INCLUDES *****************************************************************/
27 
28 #include "precomp.h"
29 #include "genlist.h"
30 #include "infsupp.h"
31 #include "mui.h"
32 #include "registry.h"
33 
34 #include "settings.h"
35 
36 #define NDEBUG
37 #include <debug.h>
38 
39 /* GLOBALS ******************************************************************/
40 
41 static ULONG DefaultLanguageIndex = 0;
42 
43 /* FUNCTIONS ****************************************************************/
44 
45 static
46 BOOLEAN
47 IsAcpiComputer(VOID)
48 {
49     UNICODE_STRING MultiKeyPathU = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\MultifunctionAdapter");
50     UNICODE_STRING IdentifierU = RTL_CONSTANT_STRING(L"Identifier");
51     UNICODE_STRING AcpiBiosIdentifier = RTL_CONSTANT_STRING(L"ACPI BIOS");
52     OBJECT_ATTRIBUTES ObjectAttributes;
53     PKEY_BASIC_INFORMATION pDeviceInformation = NULL;
54     ULONG DeviceInfoLength = sizeof(KEY_BASIC_INFORMATION) + 50 * sizeof(WCHAR);
55     PKEY_VALUE_PARTIAL_INFORMATION pValueInformation = NULL;
56     ULONG ValueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 50 * sizeof(WCHAR);
57     ULONG RequiredSize;
58     ULONG IndexDevice = 0;
59     UNICODE_STRING DeviceName, ValueName;
60     HANDLE hDevicesKey = NULL;
61     HANDLE hDeviceKey = NULL;
62     NTSTATUS Status;
63     BOOLEAN ret = FALSE;
64 
65     InitializeObjectAttributes(&ObjectAttributes,
66                                &MultiKeyPathU,
67                                OBJ_CASE_INSENSITIVE,
68                                NULL,
69                                NULL);
70     Status = NtOpenKey(&hDevicesKey,
71                        KEY_ENUMERATE_SUB_KEYS,
72                        &ObjectAttributes);
73     if (!NT_SUCCESS(Status))
74     {
75         DPRINT("NtOpenKey() failed with status 0x%08lx\n", Status);
76         goto cleanup;
77     }
78 
79     pDeviceInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, DeviceInfoLength);
80     if (!pDeviceInformation)
81     {
82         DPRINT("RtlAllocateHeap() failed\n");
83         Status = STATUS_NO_MEMORY;
84         goto cleanup;
85     }
86 
87     pValueInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, ValueInfoLength);
88     if (!pValueInformation)
89     {
90         DPRINT("RtlAllocateHeap() failed\n");
91         Status = STATUS_NO_MEMORY;
92         goto cleanup;
93     }
94 
95     while (TRUE)
96     {
97         Status = NtEnumerateKey(hDevicesKey,
98                                 IndexDevice,
99                                 KeyBasicInformation,
100                                 pDeviceInformation,
101                                 DeviceInfoLength,
102                                 &RequiredSize);
103         if (Status == STATUS_NO_MORE_ENTRIES)
104             break;
105         else if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
106         {
107             RtlFreeHeap(RtlGetProcessHeap(), 0, pDeviceInformation);
108             DeviceInfoLength = RequiredSize;
109             pDeviceInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, DeviceInfoLength);
110             if (!pDeviceInformation)
111             {
112                 DPRINT("RtlAllocateHeap() failed\n");
113                 Status = STATUS_NO_MEMORY;
114                 goto cleanup;
115             }
116             Status = NtEnumerateKey(hDevicesKey,
117                                     IndexDevice,
118                                     KeyBasicInformation,
119                                     pDeviceInformation,
120                                     DeviceInfoLength,
121                                     &RequiredSize);
122         }
123         if (!NT_SUCCESS(Status))
124         {
125             DPRINT("NtEnumerateKey() failed with status 0x%08lx\n", Status);
126             goto cleanup;
127         }
128         IndexDevice++;
129 
130         /* Open device key */
131         DeviceName.Length = DeviceName.MaximumLength = pDeviceInformation->NameLength;
132         DeviceName.Buffer = pDeviceInformation->Name;
133         InitializeObjectAttributes(&ObjectAttributes,
134                                    &DeviceName,
135                                    OBJ_CASE_INSENSITIVE,
136                                    hDevicesKey,
137                                    NULL);
138         Status = NtOpenKey(&hDeviceKey,
139                            KEY_QUERY_VALUE,
140                            &ObjectAttributes);
141         if (!NT_SUCCESS(Status))
142         {
143             DPRINT("NtOpenKey() failed with status 0x%08lx\n", Status);
144             goto cleanup;
145         }
146 
147         /* Read identifier */
148         Status = NtQueryValueKey(hDeviceKey,
149                                  &IdentifierU,
150                                  KeyValuePartialInformation,
151                                  pValueInformation,
152                                  ValueInfoLength,
153                                  &RequiredSize);
154         if (Status == STATUS_BUFFER_OVERFLOW || Status == STATUS_BUFFER_TOO_SMALL)
155         {
156             RtlFreeHeap(RtlGetProcessHeap(), 0, pValueInformation);
157             ValueInfoLength = RequiredSize;
158             pValueInformation = RtlAllocateHeap(RtlGetProcessHeap(), 0, ValueInfoLength);
159             if (!pValueInformation)
160             {
161                 DPRINT("RtlAllocateHeap() failed\n");
162                 Status = STATUS_NO_MEMORY;
163                 goto cleanup;
164             }
165             Status = NtQueryValueKey(hDeviceKey,
166                                      &IdentifierU,
167                                      KeyValuePartialInformation,
168                                      pValueInformation,
169                                      ValueInfoLength,
170                                      &RequiredSize);
171         }
172         if (!NT_SUCCESS(Status))
173         {
174             DPRINT("NtQueryValueKey() failed with status 0x%08lx\n", Status);
175             goto nextdevice;
176         }
177         else if (pValueInformation->Type != REG_SZ)
178         {
179             DPRINT("Wrong registry type: got 0x%lx, expected 0x%lx\n", pValueInformation->Type, REG_SZ);
180             goto nextdevice;
181         }
182 
183         ValueName.Length = ValueName.MaximumLength = pValueInformation->DataLength;
184         ValueName.Buffer = (PWCHAR)pValueInformation->Data;
185         if (ValueName.Length >= sizeof(WCHAR) && ValueName.Buffer[ValueName.Length / sizeof(WCHAR) - 1] == UNICODE_NULL)
186             ValueName.Length -= sizeof(WCHAR);
187         if (RtlEqualUnicodeString(&ValueName, &AcpiBiosIdentifier, FALSE))
188         {
189             DPRINT("Found ACPI BIOS\n");
190             ret = TRUE;
191             goto cleanup;
192         }
193 
194 nextdevice:
195         NtClose(hDeviceKey);
196         hDeviceKey = NULL;
197     }
198 
199 cleanup:
200     if (pDeviceInformation)
201         RtlFreeHeap(RtlGetProcessHeap(), 0, pDeviceInformation);
202     if (pValueInformation)
203         RtlFreeHeap(RtlGetProcessHeap(), 0, pValueInformation);
204     if (hDevicesKey)
205         NtClose(hDevicesKey);
206     if (hDeviceKey)
207         NtClose(hDeviceKey);
208     return ret;
209 }
210 
211 static
212 BOOLEAN
213 GetComputerIdentifier(
214     OUT PWSTR Identifier,
215     IN ULONG IdentifierLength)
216 {
217     OBJECT_ATTRIBUTES ObjectAttributes;
218     UNICODE_STRING KeyName;
219     LPCWSTR ComputerIdentifier;
220     HANDLE ProcessorsKey;
221     PKEY_FULL_INFORMATION pFullInfo;
222     ULONG Size, SizeNeeded;
223     NTSTATUS Status;
224 
225     DPRINT("GetComputerIdentifier() called\n");
226 
227     Size = sizeof(KEY_FULL_INFORMATION);
228     pFullInfo = (PKEY_FULL_INFORMATION)RtlAllocateHeap(RtlGetProcessHeap(), 0, Size);
229     if (!pFullInfo)
230     {
231         DPRINT("RtlAllocateHeap() failed\n");
232         return FALSE;
233     }
234 
235     /* Open the processors key */
236     RtlInitUnicodeString(&KeyName,
237                          L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
238     InitializeObjectAttributes(&ObjectAttributes,
239                                &KeyName,
240                                OBJ_CASE_INSENSITIVE,
241                                NULL,
242                                NULL);
243 
244     Status = NtOpenKey(&ProcessorsKey,
245                        KEY_QUERY_VALUE,
246                        &ObjectAttributes);
247     if (!NT_SUCCESS(Status))
248     {
249         DPRINT("NtOpenKey() failed (Status 0x%lx)\n", Status);
250         RtlFreeHeap(RtlGetProcessHeap(), 0, pFullInfo);
251         return FALSE;
252     }
253 
254     /* Get number of subkeys */
255     Status = NtQueryKey(ProcessorsKey,
256                         KeyFullInformation,
257                         pFullInfo,
258                         Size,
259                         &Size);
260     NtClose(ProcessorsKey);
261     if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
262     {
263         DPRINT("NtQueryKey() failed (Status 0x%lx)\n", Status);
264         RtlFreeHeap(RtlGetProcessHeap(), 0, pFullInfo);
265         return FALSE;
266     }
267 
268     /* Find computer identifier */
269     if (pFullInfo->SubKeys == 0)
270     {
271         /* Something strange happened. No processor detected */
272         RtlFreeHeap(RtlGetProcessHeap(), 0, pFullInfo);
273         return FALSE;
274     }
275 
276     if (IsAcpiComputer())
277     {
278         if (pFullInfo->SubKeys == 1)
279         {
280             /* Computer is mono-CPU */
281             ComputerIdentifier = L"ACPI UP";
282         }
283         else
284         {
285             /* Computer is multi-CPUs */
286             ComputerIdentifier = L"ACPI MP";
287         }
288     }
289     else
290     {
291         if (pFullInfo->SubKeys == 1)
292         {
293             /* Computer is mono-CPU */
294             ComputerIdentifier = L"PC UP";
295         }
296         else
297         {
298             /* Computer is multi-CPUs */
299             ComputerIdentifier = L"PC MP";
300         }
301     }
302 
303     RtlFreeHeap(RtlGetProcessHeap(), 0, pFullInfo);
304 
305     /* Copy computer identifier to return buffer */
306     SizeNeeded = (wcslen(ComputerIdentifier) + 1) * sizeof(WCHAR);
307     if (SizeNeeded > IdentifierLength)
308         return FALSE;
309 
310     RtlCopyMemory(Identifier, ComputerIdentifier, SizeNeeded);
311 
312     return TRUE;
313 }
314 
315 
316 /*
317  * Return values:
318  * 0x00: Failure, stop the enumeration;
319  * 0x01: Add the entry and continue the enumeration;
320  * 0x02: Skip the entry but continue the enumeration.
321  */
322 typedef UCHAR
323 (NTAPI *PPROCESS_ENTRY_ROUTINE)(
324     IN PCWSTR KeyName,
325     IN PCWSTR KeyValue,
326     OUT PVOID* UserData,
327     OUT PBOOLEAN Current,
328     IN PVOID Parameter OPTIONAL);
329 
330 static LONG
331 AddEntriesFromInfSection(
332     IN OUT PGENERIC_LIST List,
333     IN HINF InfFile,
334     IN PCWSTR SectionName,
335     IN PINFCONTEXT pContext,
336     IN PPROCESS_ENTRY_ROUTINE ProcessEntry,
337     IN PVOID Parameter OPTIONAL)
338 {
339     LONG TotalCount = 0;
340     PCWSTR KeyName;
341     PCWSTR KeyValue;
342     PVOID UserData;
343     BOOLEAN Current;
344     UCHAR RetVal;
345 
346     if (!SpInfFindFirstLine(InfFile, SectionName, NULL, pContext))
347         return -1;
348 
349     do
350     {
351         /*
352          * NOTE: Do not use INF_GetData() as it expects INF entries of exactly
353          * two fields ("key = value"); however we expect to be able to deal with
354          * entries having more than two fields, the only requirement being that
355          * the second field (field number 1) contains the field description.
356          */
357         if (!INF_GetDataField(pContext, 0, &KeyName))
358         {
359             DPRINT("INF_GetDataField() failed\n");
360             return -1;
361         }
362 
363         if (!INF_GetDataField(pContext, 1, &KeyValue))
364         {
365             DPRINT("INF_GetDataField() failed\n");
366             INF_FreeData(KeyName);
367             return -1;
368         }
369 
370         UserData = NULL;
371         Current  = FALSE;
372         RetVal = ProcessEntry(KeyName,
373                               KeyValue,
374                               &UserData,
375                               &Current,
376                               Parameter);
377         INF_FreeData(KeyName);
378         INF_FreeData(KeyValue);
379 
380         if (RetVal == 0)
381         {
382             DPRINT("ProcessEntry() failed\n");
383             return -1;
384         }
385         else if (RetVal == 1)
386         {
387             AppendGenericListEntry(List, UserData, Current);
388             ++TotalCount;
389         }
390         // else if (RetVal == 2), skip the entry.
391 
392     } while (SpInfFindNextLine(pContext, pContext));
393 
394     return TotalCount;
395 }
396 
397 static UCHAR
398 NTAPI
399 DefaultProcessEntry(
400     IN PCWSTR KeyName,
401     IN PCWSTR KeyValue,
402     OUT PVOID* UserData,
403     OUT PBOOLEAN Current,
404     IN PVOID Parameter OPTIONAL)
405 {
406     PWSTR CompareKey = (PWSTR)Parameter;
407 
408     PGENENTRY GenEntry;
409     SIZE_T IdSize, ValueSize;
410 
411     IdSize    = (wcslen(KeyName)  + 1) * sizeof(WCHAR);
412     ValueSize = (wcslen(KeyValue) + 1) * sizeof(WCHAR);
413 
414     GenEntry = RtlAllocateHeap(ProcessHeap, 0,
415                                sizeof(*GenEntry) + IdSize + ValueSize);
416     if (GenEntry == NULL)
417     {
418         /* Failure, stop enumeration */
419         DPRINT1("RtlAllocateHeap() failed\n");
420         return 0;
421     }
422 
423     GenEntry->Id    = (PCWSTR)((ULONG_PTR)GenEntry + sizeof(*GenEntry));
424     GenEntry->Value = (PCWSTR)((ULONG_PTR)GenEntry + sizeof(*GenEntry) + IdSize);
425     RtlStringCbCopyW((PWSTR)GenEntry->Id, IdSize, KeyName);
426     RtlStringCbCopyW((PWSTR)GenEntry->Value, ValueSize, KeyValue);
427 
428     *UserData = GenEntry;
429     *Current  = (CompareKey ? !_wcsicmp(KeyName, CompareKey) : FALSE);
430 
431     /* Add the entry */
432     return 1;
433 }
434 
435 
436 BOOLEAN
437 AddComputerTypeEntries(
438     _In_ HINF InfFile,
439     PGENERIC_LIST List,
440     _In_ PWSTR SectionName)
441 {
442     INFCONTEXT Context;
443     PCWSTR KeyName;
444     PCWSTR KeyValue;
445     WCHAR ComputerIdentifier[128];
446     WCHAR ComputerKey[32];
447     ULONG Count1, Count2;
448 
449     /* Get the computer identification */
450     if (!GetComputerIdentifier(ComputerIdentifier, 128))
451     {
452         ComputerIdentifier[0] = 0;
453     }
454 
455     DPRINT("Computer identifier: '%S'\n", ComputerIdentifier);
456 
457     /* Search for matching device identifier */
458     if (!SpInfFindFirstLine(InfFile, SectionName, NULL, &Context))
459     {
460         /* FIXME: error message */
461         return FALSE;
462     }
463 
464     do
465     {
466         BOOLEAN FoundId;
467 
468         if (!INF_GetDataField(&Context, 1, &KeyValue))
469         {
470             /* FIXME: Handle error! */
471             DPRINT("INF_GetDataField() failed\n");
472             return FALSE;
473         }
474 
475         DPRINT("KeyValue: %S\n", KeyValue);
476         FoundId = !!wcsstr(ComputerIdentifier, KeyValue);
477         INF_FreeData(KeyValue);
478 
479         if (!FoundId)
480             continue;
481 
482         if (!INF_GetDataField(&Context, 0, &KeyName))
483         {
484             /* FIXME: Handle error! */
485             DPRINT("INF_GetDataField() failed\n");
486             return FALSE;
487         }
488 
489         DPRINT("Computer key: %S\n", KeyName);
490         RtlStringCchCopyW(ComputerKey, ARRAYSIZE(ComputerKey), KeyName);
491         INF_FreeData(KeyName);
492     } while (SpInfFindNextLine(&Context, &Context));
493 
494     Count1 = AddEntriesFromInfSection(List,
495                                       InfFile,
496                                       L"Computer",
497                                       &Context,
498                                       DefaultProcessEntry,
499                                       ComputerKey);
500     Count2 = AddEntriesFromInfSection(List,
501                                       InfFile,
502                                       L"Computer.NT" INF_ARCH,
503                                       &Context,
504                                       DefaultProcessEntry,
505                                       ComputerKey);
506     if ((Count1 == -1) && (Count2 == -1))
507     {
508         return FALSE;
509     }
510 
511     return TRUE;
512 }
513 
514 PGENERIC_LIST
515 CreateComputerTypeList(
516     IN HINF InfFile)
517 {
518     PGENERIC_LIST List;
519     BOOLEAN Success;
520 
521     List = CreateGenericList();
522     if (List == NULL)
523         return NULL;
524 
525     Success = AddComputerTypeEntries(InfFile, List, L"Map.Computer");
526     Success |= AddComputerTypeEntries(InfFile, List, L"Map.Computer.NT" INF_ARCH);
527     if (!Success)
528     {
529         DestroyGenericList(List, TRUE);
530         return NULL;
531     }
532 
533     return List;
534 }
535 
536 static
537 BOOLEAN
538 GetDisplayIdentifier(
539     OUT PWSTR Identifier,
540     IN ULONG IdentifierLength)
541 {
542     OBJECT_ATTRIBUTES ObjectAttributes;
543     UNICODE_STRING KeyName;
544     WCHAR Buffer[32];
545     HANDLE BusKey;
546     HANDLE BusInstanceKey;
547     HANDLE ControllerKey;
548     HANDLE ControllerInstanceKey;
549     ULONG BusInstance;
550     ULONG ControllerInstance;
551     ULONG BufferLength;
552     ULONG ReturnedLength;
553     PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
554     NTSTATUS Status;
555 
556     DPRINT("GetDisplayIdentifier() called\n");
557 
558     /* Open the bus key */
559     RtlInitUnicodeString(&KeyName,
560                          L"\\Registry\\Machine\\HARDWARE\\Description\\System\\MultifunctionAdapter");
561     InitializeObjectAttributes(&ObjectAttributes,
562                                &KeyName,
563                                OBJ_CASE_INSENSITIVE,
564                                NULL,
565                                NULL);
566 
567     Status = NtOpenKey(&BusKey,
568                        KEY_ENUMERATE_SUB_KEYS,
569                        &ObjectAttributes);
570     if (!NT_SUCCESS(Status))
571     {
572         DPRINT("NtOpenKey() failed (Status %lx)\n", Status);
573         return FALSE;
574     }
575 
576     BusInstance = 0;
577     while (TRUE)
578     {
579         RtlStringCchPrintfW(Buffer, ARRAYSIZE(Buffer), L"%lu", BusInstance);
580         RtlInitUnicodeString(&KeyName, Buffer);
581         InitializeObjectAttributes(&ObjectAttributes,
582                                    &KeyName,
583                                    OBJ_CASE_INSENSITIVE,
584                                    BusKey,
585                                    NULL);
586 
587         Status = NtOpenKey(&BusInstanceKey,
588                            KEY_ENUMERATE_SUB_KEYS,
589                            &ObjectAttributes);
590         if (!NT_SUCCESS(Status))
591         {
592             DPRINT("NtOpenKey() failed (Status %lx)\n", Status);
593             NtClose(BusKey);
594             return FALSE;
595         }
596 
597         /* Open the controller type key */
598         RtlInitUnicodeString(&KeyName, L"DisplayController");
599         InitializeObjectAttributes(&ObjectAttributes,
600                                    &KeyName,
601                                    OBJ_CASE_INSENSITIVE,
602                                    BusInstanceKey,
603                                    NULL);
604 
605         Status = NtOpenKey(&ControllerKey,
606                            KEY_ENUMERATE_SUB_KEYS,
607                            &ObjectAttributes);
608         if (NT_SUCCESS(Status))
609         {
610             ControllerInstance = 0;
611 
612             while (TRUE)
613             {
614                 /* Open the pointer controller instance key */
615                 RtlStringCchPrintfW(Buffer, ARRAYSIZE(Buffer), L"%lu", ControllerInstance);
616                 RtlInitUnicodeString(&KeyName, Buffer);
617                 InitializeObjectAttributes(&ObjectAttributes,
618                                            &KeyName,
619                                            OBJ_CASE_INSENSITIVE,
620                                            ControllerKey,
621                                            NULL);
622 
623                 Status = NtOpenKey(&ControllerInstanceKey,
624                                    KEY_QUERY_VALUE,
625                                    &ObjectAttributes);
626                 if (!NT_SUCCESS(Status))
627                 {
628                     DPRINT("NtOpenKey() failed (Status %lx)\n", Status);
629                     NtClose(ControllerKey);
630                     NtClose(BusInstanceKey);
631                     NtClose(BusKey);
632                     return FALSE;
633                 }
634 
635                 /* Get controller identifier */
636                 RtlInitUnicodeString(&KeyName, L"Identifier");
637 
638                 BufferLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
639                                256 * sizeof(WCHAR);
640                 ValueInfo = (KEY_VALUE_PARTIAL_INFORMATION*) RtlAllocateHeap(RtlGetProcessHeap(),
641                                                                              0,
642                                                                              BufferLength);
643                 if (ValueInfo == NULL)
644                 {
645                     DPRINT("RtlAllocateHeap() failed\n");
646                     NtClose(ControllerInstanceKey);
647                     NtClose(ControllerKey);
648                     NtClose(BusInstanceKey);
649                     NtClose(BusKey);
650                     return FALSE;
651                 }
652 
653                 Status = NtQueryValueKey(ControllerInstanceKey,
654                                          &KeyName,
655                                          KeyValuePartialInformation,
656                                          ValueInfo,
657                                          BufferLength,
658                                          &ReturnedLength);
659                 if (NT_SUCCESS(Status))
660                 {
661                     DPRINT("Identifier: %S\n", (PWSTR)ValueInfo->Data);
662 
663                     BufferLength = min(ValueInfo->DataLength / sizeof(WCHAR), IdentifierLength);
664                     RtlCopyMemory(Identifier,
665                                   ValueInfo->Data,
666                                   BufferLength * sizeof(WCHAR));
667                     Identifier[BufferLength] = 0;
668 
669                     RtlFreeHeap(RtlGetProcessHeap(),
670                                 0,
671                                 ValueInfo);
672 
673                     NtClose(ControllerInstanceKey);
674                     NtClose(ControllerKey);
675                     NtClose(BusInstanceKey);
676                     NtClose(BusKey);
677                     return TRUE;
678                 }
679 
680                 NtClose(ControllerInstanceKey);
681 
682                 ControllerInstance++;
683             }
684 
685             NtClose(ControllerKey);
686         }
687 
688         NtClose(BusInstanceKey);
689 
690         BusInstance++;
691     }
692 
693     NtClose(BusKey);
694 
695     return FALSE;
696 }
697 
698 PGENERIC_LIST
699 CreateDisplayDriverList(
700     IN HINF InfFile)
701 {
702     PGENERIC_LIST List;
703     INFCONTEXT Context;
704     PCWSTR KeyName;
705     PCWSTR KeyValue;
706     WCHAR DisplayIdentifier[128];
707     WCHAR DisplayKey[32];
708 
709     /* Get the display identification */
710     if (!GetDisplayIdentifier(DisplayIdentifier, 128))
711     {
712         DisplayIdentifier[0] = 0;
713     }
714 
715     DPRINT("Display identifier: '%S'\n", DisplayIdentifier);
716 
717     /* Search for matching device identifier */
718     if (!SpInfFindFirstLine(InfFile, L"Map.Display", NULL, &Context))
719     {
720         /* FIXME: error message */
721         return NULL;
722     }
723 
724     do
725     {
726         BOOLEAN FoundId;
727 
728         if (!INF_GetDataField(&Context, 1, &KeyValue))
729         {
730             /* FIXME: Handle error! */
731             DPRINT("INF_GetDataField() failed\n");
732             return NULL;
733         }
734 
735         DPRINT("KeyValue: %S\n", KeyValue);
736         FoundId = !!wcsstr(DisplayIdentifier, KeyValue);
737         INF_FreeData(KeyValue);
738 
739         if (!FoundId)
740             continue;
741 
742         if (!INF_GetDataField(&Context, 0, &KeyName))
743         {
744             /* FIXME: Handle error! */
745             DPRINT("INF_GetDataField() failed\n");
746             return NULL;
747         }
748 
749         DPRINT("Display key: %S\n", KeyName);
750         RtlStringCchCopyW(DisplayKey, ARRAYSIZE(DisplayKey), KeyName);
751         INF_FreeData(KeyName);
752     } while (SpInfFindNextLine(&Context, &Context));
753 
754     List = CreateGenericList();
755     if (List == NULL)
756         return NULL;
757 
758     if (AddEntriesFromInfSection(List,
759                                  InfFile,
760                                  L"Display",
761                                  &Context,
762                                  DefaultProcessEntry,
763                                  DisplayKey) == -1)
764     {
765         DestroyGenericList(List, TRUE);
766         return NULL;
767     }
768 
769 #if 0
770     AppendGenericListEntry(List, L"Other display driver", NULL, TRUE);
771 #endif
772 
773     return List;
774 }
775 
776 
777 BOOLEAN
778 ProcessComputerFiles(
779     IN HINF InfFile,
780     IN PGENERIC_LIST List,
781     OUT PWSTR* AdditionalSectionName)
782 {
783     PGENERIC_LIST_ENTRY Entry;
784     static WCHAR SectionName[128];
785 
786     DPRINT("ProcessComputerFiles() called\n");
787 
788     Entry = GetCurrentListEntry(List);
789     if (Entry == NULL)
790         return FALSE;
791 
792     RtlStringCchPrintfW(SectionName, ARRAYSIZE(SectionName),
793                         L"Files.%s", ((PGENENTRY)GetListEntryData(Entry))->Id);
794     *AdditionalSectionName = SectionName;
795 
796     return TRUE;
797 }
798 
799 BOOLEAN
800 ProcessDisplayRegistry(
801     IN HINF InfFile,
802     IN PGENERIC_LIST List)
803 {
804     NTSTATUS Status;
805     PGENERIC_LIST_ENTRY Entry;
806     INFCONTEXT Context;
807     PCWSTR Buffer;
808     PCWSTR ServiceName;
809     ULONG StartValue;
810     ULONG Width, Height, Bpp;
811     OBJECT_ATTRIBUTES ObjectAttributes;
812     UNICODE_STRING KeyName;
813     HANDLE KeyHandle;
814     WCHAR RegPath[255];
815 
816     DPRINT("ProcessDisplayRegistry() called\n");
817 
818     Entry = GetCurrentListEntry(List);
819     if (Entry == NULL)
820         return FALSE;
821 
822     if (!SpInfFindFirstLine(InfFile, L"Display",
823                             ((PGENENTRY)GetListEntryData(Entry))->Id,
824                             &Context))
825     {
826         DPRINT1("SpInfFindFirstLine() failed\n");
827         return FALSE;
828     }
829 
830     /* Enable the correct driver */
831     if (!INF_GetDataField(&Context, 3, &ServiceName))
832     {
833         DPRINT1("INF_GetDataField() failed\n");
834         return FALSE;
835     }
836 
837     ASSERT(wcslen(ServiceName) < 10);
838     DPRINT("Service name: '%S'\n", ServiceName);
839 
840     RtlStringCchPrintfW(RegPath, ARRAYSIZE(RegPath),
841                         L"System\\CurrentControlSet\\Services\\%s",
842                         ServiceName);
843     RtlInitUnicodeString(&KeyName, RegPath);
844     InitializeObjectAttributes(&ObjectAttributes,
845                                &KeyName,
846                                OBJ_CASE_INSENSITIVE,
847                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
848                                NULL);
849     Status = NtOpenKey(&KeyHandle,
850                        KEY_SET_VALUE,
851                        &ObjectAttributes);
852     if (!NT_SUCCESS(Status))
853     {
854         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
855         return FALSE;
856     }
857 
858     StartValue = 1;
859     Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, KeyHandle,
860                                    L"Start",
861                                    REG_DWORD,
862                                    &StartValue,
863                                    sizeof(StartValue));
864     NtClose(KeyHandle);
865     if (!NT_SUCCESS(Status))
866     {
867         DPRINT1("RtlWriteRegistryValue() failed (Status %lx)\n", Status);
868         return FALSE;
869     }
870 
871     /* Set the resolution */
872 
873     if (!INF_GetDataField(&Context, 4, &Buffer))
874     {
875         DPRINT1("INF_GetDataField() failed\n");
876         return FALSE;
877     }
878 
879     RtlStringCchPrintfW(RegPath, ARRAYSIZE(RegPath),
880                         L"System\\CurrentControlSet\\Hardware Profiles\\Current\\System\\CurrentControlSet\\Services\\%s\\Device0",
881                         ServiceName);
882     DPRINT("RegPath: '%S'\n", RegPath);
883     RtlInitUnicodeString(&KeyName, RegPath);
884     InitializeObjectAttributes(&ObjectAttributes,
885                                &KeyName,
886                                OBJ_CASE_INSENSITIVE,
887                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
888                                NULL);
889     Status = NtOpenKey(&KeyHandle,
890                        KEY_SET_VALUE,
891                        &ObjectAttributes);
892     if (!NT_SUCCESS(Status))
893     {
894         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
895         return FALSE;
896     }
897 
898     Width = wcstoul(Buffer, NULL, 10);
899     Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, KeyHandle,
900                                    L"DefaultSettings.XResolution",
901                                    REG_DWORD,
902                                    &Width,
903                                    sizeof(Width));
904     if (!NT_SUCCESS(Status))
905     {
906         DPRINT1("RtlWriteRegistryValue() failed (Status %lx)\n", Status);
907         NtClose(KeyHandle);
908         return FALSE;
909     }
910 
911     if (!INF_GetDataField(&Context, 5, &Buffer))
912     {
913         DPRINT1("INF_GetDataField() failed\n");
914         NtClose(KeyHandle);
915         return FALSE;
916     }
917 
918     Height = wcstoul(Buffer, 0, 0);
919     Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, KeyHandle,
920                                    L"DefaultSettings.YResolution",
921                                    REG_DWORD,
922                                    &Height,
923                                    sizeof(Height));
924     if (!NT_SUCCESS(Status))
925     {
926         DPRINT1("RtlWriteRegistryValue() failed (Status %lx)\n", Status);
927         NtClose(KeyHandle);
928         return FALSE;
929     }
930 
931     if (!INF_GetDataField(&Context, 6, &Buffer))
932     {
933         DPRINT1("INF_GetDataField() failed\n");
934         NtClose(KeyHandle);
935         return FALSE;
936     }
937 
938     Bpp = wcstoul(Buffer, 0, 0);
939     Status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE, KeyHandle,
940                                    L"DefaultSettings.BitsPerPel",
941                                    REG_DWORD,
942                                    &Bpp,
943                                    sizeof(Bpp));
944     if (!NT_SUCCESS(Status))
945     {
946         DPRINT1("RtlWriteRegistryValue() failed (Status %lx)\n", Status);
947         NtClose(KeyHandle);
948         return FALSE;
949     }
950 
951     NtClose(KeyHandle);
952 
953     DPRINT("ProcessDisplayRegistry() done\n");
954 
955     return TRUE;
956 }
957 
958 BOOLEAN
959 ProcessLocaleRegistry(
960     IN PGENERIC_LIST List)
961 {
962     PGENERIC_LIST_ENTRY Entry;
963     PCWSTR LanguageId;
964     OBJECT_ATTRIBUTES ObjectAttributes;
965     UNICODE_STRING KeyName;
966     UNICODE_STRING ValueName;
967 
968     HANDLE KeyHandle;
969     NTSTATUS Status;
970 
971     Entry = GetCurrentListEntry(List);
972     if (Entry == NULL)
973         return FALSE;
974 
975     LanguageId = ((PGENENTRY)GetListEntryData(Entry))->Id;
976     if (LanguageId == NULL)
977         return FALSE;
978 
979     DPRINT("LanguageId: %S\n", LanguageId);
980 
981     /* Open the default users locale key */
982     RtlInitUnicodeString(&KeyName,
983                          L".DEFAULT\\Control Panel\\International");
984 
985     InitializeObjectAttributes(&ObjectAttributes,
986                                &KeyName,
987                                OBJ_CASE_INSENSITIVE,
988                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
989                                NULL);
990 
991     Status = NtOpenKey(&KeyHandle,
992                        KEY_SET_VALUE,
993                        &ObjectAttributes);
994     if (!NT_SUCCESS(Status))
995     {
996         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
997         return FALSE;
998     }
999 
1000     /* Set default user locale */
1001     RtlInitUnicodeString(&ValueName, L"Locale");
1002     Status = NtSetValueKey(KeyHandle,
1003                            &ValueName,
1004                            0,
1005                            REG_SZ,
1006                            (PVOID)LanguageId,
1007                            (wcslen(LanguageId) + 1) * sizeof(WCHAR));
1008     NtClose(KeyHandle);
1009     if (!NT_SUCCESS(Status))
1010     {
1011         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
1012         return FALSE;
1013     }
1014 
1015     /* Skip first 4 zeroes */
1016     if (wcslen(LanguageId) >= 4)
1017         LanguageId += 4;
1018 
1019     /* Open the NLS language key */
1020     RtlInitUnicodeString(&KeyName,
1021                          L"SYSTEM\\CurrentControlSet\\Control\\NLS\\Language");
1022 
1023     InitializeObjectAttributes(&ObjectAttributes,
1024                                &KeyName,
1025                                OBJ_CASE_INSENSITIVE,
1026                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
1027                                NULL);
1028 
1029     Status = NtOpenKey(&KeyHandle,
1030                        KEY_SET_VALUE,
1031                        &ObjectAttributes);
1032     if (!NT_SUCCESS(Status))
1033     {
1034         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
1035         return FALSE;
1036     }
1037 
1038     /* Set default language */
1039     RtlInitUnicodeString(&ValueName, L"Default");
1040     Status = NtSetValueKey(KeyHandle,
1041                            &ValueName,
1042                            0,
1043                            REG_SZ,
1044                            (PVOID)LanguageId,
1045                            (wcslen(LanguageId) + 1) * sizeof(WCHAR));
1046     if (!NT_SUCCESS(Status))
1047     {
1048         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
1049         NtClose(KeyHandle);
1050         return FALSE;
1051     }
1052 
1053     /* Set install language */
1054     RtlInitUnicodeString(&ValueName, L"InstallLanguage");
1055     Status = NtSetValueKey(KeyHandle,
1056                            &ValueName,
1057                            0,
1058                            REG_SZ,
1059                            (PVOID)LanguageId,
1060                            (wcslen(LanguageId) + 1) * sizeof(WCHAR));
1061     NtClose(KeyHandle);
1062     if (!NT_SUCCESS(Status))
1063     {
1064         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
1065         return FALSE;
1066     }
1067 
1068     return TRUE;
1069 }
1070 
1071 
1072 PGENERIC_LIST
1073 CreateKeyboardDriverList(
1074     IN HINF InfFile)
1075 {
1076     PGENERIC_LIST List;
1077     INFCONTEXT Context;
1078 
1079     List = CreateGenericList();
1080     if (List == NULL)
1081         return NULL;
1082 
1083     if (AddEntriesFromInfSection(List,
1084                                  InfFile,
1085                                  L"Keyboard",
1086                                  &Context,
1087                                  DefaultProcessEntry,
1088                                  NULL) == -1)
1089     {
1090         DestroyGenericList(List, TRUE);
1091         return NULL;
1092     }
1093 
1094     return List;
1095 }
1096 
1097 
1098 ULONG
1099 GetDefaultLanguageIndex(VOID)
1100 {
1101     return DefaultLanguageIndex;
1102 }
1103 
1104 typedef struct _LANG_ENTRY_PARAM
1105 {
1106     ULONG uIndex;
1107     PWCHAR DefaultLanguage;
1108 } LANG_ENTRY_PARAM, *PLANG_ENTRY_PARAM;
1109 
1110 static UCHAR
1111 NTAPI
1112 ProcessLangEntry(
1113     IN PCWSTR KeyName,
1114     IN PCWSTR KeyValue,
1115     OUT PVOID* UserData,
1116     OUT PBOOLEAN Current,
1117     IN PVOID Parameter OPTIONAL)
1118 {
1119     PLANG_ENTRY_PARAM LangEntryParam = (PLANG_ENTRY_PARAM)Parameter;
1120 
1121     PGENENTRY GenEntry;
1122     SIZE_T IdSize, ValueSize;
1123 
1124     if (!IsLanguageAvailable(KeyName))
1125     {
1126         /* The specified language is unavailable, skip the entry */
1127         return 2;
1128     }
1129 
1130     IdSize    = (wcslen(KeyName)  + 1) * sizeof(WCHAR);
1131     ValueSize = (wcslen(KeyValue) + 1) * sizeof(WCHAR);
1132 
1133     GenEntry = RtlAllocateHeap(ProcessHeap, 0,
1134                                sizeof(*GenEntry) + IdSize + ValueSize);
1135     if (GenEntry == NULL)
1136     {
1137         /* Failure, stop enumeration */
1138         DPRINT1("RtlAllocateHeap() failed\n");
1139         return 0;
1140     }
1141 
1142     GenEntry->Id    = (PCWSTR)((ULONG_PTR)GenEntry + sizeof(*GenEntry));
1143     GenEntry->Value = (PCWSTR)((ULONG_PTR)GenEntry + sizeof(*GenEntry) + IdSize);
1144     RtlStringCbCopyW((PWSTR)GenEntry->Id, IdSize, KeyName);
1145     RtlStringCbCopyW((PWSTR)GenEntry->Value, ValueSize, KeyValue);
1146 
1147     *UserData = GenEntry;
1148     *Current  = FALSE;
1149 
1150     if (!_wcsicmp(KeyName, LangEntryParam->DefaultLanguage))
1151         DefaultLanguageIndex = LangEntryParam->uIndex;
1152 
1153     LangEntryParam->uIndex++;
1154 
1155     /* Add the entry */
1156     return 1;
1157 }
1158 
1159 PGENERIC_LIST
1160 CreateLanguageList(
1161     IN HINF InfFile,
1162     OUT PWSTR DefaultLanguage)
1163 {
1164     PGENERIC_LIST List;
1165     INFCONTEXT Context;
1166     PCWSTR KeyValue;
1167 
1168     LANG_ENTRY_PARAM LangEntryParam;
1169 
1170     LangEntryParam.uIndex = 0;
1171     LangEntryParam.DefaultLanguage = DefaultLanguage;
1172 
1173     /* Get default language id */
1174     if (!SpInfFindFirstLine(InfFile, L"NLS", L"DefaultLanguage", &Context))
1175         return NULL;
1176 
1177     if (!INF_GetData(&Context, NULL, &KeyValue))
1178         return NULL;
1179 
1180     wcscpy(DefaultLanguage, KeyValue);
1181 
1182     List = CreateGenericList();
1183     if (List == NULL)
1184         return NULL;
1185 
1186     if (AddEntriesFromInfSection(List,
1187                                  InfFile,
1188                                  L"Language",
1189                                  &Context,
1190                                  ProcessLangEntry,
1191                                  &LangEntryParam) == -1)
1192     {
1193         DestroyGenericList(List, TRUE);
1194         return NULL;
1195     }
1196 
1197     /* Only one language available, make it the default one */
1198     if (LangEntryParam.uIndex == 1)
1199     {
1200         DefaultLanguageIndex = 0;
1201         wcscpy(DefaultLanguage,
1202                ((PGENENTRY)GetListEntryData(GetFirstListEntry(List)))->Id);
1203     }
1204 
1205     return List;
1206 }
1207 
1208 
1209 PGENERIC_LIST
1210 CreateKeyboardLayoutList(
1211     IN HINF InfFile,
1212     IN PCWSTR LanguageId,
1213     OUT PWSTR DefaultKBLayout)
1214 {
1215     PGENERIC_LIST List;
1216     INFCONTEXT Context;
1217     PCWSTR KeyValue;
1218     const MUI_LAYOUTS* LayoutsList;
1219     ULONG uIndex = 0;
1220 
1221     /* Get default layout id */
1222     if (!SpInfFindFirstLine(InfFile, L"NLS", L"DefaultLayout", &Context))
1223         return NULL;
1224 
1225     if (!INF_GetData(&Context, NULL, &KeyValue))
1226         return NULL;
1227 
1228     wcscpy(DefaultKBLayout, KeyValue);
1229 
1230     List = CreateGenericList();
1231     if (List == NULL)
1232         return NULL;
1233 
1234     LayoutsList = MUIGetLayoutsList(LanguageId);
1235 
1236     do
1237     {
1238         // NOTE: See https://svn.reactos.org/svn/reactos?view=revision&revision=68354
1239         if (AddEntriesFromInfSection(List,
1240                                      InfFile,
1241                                      L"KeyboardLayout",
1242                                      &Context,
1243                                      DefaultProcessEntry,
1244                                      DefaultKBLayout) == -1)
1245         {
1246             DestroyGenericList(List, TRUE);
1247             return NULL;
1248         }
1249 
1250         uIndex++;
1251 
1252     } while (LayoutsList[uIndex].LangID != NULL);
1253 
1254     /* Check whether some keyboard layouts have been found */
1255     /* FIXME: Handle this case */
1256     if (GetNumberOfListEntries(List) == 0)
1257     {
1258         DPRINT1("No keyboard layouts have been found\n");
1259         DestroyGenericList(List, TRUE);
1260         return NULL;
1261     }
1262 
1263     return List;
1264 }
1265 
1266 
1267 BOOLEAN
1268 ProcessKeyboardLayoutRegistry(
1269     IN PGENERIC_LIST List,
1270     IN PCWSTR LanguageId)
1271 {
1272     PGENERIC_LIST_ENTRY Entry;
1273     PCWSTR LayoutId;
1274     const MUI_LAYOUTS* LayoutsList;
1275     MUI_LAYOUTS NewLayoutsList[20];
1276     ULONG uIndex;
1277     ULONG uOldPos = 0;
1278 
1279     Entry = GetCurrentListEntry(List);
1280     if (Entry == NULL)
1281         return FALSE;
1282 
1283     LayoutId = ((PGENENTRY)GetListEntryData(Entry))->Id;
1284     if (LayoutId == NULL)
1285         return FALSE;
1286 
1287     LayoutsList = MUIGetLayoutsList(LanguageId);
1288 
1289     if (_wcsicmp(LayoutsList[0].LayoutID, LayoutId) == 0)
1290         return TRUE;
1291 
1292     for (uIndex = 1; LayoutsList[uIndex].LangID != NULL; uIndex++)
1293     {
1294         if (_wcsicmp(LayoutsList[uIndex].LayoutID, LayoutId) == 0)
1295         {
1296             uOldPos = uIndex;
1297             continue;
1298         }
1299 
1300         NewLayoutsList[uIndex].LangID   = LayoutsList[uIndex].LangID;
1301         NewLayoutsList[uIndex].LayoutID = LayoutsList[uIndex].LayoutID;
1302     }
1303 
1304     NewLayoutsList[uIndex].LangID    = NULL;
1305     NewLayoutsList[uIndex].LayoutID  = NULL;
1306     NewLayoutsList[uOldPos].LangID   = LayoutsList[0].LangID;
1307     NewLayoutsList[uOldPos].LayoutID = LayoutsList[0].LayoutID;
1308     NewLayoutsList[0].LangID         = LayoutsList[uOldPos].LangID;
1309     NewLayoutsList[0].LayoutID       = LayoutsList[uOldPos].LayoutID;
1310 
1311     return AddKbLayoutsToRegistry(NewLayoutsList);
1312 }
1313 
1314 #if 0
1315 BOOLEAN
1316 ProcessKeyboardLayoutFiles(
1317     IN PGENERIC_LIST List)
1318 {
1319     return TRUE;
1320 }
1321 #endif
1322 
1323 BOOLEAN
1324 SetGeoID(
1325     IN PCWSTR Id)
1326 {
1327     NTSTATUS Status;
1328     OBJECT_ATTRIBUTES ObjectAttributes;
1329     UNICODE_STRING Name;
1330     HANDLE KeyHandle;
1331 
1332     RtlInitUnicodeString(&Name,
1333                          L".DEFAULT\\Control Panel\\International\\Geo");
1334     InitializeObjectAttributes(&ObjectAttributes,
1335                                &Name,
1336                                OBJ_CASE_INSENSITIVE,
1337                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
1338                                NULL);
1339     Status =  NtOpenKey(&KeyHandle,
1340                         KEY_SET_VALUE,
1341                         &ObjectAttributes);
1342     if (!NT_SUCCESS(Status))
1343     {
1344         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
1345         return FALSE;
1346     }
1347 
1348     RtlInitUnicodeString(&Name, L"Nation");
1349     Status = NtSetValueKey(KeyHandle,
1350                            &Name,
1351                            0,
1352                            REG_SZ,
1353                            (PVOID)Id,
1354                            (wcslen(Id) + 1) * sizeof(WCHAR));
1355     NtClose(KeyHandle);
1356     if (!NT_SUCCESS(Status))
1357     {
1358         DPRINT1("NtSetValueKey() failed (Status = %lx)\n", Status);
1359         return FALSE;
1360     }
1361 
1362     return TRUE;
1363 }
1364 
1365 
1366 BOOLEAN
1367 SetDefaultPagefile(
1368     IN WCHAR Drive)
1369 {
1370     NTSTATUS Status;
1371     HANDLE KeyHandle;
1372     OBJECT_ATTRIBUTES ObjectAttributes;
1373     UNICODE_STRING KeyName = RTL_CONSTANT_STRING(L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management");
1374     UNICODE_STRING ValueName = RTL_CONSTANT_STRING(L"PagingFiles");
1375     WCHAR ValueBuffer[] = L"?:\\pagefile.sys 0 0\0";
1376 
1377     InitializeObjectAttributes(&ObjectAttributes,
1378                                &KeyName,
1379                                OBJ_CASE_INSENSITIVE,
1380                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
1381                                NULL);
1382     Status = NtOpenKey(&KeyHandle,
1383                        KEY_ALL_ACCESS,
1384                        &ObjectAttributes);
1385     if (!NT_SUCCESS(Status))
1386         return FALSE;
1387 
1388     ValueBuffer[0] = Drive;
1389 
1390     NtSetValueKey(KeyHandle,
1391                   &ValueName,
1392                   0,
1393                   REG_MULTI_SZ,
1394                   (PVOID)&ValueBuffer,
1395                   sizeof(ValueBuffer));
1396 
1397     NtClose(KeyHandle);
1398     return TRUE;
1399 }
1400 
1401 /* EOF */
1402