1 /** 2 This library provides Win32 Registry facilities. 3 4 Copyright: Copyright 2003-2004 by Matthew Wilson and Synesis Software 5 Written by Matthew Wilson 6 7 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 8 9 Author: Matthew Wilson, Kenji Hara 10 11 History: 12 Created 15th March 2003, 13 Updated 25th April 2004, 14 15 Source: $(PHOBOSSRC std/windows/registry.d) 16 */ 17 /* ///////////////////////////////////////////////////////////////////////////// 18 * 19 * This software is provided 'as-is', without any express or implied 20 * warranty. In no event will the authors be held liable for any damages 21 * arising from the use of this software. 22 * 23 * Permission is granted to anyone to use this software for any purpose, 24 * including commercial applications, and to alter it and redistribute it 25 * freely, in both source and binary form, subject to the following 26 * restrictions: 27 * 28 * - The origin of this software must not be misrepresented; you must not 29 * claim that you wrote the original software. If you use this software 30 * in a product, an acknowledgment in the product documentation would be 31 * appreciated but is not required. 32 * - Altered source versions must be plainly marked as such, and must not 33 * be misrepresented as being the original software. 34 * - This notice may not be removed or altered from any source 35 * distribution. 36 * 37 * ////////////////////////////////////////////////////////////////////////// */ 38 module std.windows.registry; 39 version (Windows): 40 41 import core.sys.windows.winbase, core.sys.windows.windef, core.sys.windows.winreg; 42 import std.array; 43 import std.conv; 44 import std.exception; 45 import std.internal.cstring; 46 import std.internal.windows.advapi32; 47 import std.system : Endian, endian; 48 import std.windows.syserror; 49 50 //debug = winreg; 51 debug(winreg) import std.stdio; 52 53 private 54 { 55 import core.sys.windows.winbase : lstrlenW; 56 57 void enforceSucc(LONG res, lazy string message, string fn = __FILE__, size_t ln = __LINE__) 58 { 59 if (res != ERROR_SUCCESS) 60 throw new RegistryException(message, res, fn, ln); 61 } 62 } 63 64 /* ************* Exceptions *************** */ 65 66 // Do not use. Left for compatibility. 67 class Win32Exception : WindowsException 68 { 69 @safe 70 this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) 71 { 72 super(0, message, fn, ln); 73 } 74 75 @safe 76 this(string message, int errnum, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) 77 { 78 super(errnum, message, fn, ln); 79 } 80 81 @property int error() { return super.code; } 82 } 83 84 version (StdUnittest) import std..string : startsWith, endsWith; 85 86 @safe unittest 87 { 88 // Test that we can throw and catch one by its own type 89 string message = "Test W1"; 90 91 auto e = collectException!Win32Exception( 92 enforce(false, new Win32Exception(message))); 93 assert(e.msg.startsWith(message)); 94 } 95 96 @system unittest 97 { 98 // ditto 99 string message = "Test W2"; 100 int code = 5; 101 102 auto e = collectException!Win32Exception( 103 enforce(false, new Win32Exception(message, code))); 104 assert(e.error == code); 105 assert(e.msg.startsWith(message)); 106 } 107 108 /** 109 Exception class thrown by the std.windows.registry classes. 110 */ 111 class RegistryException 112 : Win32Exception 113 { 114 public: 115 /** 116 Creates an instance of the exception. 117 118 Params: 119 message = The message associated with the exception. 120 */ 121 @safe 122 this(string message, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) 123 { 124 super(message, fn, ln, next); 125 } 126 127 /** 128 Creates an instance of the exception, with the given. 129 130 Params: 131 message = The message associated with the exception. 132 error = The Win32 error number associated with the exception. 133 */ 134 @safe 135 this(string message, int error, string fn = __FILE__, size_t ln = __LINE__, Throwable next = null) 136 { 137 super(message, error, fn, ln, next); 138 } 139 } 140 141 @system unittest 142 { 143 // (i) Test that we can throw and catch one by its own type 144 string message = "Test 1"; 145 int code = 3; 146 147 auto e = collectException!RegistryException( 148 enforce(false, new RegistryException(message, code))); 149 assert(e.error == code); 150 assert(e.msg.startsWith(message)); 151 } 152 153 @safe unittest 154 { 155 // ditto 156 string message = "Test 2"; 157 158 auto e = collectException!RegistryException( 159 enforce(false, new RegistryException(message))); 160 assert(e.msg.startsWith(message)); 161 } 162 163 /* ************* public enumerations *************** */ 164 165 /** 166 Enumeration of the recognised registry access modes. 167 */ 168 enum REGSAM 169 { 170 KEY_QUERY_VALUE = 0x0001, /// Permission to query subkey data 171 KEY_SET_VALUE = 0x0002, /// Permission to set subkey data 172 KEY_CREATE_SUB_KEY = 0x0004, /// Permission to create subkeys 173 KEY_ENUMERATE_SUB_KEYS = 0x0008, /// Permission to enumerate subkeys 174 KEY_NOTIFY = 0x0010, /// Permission for change notification 175 KEY_CREATE_LINK = 0x0020, /// Permission to create a symbolic link 176 KEY_WOW64_32KEY = 0x0200, /// Enables a 64- or 32-bit application to open a 32-bit key 177 KEY_WOW64_64KEY = 0x0100, /// Enables a 64- or 32-bit application to open a 64-bit key 178 KEY_WOW64_RES = 0x0300, /// 179 KEY_READ = (STANDARD_RIGHTS_READ 180 | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) 181 & ~(SYNCHRONIZE), 182 /// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, 183 /// KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY access rights 184 KEY_WRITE = (STANDARD_RIGHTS_WRITE 185 | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) 186 & ~(SYNCHRONIZE), 187 /// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, 188 /// and KEY_CREATE_SUB_KEY access rights 189 KEY_EXECUTE = KEY_READ & ~(SYNCHRONIZE), 190 /// Permission for read access 191 KEY_ALL_ACCESS = (STANDARD_RIGHTS_ALL 192 | KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY 193 | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK) 194 & ~(SYNCHRONIZE), 195 /// Combines the KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, 196 /// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and 197 /// KEY_SET_VALUE access rights, plus all the standard 198 /// access rights except SYNCHRONIZE 199 } 200 201 /** 202 Enumeration of the recognised registry value types. 203 */ 204 enum REG_VALUE_TYPE : DWORD 205 { 206 REG_UNKNOWN = -1, /// 207 REG_NONE = 0, /// The null value type. (In practise this is treated as a zero-length binary array by the Win32 registry) 208 REG_SZ = 1, /// A zero-terminated string 209 REG_EXPAND_SZ = 2, /// A zero-terminated string containing expandable environment variable references 210 REG_BINARY = 3, /// A binary blob 211 REG_DWORD = 4, /// A 32-bit unsigned integer 212 REG_DWORD_LITTLE_ENDIAN = 4, /// A 32-bit unsigned integer, stored in little-endian byte order 213 REG_DWORD_BIG_ENDIAN = 5, /// A 32-bit unsigned integer, stored in big-endian byte order 214 REG_LINK = 6, /// A registry link 215 REG_MULTI_SZ = 7, /// A set of zero-terminated strings 216 REG_RESOURCE_LIST = 8, /// A hardware resource list 217 REG_FULL_RESOURCE_DESCRIPTOR = 9, /// A hardware resource descriptor 218 REG_RESOURCE_REQUIREMENTS_LIST = 10, /// A hardware resource requirements list 219 REG_QWORD = 11, /// A 64-bit unsigned integer 220 REG_QWORD_LITTLE_ENDIAN = 11, /// A 64-bit unsigned integer, stored in little-endian byte order 221 } 222 223 224 /* ************* private *************** */ 225 226 import core.sys.windows.winnt : 227 DELETE , 228 READ_CONTROL , 229 WRITE_DAC , 230 WRITE_OWNER , 231 SYNCHRONIZE , 232 233 STANDARD_RIGHTS_REQUIRED, 234 235 STANDARD_RIGHTS_READ , 236 STANDARD_RIGHTS_WRITE , 237 STANDARD_RIGHTS_EXECUTE , 238 239 STANDARD_RIGHTS_ALL , 240 241 SPECIFIC_RIGHTS_ALL ; 242 243 import core.sys.windows.winreg : 244 REG_CREATED_NEW_KEY , 245 REG_OPENED_EXISTING_KEY ; 246 247 // Returns samDesired but without WoW64 flags if not in WoW64 mode 248 // for compatibility with Windows 2000 249 private REGSAM compatibleRegsam(in REGSAM samDesired) 250 { 251 return isWow64 ? samDesired : cast(REGSAM)(samDesired & ~REGSAM.KEY_WOW64_RES); 252 } 253 254 ///Returns true, if we are in WoW64 mode and have WoW64 flags 255 private bool haveWoW64Job(in REGSAM samDesired) 256 { 257 return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES); 258 } 259 260 private REG_VALUE_TYPE _RVT_from_Endian(Endian endian) 261 { 262 final switch (endian) 263 { 264 case Endian.bigEndian: 265 return REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN; 266 267 case Endian.littleEndian: 268 return REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN; 269 } 270 } 271 272 private LONG regCloseKey(in HKEY hkey) 273 in 274 { 275 assert(hkey !is null); 276 } 277 do 278 { 279 /* No need to attempt to close any of the standard hive keys. 280 * Although it's documented that calling RegCloseKey() on any of 281 * these hive keys is ignored, we'd rather not trust the Win32 282 * API. 283 */ 284 if (cast(uint) hkey & 0x80000000) 285 { 286 switch (cast(uint) hkey) 287 { 288 case HKEY_CLASSES_ROOT: 289 case HKEY_CURRENT_USER: 290 case HKEY_LOCAL_MACHINE: 291 case HKEY_USERS: 292 case HKEY_PERFORMANCE_DATA: 293 case HKEY_PERFORMANCE_TEXT: 294 case HKEY_PERFORMANCE_NLSTEXT: 295 case HKEY_CURRENT_CONFIG: 296 case HKEY_DYN_DATA: 297 return ERROR_SUCCESS; 298 default: 299 /* Do nothing */ 300 break; 301 } 302 } 303 304 return RegCloseKey(hkey); 305 } 306 307 private void regFlushKey(in HKEY hkey) 308 in 309 { 310 assert(hkey !is null); 311 } 312 do 313 { 314 immutable res = RegFlushKey(hkey); 315 enforceSucc(res, "Key cannot be flushed"); 316 } 317 318 private HKEY regCreateKey(in HKEY hkey, in string subKey, in DWORD dwOptions, in REGSAM samDesired, 319 in LPSECURITY_ATTRIBUTES lpsa, out DWORD disposition) 320 in 321 { 322 assert(hkey !is null); 323 assert(subKey !is null); 324 } 325 do 326 { 327 HKEY hkeyResult; 328 enforceSucc(RegCreateKeyExW( 329 hkey, subKey.tempCStringW(), 0, null, dwOptions, 330 compatibleRegsam(samDesired), cast(LPSECURITY_ATTRIBUTES) lpsa, 331 &hkeyResult, &disposition), 332 "Failed to create requested key: \"" ~ subKey ~ "\""); 333 334 return hkeyResult; 335 } 336 337 private void regDeleteKey(in HKEY hkey, in string subKey, in REGSAM samDesired) 338 in 339 { 340 assert(hkey !is null); 341 assert(subKey !is null); 342 } 343 do 344 { 345 LONG res; 346 if (haveWoW64Job(samDesired)) 347 { 348 loadAdvapi32(); 349 res = pRegDeleteKeyExW(hkey, subKey.tempCStringW(), samDesired, 0); 350 } 351 else 352 { 353 res = RegDeleteKeyW(hkey, subKey.tempCStringW()); 354 } 355 enforceSucc(res, "Key cannot be deleted: \"" ~ subKey ~ "\""); 356 } 357 358 private void regDeleteValue(in HKEY hkey, in string valueName) 359 in 360 { 361 assert(hkey !is null); 362 assert(valueName !is null); 363 } 364 do 365 { 366 enforceSucc(RegDeleteValueW(hkey, valueName.tempCStringW()), 367 "Value cannot be deleted: \"" ~ valueName ~ "\""); 368 } 369 370 private HKEY regDup(HKEY hkey) 371 in 372 { 373 assert(hkey !is null); 374 } 375 do 376 { 377 /* Can't duplicate standard keys, but don't need to, so can just return */ 378 if (cast(uint) hkey & 0x80000000) 379 { 380 switch (cast(uint) hkey) 381 { 382 case HKEY_CLASSES_ROOT: 383 case HKEY_CURRENT_USER: 384 case HKEY_LOCAL_MACHINE: 385 case HKEY_USERS: 386 case HKEY_PERFORMANCE_DATA: 387 case HKEY_PERFORMANCE_TEXT: 388 case HKEY_PERFORMANCE_NLSTEXT: 389 case HKEY_CURRENT_CONFIG: 390 case HKEY_DYN_DATA: 391 return hkey; 392 default: 393 /* Do nothing */ 394 break; 395 } 396 } 397 398 HKEY hkeyDup; 399 immutable res = RegOpenKeyW(hkey, null, &hkeyDup); 400 401 debug(winreg) 402 { 403 if (res != ERROR_SUCCESS) 404 { 405 writefln("regDup() failed: 0x%08x 0x%08x %d", hkey, hkeyDup, res); 406 } 407 408 assert(res == ERROR_SUCCESS); 409 } 410 411 return (res == ERROR_SUCCESS) ? hkeyDup : null; 412 } 413 414 private LONG regEnumKeyName(in HKEY hkey, in DWORD index, ref wchar[] name, out DWORD cchName) 415 in 416 { 417 assert(hkey !is null); 418 assert(name !is null); 419 assert(name.length > 0); 420 } 421 out(res) 422 { 423 assert(res != ERROR_MORE_DATA); 424 } 425 do 426 { 427 // The Registry API lies about the lengths of a very few sub-key lengths 428 // so we have to test to see if it whinges about more data, and provide 429 // more if it does. 430 for (;;) 431 { 432 cchName = to!DWORD(name.length); 433 immutable res = RegEnumKeyExW(hkey, index, name.ptr, &cchName, null, null, null, null); 434 if (res != ERROR_MORE_DATA) 435 return res; 436 437 // Now need to increase the size of the buffer and try again 438 name.length *= 2; 439 } 440 441 assert(0); 442 } 443 444 445 private LONG regEnumValueName(in HKEY hkey, in DWORD dwIndex, ref wchar[] name, out DWORD cchName) 446 in 447 { 448 assert(hkey !is null); 449 } 450 do 451 { 452 for (;;) 453 { 454 cchName = to!DWORD(name.length); 455 immutable res = RegEnumValueW(hkey, dwIndex, name.ptr, &cchName, null, null, null, null); 456 if (res != ERROR_MORE_DATA) 457 return res; 458 459 name.length *= 2; 460 } 461 462 assert(0); 463 } 464 465 private LONG regGetNumSubKeys(in HKEY hkey, out DWORD cSubKeys, out DWORD cchSubKeyMaxLen) 466 in 467 { 468 assert(hkey !is null); 469 } 470 do 471 { 472 return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys, 473 &cchSubKeyMaxLen, null, null, null, null, null, null); 474 } 475 476 private LONG regGetNumValues(in HKEY hkey, out DWORD cValues, out DWORD cchValueMaxLen) 477 in 478 { 479 assert(hkey !is null); 480 } 481 do 482 { 483 return RegQueryInfoKeyW(hkey, null, null, null, null, null, null, 484 &cValues, &cchValueMaxLen, null, null, null); 485 } 486 487 private REG_VALUE_TYPE regGetValueType(in HKEY hkey, in string name) 488 in 489 { 490 assert(hkey !is null); 491 } 492 do 493 { 494 REG_VALUE_TYPE type; 495 enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, null, null), 496 "Value cannot be opened: \"" ~ name ~ "\""); 497 498 return type; 499 } 500 501 private HKEY regOpenKey(in HKEY hkey, in string subKey, in REGSAM samDesired) 502 in 503 { 504 assert(hkey !is null); 505 assert(subKey !is null); 506 } 507 do 508 { 509 HKEY hkeyResult; 510 enforceSucc(RegOpenKeyExW(hkey, subKey.tempCStringW(), 0, compatibleRegsam(samDesired), &hkeyResult), 511 "Failed to open requested key: \"" ~ subKey ~ "\""); 512 513 return hkeyResult; 514 } 515 516 private void regQueryValue(in HKEY hkey, string name, out string value, REG_VALUE_TYPE reqType) 517 in 518 { 519 assert(hkey !is null); 520 } 521 do 522 { 523 import core.bitop : bswap; 524 525 REG_VALUE_TYPE type; 526 527 // See https://issues.dlang.org/show_bug.cgi?id=961 on this 528 union U 529 { 530 uint dw; 531 ulong qw; 532 } 533 U u; 534 void* data = &u.qw; 535 DWORD cbData = u.qw.sizeof; 536 537 auto keynameTmp = name.tempCStringW(); 538 LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData); 539 if (res == ERROR_MORE_DATA) 540 { 541 data = (new ubyte[cbData]).ptr; 542 res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data, &cbData); 543 } 544 545 enforceSucc(res, 546 "Cannot read the requested value"); 547 enforce(type == reqType, 548 new RegistryException("Value type has been changed since the value was acquired")); 549 550 switch (type) 551 { 552 case REG_VALUE_TYPE.REG_SZ: 553 case REG_VALUE_TYPE.REG_EXPAND_SZ: 554 auto wstr = (cast(immutable(wchar)*)data)[0 .. cbData / wchar.sizeof]; 555 assert(wstr.length > 0 && wstr[$-1] == '\0'); 556 if (wstr.length && wstr[$-1] == '\0') 557 wstr.length = wstr.length - 1; 558 assert(wstr.length == 0 || wstr[$-1] != '\0'); 559 value = wstr.to!string; 560 break; 561 562 case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN: 563 version (LittleEndian) 564 value = to!string(u.dw); 565 else 566 value = to!string(bswap(u.dw)); 567 break; 568 569 case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: 570 version (LittleEndian) 571 value = to!string(bswap(u.dw)); 572 else 573 value = to!string(u.dw); 574 break; 575 576 case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN: 577 value = to!string(u.qw); 578 break; 579 580 case REG_VALUE_TYPE.REG_BINARY: 581 case REG_VALUE_TYPE.REG_MULTI_SZ: 582 default: 583 throw new RegistryException("Cannot read the given value as a string"); 584 } 585 } 586 587 private void regQueryValue(in HKEY hkey, in string name, out string[] value, REG_VALUE_TYPE reqType) 588 in 589 { 590 assert(hkey !is null); 591 } 592 do 593 { 594 REG_VALUE_TYPE type; 595 596 auto keynameTmp = name.tempCStringW(); 597 wchar[] data = new wchar[256]; 598 DWORD cbData = to!DWORD(data.length * wchar.sizeof); 599 LONG res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); 600 if (res == ERROR_MORE_DATA) 601 { 602 data.length = cbData / wchar.sizeof; 603 res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); 604 } 605 else if (res == ERROR_SUCCESS) 606 { 607 data.length = cbData / wchar.sizeof; 608 } 609 enforceSucc(res, "Cannot read the requested value"); 610 enforce(type == REG_VALUE_TYPE.REG_MULTI_SZ, 611 new RegistryException("Cannot read the given value as a string")); 612 enforce(type == reqType, 613 new RegistryException("Value type has been changed since the value was acquired")); 614 615 // Remove last two (or one) null terminator 616 assert(data.length > 0 && data[$-1] == '\0'); 617 data.length = data.length - 1; 618 if (data.length > 0 && data[$-1] == '\0') 619 data.length = data.length - 1; 620 621 auto list = std.array.split(data[], "\0"); 622 value.length = list.length; 623 foreach (i, ref v; value) 624 { 625 v = list[i].to!string; 626 } 627 } 628 629 private void regQueryValue(in HKEY hkey, in string name, out uint value, REG_VALUE_TYPE reqType) 630 in 631 { 632 assert(hkey !is null); 633 } 634 do 635 { 636 import core.bitop : bswap; 637 638 REG_VALUE_TYPE type; 639 640 DWORD cbData = value.sizeof; 641 enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData), 642 "Cannot read the requested value"); 643 enforce(type == reqType, 644 new RegistryException("Value type has been changed since the value was acquired")); 645 646 switch (type) 647 { 648 case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN: 649 version (LittleEndian) 650 static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); 651 else 652 value = bswap(value); 653 break; 654 655 case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN: 656 version (LittleEndian) 657 value = bswap(value); 658 else 659 static assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN); 660 break; 661 662 default: 663 throw new RegistryException("Cannot read the given value as a 32-bit integer"); 664 } 665 } 666 667 private void regQueryValue(in HKEY hkey, in string name, out ulong value, REG_VALUE_TYPE reqType) 668 in 669 { 670 assert(hkey !is null); 671 } 672 do 673 { 674 REG_VALUE_TYPE type; 675 676 DWORD cbData = value.sizeof; 677 enforceSucc(RegQueryValueExW(hkey, name.tempCStringW(), null, cast(LPDWORD) &type, &value, &cbData), 678 "Cannot read the requested value"); 679 enforce(type == reqType, 680 new RegistryException("Value type has been changed since the value was acquired")); 681 682 switch (type) 683 { 684 case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN: 685 break; 686 687 default: 688 throw new RegistryException("Cannot read the given value as a 64-bit integer"); 689 } 690 } 691 692 private void regQueryValue(in HKEY hkey, in string name, out byte[] value, REG_VALUE_TYPE reqType) 693 in 694 { 695 assert(hkey !is null); 696 } 697 do 698 { 699 REG_VALUE_TYPE type; 700 701 byte[] data = new byte[100]; 702 DWORD cbData = to!DWORD(data.length); 703 LONG res; 704 auto keynameTmp = name.tempCStringW(); 705 res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); 706 if (res == ERROR_MORE_DATA) 707 { 708 data.length = cbData; 709 res = RegQueryValueExW(hkey, keynameTmp, null, cast(LPDWORD) &type, data.ptr, &cbData); 710 } 711 enforceSucc(res, "Cannot read the requested value"); 712 enforce(type == reqType, 713 new RegistryException("Value type has been changed since the value was acquired")); 714 715 switch (type) 716 { 717 case REG_VALUE_TYPE.REG_BINARY: 718 data.length = cbData; 719 value = data; 720 break; 721 722 default: 723 throw new RegistryException("Cannot read the given value as a string"); 724 } 725 } 726 727 private void regSetValue(in HKEY hkey, in string subKey, in REG_VALUE_TYPE type, in LPCVOID lpData, in DWORD cbData) 728 in 729 { 730 assert(hkey !is null); 731 } 732 do 733 { 734 enforceSucc(RegSetValueExW(hkey, subKey.tempCStringW(), 0, type, cast(BYTE*) lpData, cbData), 735 "Value cannot be set: \"" ~ subKey ~ "\""); 736 } 737 738 private void regProcessNthKey(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg) 739 { 740 DWORD cSubKeys; 741 DWORD cchSubKeyMaxLen; 742 743 immutable res = regGetNumSubKeys(key.m_hkey, cSubKeys, cchSubKeyMaxLen); 744 assert(res == ERROR_SUCCESS); 745 746 wchar[] sName = new wchar[cchSubKeyMaxLen + 1]; 747 748 // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open). 749 dg((DWORD index, out string name) 750 { 751 DWORD cchName; 752 immutable res = regEnumKeyName(key.m_hkey, index, sName, cchName); 753 if (res == ERROR_SUCCESS) 754 { 755 name = sName[0 .. cchName].to!string; 756 } 757 return res; 758 }); 759 } 760 761 private void regProcessNthValue(Key key, scope void delegate(scope LONG delegate(DWORD, out string)) dg) 762 { 763 DWORD cValues; 764 DWORD cchValueMaxLen; 765 766 immutable res = regGetNumValues(key.m_hkey, cValues, cchValueMaxLen); 767 assert(res == ERROR_SUCCESS); 768 769 wchar[] sName = new wchar[cchValueMaxLen + 1]; 770 771 // Capture `key` in the lambda to keep the object alive (and so its HKEY handle open). 772 dg((DWORD index, out string name) 773 { 774 DWORD cchName; 775 immutable res = regEnumValueName(key.m_hkey, index, sName, cchName); 776 if (res == ERROR_SUCCESS) 777 { 778 name = sName[0 .. cchName].to!string; 779 } 780 return res; 781 }); 782 } 783 784 /* ************* public classes *************** */ 785 786 /** 787 This class represents a registry key. 788 */ 789 class Key 790 { 791 @safe pure nothrow 792 invariant() 793 { 794 assert(m_hkey !is null); 795 } 796 797 private: 798 @safe pure nothrow 799 this(HKEY hkey, string name, bool created) 800 in 801 { 802 assert(hkey !is null); 803 } 804 do 805 { 806 m_hkey = hkey; 807 m_name = name; 808 } 809 810 ~this() 811 { 812 regCloseKey(m_hkey); 813 814 // Even though this is horried waste-of-cycles programming 815 // we're doing it here so that the 816 m_hkey = null; 817 } 818 819 public: 820 /// The name of the key 821 @property string name() @safe pure nothrow const 822 { 823 return m_name; 824 } 825 826 /** 827 The number of sub keys. 828 */ 829 @property size_t keyCount() const 830 { 831 uint cSubKeys; 832 uint cchSubKeyMaxLen; 833 enforceSucc(regGetNumSubKeys(m_hkey, cSubKeys, cchSubKeyMaxLen), 834 "Number of sub-keys cannot be determined"); 835 836 return cSubKeys; 837 } 838 839 /** 840 An enumerable sequence of all the sub-keys of this key. 841 */ 842 @property KeySequence keys() @safe pure 843 { 844 return new KeySequence(this); 845 } 846 847 /** 848 An enumerable sequence of the names of all the sub-keys of this key. 849 */ 850 @property KeyNameSequence keyNames() @safe pure 851 { 852 return new KeyNameSequence(this); 853 } 854 855 /** 856 The number of values. 857 */ 858 @property size_t valueCount() const 859 { 860 uint cValues; 861 uint cchValueMaxLen; 862 enforceSucc(regGetNumValues(m_hkey, cValues, cchValueMaxLen), 863 "Number of values cannot be determined"); 864 865 return cValues; 866 } 867 868 /** 869 An enumerable sequence of all the values of this key. 870 */ 871 @property ValueSequence values() @safe pure 872 { 873 return new ValueSequence(this); 874 } 875 876 /** 877 An enumerable sequence of the names of all the values of this key. 878 */ 879 @property ValueNameSequence valueNames() @safe pure 880 { 881 return new ValueNameSequence(this); 882 } 883 884 public: 885 /** 886 Returns the named sub-key of this key. 887 888 Params: 889 name = The name of the subkey to create. May not be `null`. 890 Returns: 891 The created key. 892 Throws: 893 `RegistryException` is thrown if the key cannot be created. 894 */ 895 Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS) 896 { 897 enforce(!name.empty, new RegistryException("Key name is invalid")); 898 899 DWORD disposition; 900 HKEY hkey = regCreateKey(m_hkey, name, 0, access, null, disposition); 901 assert(hkey !is null); 902 903 // Potential resource leak here!! 904 // 905 // If the allocation of the memory for Key fails, the HKEY could be 906 // lost. Hence, we catch such a failure by the finally, and release 907 // the HKEY there. If the creation of 908 try 909 { 910 Key key = new Key(hkey, name, disposition == REG_CREATED_NEW_KEY); 911 hkey = null; 912 return key; 913 } 914 finally 915 { 916 if (hkey !is null) 917 { 918 regCloseKey(hkey); 919 } 920 } 921 } 922 923 /** 924 Returns the named sub-key of this key. 925 926 Params: 927 name = The name of the subkey to aquire. If name is the empty 928 string, then the called key is duplicated. 929 access = The desired access; one of the `REGSAM` enumeration. 930 Returns: 931 The aquired key. 932 Throws: 933 This function never returns `null`. If a key corresponding to 934 the requested name is not found, `RegistryException` is thrown. 935 */ 936 Key getKey(string name, REGSAM access = REGSAM.KEY_READ) 937 { 938 if (name.empty) 939 return new Key(regDup(m_hkey), m_name, false); 940 941 HKEY hkey = regOpenKey(m_hkey, name, access); 942 assert(hkey !is null); 943 944 // Potential resource leak here!! 945 // 946 // If the allocation of the memory for Key fails, the HKEY could be 947 // lost. Hence, we catch such a failure by the finally, and release 948 // the HKEY there. If the creation of 949 try 950 { 951 Key key = new Key(hkey, name, false); 952 hkey = null; 953 return key; 954 } 955 finally 956 { 957 if (hkey != null) 958 { 959 regCloseKey(hkey); 960 } 961 } 962 } 963 964 /** 965 Deletes the named key. 966 967 Params: 968 name = The name of the key to delete. May not be `null`. 969 */ 970 void deleteKey(string name, REGSAM access = cast(REGSAM) 0) 971 { 972 enforce(!name.empty, new RegistryException("Key name is invalid")); 973 974 regDeleteKey(m_hkey, name, access); 975 } 976 977 /** 978 Returns the named value. 979 If `name` is the empty string, then the default value is returned. 980 981 Returns: 982 This function never returns `null`. If a value corresponding 983 to the requested name is not found, `RegistryException` is thrown. 984 */ 985 Value getValue(string name) 986 { 987 return new Value(this, name, regGetValueType(m_hkey, name)); 988 } 989 990 /** 991 Sets the named value with the given 32-bit unsigned integer value. 992 993 Params: 994 name = The name of the value to set. If it is the empty string, 995 sets the default value. 996 value = The 32-bit unsigned value to set. 997 Throws: 998 If a value corresponding to the requested name is not found, 999 `RegistryException` is thrown. 1000 */ 1001 void setValue(string name, uint value) 1002 { 1003 setValue(name, value, endian); 1004 } 1005 1006 /** 1007 Sets the named value with the given 32-bit unsigned integer value, 1008 according to the desired byte-ordering. 1009 1010 Params: 1011 name = The name of the value to set. If it is the empty string, 1012 sets the default value. 1013 value = The 32-bit unsigned value to set. 1014 endian = Can be `Endian.BigEndian` or `Endian.LittleEndian`. 1015 Throws: 1016 If a value corresponding to the requested name is not found, 1017 `RegistryException` is thrown. 1018 */ 1019 void setValue(string name, uint value, Endian endian) 1020 { 1021 REG_VALUE_TYPE type = _RVT_from_Endian(endian); 1022 1023 assert(type == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN || 1024 type == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); 1025 1026 regSetValue(m_hkey, name, type, &value, value.sizeof); 1027 } 1028 1029 /** 1030 Sets the named value with the given 64-bit unsigned integer value. 1031 1032 Params: 1033 name = The name of the value to set. If it is the empty string, 1034 sets the default value. 1035 value = The 64-bit unsigned value to set. 1036 Throws: 1037 If a value corresponding to the requested name is not found, 1038 `RegistryException` is thrown. 1039 */ 1040 void setValue(string name, ulong value) 1041 { 1042 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_QWORD, &value, value.sizeof); 1043 } 1044 1045 /** 1046 Sets the named value with the given string value. 1047 1048 Params: 1049 name = The name of the value to set. If it is the empty string, 1050 sets the default value. 1051 value = The string value to set. 1052 Throws: 1053 If a value corresponding to the requested name is not found, 1054 `RegistryException` is thrown. 1055 */ 1056 void setValue(string name, string value) 1057 { 1058 setValue(name, value, false); 1059 } 1060 1061 /** 1062 Sets the named value with the given string value. 1063 1064 Params: 1065 name = The name of the value to set. If it is the empty string, 1066 sets the default value. 1067 value = The string value to set. 1068 asEXPAND_SZ = If `true`, the value will be stored as an 1069 expandable environment string, otherwise as a normal string. 1070 Throws: 1071 If a value corresponding to the requested name is not found, 1072 `RegistryException` is thrown. 1073 */ 1074 void setValue(string name, string value, bool asEXPAND_SZ) 1075 { 1076 auto pszTmp = value.tempCStringW(); 1077 const(void)* data = pszTmp; 1078 DWORD len = to!DWORD(lstrlenW(pszTmp) * wchar.sizeof); 1079 1080 regSetValue(m_hkey, name, 1081 asEXPAND_SZ ? REG_VALUE_TYPE.REG_EXPAND_SZ 1082 : REG_VALUE_TYPE.REG_SZ, 1083 data, len); 1084 } 1085 1086 /** 1087 Sets the named value with the given multiple-strings value. 1088 1089 Params: 1090 name = The name of the value to set. If it is the empty string, 1091 sets the default value. 1092 value = The multiple-strings value to set. 1093 Throws: 1094 If a value corresponding to the requested name is not found, 1095 `RegistryException` is thrown. 1096 */ 1097 void setValue(string name, string[] value) 1098 { 1099 wstring[] data = new wstring[value.length+1]; 1100 foreach (i, ref s; data[0..$-1]) 1101 { 1102 s = value[i].to!wstring; 1103 } 1104 data[$-1] = "\0"; 1105 auto ws = std.array.join(data, "\0"w); 1106 1107 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, ws.ptr, to!uint(ws.length * wchar.sizeof)); 1108 } 1109 1110 /** 1111 Sets the named value with the given binary value. 1112 1113 Params: 1114 name = The name of the value to set. If it is the empty string, 1115 sets the default value. 1116 value = The binary value to set. 1117 Throws: 1118 If a value corresponding to the requested name is not found, 1119 `RegistryException` is thrown. 1120 */ 1121 void setValue(string name, byte[] value) 1122 { 1123 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_BINARY, value.ptr, to!DWORD(value.length)); 1124 } 1125 1126 /** 1127 Deletes the named value. 1128 1129 Params: 1130 name = The name of the value to delete. May not be `null`. 1131 Throws: 1132 If a value of the requested name is not found, 1133 `RegistryException` is thrown. 1134 */ 1135 void deleteValue(string name) 1136 { 1137 regDeleteValue(m_hkey, name); 1138 } 1139 1140 /** 1141 Flushes any changes to the key to disk. 1142 */ 1143 void flush() 1144 { 1145 regFlushKey(m_hkey); 1146 } 1147 1148 private: 1149 HKEY m_hkey; 1150 string m_name; 1151 } 1152 1153 /** 1154 This class represents a value of a registry key. 1155 */ 1156 class Value 1157 { 1158 @safe pure nothrow 1159 invariant() 1160 { 1161 assert(m_key !is null); 1162 } 1163 1164 private: 1165 @safe pure nothrow 1166 this(Key key, string name, REG_VALUE_TYPE type) 1167 in 1168 { 1169 assert(null !is key); 1170 } 1171 do 1172 { 1173 m_key = key; 1174 m_type = type; 1175 m_name = name; 1176 } 1177 1178 public: 1179 /** 1180 The name of the value. 1181 If the value represents a default value of a key, which has no name, 1182 the returned string will be of zero length. 1183 */ 1184 @property string name() @safe pure nothrow const 1185 { 1186 return m_name; 1187 } 1188 1189 /** 1190 The type of value. 1191 */ 1192 @property REG_VALUE_TYPE type() @safe pure nothrow const 1193 { 1194 return m_type; 1195 } 1196 1197 /** 1198 Obtains the current value of the value as a string. 1199 If the value's type is REG_EXPAND_SZ the returned value is <b>not</b> 1200 expanded; `value_EXPAND_SZ` should be called 1201 1202 Returns: 1203 The contents of the value. 1204 Throws: 1205 `RegistryException` if the type of the value is not REG_SZ, 1206 REG_EXPAND_SZ, REG_DWORD, or REG_QWORD. 1207 */ 1208 @property string value_SZ() const 1209 { 1210 string value; 1211 1212 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1213 1214 return value; 1215 } 1216 1217 /** 1218 Obtains the current value as a string, within which any environment 1219 variables have undergone expansion. 1220 This function works with the same value-types as `value_SZ`. 1221 1222 Returns: 1223 The contents of the value. 1224 */ 1225 @property string value_EXPAND_SZ() const 1226 { 1227 string value = value_SZ; 1228 1229 // ExpandEnvironemntStrings(): 1230 // http://msdn2.microsoft.com/en-us/library/ms724265.aspx 1231 const srcTmp = value.tempCStringW(); 1232 DWORD cchRequired = ExpandEnvironmentStringsW(srcTmp, null, 0); 1233 wchar[] newValue = new wchar[cchRequired]; 1234 1235 immutable DWORD count = enforce!Win32Exception( 1236 ExpandEnvironmentStringsW(srcTmp, newValue.ptr, to!DWORD(newValue.length)), 1237 "Failed to expand environment variables"); 1238 1239 return newValue[0 .. count-1].to!string; // remove trailing 0 1240 } 1241 1242 /** 1243 Obtains the current value as an array of strings. 1244 1245 Returns: 1246 The contents of the value. 1247 Throws: 1248 `RegistryException` if the type of the value is not REG_MULTI_SZ. 1249 */ 1250 @property string[] value_MULTI_SZ() const 1251 { 1252 string[] value; 1253 1254 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1255 1256 return value; 1257 } 1258 1259 /** 1260 Obtains the current value as a 32-bit unsigned integer, ordered 1261 correctly according to the current architecture. 1262 1263 Returns: 1264 The contents of the value. 1265 Throws: 1266 `RegistryException` is thrown for all types other than 1267 REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN. 1268 */ 1269 @property uint value_DWORD() const 1270 { 1271 uint value; 1272 1273 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1274 1275 return value; 1276 } 1277 1278 /** 1279 Obtains the value as a 64-bit unsigned integer, ordered correctly 1280 according to the current architecture. 1281 1282 Returns: 1283 The contents of the value. 1284 Throws: 1285 `RegistryException` if the type of the value is not REG_QWORD. 1286 */ 1287 @property ulong value_QWORD() const 1288 { 1289 ulong value; 1290 1291 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1292 1293 return value; 1294 } 1295 1296 /** 1297 Obtains the value as a binary blob. 1298 1299 Returns: 1300 The contents of the value. 1301 Throws: 1302 `RegistryException` if the type of the value is not REG_BINARY. 1303 */ 1304 @property byte[] value_BINARY() const 1305 { 1306 byte[] value; 1307 1308 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1309 1310 return value; 1311 } 1312 1313 private: 1314 Key m_key; 1315 REG_VALUE_TYPE m_type; 1316 string m_name; 1317 } 1318 1319 /** 1320 Represents the local system registry. 1321 */ 1322 final class Registry 1323 { 1324 private: 1325 @disable this(); 1326 1327 public: 1328 /// Returns the root key for the HKEY_CLASSES_ROOT hive 1329 static @property Key classesRoot() { return new Key(HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", false); } 1330 /// Returns the root key for the HKEY_CURRENT_USER hive 1331 static @property Key currentUser() { return new Key(HKEY_CURRENT_USER, "HKEY_CURRENT_USER", false); } 1332 /// Returns the root key for the HKEY_LOCAL_MACHINE hive 1333 static @property Key localMachine() { return new Key(HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", false); } 1334 /// Returns the root key for the HKEY_USERS hive 1335 static @property Key users() { return new Key(HKEY_USERS, "HKEY_USERS", false); } 1336 /// Returns the root key for the HKEY_PERFORMANCE_DATA hive 1337 static @property Key performanceData() { return new Key(HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA", false); } 1338 /// Returns the root key for the HKEY_CURRENT_CONFIG hive 1339 static @property Key currentConfig() { return new Key(HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", false); } 1340 /// Returns the root key for the HKEY_DYN_DATA hive 1341 static @property Key dynData() { return new Key(HKEY_DYN_DATA, "HKEY_DYN_DATA", false); } 1342 } 1343 1344 /** 1345 An enumerable sequence representing the names of the sub-keys of a registry Key. 1346 1347 Example: 1348 ---- 1349 Key key = ... 1350 foreach (string subkeyName; key.keyNames) 1351 { 1352 // using subkeyName 1353 } 1354 ---- 1355 */ 1356 class KeyNameSequence 1357 { 1358 @safe pure nothrow 1359 invariant() 1360 { 1361 assert(m_key !is null); 1362 } 1363 1364 private: 1365 @safe pure nothrow 1366 this(Key key) 1367 { 1368 m_key = key; 1369 } 1370 1371 public: 1372 /** 1373 The number of keys. 1374 */ 1375 @property size_t count() const 1376 { 1377 return m_key.keyCount; 1378 } 1379 1380 /** 1381 The name of the key at the given index. 1382 1383 Params: 1384 index = The 0-based index of the key to retrieve. 1385 Returns: 1386 The name of the key corresponding to the given index. 1387 Throws: 1388 RegistryException if no corresponding key is retrieved. 1389 */ 1390 string getKeyName(size_t index) 1391 { 1392 string name; 1393 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1394 { 1395 enforceSucc(getName(to!DWORD(index), name), "Invalid key"); 1396 }); 1397 return name; 1398 } 1399 1400 /** 1401 The name of the key at the given index. 1402 1403 Params: 1404 index = The 0-based index of the key to retrieve. 1405 Returns: 1406 The name of the key corresponding to the given index. 1407 Throws: 1408 `RegistryException` if no corresponding key is retrieved. 1409 */ 1410 string opIndex(size_t index) 1411 { 1412 return getKeyName(index); 1413 } 1414 1415 public: 1416 /// 1417 int opApply(scope int delegate(ref string name) dg) 1418 { 1419 int result; 1420 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1421 { 1422 for (DWORD index = 0; !result; ++index) 1423 { 1424 string name; 1425 immutable res = getName(index, name); 1426 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1427 break; 1428 enforceSucc(res, "Key name enumeration incomplete"); 1429 1430 result = dg(name); 1431 } 1432 }); 1433 return result; 1434 } 1435 1436 private: 1437 Key m_key; 1438 } 1439 1440 1441 /** 1442 An enumerable sequence representing the sub-keys of a registry Key. 1443 1444 Example: 1445 ---- 1446 Key key = ... 1447 foreach (Key subkey; key.keys) 1448 { 1449 // using subkey 1450 } 1451 ---- 1452 */ 1453 class KeySequence 1454 { 1455 @safe pure nothrow 1456 invariant() 1457 { 1458 assert(m_key !is null); 1459 } 1460 1461 private: 1462 @safe pure nothrow 1463 this(Key key) 1464 { 1465 m_key = key; 1466 } 1467 1468 public: 1469 /** 1470 The number of keys. 1471 */ 1472 @property size_t count() const 1473 { 1474 return m_key.keyCount; 1475 } 1476 1477 /** 1478 The key at the given index. 1479 1480 Params: 1481 index = The 0-based index of the key to retrieve. 1482 Returns: 1483 The key corresponding to the given index. 1484 Throws: 1485 `RegistryException` if no corresponding key is retrieved. 1486 */ 1487 Key getKey(size_t index) 1488 { 1489 string name; 1490 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1491 { 1492 enforceSucc(getName(to!DWORD(index), name), "Invalid key"); 1493 }); 1494 return m_key.getKey(name); 1495 } 1496 1497 /** 1498 The key at the given index. 1499 1500 Params: 1501 index = The 0-based index of the key to retrieve. 1502 Returns: 1503 The key corresponding to the given index. 1504 Throws: 1505 `RegistryException` if no corresponding key is retrieved. 1506 */ 1507 Key opIndex(size_t index) 1508 { 1509 return getKey(index); 1510 } 1511 1512 public: 1513 /// 1514 int opApply(scope int delegate(ref Key key) dg) 1515 { 1516 int result = 0; 1517 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1518 { 1519 for (DWORD index = 0; !result; ++index) 1520 { 1521 string name; 1522 immutable res = getName(index, name); 1523 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1524 break; 1525 enforceSucc(res, "Key enumeration incomplete"); 1526 1527 try 1528 { 1529 Key key = m_key.getKey(name); 1530 result = dg(key); 1531 } 1532 catch (RegistryException e) 1533 { 1534 // Skip inaccessible keys; they are 1535 // accessible via the KeyNameSequence 1536 if (e.error == ERROR_ACCESS_DENIED) 1537 continue; 1538 1539 throw e; 1540 } 1541 } 1542 }); 1543 return result; 1544 } 1545 1546 private: 1547 Key m_key; 1548 } 1549 1550 /** 1551 An enumerable sequence representing the names of the values of a registry Key. 1552 1553 Example: 1554 ---- 1555 Key key = ... 1556 foreach (string valueName; key.valueNames) 1557 { 1558 // using valueName 1559 } 1560 ---- 1561 */ 1562 class ValueNameSequence 1563 { 1564 @safe pure nothrow 1565 invariant() 1566 { 1567 assert(m_key !is null); 1568 } 1569 1570 private: 1571 @safe pure nothrow 1572 this(Key key) 1573 { 1574 m_key = key; 1575 } 1576 1577 public: 1578 /** 1579 The number of values. 1580 */ 1581 @property size_t count() const 1582 { 1583 return m_key.valueCount; 1584 } 1585 1586 /** 1587 The name of the value at the given index. 1588 1589 Params: 1590 index = The 0-based index of the value to retrieve. 1591 Returns: 1592 The name of the value corresponding to the given index. 1593 Throws: 1594 `RegistryException` if no corresponding value is retrieved. 1595 */ 1596 string getValueName(size_t index) 1597 { 1598 string name; 1599 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1600 { 1601 enforceSucc(getName(to!DWORD(index), name), "Invalid value"); 1602 }); 1603 return name; 1604 } 1605 1606 /** 1607 The name of the value at the given index. 1608 1609 Params: 1610 index = The 0-based index of the value to retrieve. 1611 Returns: 1612 The name of the value corresponding to the given index. 1613 Throws: 1614 `RegistryException` if no corresponding value is retrieved. 1615 */ 1616 string opIndex(size_t index) 1617 { 1618 return getValueName(index); 1619 } 1620 1621 public: 1622 /// 1623 int opApply(scope int delegate(ref string name) dg) 1624 { 1625 int result = 0; 1626 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1627 { 1628 for (DWORD index = 0; !result; ++index) 1629 { 1630 string name; 1631 immutable res = getName(index, name); 1632 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1633 break; 1634 enforceSucc(res, "Value name enumeration incomplete"); 1635 1636 result = dg(name); 1637 } 1638 }); 1639 return result; 1640 } 1641 1642 private: 1643 Key m_key; 1644 } 1645 1646 /** 1647 An enumerable sequence representing the values of a registry Key. 1648 1649 Example: 1650 ---- 1651 Key key = ... 1652 foreach (Value value; key.values) 1653 { 1654 // using value 1655 } 1656 ---- 1657 */ 1658 class ValueSequence 1659 { 1660 @safe pure nothrow 1661 invariant() 1662 { 1663 assert(m_key !is null); 1664 } 1665 1666 private: 1667 @safe pure nothrow 1668 this(Key key) 1669 { 1670 m_key = key; 1671 } 1672 1673 public: 1674 /// The number of values 1675 @property size_t count() const 1676 { 1677 return m_key.valueCount; 1678 } 1679 1680 /** 1681 The value at the given `index`. 1682 1683 Params: 1684 index = The 0-based index of the value to retrieve 1685 Returns: 1686 The value corresponding to the given index. 1687 Throws: 1688 `RegistryException` if no corresponding value is retrieved 1689 */ 1690 Value getValue(size_t index) 1691 { 1692 string name; 1693 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1694 { 1695 enforceSucc(getName(to!DWORD(index), name), "Invalid value"); 1696 }); 1697 return m_key.getValue(name); 1698 } 1699 1700 /** 1701 The value at the given `index`. 1702 1703 Params: 1704 index = The 0-based index of the value to retrieve. 1705 Returns: 1706 The value corresponding to the given index. 1707 Throws: 1708 `RegistryException` if no corresponding value is retrieved. 1709 */ 1710 Value opIndex(size_t index) 1711 { 1712 return getValue(index); 1713 } 1714 1715 public: 1716 /// 1717 int opApply(scope int delegate(ref Value value) dg) 1718 { 1719 int result = 0; 1720 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1721 { 1722 for (DWORD index = 0; !result; ++index) 1723 { 1724 string name; 1725 immutable res = getName(index, name); 1726 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1727 break; 1728 enforceSucc(res, "Value enumeration incomplete"); 1729 1730 Value value = m_key.getValue(name); 1731 result = dg(value); 1732 } 1733 }); 1734 return result; 1735 } 1736 1737 private: 1738 Key m_key; 1739 } 1740 1741 1742 @system unittest 1743 { 1744 debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); 1745 debug(winreg) writefln("std.windows.registry.unittest read"); 1746 1747 /+ 1748 // Mask for test speed up 1749 1750 Key HKCR = Registry.classesRoot; 1751 Key CLSID = HKCR.getKey("CLSID"); 1752 1753 foreach (Key key; CLSID.keys) 1754 { 1755 foreach (Value val; key.values) 1756 { 1757 } 1758 } 1759 +/ 1760 Key HKCU = Registry.currentUser; 1761 assert(HKCU); 1762 1763 // Enumerate all subkeys of key Software 1764 Key softwareKey = HKCU.getKey("Software"); 1765 assert(softwareKey); 1766 foreach (Key key; softwareKey.keys) 1767 { 1768 //writefln("Key %s", key.name); 1769 foreach (Value val; key.values) 1770 { 1771 } 1772 } 1773 } 1774 1775 @system unittest 1776 { 1777 debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); 1778 debug(winreg) writefln("std.windows.registry.unittest write"); 1779 1780 // Warning: This unit test writes to the registry. 1781 // The test can fail if you don't have sufficient rights 1782 1783 Key HKCU = Registry.currentUser; 1784 assert(HKCU); 1785 1786 // Create a new key 1787 string unittestKeyName = "Temporary key for a D UnitTest which can be deleted afterwards"; 1788 Key unittestKey = HKCU.createKey(unittestKeyName); 1789 assert(unittestKey); 1790 Key cityKey = unittestKey.createKey( 1791 "CityCollection using foreign names with umlauts and accents: " 1792 ~"\u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df" 1793 ); 1794 cityKey.setValue("K\u00f6ln", "Germany"); // Cologne 1795 cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk 1796 cityKey.setValue("\u5317\u4eac", "China"); // Bejing 1797 bool foundCologne, foundMinsk, foundBeijing; 1798 foreach (Value v; cityKey.values) 1799 { 1800 auto vname = v.name; 1801 auto vvalue_SZ = v.value_SZ; 1802 if (v.name == "K\u00f6ln") 1803 { 1804 foundCologne = true; 1805 assert(v.value_SZ == "Germany"); 1806 } 1807 if (v.name == "\u041c\u0438\u043d\u0441\u043a") 1808 { 1809 foundMinsk = true; 1810 assert(v.value_SZ == "Belarus"); 1811 } 1812 if (v.name == "\u5317\u4eac") 1813 { 1814 foundBeijing = true; 1815 assert(v.value_SZ == "China"); 1816 } 1817 } 1818 assert(foundCologne); 1819 assert(foundMinsk); 1820 assert(foundBeijing); 1821 1822 Key stateKey = unittestKey.createKey("StateCollection"); 1823 stateKey.setValue("Germany", ["D\u00fcsseldorf", "K\u00f6ln", "Hamburg"]); 1824 Value v = stateKey.getValue("Germany"); 1825 string[] actual = v.value_MULTI_SZ; 1826 assert(actual.length == 3); 1827 assert(actual[0] == "D\u00fcsseldorf"); 1828 assert(actual[1] == "K\u00f6ln"); 1829 assert(actual[2] == "Hamburg"); 1830 1831 Key numberKey = unittestKey.createKey("Number"); 1832 numberKey.setValue("One", 1); 1833 Value one = numberKey.getValue("One"); 1834 assert(one.value_SZ == "1"); 1835 assert(one.value_DWORD == 1); 1836 1837 unittestKey.deleteKey(numberKey.name); 1838 unittestKey.deleteKey(stateKey.name); 1839 unittestKey.deleteKey(cityKey.name); 1840 HKCU.deleteKey(unittestKeyName); 1841 1842 auto e = collectException!RegistryException(HKCU.getKey("cDhmxsX9K23a8Uf869uB")); 1843 assert(e.msg.endsWith(" (error 2)")); 1844 } 1845 1846 @system unittest 1847 { 1848 Key HKCU = Registry.currentUser; 1849 assert(HKCU); 1850 1851 Key key = HKCU.getKey("Control Panel"); 1852 assert(key); 1853 assert(key.keyCount >= 2); 1854 1855 // Make sure `key` isn't garbage-collected while iterating over it. 1856 // Trigger a collection in the first iteration and check whether we 1857 // make it successfully to the second iteration. 1858 int i = 0; 1859 foreach (name; key.keyNames) 1860 { 1861 if (i++ > 0) 1862 break; 1863 1864 import core.memory; 1865 GC.collect(); 1866 } 1867 assert(i == 2); 1868 }