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 /** 885 Returns the named sub-key of this key. 886 887 Params: 888 name = The name of the subkey to create. May not be `null`. 889 Returns: 890 The created key. 891 Throws: 892 `RegistryException` is thrown if the key cannot be created. 893 */ 894 Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS) 895 { 896 enforce(!name.empty, new RegistryException("Key name is invalid")); 897 898 DWORD disposition; 899 HKEY hkey = regCreateKey(m_hkey, name, 0, access, null, disposition); 900 assert(hkey !is null); 901 902 // Potential resource leak here!! 903 // 904 // If the allocation of the memory for Key fails, the HKEY could be 905 // lost. Hence, we catch such a failure by the finally, and release 906 // the HKEY there. If the creation of 907 try 908 { 909 Key key = new Key(hkey, name, disposition == REG_CREATED_NEW_KEY); 910 hkey = null; 911 return key; 912 } 913 finally 914 { 915 if (hkey !is null) 916 { 917 regCloseKey(hkey); 918 } 919 } 920 } 921 922 /** 923 Returns the named sub-key of this key. 924 925 Params: 926 name = The name of the subkey to aquire. If name is the empty 927 string, then the called key is duplicated. 928 access = The desired access; one of the `REGSAM` enumeration. 929 Returns: 930 The aquired key. 931 Throws: 932 This function never returns `null`. If a key corresponding to 933 the requested name is not found, `RegistryException` is thrown. 934 */ 935 Key getKey(string name, REGSAM access = REGSAM.KEY_READ) 936 { 937 if (name.empty) 938 return new Key(regDup(m_hkey), m_name, false); 939 940 HKEY hkey = regOpenKey(m_hkey, name, access); 941 assert(hkey !is null); 942 943 // Potential resource leak here!! 944 // 945 // If the allocation of the memory for Key fails, the HKEY could be 946 // lost. Hence, we catch such a failure by the finally, and release 947 // the HKEY there. If the creation of 948 try 949 { 950 Key key = new Key(hkey, name, false); 951 hkey = null; 952 return key; 953 } 954 finally 955 { 956 if (hkey != null) 957 { 958 regCloseKey(hkey); 959 } 960 } 961 } 962 963 /** 964 Deletes the named key. 965 966 Params: 967 name = The name of the key to delete. May not be `null`. 968 */ 969 void deleteKey(string name, REGSAM access = cast(REGSAM) 0) 970 { 971 enforce(!name.empty, new RegistryException("Key name is invalid")); 972 973 regDeleteKey(m_hkey, name, access); 974 } 975 976 /** 977 Returns the named value. 978 If `name` is the empty string, then the default value is returned. 979 980 Returns: 981 This function never returns `null`. If a value corresponding 982 to the requested name is not found, `RegistryException` is thrown. 983 */ 984 Value getValue(string name) 985 { 986 return new Value(this, name, regGetValueType(m_hkey, name)); 987 } 988 989 /** 990 Sets the named value with the given 32-bit unsigned integer value. 991 992 Params: 993 name = The name of the value to set. If it is the empty string, 994 sets the default value. 995 value = The 32-bit unsigned value to set. 996 Throws: 997 If a value corresponding to the requested name is not found, 998 `RegistryException` is thrown. 999 */ 1000 void setValue(string name, uint value) 1001 { 1002 setValue(name, value, endian); 1003 } 1004 1005 /** 1006 Sets the named value with the given 32-bit unsigned integer value, 1007 according to the desired byte-ordering. 1008 1009 Params: 1010 name = The name of the value to set. If it is the empty string, 1011 sets the default value. 1012 value = The 32-bit unsigned value to set. 1013 endian = Can be `Endian.BigEndian` or `Endian.LittleEndian`. 1014 Throws: 1015 If a value corresponding to the requested name is not found, 1016 `RegistryException` is thrown. 1017 */ 1018 void setValue(string name, uint value, Endian endian) 1019 { 1020 REG_VALUE_TYPE type = _RVT_from_Endian(endian); 1021 1022 assert(type == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN || 1023 type == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN); 1024 1025 regSetValue(m_hkey, name, type, &value, value.sizeof); 1026 } 1027 1028 /** 1029 Sets the named value with the given 64-bit unsigned integer value. 1030 1031 Params: 1032 name = The name of the value to set. If it is the empty string, 1033 sets the default value. 1034 value = The 64-bit unsigned value to set. 1035 Throws: 1036 If a value corresponding to the requested name is not found, 1037 `RegistryException` is thrown. 1038 */ 1039 void setValue(string name, ulong value) 1040 { 1041 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_QWORD, &value, value.sizeof); 1042 } 1043 1044 /** 1045 Sets the named value with the given string value. 1046 1047 Params: 1048 name = The name of the value to set. If it is the empty string, 1049 sets the default value. 1050 value = The string value to set. 1051 Throws: 1052 If a value corresponding to the requested name is not found, 1053 `RegistryException` is thrown. 1054 */ 1055 void setValue(string name, string value) 1056 { 1057 setValue(name, value, false); 1058 } 1059 1060 /** 1061 Sets the named value with the given string value. 1062 1063 Params: 1064 name = The name of the value to set. If it is the empty string, 1065 sets the default value. 1066 value = The string value to set. 1067 asEXPAND_SZ = If `true`, the value will be stored as an 1068 expandable environment string, otherwise as a normal string. 1069 Throws: 1070 If a value corresponding to the requested name is not found, 1071 `RegistryException` is thrown. 1072 */ 1073 void setValue(string name, string value, bool asEXPAND_SZ) 1074 { 1075 auto pszTmp = value.tempCStringW(); 1076 const(void)* data = pszTmp; 1077 DWORD len = to!DWORD(lstrlenW(pszTmp) * wchar.sizeof); 1078 1079 regSetValue(m_hkey, name, 1080 asEXPAND_SZ ? REG_VALUE_TYPE.REG_EXPAND_SZ 1081 : REG_VALUE_TYPE.REG_SZ, 1082 data, len); 1083 } 1084 1085 /** 1086 Sets the named value with the given multiple-strings value. 1087 1088 Params: 1089 name = The name of the value to set. If it is the empty string, 1090 sets the default value. 1091 value = The multiple-strings value to set. 1092 Throws: 1093 If a value corresponding to the requested name is not found, 1094 `RegistryException` is thrown. 1095 */ 1096 void setValue(string name, string[] value) 1097 { 1098 wstring[] data = new wstring[value.length+1]; 1099 foreach (i, ref s; data[0..$-1]) 1100 { 1101 s = value[i].to!wstring; 1102 } 1103 data[$-1] = "\0"; 1104 auto ws = std.array.join(data, "\0"w); 1105 1106 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, ws.ptr, to!uint(ws.length * wchar.sizeof)); 1107 } 1108 1109 /** 1110 Sets the named value with the given binary value. 1111 1112 Params: 1113 name = The name of the value to set. If it is the empty string, 1114 sets the default value. 1115 value = The binary value to set. 1116 Throws: 1117 If a value corresponding to the requested name is not found, 1118 `RegistryException` is thrown. 1119 */ 1120 void setValue(string name, byte[] value) 1121 { 1122 regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_BINARY, value.ptr, to!DWORD(value.length)); 1123 } 1124 1125 /** 1126 Deletes the named value. 1127 1128 Params: 1129 name = The name of the value to delete. May not be `null`. 1130 Throws: 1131 If a value of the requested name is not found, 1132 `RegistryException` is thrown. 1133 */ 1134 void deleteValue(string name) 1135 { 1136 regDeleteValue(m_hkey, name); 1137 } 1138 1139 /** 1140 Flushes any changes to the key to disk. 1141 */ 1142 void flush() 1143 { 1144 regFlushKey(m_hkey); 1145 } 1146 1147 private: 1148 HKEY m_hkey; 1149 string m_name; 1150 } 1151 1152 /** 1153 This class represents a value of a registry key. 1154 */ 1155 class Value 1156 { 1157 @safe pure nothrow 1158 invariant() 1159 { 1160 assert(m_key !is null); 1161 } 1162 1163 private: 1164 @safe pure nothrow 1165 this(Key key, string name, REG_VALUE_TYPE type) 1166 in 1167 { 1168 assert(null !is key); 1169 } 1170 do 1171 { 1172 m_key = key; 1173 m_type = type; 1174 m_name = name; 1175 } 1176 1177 public: 1178 /** 1179 The name of the value. 1180 If the value represents a default value of a key, which has no name, 1181 the returned string will be of zero length. 1182 */ 1183 @property string name() @safe pure nothrow const 1184 { 1185 return m_name; 1186 } 1187 1188 /** 1189 The type of value. 1190 */ 1191 @property REG_VALUE_TYPE type() @safe pure nothrow const 1192 { 1193 return m_type; 1194 } 1195 1196 /** 1197 Obtains the current value of the value as a string. 1198 If the value's type is REG_EXPAND_SZ the returned value is <b>not</b> 1199 expanded; `value_EXPAND_SZ` should be called 1200 1201 Returns: 1202 The contents of the value. 1203 Throws: 1204 `RegistryException` if the type of the value is not REG_SZ, 1205 REG_EXPAND_SZ, REG_DWORD, or REG_QWORD. 1206 */ 1207 @property string value_SZ() const 1208 { 1209 string value; 1210 1211 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1212 1213 return value; 1214 } 1215 1216 /** 1217 Obtains the current value as a string, within which any environment 1218 variables have undergone expansion. 1219 This function works with the same value-types as `value_SZ`. 1220 1221 Returns: 1222 The contents of the value. 1223 */ 1224 @property string value_EXPAND_SZ() const 1225 { 1226 string value = value_SZ; 1227 1228 // ExpandEnvironemntStrings(): 1229 // http://msdn2.microsoft.com/en-us/library/ms724265.aspx 1230 const srcTmp = value.tempCStringW(); 1231 DWORD cchRequired = ExpandEnvironmentStringsW(srcTmp, null, 0); 1232 wchar[] newValue = new wchar[cchRequired]; 1233 1234 immutable DWORD count = enforce!Win32Exception( 1235 ExpandEnvironmentStringsW(srcTmp, newValue.ptr, to!DWORD(newValue.length)), 1236 "Failed to expand environment variables"); 1237 1238 return newValue[0 .. count-1].to!string; // remove trailing 0 1239 } 1240 1241 /** 1242 Obtains the current value as an array of strings. 1243 1244 Returns: 1245 The contents of the value. 1246 Throws: 1247 `RegistryException` if the type of the value is not REG_MULTI_SZ. 1248 */ 1249 @property string[] value_MULTI_SZ() const 1250 { 1251 string[] value; 1252 1253 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1254 1255 return value; 1256 } 1257 1258 /** 1259 Obtains the current value as a 32-bit unsigned integer, ordered 1260 correctly according to the current architecture. 1261 1262 Returns: 1263 The contents of the value. 1264 Throws: 1265 `RegistryException` is thrown for all types other than 1266 REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN. 1267 */ 1268 @property uint value_DWORD() const 1269 { 1270 uint value; 1271 1272 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1273 1274 return value; 1275 } 1276 1277 /** 1278 Obtains the value as a 64-bit unsigned integer, ordered correctly 1279 according to the current architecture. 1280 1281 Returns: 1282 The contents of the value. 1283 Throws: 1284 `RegistryException` if the type of the value is not REG_QWORD. 1285 */ 1286 @property ulong value_QWORD() const 1287 { 1288 ulong value; 1289 1290 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1291 1292 return value; 1293 } 1294 1295 /** 1296 Obtains the value as a binary blob. 1297 1298 Returns: 1299 The contents of the value. 1300 Throws: 1301 `RegistryException` if the type of the value is not REG_BINARY. 1302 */ 1303 @property byte[] value_BINARY() const 1304 { 1305 byte[] value; 1306 1307 regQueryValue(m_key.m_hkey, m_name, value, m_type); 1308 1309 return value; 1310 } 1311 1312 private: 1313 Key m_key; 1314 REG_VALUE_TYPE m_type; 1315 string m_name; 1316 } 1317 1318 /** 1319 Represents the local system registry. 1320 */ 1321 final class Registry 1322 { 1323 private: 1324 @disable this(); 1325 1326 public: 1327 /// Returns the root key for the HKEY_CLASSES_ROOT hive 1328 static @property Key classesRoot() { return new Key(HKEY_CLASSES_ROOT, "HKEY_CLASSES_ROOT", false); } 1329 /// Returns the root key for the HKEY_CURRENT_USER hive 1330 static @property Key currentUser() { return new Key(HKEY_CURRENT_USER, "HKEY_CURRENT_USER", false); } 1331 /// Returns the root key for the HKEY_LOCAL_MACHINE hive 1332 static @property Key localMachine() { return new Key(HKEY_LOCAL_MACHINE, "HKEY_LOCAL_MACHINE", false); } 1333 /// Returns the root key for the HKEY_USERS hive 1334 static @property Key users() { return new Key(HKEY_USERS, "HKEY_USERS", false); } 1335 /// Returns the root key for the HKEY_PERFORMANCE_DATA hive 1336 static @property Key performanceData() { return new Key(HKEY_PERFORMANCE_DATA, "HKEY_PERFORMANCE_DATA", false); } 1337 /// Returns the root key for the HKEY_CURRENT_CONFIG hive 1338 static @property Key currentConfig() { return new Key(HKEY_CURRENT_CONFIG, "HKEY_CURRENT_CONFIG", false); } 1339 /// Returns the root key for the HKEY_DYN_DATA hive 1340 static @property Key dynData() { return new Key(HKEY_DYN_DATA, "HKEY_DYN_DATA", false); } 1341 } 1342 1343 /** 1344 An enumerable sequence representing the names of the sub-keys of a registry Key. 1345 1346 Example: 1347 ---- 1348 Key key = ... 1349 foreach (string subkeyName; key.keyNames) 1350 { 1351 // using subkeyName 1352 } 1353 ---- 1354 */ 1355 class KeyNameSequence 1356 { 1357 @safe pure nothrow 1358 invariant() 1359 { 1360 assert(m_key !is null); 1361 } 1362 1363 private: 1364 @safe pure nothrow 1365 this(Key key) 1366 { 1367 m_key = key; 1368 } 1369 1370 public: 1371 /** 1372 The number of keys. 1373 */ 1374 @property size_t count() const 1375 { 1376 return m_key.keyCount; 1377 } 1378 1379 /** 1380 The name of the key at the given index. 1381 1382 Params: 1383 index = The 0-based index of the key to retrieve. 1384 Returns: 1385 The name of the key corresponding to the given index. 1386 Throws: 1387 RegistryException if no corresponding key is retrieved. 1388 */ 1389 string getKeyName(size_t index) 1390 { 1391 string name; 1392 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1393 { 1394 enforceSucc(getName(to!DWORD(index), name), "Invalid key"); 1395 }); 1396 return name; 1397 } 1398 1399 /** 1400 The name of the key at the given index. 1401 1402 Params: 1403 index = The 0-based index of the key to retrieve. 1404 Returns: 1405 The name of the key corresponding to the given index. 1406 Throws: 1407 `RegistryException` if no corresponding key is retrieved. 1408 */ 1409 string opIndex(size_t index) 1410 { 1411 return getKeyName(index); 1412 } 1413 1414 /// 1415 int opApply(scope int delegate(ref string name) dg) 1416 { 1417 int result; 1418 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1419 { 1420 for (DWORD index = 0; !result; ++index) 1421 { 1422 string name; 1423 immutable res = getName(index, name); 1424 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1425 break; 1426 enforceSucc(res, "Key name enumeration incomplete"); 1427 1428 result = dg(name); 1429 } 1430 }); 1431 return result; 1432 } 1433 1434 private: 1435 Key m_key; 1436 } 1437 1438 1439 /** 1440 An enumerable sequence representing the sub-keys of a registry Key. 1441 1442 Example: 1443 ---- 1444 Key key = ... 1445 foreach (Key subkey; key.keys) 1446 { 1447 // using subkey 1448 } 1449 ---- 1450 */ 1451 class KeySequence 1452 { 1453 @safe pure nothrow 1454 invariant() 1455 { 1456 assert(m_key !is null); 1457 } 1458 1459 private: 1460 @safe pure nothrow 1461 this(Key key) 1462 { 1463 m_key = key; 1464 } 1465 1466 public: 1467 /** 1468 The number of keys. 1469 */ 1470 @property size_t count() const 1471 { 1472 return m_key.keyCount; 1473 } 1474 1475 /** 1476 The key at the given index. 1477 1478 Params: 1479 index = The 0-based index of the key to retrieve. 1480 Returns: 1481 The key corresponding to the given index. 1482 Throws: 1483 `RegistryException` if no corresponding key is retrieved. 1484 */ 1485 Key getKey(size_t index) 1486 { 1487 string name; 1488 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1489 { 1490 enforceSucc(getName(to!DWORD(index), name), "Invalid key"); 1491 }); 1492 return m_key.getKey(name); 1493 } 1494 1495 /** 1496 The key at the given index. 1497 1498 Params: 1499 index = The 0-based index of the key to retrieve. 1500 Returns: 1501 The key corresponding to the given index. 1502 Throws: 1503 `RegistryException` if no corresponding key is retrieved. 1504 */ 1505 Key opIndex(size_t index) 1506 { 1507 return getKey(index); 1508 } 1509 1510 /// 1511 int opApply(scope int delegate(ref Key key) dg) 1512 { 1513 int result = 0; 1514 regProcessNthKey(m_key, (scope LONG delegate(DWORD, out string) getName) 1515 { 1516 for (DWORD index = 0; !result; ++index) 1517 { 1518 string name; 1519 immutable res = getName(index, name); 1520 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1521 break; 1522 enforceSucc(res, "Key enumeration incomplete"); 1523 1524 try 1525 { 1526 Key key = m_key.getKey(name); 1527 result = dg(key); 1528 } 1529 catch (RegistryException e) 1530 { 1531 // Skip inaccessible keys; they are 1532 // accessible via the KeyNameSequence 1533 if (e.error == ERROR_ACCESS_DENIED) 1534 continue; 1535 1536 throw e; 1537 } 1538 } 1539 }); 1540 return result; 1541 } 1542 1543 private: 1544 Key m_key; 1545 } 1546 1547 /** 1548 An enumerable sequence representing the names of the values of a registry Key. 1549 1550 Example: 1551 ---- 1552 Key key = ... 1553 foreach (string valueName; key.valueNames) 1554 { 1555 // using valueName 1556 } 1557 ---- 1558 */ 1559 class ValueNameSequence 1560 { 1561 @safe pure nothrow 1562 invariant() 1563 { 1564 assert(m_key !is null); 1565 } 1566 1567 private: 1568 @safe pure nothrow 1569 this(Key key) 1570 { 1571 m_key = key; 1572 } 1573 1574 public: 1575 /** 1576 The number of values. 1577 */ 1578 @property size_t count() const 1579 { 1580 return m_key.valueCount; 1581 } 1582 1583 /** 1584 The name of the value at the given index. 1585 1586 Params: 1587 index = The 0-based index of the value to retrieve. 1588 Returns: 1589 The name of the value corresponding to the given index. 1590 Throws: 1591 `RegistryException` if no corresponding value is retrieved. 1592 */ 1593 string getValueName(size_t index) 1594 { 1595 string name; 1596 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1597 { 1598 enforceSucc(getName(to!DWORD(index), name), "Invalid value"); 1599 }); 1600 return name; 1601 } 1602 1603 /** 1604 The name of the value at the given index. 1605 1606 Params: 1607 index = The 0-based index of the value to retrieve. 1608 Returns: 1609 The name of the value corresponding to the given index. 1610 Throws: 1611 `RegistryException` if no corresponding value is retrieved. 1612 */ 1613 string opIndex(size_t index) 1614 { 1615 return getValueName(index); 1616 } 1617 1618 /// 1619 int opApply(scope int delegate(ref string name) dg) 1620 { 1621 int result = 0; 1622 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1623 { 1624 for (DWORD index = 0; !result; ++index) 1625 { 1626 string name; 1627 immutable res = getName(index, name); 1628 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1629 break; 1630 enforceSucc(res, "Value name enumeration incomplete"); 1631 1632 result = dg(name); 1633 } 1634 }); 1635 return result; 1636 } 1637 1638 private: 1639 Key m_key; 1640 } 1641 1642 /** 1643 An enumerable sequence representing the values of a registry Key. 1644 1645 Example: 1646 ---- 1647 Key key = ... 1648 foreach (Value value; key.values) 1649 { 1650 // using value 1651 } 1652 ---- 1653 */ 1654 class ValueSequence 1655 { 1656 @safe pure nothrow 1657 invariant() 1658 { 1659 assert(m_key !is null); 1660 } 1661 1662 private: 1663 @safe pure nothrow 1664 this(Key key) 1665 { 1666 m_key = key; 1667 } 1668 1669 public: 1670 /// The number of values 1671 @property size_t count() const 1672 { 1673 return m_key.valueCount; 1674 } 1675 1676 /** 1677 The value at the given `index`. 1678 1679 Params: 1680 index = The 0-based index of the value to retrieve 1681 Returns: 1682 The value corresponding to the given index. 1683 Throws: 1684 `RegistryException` if no corresponding value is retrieved 1685 */ 1686 Value getValue(size_t index) 1687 { 1688 string name; 1689 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1690 { 1691 enforceSucc(getName(to!DWORD(index), name), "Invalid value"); 1692 }); 1693 return m_key.getValue(name); 1694 } 1695 1696 /** 1697 The value at the given `index`. 1698 1699 Params: 1700 index = The 0-based index of the value to retrieve. 1701 Returns: 1702 The value corresponding to the given index. 1703 Throws: 1704 `RegistryException` if no corresponding value is retrieved. 1705 */ 1706 Value opIndex(size_t index) 1707 { 1708 return getValue(index); 1709 } 1710 1711 /// 1712 int opApply(scope int delegate(ref Value value) dg) 1713 { 1714 int result = 0; 1715 regProcessNthValue(m_key, (scope LONG delegate(DWORD, out string) getName) 1716 { 1717 for (DWORD index = 0; !result; ++index) 1718 { 1719 string name; 1720 immutable res = getName(index, name); 1721 if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete 1722 break; 1723 enforceSucc(res, "Value enumeration incomplete"); 1724 1725 Value value = m_key.getValue(name); 1726 result = dg(value); 1727 } 1728 }); 1729 return result; 1730 } 1731 1732 private: 1733 Key m_key; 1734 } 1735 1736 1737 @system unittest 1738 { 1739 debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); 1740 debug(winreg) writefln("std.windows.registry.unittest read"); 1741 1742 /+ 1743 // Mask for test speed up 1744 1745 Key HKCR = Registry.classesRoot; 1746 Key CLSID = HKCR.getKey("CLSID"); 1747 1748 foreach (Key key; CLSID.keys) 1749 { 1750 foreach (Value val; key.values) 1751 { 1752 } 1753 } 1754 +/ 1755 Key HKCU = Registry.currentUser; 1756 assert(HKCU); 1757 1758 // Enumerate all subkeys of key Software 1759 Key softwareKey = HKCU.getKey("Software"); 1760 assert(softwareKey); 1761 foreach (Key key; softwareKey.keys) 1762 { 1763 //writefln("Key %s", key.name); 1764 foreach (Value val; key.values) 1765 { 1766 } 1767 } 1768 } 1769 1770 @system unittest 1771 { 1772 debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded."); 1773 debug(winreg) writefln("std.windows.registry.unittest write"); 1774 1775 // Warning: This unit test writes to the registry. 1776 // The test can fail if you don't have sufficient rights 1777 1778 Key HKCU = Registry.currentUser; 1779 assert(HKCU); 1780 1781 // Create a new key 1782 string unittestKeyName = "Temporary key for a D UnitTest which can be deleted afterwards"; 1783 Key unittestKey = HKCU.createKey(unittestKeyName); 1784 assert(unittestKey); 1785 Key cityKey = unittestKey.createKey( 1786 "CityCollection using foreign names with umlauts and accents: " 1787 ~"\u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df" 1788 ); 1789 cityKey.setValue("K\u00f6ln", "Germany"); // Cologne 1790 cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk 1791 cityKey.setValue("\u5317\u4eac", "China"); // Bejing 1792 bool foundCologne, foundMinsk, foundBeijing; 1793 foreach (Value v; cityKey.values) 1794 { 1795 auto vname = v.name; 1796 auto vvalue_SZ = v.value_SZ; 1797 if (v.name == "K\u00f6ln") 1798 { 1799 foundCologne = true; 1800 assert(v.value_SZ == "Germany"); 1801 } 1802 if (v.name == "\u041c\u0438\u043d\u0441\u043a") 1803 { 1804 foundMinsk = true; 1805 assert(v.value_SZ == "Belarus"); 1806 } 1807 if (v.name == "\u5317\u4eac") 1808 { 1809 foundBeijing = true; 1810 assert(v.value_SZ == "China"); 1811 } 1812 } 1813 assert(foundCologne); 1814 assert(foundMinsk); 1815 assert(foundBeijing); 1816 1817 Key stateKey = unittestKey.createKey("StateCollection"); 1818 stateKey.setValue("Germany", ["D\u00fcsseldorf", "K\u00f6ln", "Hamburg"]); 1819 Value v = stateKey.getValue("Germany"); 1820 string[] actual = v.value_MULTI_SZ; 1821 assert(actual.length == 3); 1822 assert(actual[0] == "D\u00fcsseldorf"); 1823 assert(actual[1] == "K\u00f6ln"); 1824 assert(actual[2] == "Hamburg"); 1825 1826 Key numberKey = unittestKey.createKey("Number"); 1827 numberKey.setValue("One", 1); 1828 Value one = numberKey.getValue("One"); 1829 assert(one.value_SZ == "1"); 1830 assert(one.value_DWORD == 1); 1831 1832 unittestKey.deleteKey(numberKey.name); 1833 unittestKey.deleteKey(stateKey.name); 1834 unittestKey.deleteKey(cityKey.name); 1835 HKCU.deleteKey(unittestKeyName); 1836 1837 auto e = collectException!RegistryException(HKCU.getKey("cDhmxsX9K23a8Uf869uB")); 1838 assert(e.msg.endsWith(" (error 2)")); 1839 } 1840 1841 @system unittest 1842 { 1843 Key HKCU = Registry.currentUser; 1844 assert(HKCU); 1845 1846 Key key = HKCU.getKey("Control Panel"); 1847 assert(key); 1848 assert(key.keyCount >= 2); 1849 1850 // Make sure `key` isn't garbage-collected while iterating over it. 1851 // Trigger a collection in the first iteration and check whether we 1852 // make it successfully to the second iteration. 1853 int i = 0; 1854 foreach (name; key.keyNames) 1855 { 1856 if (i++ > 0) 1857 break; 1858 1859 import core.memory : GC; 1860 GC.collect(); 1861 } 1862 assert(i == 2); 1863 }