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
error()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
compatibleRegsam(in REGSAM samDesired)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
haveWoW64Job(in REGSAM samDesired)255 private bool haveWoW64Job(in REGSAM samDesired)
256 {
257 return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES);
258 }
259
_RVT_from_Endian(Endian endian)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
regCloseKey(in HKEY hkey)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
regFlushKey(in HKEY hkey)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
regCreateKey(in HKEY hkey,in string subKey,in DWORD dwOptions,in REGSAM samDesired,in LPSECURITY_ATTRIBUTES lpsa,out DWORD disposition)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
regDeleteKey(in HKEY hkey,in string subKey,in REGSAM samDesired)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
regDeleteValue(in HKEY hkey,in string valueName)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
regDup(HKEY hkey)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
debug(winreg)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
regEnumKeyName(in HKEY hkey,in DWORD index,ref wchar[]name,out DWORD cchName)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 }
out(res)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
regEnumValueName(in HKEY hkey,in DWORD dwIndex,ref wchar[]name,out DWORD cchName)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
regGetNumSubKeys(in HKEY hkey,out DWORD cSubKeys,out DWORD cchSubKeyMaxLen)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
regGetNumValues(in HKEY hkey,out DWORD cValues,out DWORD cchValueMaxLen)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
regGetValueType(in HKEY hkey,in string name)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
regOpenKey(in HKEY hkey,in string subKey,in REGSAM samDesired)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
regQueryValue(in HKEY hkey,string name,out string value,REG_VALUE_TYPE reqType)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
regQueryValue(in HKEY hkey,in string name,out string[]value,REG_VALUE_TYPE reqType)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;
foreach(i,ref v;value)623 foreach (i, ref v; value)
624 {
625 v = list[i].to!string;
626 }
627 }
628
regQueryValue(in HKEY hkey,in string name,out uint value,REG_VALUE_TYPE reqType)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
regQueryValue(in HKEY hkey,in string name,out ulong value,REG_VALUE_TYPE reqType)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
regQueryValue(in HKEY hkey,in string name,out byte[]value,REG_VALUE_TYPE reqType)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
regSetValue(in HKEY hkey,in string subKey,in REG_VALUE_TYPE type,in LPCVOID lpData,in DWORD cbData)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
regProcessNthKey(Key key,scope void delegate (scope LONG delegate (DWORD,out string))dg)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
regProcessNthValue(Key key,scope void delegate (scope LONG delegate (DWORD,out string))dg)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
invariant()792 invariant()
793 {
794 assert(m_hkey !is null);
795 }
796
797 private:
798 @safe pure nothrow
this(HKEY hkey,string name,bool created)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
~this()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
name()821 @property string name() @safe pure nothrow const
822 {
823 return m_name;
824 }
825
826 /**
827 The number of sub keys.
828 */
keyCount()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 */
keys()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 */
keyNames()850 @property KeyNameSequence keyNames() @safe pure
851 {
852 return new KeyNameSequence(this);
853 }
854
855 /**
856 The number of values.
857 */
valueCount()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 */
values()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 */
valueNames()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 */
getValue(string name)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 */
setValue(string name,uint value)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 */
setValue(string name,uint value,Endian endian)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 */
setValue(string name,ulong value)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 */
setValue(string name,string value)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 */
setValue(string name,string value,bool asEXPAND_SZ)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 */
setValue(string name,string[]value)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 */
setValue(string name,byte[]value)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 */
deleteValue(string name)1134 void deleteValue(string name)
1135 {
1136 regDeleteValue(m_hkey, name);
1137 }
1138
1139 /**
1140 Flushes any changes to the key to disk.
1141 */
flush()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
invariant()1158 invariant()
1159 {
1160 assert(m_key !is null);
1161 }
1162
1163 private:
1164 @safe pure nothrow
this(Key key,string name,REG_VALUE_TYPE type)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 */
name()1183 @property string name() @safe pure nothrow const
1184 {
1185 return m_name;
1186 }
1187
1188 /**
1189 The type of value.
1190 */
type()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 */
value_SZ()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 */
value_EXPAND_SZ()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 */
value_MULTI_SZ()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 */
value_DWORD()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 */
value_QWORD()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 */
value_BINARY()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
classesRoot()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
currentUser()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
localMachine()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
users()1334 static @property Key users() { return new Key(HKEY_USERS, "HKEY_USERS", false); }
1335 /// Returns the root key for the HKEY_PERFORMANCE_DATA hive
performanceData()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
currentConfig()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
dynData()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
invariant()1358 invariant()
1359 {
1360 assert(m_key !is null);
1361 }
1362
1363 private:
1364 @safe pure nothrow
this(Key key)1365 this(Key key)
1366 {
1367 m_key = key;
1368 }
1369
1370 public:
1371 /**
1372 The number of keys.
1373 */
count()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 */
getKeyName(size_t index)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 */
opIndex(size_t index)1409 string opIndex(size_t index)
1410 {
1411 return getKeyName(index);
1412 }
1413
1414 ///
opApply(scope int delegate (ref string name)dg)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
invariant()1454 invariant()
1455 {
1456 assert(m_key !is null);
1457 }
1458
1459 private:
1460 @safe pure nothrow
this(Key key)1461 this(Key key)
1462 {
1463 m_key = key;
1464 }
1465
1466 public:
1467 /**
1468 The number of keys.
1469 */
count()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 */
getKey(size_t index)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 */
opIndex(size_t index)1505 Key opIndex(size_t index)
1506 {
1507 return getKey(index);
1508 }
1509
1510 ///
opApply(scope int delegate (ref Key key)dg)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
invariant()1562 invariant()
1563 {
1564 assert(m_key !is null);
1565 }
1566
1567 private:
1568 @safe pure nothrow
this(Key key)1569 this(Key key)
1570 {
1571 m_key = key;
1572 }
1573
1574 public:
1575 /**
1576 The number of values.
1577 */
count()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 */
getValueName(size_t index)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 */
opIndex(size_t index)1613 string opIndex(size_t index)
1614 {
1615 return getValueName(index);
1616 }
1617
1618 ///
opApply(scope int delegate (ref string name)dg)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
invariant()1657 invariant()
1658 {
1659 assert(m_key !is null);
1660 }
1661
1662 private:
1663 @safe pure nothrow
this(Key key)1664 this(Key key)
1665 {
1666 m_key = key;
1667 }
1668
1669 public:
1670 /// The number of values
count()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 */
getValue(size_t index)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 */
opIndex(size_t index)1706 Value opIndex(size_t index)
1707 {
1708 return getValue(index);
1709 }
1710
1711 ///
opApply(scope int delegate (ref Value value)dg)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;
1860 GC.collect();
1861 }
1862 assert(i == 2);
1863 }
1864