The OpenD Programming Language

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