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