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