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