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