"Fossies" - the Fresh Open Source Software Archive

Member "dmd2/src/phobos/std/windows/registry.d" (20 Nov 2020, 51701 Bytes) of package /linux/misc/dmd.2.094.2.linux.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) D source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    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 }