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