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