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