1 // Written by Christopher E. Miller 2 // See the included license.txt for copyright and license details. 3 4 5 // Not actually part of forms, but is handy. 6 7 8 module dfl.registry; 9 10 private import dfl.internal.dlib; 11 12 private import dfl.internal.winapi, dfl.base, dfl.internal.utf; 13 14 15 class DflRegistryException: DflException { // package 16 this(Dstring msg, int errorCode = 0) { 17 this.errorCode = errorCode; 18 debug { 19 if(errorCode) { 20 msg = msg ~ " (error " ~ intToString(errorCode) ~ ")"; // Dup. 21 } 22 } 23 super(msg); 24 } 25 26 27 int errorCode; 28 } 29 30 31 32 class Registry { // docmain 33 private this() {} 34 35 36 static: 37 38 39 @property RegistryKey classesRoot() { // getter 40 if(!_classesRoot) { 41 _classesRoot = new RegistryKey(HKEY_CLASSES_ROOT, false); 42 } 43 return _classesRoot; 44 } 45 46 /// ditto 47 @property RegistryKey currentConfig() { // getter 48 if(!_currentConfig) { 49 _currentConfig = new RegistryKey(HKEY_CURRENT_CONFIG, false); 50 } 51 return _currentConfig; 52 } 53 54 /// ditto 55 @property RegistryKey currentUser() { // getter 56 if(!_currentUser) { 57 _currentUser = new RegistryKey(HKEY_CURRENT_USER, false); 58 } 59 return _currentUser; 60 } 61 62 /// ditto 63 @property RegistryKey dynData() { // getter 64 if(!_dynData) { 65 _dynData = new RegistryKey(HKEY_DYN_DATA, false); 66 } 67 return _dynData; 68 } 69 70 /// ditto 71 @property RegistryKey localMachine() { // getter 72 if(!_localMachine) { 73 _localMachine = new RegistryKey(HKEY_LOCAL_MACHINE, false); 74 } 75 return _localMachine; 76 } 77 78 /// ditto 79 @property RegistryKey performanceData() { // getter 80 if(!_performanceData) { 81 _performanceData = new RegistryKey(HKEY_PERFORMANCE_DATA, false); 82 } 83 return _performanceData; 84 } 85 86 /// ditto 87 @property RegistryKey users() { // getter 88 if(!_users) { 89 _users = new RegistryKey(HKEY_USERS, false); 90 } 91 return _users; 92 } 93 94 95 private: 96 RegistryKey _classesRoot; 97 RegistryKey _currentConfig; 98 RegistryKey _currentUser; 99 RegistryKey _dynData; 100 RegistryKey _localMachine; 101 RegistryKey _performanceData; 102 RegistryKey _users; 103 104 105 /+ 106 static this() { 107 _classesRoot = new RegistryKey(HKEY_CLASSES_ROOT, false); 108 _currentConfig = new RegistryKey(HKEY_CURRENT_CONFIG, false); 109 _currentUser = new RegistryKey(HKEY_CURRENT_USER, false); 110 _dynData = new RegistryKey(HKEY_DYN_DATA, false); 111 _localMachine = new RegistryKey(HKEY_LOCAL_MACHINE, false); 112 _performanceData = new RegistryKey(HKEY_PERFORMANCE_DATA, false); 113 _users = new RegistryKey(HKEY_USERS, false); 114 } 115 +/ 116 } 117 118 119 private enum uint MAX_REG_BUFFER = 256; 120 121 122 123 abstract class RegistryValue { 124 @property DWORD valueType(); // getter 125 override Dstring toString(); 126 /+ package +/ protected LONG save(HKEY hkey, Dstring name); // package 127 package final @property RegistryValue _reg() { 128 return this; 129 } 130 } 131 132 133 134 class RegistryValueSz: RegistryValue { 135 136 Dstring value; 137 138 139 140 this(Dstring str) { 141 this.value = str; 142 } 143 144 /// ditto 145 this() { 146 } 147 148 149 override @property DWORD valueType() { // getter 150 return REG_SZ; 151 } 152 153 154 override Dstring toString() { 155 return value; 156 } 157 158 159 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 160 auto valuez = unsafeStringz(value); 161 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_SZ, cast(BYTE*)valuez, value.length + 1); 162 } 163 } 164 165 166 /+ 167 // Extra. 168 169 class RegistryValueSzW: RegistryValue { 170 171 wDstring value; 172 173 174 175 this(wDstring str) { 176 this.value = str; 177 } 178 179 /// ditto 180 this() { 181 } 182 183 184 override @property DWORD valueType() { // getter 185 return REG_SZ; 186 } 187 188 189 override Dstring toString() { 190 return utf16stringtoUtf8string(value); 191 } 192 193 194 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 195 if(dfl.internal.utf.useUnicode) { 196 197 } else { 198 199 } 200 } 201 } 202 +/ 203 204 205 206 class RegistryValueMultiSz: RegistryValue { 207 208 Dstring[] value; 209 210 211 212 this(Dstring[] strs) { 213 this.value = strs; 214 } 215 216 /// ditto 217 this() { 218 } 219 220 221 override @property DWORD valueType() { // getter 222 return REG_MULTI_SZ; 223 } 224 225 226 override Dstring toString() { 227 Dstring result; 228 foreach(Dstring str; value) { 229 result ~= str ~ "\r\n"; 230 } 231 if(result.length) { 232 result = result[0 .. result.length - 2]; // Exclude last \r\n. 233 } 234 return result; 235 } 236 237 238 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 239 char[] multi; 240 uint i; 241 242 i = value.length + 1; // Each NUL and the extra terminating NUL. 243 foreach(Dstring s; value) { 244 i += s.length; 245 } 246 247 multi = new char[i]; 248 foreach(Dstring s; value) { 249 if(!s.length) { 250 throw new DflRegistryException("Empty strings are not allowed in multi_sz registry values"); 251 } 252 253 multi[i .. i + s.length] = s[]; 254 i += s.length; 255 multi[i++] = 0; 256 } 257 multi[i++] = 0; 258 assert(i == multi.length); 259 260 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_MULTI_SZ, cast(BYTE*)multi, multi.length); 261 } 262 } 263 264 265 266 class RegistryValueExpandSz: RegistryValue { 267 268 Dstring value; 269 270 271 272 this(Dstring str) { 273 this.value = str; 274 } 275 276 /// ditto 277 this() { 278 } 279 280 281 override @property DWORD valueType() { // getter 282 return REG_EXPAND_SZ; 283 } 284 285 286 override Dstring toString() { 287 return value; 288 } 289 290 291 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 292 auto valuez = unsafeStringz(value); 293 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_EXPAND_SZ, cast(BYTE*)valuez, value.length + 1); 294 } 295 } 296 297 298 private Dstring dwordToString(DWORD dw) 299 out(result) { 300 assert(result.length == 10); 301 assert(result[0 .. 2] == "0x"); 302 foreach(char ch; result[2 .. result.length]) { 303 assert(charIsHexDigit(ch)); 304 } 305 } 306 body { 307 char[] result; 308 Dstring stmp; 309 uint ntmp; 310 311 stmp = uintToHexString(dw); 312 assert(stmp.length <= 8); 313 ntmp = 8 - stmp.length + 2; // Plus 0x. 314 result = new char[ntmp + stmp.length]; 315 result[0 .. 2] = "0x"; 316 result[2 .. ntmp] = '0'; 317 result[ntmp .. result.length] = stmp[]; 318 319 //return result; 320 return cast(Dstring)result; // Needed in D2. 321 } 322 323 324 unittest { 325 assert(dwordToString(0x8934) == "0x00008934"); 326 assert(dwordToString(0xF00BA2) == "0x00F00BA2"); 327 assert(dwordToString(0xBADBEEF0) == "0xBADBEEF0"); 328 assert(dwordToString(0xCAFEBEEF) == "0xCAFEBEEF"); 329 assert(dwordToString(0x09090BB) == "0x009090BB"); 330 assert(dwordToString(0) == "0x00000000"); 331 } 332 333 334 335 class RegistryValueDword: RegistryValue { 336 337 DWORD value; 338 339 340 341 this(DWORD dw) { 342 this.value = dw; 343 } 344 345 /// ditto 346 this() { 347 } 348 349 350 override @property DWORD valueType() { // getter 351 return REG_DWORD; 352 } 353 354 355 override Dstring toString() { 356 return dwordToString(value); 357 } 358 359 360 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 361 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_DWORD, cast(BYTE*)&value, DWORD.sizeof); 362 } 363 } 364 365 /// ditto 366 alias RegistryValueDword RegistryValueDwordLittleEndian; 367 368 /// ditto 369 class RegistryValueDwordBigEndian: RegistryValue { 370 371 DWORD value; 372 373 374 375 this(DWORD dw) { 376 this.value = dw; 377 } 378 379 /// ditto 380 this() { 381 } 382 383 384 override @property DWORD valueType() { // getter 385 return REG_DWORD_BIG_ENDIAN; 386 } 387 388 389 override Dstring toString() { 390 return dwordToString(value); 391 } 392 393 394 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 395 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_DWORD_BIG_ENDIAN, cast(BYTE*)&value, DWORD.sizeof); 396 } 397 } 398 399 400 401 class RegistryValueBinary: RegistryValue { 402 403 void[] value; 404 405 406 407 this(void[] val) { 408 this.value = val; 409 } 410 411 /// ditto 412 this() { 413 } 414 415 416 override @property DWORD valueType() { // getter 417 return REG_BINARY; 418 } 419 420 421 override Dstring toString() { 422 return "Binary"; 423 } 424 425 426 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 427 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_BINARY, cast(BYTE*)value, value.length); 428 } 429 } 430 431 432 433 class RegistryValueLink: RegistryValue { 434 435 void[] value; 436 437 438 439 this(void[] val) { 440 this.value = val; 441 } 442 443 /// ditto 444 this() { 445 } 446 447 448 override @property DWORD valueType() { // getter 449 return REG_LINK; 450 } 451 452 453 override Dstring toString() { 454 return "Symbolic Link"; 455 } 456 457 458 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 459 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_LINK, cast(BYTE*)value, value.length); 460 } 461 } 462 463 464 465 class RegistryValueResourceList: RegistryValue { 466 467 void[] value; 468 469 470 471 this(void[] val) { 472 this.value = val; 473 } 474 475 /// ditto 476 this() { 477 } 478 479 480 override @property DWORD valueType() { // getter 481 return REG_RESOURCE_LIST; 482 } 483 484 485 override Dstring toString() { 486 return "Resource List"; 487 } 488 489 490 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 491 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_RESOURCE_LIST, cast(BYTE*)value, value.length); 492 } 493 } 494 495 496 497 class RegistryValueNone: RegistryValue { 498 499 void[] value; 500 501 502 503 this(void[] val) { 504 this.value = val; 505 } 506 507 /// ditto 508 this() { 509 } 510 511 512 override @property DWORD valueType() { // getter 513 return REG_NONE; 514 } 515 516 517 override Dstring toString() { 518 return "None"; 519 } 520 521 522 /+ package +/ protected override LONG save(HKEY hkey, Dstring name) { // package 523 return RegSetValueExA(hkey, unsafeStringz(name), 0, REG_NONE, cast(BYTE*)value, value.length); 524 } 525 } 526 527 528 529 enum RegistryHive: size_t { 530 /+ 531 // DMD 0.98: 532 // C:\dmd\bin\..\src\phobos\std\c\windows\windows.d(493): cast(HKEY)(2147483648) is not an expression 533 // ... 534 CLASSES_ROOT = cast(size_t)HKEY_CLASSES_ROOT, 535 CURRENT_CONFIG = cast(size_t)HKEY_CURRENT_CONFIG, 536 CURRENT_USER = cast(size_t)HKEY_CURRENT_USER, 537 DYN_DATA = cast(size_t)HKEY_DYN_DATA, 538 LOCAL_MACHINE = cast(size_t)HKEY_LOCAL_MACHINE, 539 PERFORMANCE_DATA = cast(size_t)HKEY_PERFORMANCE_DATA, 540 USERS = cast(size_t)HKEY_USERS, 541 +/ 542 543 CLASSES_ROOT = 0x80000000, /// 544 CURRENT_CONFIG = 0x80000005, /// ditto 545 CURRENT_USER = 0x80000001, /// ditto 546 DYN_DATA = 0x80000006, /// ditto 547 LOCAL_MACHINE = 0x80000002, /// ditto 548 PERFORMANCE_DATA = 0x80000004, /// ditto 549 USERS = 0x80000003, /// ditto 550 } 551 552 553 554 class RegistryKey { // docmain 555 private: 556 HKEY hkey; 557 bool owned = true; 558 559 560 public: 561 final: 562 /+ 563 // An absolute key path. 564 // This doesn't work. 565 final @property Dstring name() { // getter 566 Dstring buf; 567 DWORD buflen; 568 569 buf = new char[MAX_REG_BUFFER]; 570 buflen = buf.length; 571 if(ERROR_SUCCESS != RegQueryInfoKeyA(hkey, buf, &buflen, null, null, 572 null, null, null, null, null, null, null)) { 573 infoErr(); 574 } 575 576 return buf[0 .. buflen]; 577 } 578 +/ 579 580 581 582 final @property int subKeyCount() { // getter 583 DWORD count; 584 585 LONG rr = RegQueryInfoKeyA(hkey, null, null, null, &count, 586 null, null, null, null, null, null, null); 587 if(ERROR_SUCCESS != rr) { 588 infoErr(rr); 589 } 590 591 return count; 592 } 593 594 595 596 final @property int valueCount() { // getter 597 DWORD count; 598 599 LONG rr = RegQueryInfoKeyA(hkey, null, null, null, null, 600 null, null, &count, null, null, null, null); 601 if(ERROR_SUCCESS != rr) { 602 infoErr(rr); 603 } 604 605 return count; 606 } 607 608 609 610 final void close() { 611 //if(!owned) 612 RegCloseKey(hkey); 613 } 614 615 616 617 final RegistryKey createSubKey(Dstring name) { 618 HKEY newHkey; 619 DWORD cdisp; 620 621 LONG rr = RegCreateKeyExA(hkey, unsafeStringz(name), 0, null, 0, KEY_ALL_ACCESS, null, &newHkey, &cdisp); 622 if(ERROR_SUCCESS != rr) { 623 throw new DflRegistryException("Unable to create registry key", rr); 624 } 625 626 return new RegistryKey(newHkey); 627 } 628 629 630 631 final void deleteSubKey(Dstring name, bool throwIfMissing) { 632 HKEY openHkey; 633 634 if(!name.length || !name[0]) { 635 throw new DflRegistryException("Unable to delete subkey"); 636 } 637 638 auto namez = unsafeStringz(name); 639 640 LONG opencode = RegOpenKeyExA(hkey, namez, 0, KEY_ALL_ACCESS, &openHkey); 641 if(ERROR_SUCCESS == opencode) { 642 DWORD count; 643 644 LONG querycode = RegQueryInfoKeyA(openHkey, null, null, null, &count, 645 null, null, null, null, null, null, null); 646 if(ERROR_SUCCESS == querycode) { 647 RegCloseKey(openHkey); 648 649 LONG delcode; 650 if(!count) { 651 delcode = RegDeleteKeyA(hkey, namez); 652 if(ERROR_SUCCESS == delcode) { 653 return; // OK. 654 } 655 656 throw new DflRegistryException("Unable to delete subkey", delcode); 657 } 658 659 throw new DflRegistryException("Cannot delete registry key with subkeys"); 660 } 661 662 RegCloseKey(openHkey); 663 664 throw new DflRegistryException("Unable to delete registry key", querycode); 665 } else { 666 if(!throwIfMissing) { 667 switch(opencode) { 668 case ERROR_FILE_NOT_FOUND: 669 return; 670 671 default: 672 } 673 } 674 675 throw new DflRegistryException("Unable to delete registry key", opencode); 676 } 677 } 678 679 /// ditto 680 final void deleteSubKey(Dstring name) { 681 deleteSubKey(name, true); 682 } 683 684 685 686 final void deleteSubKeyTree(Dstring name) { 687 _deleteSubKeyTree(hkey, name); 688 } 689 690 691 // Note: name is not written to! it's just not "invariant". 692 private static void _deleteSubKeyTree(HKEY shkey, Dstring name) { 693 HKEY openHkey; 694 695 auto namez = unsafeStringz(name); 696 697 if(ERROR_SUCCESS == RegOpenKeyExA(shkey, namez, 0, KEY_ALL_ACCESS, &openHkey)) { 698 void ouch(LONG why = 0) { 699 throw new DflRegistryException("Unable to delete entire subkey tree", why); 700 } 701 702 703 DWORD count; 704 705 LONG querycode = RegQueryInfoKeyA(openHkey, null, null, null, &count, 706 null, null, null, null, null, null, null); 707 if(ERROR_SUCCESS == querycode) { 708 if(!count) { 709 del_me: 710 RegCloseKey(openHkey); 711 LONG delcode = RegDeleteKeyA(shkey, namez); 712 if(ERROR_SUCCESS == delcode) { 713 return; // OK. 714 } 715 716 ouch(delcode); 717 } else { 718 try { 719 // deleteSubKeyTree on all subkeys. 720 721 char[MAX_REG_BUFFER] skn; 722 DWORD len; 723 724 next_subkey: 725 len = skn.length; 726 LONG enumcode = RegEnumKeyExA(openHkey, 0, skn.ptr, &len, null, null, null, null); 727 switch(enumcode) { 728 case ERROR_SUCCESS: 729 //_deleteSubKeyTree(openHkey, skn[0 .. len]); 730 _deleteSubKeyTree(openHkey, cast(Dstring)skn[0 .. len]); // Needed in D2. WARNING: NOT REALLY INVARIANT. 731 goto next_subkey; 732 733 case ERROR_NO_MORE_ITEMS: 734 // Done! 735 break; 736 737 default: 738 ouch(enumcode); 739 } 740 741 // Now go back to delete the origional key. 742 goto del_me; 743 } 744 finally { 745 RegCloseKey(openHkey); 746 } 747 } 748 } else { 749 ouch(querycode); 750 } 751 } 752 } 753 754 755 756 final void deleteValue(Dstring name, bool throwIfMissing) { 757 LONG rr = RegDeleteValueA(hkey, unsafeStringz(name)); 758 switch(rr) { 759 case ERROR_SUCCESS: 760 break; 761 762 case ERROR_FILE_NOT_FOUND: 763 if(!throwIfMissing) { 764 break; 765 } 766 goto default; 767 default: 768 throw new DflRegistryException("Unable to delete registry value", rr); 769 } 770 } 771 772 /// ditto 773 final void deleteValue(Dstring name) { 774 deleteValue(name, true); 775 } 776 777 778 override Dequ opEquals(Object o) { 779 RegistryKey rk; 780 781 rk = cast(RegistryKey)o; 782 if(!rk) { 783 return false; 784 } 785 return opEquals(rk); 786 } 787 788 789 Dequ opEquals(RegistryKey rk) { 790 return hkey == rk.hkey; 791 } 792 793 794 795 final void flush() { 796 RegFlushKey(hkey); 797 } 798 799 800 801 final Dstring[] getSubKeyNames() { 802 char[MAX_REG_BUFFER] buf; 803 DWORD len; 804 DWORD idx; 805 Dstring[] result; 806 807 key_names: 808 for(idx = 0;; idx++) { 809 len = buf.length; 810 LONG rr = RegEnumKeyExA(hkey, idx, buf.ptr, &len, null, null, null, null); 811 switch(rr) { 812 case ERROR_SUCCESS: 813 //result ~= buf[0 .. len].dup; 814 //result ~= buf[0 .. len].idup; // Needed in D2. Doesn't work in D1. 815 result ~= cast(Dstring)buf[0 .. len].dup; // Needed in D2. 816 break; 817 818 case ERROR_NO_MORE_ITEMS: 819 // Done! 820 break key_names; 821 822 default: 823 throw new DflRegistryException("Unable to obtain subkey names", rr); 824 } 825 } 826 827 return result; 828 } 829 830 831 832 final RegistryValue getValue(Dstring name, RegistryValue defaultValue) { 833 DWORD type; 834 DWORD len; 835 ubyte[] data; 836 837 len = 0; 838 LONG querycode = RegQueryValueExA(hkey, unsafeStringz(name), null, &type, null, &len); 839 switch(querycode) { 840 case ERROR_SUCCESS: 841 // Good. 842 break; 843 844 case ERROR_FILE_NOT_FOUND: 845 // Value doesn't exist. 846 return defaultValue; 847 848 default: errquerycode: 849 throw new DflRegistryException("Unable to get registry value", querycode); 850 } 851 852 data = new ubyte[len]; 853 // Note: reusing querycode here and above. 854 querycode = RegQueryValueExA(hkey, unsafeStringz(name), null, &type, data.ptr, &len); 855 if(ERROR_SUCCESS != querycode) { 856 goto errquerycode; 857 } 858 859 switch(type) { 860 case REG_SZ: 861 with(new RegistryValueSz) { 862 assert(!data[data.length - 1]); 863 value = cast(Dstring)data[0 .. data.length - 1]; 864 defaultValue = _reg; 865 } 866 break; 867 868 case REG_DWORD: // REG_DWORD_LITTLE_ENDIAN 869 with(new RegistryValueDword) { 870 assert(data.length == DWORD.sizeof); 871 value = *(cast(DWORD*)cast(void*)data); 872 defaultValue = _reg; 873 } 874 break; 875 876 case REG_EXPAND_SZ: 877 with(new RegistryValueExpandSz) { 878 assert(!data[data.length - 1]); 879 value = cast(Dstring)data[0 .. data.length - 1]; 880 defaultValue = _reg; 881 } 882 break; 883 884 case REG_MULTI_SZ: 885 with(new RegistryValueMultiSz) { 886 Dstring s; 887 888 next_sz: 889 s = stringFromStringz(cast(char*)data); 890 if(s.length) { 891 value ~= s; 892 data = data[s.length + 1 .. data.length]; 893 goto next_sz; 894 } 895 896 defaultValue = _reg; 897 } 898 break; 899 900 case REG_BINARY: 901 with(new RegistryValueBinary) { 902 value = data; 903 defaultValue = _reg; 904 } 905 break; 906 907 case REG_DWORD_BIG_ENDIAN: 908 with(new RegistryValueDwordBigEndian) { 909 assert(data.length == DWORD.sizeof); 910 value = *(cast(DWORD*)cast(void*)data); 911 defaultValue = _reg; 912 } 913 break; 914 915 case REG_LINK: 916 with(new RegistryValueLink) { 917 value = data; 918 defaultValue = _reg; 919 } 920 break; 921 922 case REG_RESOURCE_LIST: 923 with(new RegistryValueResourceList) { 924 value = data; 925 defaultValue = _reg; 926 } 927 break; 928 929 case REG_NONE: 930 with(new RegistryValueNone) { 931 value = data; 932 defaultValue = _reg; 933 } 934 break; 935 936 default: 937 throw new DflRegistryException("Unknown type for registry value"); 938 } 939 940 return defaultValue; 941 } 942 943 /// ditto 944 final RegistryValue getValue(Dstring name) { 945 return getValue(name, null); 946 } 947 948 949 950 final Dstring[] getValueNames() { 951 char[MAX_REG_BUFFER] buf; 952 DWORD len; 953 DWORD idx; 954 Dstring[] result; 955 956 value_names: 957 for(idx = 0;; idx++) { 958 len = buf.length; 959 LONG rr = RegEnumValueA(hkey, idx, buf.ptr, &len, null, null, null, null); 960 switch(rr) { 961 case ERROR_SUCCESS: 962 //result ~= buf[0 .. len].dup; 963 //result ~= buf[0 .. len].idup; // Needed in D2. Doesn't work in D1. 964 result ~= cast(Dstring)buf[0 .. len].dup; // Needed in D2. 965 break; 966 967 case ERROR_NO_MORE_ITEMS: 968 // Done! 969 break value_names; 970 971 default: 972 throw new DflRegistryException("Unable to obtain value names", rr); 973 } 974 } 975 976 return result; 977 } 978 979 980 981 static RegistryKey openRemoteBaseKey(RegistryHive hhive, Dstring machineName) { 982 HKEY openHkey; 983 984 LONG rr = RegConnectRegistryA(unsafeStringz(machineName), cast(HKEY)hhive, &openHkey); 985 if(ERROR_SUCCESS != rr) { 986 throw new DflRegistryException("Unable to open remote base key", rr); 987 } 988 989 return new RegistryKey(openHkey); 990 } 991 992 993 994 // Returns null on error. 995 final RegistryKey openSubKey(Dstring name, bool writeAccess) { 996 HKEY openHkey; 997 998 if(ERROR_SUCCESS != RegOpenKeyExA(hkey, unsafeStringz(name), 0, 999 writeAccess ? KEY_READ | KEY_WRITE : KEY_READ, &openHkey)) { 1000 return null; 1001 } 1002 1003 return new RegistryKey(openHkey); 1004 } 1005 1006 /// ditto 1007 final RegistryKey openSubKey(Dstring name) { 1008 return openSubKey(name, false); 1009 } 1010 1011 1012 1013 final void setValue(Dstring name, RegistryValue value) { 1014 LONG rr = value.save(hkey, name); 1015 if(ERROR_SUCCESS != rr) { 1016 throw new DflRegistryException("Unable to set registry value", rr); 1017 } 1018 } 1019 1020 /// ditto 1021 // Shortcut. 1022 final void setValue(Dstring name, Dstring value) { 1023 scope rv = new RegistryValueSz(value); 1024 setValue(name, rv); 1025 } 1026 1027 /// ditto 1028 // Shortcut. 1029 final void setValue(Dstring name, Dstring[] value) { 1030 scope rv = new RegistryValueMultiSz(value); 1031 setValue(name, rv); 1032 } 1033 1034 /// ditto 1035 // Shortcut. 1036 final void setValue(Dstring name, DWORD value) { 1037 scope rv = new RegistryValueDword(value); 1038 setValue(name, rv); 1039 } 1040 1041 1042 1043 // Used internally. 1044 final @property HKEY handle() { // getter 1045 return hkey; 1046 } 1047 1048 1049 // Used internally. 1050 this(HKEY hkey, bool owned = true) { 1051 this.hkey = hkey; 1052 this.owned = owned; 1053 } 1054 1055 1056 ~this() { 1057 if(owned) { 1058 RegCloseKey(hkey); 1059 } 1060 } 1061 1062 1063 private void infoErr(LONG why) { 1064 throw new DflRegistryException("Unable to obtain registry information", why); 1065 } 1066 } 1067