]> git.ipfire.org Git - thirdparty/gcc.git/blame - libphobos/src/std/windows/registry.d
d: Import dmd b8384668f, druntime e6caaab9, phobos 5ab9ad256 (v2.098.0-beta.1)
[thirdparty/gcc.git] / libphobos / src / std / windows / registry.d
CommitLineData
b4c522fa
IB
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
5fee5ec3 15 Source: $(PHOBOSSRC std/windows/registry.d)
b4c522fa
IB
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 * ////////////////////////////////////////////////////////////////////////// */
38module std.windows.registry;
39version (Windows):
40
5fee5ec3 41import core.sys.windows.winbase, core.sys.windows.windef, core.sys.windows.winreg;
b4c522fa
IB
42import std.array;
43import std.conv;
44import std.exception;
45import std.internal.cstring;
46import std.internal.windows.advapi32;
47import std.system : Endian, endian;
48import std.windows.syserror;
49
50//debug = winreg;
51debug(winreg) import std.stdio;
52
53private
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.
67class 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
5fee5ec3 84version (StdUnittest) import std.string : startsWith, endsWith;
b4c522fa
IB
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 */
111class RegistryException
112 : Win32Exception
113{
114public:
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 */
168enum 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 */
204enum 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
226import 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
243import 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
249private 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
255private bool haveWoW64Job(in REGSAM samDesired)
256{
257 return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES);
258}
259
260private 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
272private LONG regCloseKey(in HKEY hkey)
273in
274{
275 assert(hkey !is null);
276}
5fee5ec3 277do
b4c522fa
IB
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
307private void regFlushKey(in HKEY hkey)
308in
309{
310 assert(hkey !is null);
311}
5fee5ec3 312do
b4c522fa
IB
313{
314 immutable res = RegFlushKey(hkey);
315 enforceSucc(res, "Key cannot be flushed");
316}
317
318private HKEY regCreateKey(in HKEY hkey, in string subKey, in DWORD dwOptions, in REGSAM samDesired,
319 in LPSECURITY_ATTRIBUTES lpsa, out DWORD disposition)
320in
321{
322 assert(hkey !is null);
323 assert(subKey !is null);
324}
5fee5ec3 325do
b4c522fa
IB
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
337private void regDeleteKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
338in
339{
340 assert(hkey !is null);
341 assert(subKey !is null);
342}
5fee5ec3 343do
b4c522fa
IB
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
358private void regDeleteValue(in HKEY hkey, in string valueName)
359in
360{
361 assert(hkey !is null);
362 assert(valueName !is null);
363}
5fee5ec3 364do
b4c522fa
IB
365{
366 enforceSucc(RegDeleteValueW(hkey, valueName.tempCStringW()),
367 "Value cannot be deleted: \"" ~ valueName ~ "\"");
368}
369
370private HKEY regDup(HKEY hkey)
371in
372{
373 assert(hkey !is null);
374}
5fee5ec3 375do
b4c522fa
IB
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
414private LONG regEnumKeyName(in HKEY hkey, in DWORD index, ref wchar[] name, out DWORD cchName)
415in
416{
417 assert(hkey !is null);
418 assert(name !is null);
419 assert(name.length > 0);
420}
421out(res)
422{
423 assert(res != ERROR_MORE_DATA);
424}
5fee5ec3 425do
b4c522fa
IB
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
445private LONG regEnumValueName(in HKEY hkey, in DWORD dwIndex, ref wchar[] name, out DWORD cchName)
446in
447{
448 assert(hkey !is null);
449}
5fee5ec3 450do
b4c522fa
IB
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
465private LONG regGetNumSubKeys(in HKEY hkey, out DWORD cSubKeys, out DWORD cchSubKeyMaxLen)
466in
467{
468 assert(hkey !is null);
469}
5fee5ec3 470do
b4c522fa
IB
471{
472 return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys,
473 &cchSubKeyMaxLen, null, null, null, null, null, null);
474}
475
476private LONG regGetNumValues(in HKEY hkey, out DWORD cValues, out DWORD cchValueMaxLen)
477in
478{
479 assert(hkey !is null);
480}
5fee5ec3 481do
b4c522fa
IB
482{
483 return RegQueryInfoKeyW(hkey, null, null, null, null, null, null,
484 &cValues, &cchValueMaxLen, null, null, null);
485}
486
487private REG_VALUE_TYPE regGetValueType(in HKEY hkey, in string name)
488in
489{
490 assert(hkey !is null);
491}
5fee5ec3 492do
b4c522fa
IB
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
501private HKEY regOpenKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
502in
503{
504 assert(hkey !is null);
505 assert(subKey !is null);
506}
5fee5ec3 507do
b4c522fa
IB
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
516private void regQueryValue(in HKEY hkey, string name, out string value, REG_VALUE_TYPE reqType)
517in
518{
519 assert(hkey !is null);
520}
5fee5ec3 521do
b4c522fa
IB
522{
523 import core.bitop : bswap;
524
525 REG_VALUE_TYPE type;
526
5fee5ec3 527 // See https://issues.dlang.org/show_bug.cgi?id=961 on this
b4c522fa
IB
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
587private void regQueryValue(in HKEY hkey, in string name, out string[] value, REG_VALUE_TYPE reqType)
588in
589{
590 assert(hkey !is null);
591}
5fee5ec3 592do
b4c522fa
IB
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
629private void regQueryValue(in HKEY hkey, in string name, out uint value, REG_VALUE_TYPE reqType)
630in
631{
632 assert(hkey !is null);
633}
5fee5ec3 634do
b4c522fa
IB
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
667private void regQueryValue(in HKEY hkey, in string name, out ulong value, REG_VALUE_TYPE reqType)
668in
669{
670 assert(hkey !is null);
671}
5fee5ec3 672do
b4c522fa
IB
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
692private void regQueryValue(in HKEY hkey, in string name, out byte[] value, REG_VALUE_TYPE reqType)
693in
694{
695 assert(hkey !is null);
696}
5fee5ec3 697do
b4c522fa
IB
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
727private void regSetValue(in HKEY hkey, in string subKey, in REG_VALUE_TYPE type, in LPCVOID lpData, in DWORD cbData)
728in
729{
730 assert(hkey !is null);
731}
5fee5ec3 732do
b4c522fa
IB
733{
734 enforceSucc(RegSetValueExW(hkey, subKey.tempCStringW(), 0, type, cast(BYTE*) lpData, cbData),
735 "Value cannot be set: \"" ~ subKey ~ "\"");
736}
737
738private 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
761private 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 */
789class Key
790{
791 @safe pure nothrow
792 invariant()
793 {
794 assert(m_hkey !is null);
795 }
796
797private:
798 @safe pure nothrow
799 this(HKEY hkey, string name, bool created)
800 in
801 {
802 assert(hkey !is null);
803 }
5fee5ec3 804 do
b4c522fa
IB
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
819public:
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
884public:
885 /**
886 Returns the named sub-key of this key.
887
888 Params:
5fee5ec3 889 name = The name of the subkey to create. May not be `null`.
b4c522fa
IB
890 Returns:
891 The created key.
892 Throws:
5fee5ec3 893 `RegistryException` is thrown if the key cannot be created.
b4c522fa
IB
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.
5fee5ec3 929 access = The desired access; one of the `REGSAM` enumeration.
b4c522fa
IB
930 Returns:
931 The aquired key.
932 Throws:
5fee5ec3
IB
933 This function never returns `null`. If a key corresponding to
934 the requested name is not found, `RegistryException` is thrown.
b4c522fa
IB
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:
5fee5ec3 968 name = The name of the key to delete. May not be `null`.
b4c522fa
IB
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.
5fee5ec3 979 If `name` is the empty string, then the default value is returned.
b4c522fa
IB
980
981 Returns:
5fee5ec3
IB
982 This function never returns `null`. If a value corresponding
983 to the requested name is not found, `RegistryException` is thrown.
b4c522fa
IB
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,
5fee5ec3 999 `RegistryException` is thrown.
b4c522fa
IB
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.
5fee5ec3 1014 endian = Can be `Endian.BigEndian` or `Endian.LittleEndian`.
b4c522fa
IB
1015 Throws:
1016 If a value corresponding to the requested name is not found,
5fee5ec3 1017 `RegistryException` is thrown.
b4c522fa
IB
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,
5fee5ec3 1038 `RegistryException` is thrown.
b4c522fa
IB
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,
5fee5ec3 1054 `RegistryException` is thrown.
b4c522fa
IB
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.
5fee5ec3 1068 asEXPAND_SZ = If `true`, the value will be stored as an
b4c522fa
IB
1069 expandable environment string, otherwise as a normal string.
1070 Throws:
1071 If a value corresponding to the requested name is not found,
5fee5ec3 1072 `RegistryException` is thrown.
b4c522fa
IB
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,
5fee5ec3 1095 `RegistryException` is thrown.
b4c522fa
IB
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,
5fee5ec3 1119 `RegistryException` is thrown.
b4c522fa
IB
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:
5fee5ec3 1130 name = The name of the value to delete. May not be `null`.
b4c522fa
IB
1131 Throws:
1132 If a value of the requested name is not found,
5fee5ec3 1133 `RegistryException` is thrown.
b4c522fa
IB
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
1148private:
1149 HKEY m_hkey;
1150 string m_name;
1151}
1152
1153/**
1154 This class represents a value of a registry key.
1155 */
1156class Value
1157{
1158 @safe pure nothrow
1159 invariant()
1160 {
1161 assert(m_key !is null);
1162 }
1163
1164private:
1165 @safe pure nothrow
1166 this(Key key, string name, REG_VALUE_TYPE type)
1167 in
1168 {
1169 assert(null !is key);
1170 }
5fee5ec3 1171 do
b4c522fa
IB
1172 {
1173 m_key = key;
1174 m_type = type;
1175 m_name = name;
1176 }
1177
1178public:
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>
5fee5ec3 1200 expanded; `value_EXPAND_SZ` should be called
b4c522fa
IB
1201
1202 Returns:
1203 The contents of the value.
1204 Throws:
5fee5ec3 1205 `RegistryException` if the type of the value is not REG_SZ,
b4c522fa
IB
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.
5fee5ec3 1220 This function works with the same value-types as `value_SZ`.
b4c522fa
IB
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:
5fee5ec3 1248 `RegistryException` if the type of the value is not REG_MULTI_SZ.
b4c522fa
IB
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:
5fee5ec3 1266 `RegistryException` is thrown for all types other than
b4c522fa
IB
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:
5fee5ec3 1285 `RegistryException` if the type of the value is not REG_QWORD.
b4c522fa
IB
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:
5fee5ec3 1302 `RegistryException` if the type of the value is not REG_BINARY.
b4c522fa
IB
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
1313private:
1314 Key m_key;
1315 REG_VALUE_TYPE m_type;
1316 string m_name;
1317}
1318
1319/**
1320 Represents the local system registry.
1321 */
1322final class Registry
1323{
1324private:
5fee5ec3 1325 @disable this();
b4c522fa
IB
1326
1327public:
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
1347Example:
1348----
1349Key key = ...
1350foreach (string subkeyName; key.keyNames)
1351{
1352 // using subkeyName
1353}
1354----
1355 */
1356class KeyNameSequence
1357{
1358 @safe pure nothrow
1359 invariant()
1360 {
1361 assert(m_key !is null);
1362 }
1363
1364private:
1365 @safe pure nothrow
1366 this(Key key)
1367 {
1368 m_key = key;
1369 }
1370
1371public:
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:
5fee5ec3 1408 `RegistryException` if no corresponding key is retrieved.
b4c522fa
IB
1409 */
1410 string opIndex(size_t index)
1411 {
1412 return getKeyName(index);
1413 }
1414
1415public:
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
1436private:
1437 Key m_key;
1438}
1439
1440
1441/**
1442 An enumerable sequence representing the sub-keys of a registry Key.
1443
1444Example:
1445----
1446Key key = ...
1447foreach (Key subkey; key.keys)
1448{
1449 // using subkey
1450}
1451----
1452 */
1453class KeySequence
1454{
1455 @safe pure nothrow
1456 invariant()
1457 {
1458 assert(m_key !is null);
1459 }
1460
1461private:
1462 @safe pure nothrow
1463 this(Key key)
1464 {
1465 m_key = key;
1466 }
1467
1468public:
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:
5fee5ec3 1485 `RegistryException` if no corresponding key is retrieved.
b4c522fa
IB
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:
5fee5ec3 1505 `RegistryException` if no corresponding key is retrieved.
b4c522fa
IB
1506 */
1507 Key opIndex(size_t index)
1508 {
1509 return getKey(index);
1510 }
1511
1512public:
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
1546private:
1547 Key m_key;
1548}
1549
1550/**
1551 An enumerable sequence representing the names of the values of a registry Key.
1552
1553Example:
1554----
1555Key key = ...
1556foreach (string valueName; key.valueNames)
1557{
1558 // using valueName
1559}
1560----
1561 */
1562class ValueNameSequence
1563{
1564 @safe pure nothrow
1565 invariant()
1566 {
1567 assert(m_key !is null);
1568 }
1569
1570private:
1571 @safe pure nothrow
1572 this(Key key)
1573 {
1574 m_key = key;
1575 }
1576
1577public:
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:
5fee5ec3 1594 `RegistryException` if no corresponding value is retrieved.
b4c522fa
IB
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:
5fee5ec3 1614 `RegistryException` if no corresponding value is retrieved.
b4c522fa
IB
1615 */
1616 string opIndex(size_t index)
1617 {
1618 return getValueName(index);
1619 }
1620
1621public:
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
1642private:
1643 Key m_key;
1644}
1645
1646/**
1647 An enumerable sequence representing the values of a registry Key.
1648
1649Example:
1650----
1651Key key = ...
1652foreach (Value value; key.values)
1653{
1654 // using value
1655}
1656----
1657 */
1658class ValueSequence
1659{
1660 @safe pure nothrow
1661 invariant()
1662 {
1663 assert(m_key !is null);
1664 }
1665
1666private:
1667 @safe pure nothrow
1668 this(Key key)
1669 {
1670 m_key = key;
1671 }
1672
1673public:
1674 /// The number of values
1675 @property size_t count() const
1676 {
1677 return m_key.valueCount;
1678 }
1679
1680 /**
5fee5ec3 1681 The value at the given `index`.
b4c522fa
IB
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:
5fee5ec3 1688 `RegistryException` if no corresponding value is retrieved
b4c522fa
IB
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 /**
5fee5ec3 1701 The value at the given `index`.
b4c522fa
IB
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:
5fee5ec3 1708 `RegistryException` if no corresponding value is retrieved.
b4c522fa
IB
1709 */
1710 Value opIndex(size_t index)
1711 {
1712 return getValue(index);
1713 }
1714
1715public:
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
1737private:
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}