mirror of
https://github.com/dlang/phobos.git
synced 2025-05-06 02:45:12 +03:00
2174 lines
61 KiB
D
2174 lines
61 KiB
D
/**
|
|
This library provides Win32 Registry facilities.
|
|
|
|
Copyright: Copyright 2003-2004 by Matthew Wilson and Synesis Software
|
|
Written by Matthew Wilson
|
|
|
|
License:
|
|
|
|
Author: Matthew Wilson, Kenji Hara
|
|
|
|
Histry:
|
|
Created 15th March 2003,
|
|
Updated 25th April 2004,
|
|
|
|
Source: $(PHOBOSSRC std/windows/_registry.d)
|
|
*/
|
|
/* /////////////////////////////////////////////////////////////////////////////
|
|
*
|
|
* This software is provided 'as-is', without any express or implied
|
|
* warranty. In no event will the authors be held liable for any damages
|
|
* arising from the use of this software.
|
|
*
|
|
* Permission is granted to anyone to use this software for any purpose,
|
|
* including commercial applications, and to alter it and redistribute it
|
|
* freely, in both source and binary form, subject to the following
|
|
* restrictions:
|
|
*
|
|
* - The origin of this software must not be misrepresented; you must not
|
|
* claim that you wrote the original software. If you use this software
|
|
* in a product, an acknowledgment in the product documentation would be
|
|
* appreciated but is not required.
|
|
* - Altered source versions must be plainly marked as such, and must not
|
|
* be misrepresented as being the original software.
|
|
* - This notice may not be removed or altered from any source
|
|
* distribution.
|
|
*
|
|
* ////////////////////////////////////////////////////////////////////////// */
|
|
module std.windows.registry;
|
|
|
|
pragma(lib, "advapi32.lib");
|
|
|
|
import std.system : Endian, endian;
|
|
import std.exception;
|
|
import std.c.windows.windows;
|
|
import std.windows.syserror;
|
|
import std.windows.charset: toMBSz, fromMBSz;
|
|
import std.conv;
|
|
import std.utf : toUTFz, toUTF8, toUTF16;
|
|
import std.__fileinit : useWfuncs;
|
|
|
|
//debug = winreg;
|
|
debug(winreg) import std.stdio;
|
|
|
|
private
|
|
{
|
|
template SelUni(alias Asym, alias Wsym)
|
|
{
|
|
template SelUni(Char)
|
|
{
|
|
static if (is(Char == char))
|
|
{
|
|
alias Asym SelUni;
|
|
}
|
|
else
|
|
{
|
|
static assert (is(Char == wchar));
|
|
alias Wsym SelUni;
|
|
}
|
|
}
|
|
}
|
|
|
|
alias std.utf.toUTFz!(const(wchar)*, string) toUTF16z;
|
|
|
|
alias SelUni!(RegQueryValueExA, RegQueryValueExW) RegQueryValueEx;
|
|
|
|
extern (Windows) int lstrlenA(LPCSTR lpString);
|
|
extern (Windows) int lstrlenW(LPCWSTR lpString);
|
|
}
|
|
|
|
/* ************* Exceptions *************** */
|
|
|
|
/**
|
|
*/
|
|
class Win32Exception : Exception
|
|
{
|
|
int error;
|
|
|
|
this(string message, string fn = __FILE__, size_t ln = __LINE__)
|
|
{
|
|
super(msg, fn, ln);
|
|
}
|
|
|
|
this(string msg, int errnum, string fn = __FILE__, size_t ln = __LINE__)
|
|
{
|
|
super(text(msg, " (", error, ")"), fn, ln);
|
|
error = errnum;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Exception class thrown by the std.windows.registry classes.
|
|
*/
|
|
class RegistryException
|
|
: Win32Exception
|
|
{
|
|
public:
|
|
/**
|
|
Creates an instance of the exception.
|
|
|
|
Params:
|
|
message = The message associated with the exception.
|
|
*/
|
|
this(string message, string fn = __FILE__, size_t ln = __LINE__)
|
|
{
|
|
super(message, fn, ln);
|
|
}
|
|
|
|
/**
|
|
Creates an instance of the exception, with the given.
|
|
|
|
Params:
|
|
message = The message associated with the exception.
|
|
error = The Win32 error number associated with the exception.
|
|
*/
|
|
this(string message, int error, string fn = __FILE__, size_t ln = __LINE__)
|
|
{
|
|
super(message, error, fn, ln);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// (i) Test that we can throw and catch one by its own type
|
|
try
|
|
{
|
|
string message = "Test 1";
|
|
int code = 3;
|
|
|
|
try
|
|
{
|
|
throw new RegistryException(message, code);
|
|
}
|
|
catch (RegistryException e)
|
|
{
|
|
assert(e.error == code);
|
|
}
|
|
}
|
|
catch (Exception /*e*/)
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
/* ************* public enumerations *************** */
|
|
|
|
/**
|
|
Enumeration of the recognised registry access modes.
|
|
*/
|
|
enum REGSAM
|
|
{
|
|
KEY_QUERY_VALUE = 0x0001, /// Permission to query subkey data
|
|
KEY_SET_VALUE = 0x0002, /// Permission to set subkey data
|
|
KEY_CREATE_SUB_KEY = 0x0004, /// Permission to create subkeys
|
|
KEY_ENUMERATE_SUB_KEYS = 0x0008, /// Permission to enumerate subkeys
|
|
KEY_NOTIFY = 0x0010, /// Permission for change notification
|
|
KEY_CREATE_LINK = 0x0020, /// Permission to create a symbolic link
|
|
KEY_WOW64_32KEY = 0x0200, /// Enables a 64- or 32-bit application to open a 32-bit key
|
|
KEY_WOW64_64KEY = 0x0100, /// Enables a 64- or 32-bit application to open a 64-bit key
|
|
KEY_WOW64_RES = 0x0300, ///
|
|
KEY_READ = (STANDARD_RIGHTS_READ
|
|
| KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY)
|
|
& ~(SYNCHRONIZE),
|
|
/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE,
|
|
/// KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY access rights
|
|
KEY_WRITE = (STANDARD_RIGHTS_WRITE
|
|
| KEY_SET_VALUE | KEY_CREATE_SUB_KEY)
|
|
& ~(SYNCHRONIZE),
|
|
/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE,
|
|
/// and KEY_CREATE_SUB_KEY access rights
|
|
KEY_EXECUTE = KEY_READ & ~(SYNCHRONIZE),
|
|
/// Permission for read access
|
|
KEY_ALL_ACCESS = (STANDARD_RIGHTS_ALL
|
|
| KEY_QUERY_VALUE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY
|
|
| KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_CREATE_LINK)
|
|
& ~(SYNCHRONIZE),
|
|
/// Combines the KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS,
|
|
/// KEY_NOTIFY, KEY_CREATE_SUB_KEY, KEY_CREATE_LINK, and
|
|
/// KEY_SET_VALUE access rights, plus all the standard
|
|
/// access rights except SYNCHRONIZE
|
|
}
|
|
|
|
/**
|
|
Enumeration of the recognised registry value types.
|
|
*/
|
|
enum REG_VALUE_TYPE : DWORD
|
|
{
|
|
REG_UNKNOWN = -1, ///
|
|
REG_NONE = 0, /// The null value type. (In practise this is treated as a zero-length binary array by the Win32 registry)
|
|
REG_SZ = 1, /// A zero-terminated string
|
|
REG_EXPAND_SZ = 2, /// A zero-terminated string containing expandable environment variable references
|
|
REG_BINARY = 3, /// A binary blob
|
|
REG_DWORD = 4, /// A 32-bit unsigned integer
|
|
REG_DWORD_LITTLE_ENDIAN = 4, /// A 32-bit unsigned integer, stored in little-endian byte order
|
|
REG_DWORD_BIG_ENDIAN = 5, /// A 32-bit unsigned integer, stored in big-endian byte order
|
|
REG_LINK = 6, /// A registry link
|
|
REG_MULTI_SZ = 7, /// A set of zero-terminated strings
|
|
REG_RESOURCE_LIST = 8, /// A hardware resource list
|
|
REG_FULL_RESOURCE_DESCRIPTOR = 9, /// A hardware resource descriptor
|
|
REG_RESOURCE_REQUIREMENTS_LIST = 10, /// A hardware resource requirements list
|
|
REG_QWORD = 11, /// A 64-bit unsigned integer
|
|
REG_QWORD_LITTLE_ENDIAN = 11, /// A 64-bit unsigned integer, stored in little-endian byte order
|
|
}
|
|
|
|
|
|
/* ************* private *************** */
|
|
|
|
private
|
|
{
|
|
enum DWORD DELETE = 0x00010000L;
|
|
enum DWORD READ_CONTROL = 0x00020000L;
|
|
enum DWORD WRITE_DAC = 0x00040000L;
|
|
enum DWORD WRITE_OWNER = 0x00080000L;
|
|
enum DWORD SYNCHRONIZE = 0x00100000L;
|
|
|
|
enum DWORD STANDARD_RIGHTS_REQUIRED = 0x000F0000L;
|
|
|
|
enum DWORD STANDARD_RIGHTS_READ = 0x00020000L/* READ_CONTROL */;
|
|
enum DWORD STANDARD_RIGHTS_WRITE = 0x00020000L/* READ_CONTROL */;
|
|
enum DWORD STANDARD_RIGHTS_EXECUTE = 0x00020000L/* READ_CONTROL */;
|
|
|
|
enum DWORD STANDARD_RIGHTS_ALL = 0x001F0000L;
|
|
|
|
enum DWORD SPECIFIC_RIGHTS_ALL = 0x0000FFFFL;
|
|
|
|
enum DWORD REG_CREATED_NEW_KEY = 0x00000001;
|
|
enum DWORD REG_OPENED_EXISTING_KEY = 0x00000002;
|
|
}
|
|
|
|
private extern (Windows)
|
|
{
|
|
LONG function(in HKEY hkey, in LPCSTR lpSubKey, in REGSAM samDesired, in DWORD reserved) pRegDeleteKeyExA;
|
|
LONG function(in HKEY hkey, in LPCWSTR lpSubKey, in REGSAM samDesired, in DWORD reserved) pRegDeleteKeyExW;
|
|
}
|
|
|
|
shared static this()
|
|
{
|
|
// WOW64 is the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows
|
|
// IsWow64Process Function - Minimum supported client - Windows Vista, Windows XP with SP2
|
|
alias extern(Windows) BOOL function(HANDLE, PBOOL) fptr_t;
|
|
auto IsWow64Process =
|
|
cast(fptr_t)GetProcAddress(enforce(GetModuleHandleA("kernel32")), "IsWow64Process");
|
|
BOOL bIsWow64;
|
|
isWow64 = IsWow64Process && IsWow64Process(GetCurrentProcess(), &bIsWow64) && bIsWow64;
|
|
|
|
advapi32Mutex = new shared(Object)();
|
|
}
|
|
|
|
shared static ~this()
|
|
{
|
|
freeAdvapi32();
|
|
}
|
|
|
|
private
|
|
{
|
|
immutable bool isWow64;
|
|
shared Object advapi32Mutex;
|
|
shared HMODULE hAdvapi32 = null;
|
|
|
|
// Returns samDesired but without WoW64 flags if not in WoW64 mode
|
|
// for compatibility with Windows 2000
|
|
REGSAM compatibleRegsam(in REGSAM samDesired)
|
|
{
|
|
return isWow64 ? samDesired : cast(REGSAM)(samDesired & ~REGSAM.KEY_WOW64_RES);
|
|
}
|
|
|
|
///Returns true, if we are in WoW64 mode and have WoW64 flags
|
|
bool haveWoW64Job(in REGSAM samDesired)
|
|
{
|
|
return isWow64 && (samDesired & REGSAM.KEY_WOW64_RES);
|
|
}
|
|
}
|
|
|
|
// It will free Advapi32.dll, which may be loaded for RegDeleteKeyEx function
|
|
void freeAdvapi32()
|
|
{
|
|
synchronized (advapi32Mutex)
|
|
if (hAdvapi32)
|
|
{
|
|
pRegDeleteKeyExA = null;
|
|
pRegDeleteKeyExW = null;
|
|
hAdvapi32 = null;
|
|
enforce(FreeLibrary(cast(void*) hAdvapi32), `FreeLibrary(hAdvapi32)`);
|
|
}
|
|
}
|
|
|
|
private REG_VALUE_TYPE _RVT_from_Endian(Endian endian)
|
|
{
|
|
final switch (endian)
|
|
{
|
|
case Endian.bigEndian:
|
|
return REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN;
|
|
|
|
case Endian.littleEndian:
|
|
return REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN;
|
|
}
|
|
}
|
|
|
|
/+
|
|
private string expand_environment_strings(in string value)
|
|
in
|
|
{
|
|
assert(value !is null);
|
|
}
|
|
body
|
|
{
|
|
LPCSTR lpSrc = toMBSz(value);
|
|
DWORD cchRequired = ExpandEnvironmentStringsA(lpSrc, null, 0);
|
|
char[] newValue = new char[cchRequired];
|
|
|
|
if (!ExpandEnvironmentStringsA(lpSrc, newValue, newValue.length))
|
|
throw new Win32Exception("Failed to expand environment variables");
|
|
|
|
return newValue;
|
|
}
|
|
+/
|
|
|
|
|
|
private LONG regCloseKey(in HKEY hkey)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
/* No need to attempt to close any of the standard hive keys.
|
|
* Although it's documented that calling RegCloseKey() on any of
|
|
* these hive keys is ignored, we'd rather not trust the Win32
|
|
* API.
|
|
*/
|
|
if (cast(uint)hkey & 0x80000000)
|
|
{
|
|
switch (cast(uint)hkey)
|
|
{
|
|
case HKEY_CLASSES_ROOT:
|
|
case HKEY_CURRENT_USER:
|
|
case HKEY_LOCAL_MACHINE:
|
|
case HKEY_USERS:
|
|
case HKEY_PERFORMANCE_DATA:
|
|
case HKEY_PERFORMANCE_TEXT:
|
|
case HKEY_PERFORMANCE_NLSTEXT:
|
|
case HKEY_CURRENT_CONFIG:
|
|
case HKEY_DYN_DATA:
|
|
return ERROR_SUCCESS;
|
|
default:
|
|
/* Do nothing */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return RegCloseKey(hkey);
|
|
}
|
|
|
|
private void regFlushKey(in HKEY hkey)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
LONG res = RegFlushKey(hkey);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Key cannot be flushed", res);
|
|
}
|
|
|
|
private HKEY regCreateKey(in HKEY hkey, in string subKey, in DWORD dwOptions, in REGSAM samDesired,
|
|
in LPSECURITY_ATTRIBUTES lpsa, out DWORD disposition)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
assert(subKey !is null);
|
|
}
|
|
body
|
|
{
|
|
HKEY hkeyResult;
|
|
LONG res;
|
|
|
|
if (useWfuncs)
|
|
{
|
|
res = RegCreateKeyExW(
|
|
hkey, toUTF16z(subKey), 0, null, dwOptions, compatibleRegsam(samDesired),
|
|
cast(LPSECURITY_ATTRIBUTES) lpsa, &hkeyResult, &disposition);
|
|
}
|
|
else
|
|
{
|
|
res = RegCreateKeyExA(
|
|
hkey, toMBSz(subKey), 0, null, dwOptions, compatibleRegsam(samDesired),
|
|
cast(LPSECURITY_ATTRIBUTES) lpsa, &hkeyResult, &disposition);
|
|
}
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Failed to create requested key: \"" ~ subKey ~ "\"", res);
|
|
|
|
return hkeyResult;
|
|
}
|
|
|
|
private void regDeleteKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
assert(subKey !is null);
|
|
}
|
|
body
|
|
{
|
|
LONG res;
|
|
|
|
if (haveWoW64Job(samDesired))
|
|
{
|
|
if (useWfuncs)
|
|
{
|
|
if (!pRegDeleteKeyExW)
|
|
synchronized (advapi32Mutex)
|
|
{
|
|
hAdvapi32 = cast(shared) enforce(
|
|
LoadLibraryW("Advapi32.dll"), `LoadLibraryW("Advapi32.dll")`
|
|
);
|
|
|
|
pRegDeleteKeyExW = cast(typeof(pRegDeleteKeyExW))enforce(GetProcAddress(
|
|
cast(void*) hAdvapi32 , "RegDeleteKeyExW"),
|
|
`GetProcAddress(hAdvapi32 , "RegDeleteKeyExW")`
|
|
);
|
|
}
|
|
res = pRegDeleteKeyExW(hkey, toUTF16z(subKey), samDesired, 0);
|
|
}
|
|
else
|
|
{
|
|
if (!pRegDeleteKeyExA)
|
|
synchronized (advapi32Mutex)
|
|
{
|
|
hAdvapi32 = cast(shared) enforce(
|
|
LoadLibraryA("Advapi32.dll"), `LoadLibraryA("Advapi32.dll")`
|
|
);
|
|
|
|
pRegDeleteKeyExA = cast(typeof(pRegDeleteKeyExA))enforce(GetProcAddress(
|
|
cast(void*) hAdvapi32 , "RegDeleteKeyExA"),
|
|
`GetProcAddress(hAdvapi32 , "RegDeleteKeyExA")`
|
|
);
|
|
}
|
|
res = pRegDeleteKeyExA(hkey, toMBSz(subKey), samDesired, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (useWfuncs)
|
|
{
|
|
res = RegDeleteKeyW(hkey, toUTF16z(subKey));
|
|
}
|
|
else
|
|
{
|
|
res = RegDeleteKeyA(hkey, toMBSz(subKey));
|
|
}
|
|
}
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value cannot be deleted: \"" ~ subKey ~ "\"", res);
|
|
}
|
|
|
|
private void regDeleteValue(in HKEY hkey, in string valueName)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
assert(valueName !is null);
|
|
}
|
|
body
|
|
{
|
|
LONG res = useWfuncs ? RegDeleteValueW(hkey, toUTF16z(valueName))
|
|
: RegDeleteValueA(hkey, toMBSz(valueName));
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value cannot be deleted: \"" ~ valueName ~ "\"", res);
|
|
}
|
|
|
|
private HKEY regDup(HKEY hkey)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
/* Can't duplicate standard keys, but don't need to, so can just return */
|
|
if (cast(uint)hkey & 0x80000000)
|
|
{
|
|
switch (cast(uint)hkey)
|
|
{
|
|
case HKEY_CLASSES_ROOT:
|
|
case HKEY_CURRENT_USER:
|
|
case HKEY_LOCAL_MACHINE:
|
|
case HKEY_USERS:
|
|
case HKEY_PERFORMANCE_DATA:
|
|
case HKEY_PERFORMANCE_TEXT:
|
|
case HKEY_PERFORMANCE_NLSTEXT:
|
|
case HKEY_CURRENT_CONFIG:
|
|
case HKEY_DYN_DATA:
|
|
return hkey;
|
|
default:
|
|
/* Do nothing */
|
|
break;
|
|
}
|
|
}
|
|
|
|
HKEY hkeyDup;
|
|
LONG res = useWfuncs
|
|
? RegOpenKeyW(hkey, null, &hkeyDup)
|
|
: RegOpenKeyA(hkey, null, &hkeyDup);
|
|
|
|
debug(winreg)
|
|
{
|
|
if (res != ERROR_SUCCESS)
|
|
{
|
|
writefln("regDup() failed: 0x%08x 0x%08x %d", hkey, hkeyDup, res);
|
|
}
|
|
|
|
assert(res == ERROR_SUCCESS);
|
|
}
|
|
|
|
return (res == ERROR_SUCCESS) ? hkeyDup : null;
|
|
}
|
|
|
|
private LONG regEnumKeyName(Char)(in HKEY hkey, in DWORD index, ref Char[] name, out DWORD cchName)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
assert(name !is null);
|
|
assert(name.length > 0);
|
|
}
|
|
out(res)
|
|
{
|
|
assert(res != ERROR_MORE_DATA);
|
|
}
|
|
body
|
|
{
|
|
alias SelUni!(RegEnumKeyExA, RegEnumKeyExW) RegEnumKeyEx;
|
|
|
|
LONG res;
|
|
|
|
// The Registry API lies about the lengths of a very few sub-key lengths
|
|
// so we have to test to see if it whinges about more data, and provide
|
|
// more if it does.
|
|
for (;;)
|
|
{
|
|
cchName = to!DWORD(name.length);
|
|
res = RegEnumKeyEx!Char(hkey, index, name.ptr, &cchName, null, null, null, null);
|
|
if (res != ERROR_MORE_DATA)
|
|
break;
|
|
|
|
// Now need to increase the size of the buffer and try again
|
|
name.length = name.length * 2;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
private LONG regEnumValueName(Char)(in HKEY hkey, in DWORD dwIndex, ref Char[] name, out DWORD cchName)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
alias SelUni!(RegEnumValueA, RegEnumValueW) RegEnumValue;
|
|
|
|
LONG res;
|
|
|
|
for (;;)
|
|
{
|
|
cchName = to!DWORD(name.length);
|
|
res = RegEnumValue!Char(hkey, dwIndex, name.ptr, &cchName, null, null, null, null);
|
|
if (res != ERROR_MORE_DATA)
|
|
break;
|
|
|
|
name.length = name.length * 2;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
private LONG regGetNumSubKeys(Char)(in HKEY hkey, out DWORD cSubKeys, out DWORD cchSubKeyMaxLen)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
static if (is(Char == wchar))
|
|
{
|
|
return RegQueryInfoKeyW(hkey, null, null, null, &cSubKeys,
|
|
&cchSubKeyMaxLen, null, null, null, null, null, null);
|
|
}
|
|
else
|
|
{
|
|
return RegQueryInfoKeyA(hkey, null, null, null, &cSubKeys,
|
|
&cchSubKeyMaxLen, null, null, null, null, null, null);
|
|
}
|
|
}
|
|
|
|
private LONG regGetNumValues(Char)(in HKEY hkey, out DWORD cValues, out DWORD cchValueMaxLen)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
static if (is(Char == wchar))
|
|
{
|
|
return RegQueryInfoKeyW(hkey, null, null, null, null, null, null,
|
|
&cValues, &cchValueMaxLen, null, null, null);
|
|
}
|
|
else
|
|
{
|
|
return RegQueryInfoKeyA(hkey, null, null, null, null, null, null,
|
|
&cValues, &cchValueMaxLen, null, null, null);
|
|
}
|
|
}
|
|
|
|
private REG_VALUE_TYPE regGetValueType(in HKEY hkey, in string name)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
LONG res = useWfuncs
|
|
? RegQueryValueExW(hkey, toUTF16z(name), null, cast(LPDWORD) &type, null, null)
|
|
: RegQueryValueExA(hkey, toMBSz(name), null, cast(LPDWORD) &type, null, null);
|
|
//if (res == ERROR_MORE_DATA)
|
|
// res = ERROR_SUCCESS;
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value cannot be opened: \"" ~ name ~ "\"", res);
|
|
|
|
return type;
|
|
}
|
|
|
|
private HKEY regOpenKey(in HKEY hkey, in string subKey, in REGSAM samDesired)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
assert(subKey !is null);
|
|
}
|
|
body
|
|
{
|
|
HKEY hkeyResult;
|
|
LONG res = useWfuncs
|
|
? RegOpenKeyExW(hkey, toUTF16z(subKey), 0, compatibleRegsam(samDesired), &hkeyResult)
|
|
: RegOpenKeyExA(hkey, toMBSz(subKey), 0, compatibleRegsam(samDesired), &hkeyResult);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Failed to open requested key: \"" ~ subKey ~ "\"", res);
|
|
|
|
return hkeyResult;
|
|
}
|
|
|
|
private void regQueryValue(in HKEY hkey, string name, out string value, REG_VALUE_TYPE reqType)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
|
|
// See bugzilla 961 on this
|
|
union U
|
|
{
|
|
uint dw;
|
|
ulong qw;
|
|
};
|
|
U u;
|
|
void* data = &u.qw;
|
|
DWORD cbData = u.qw.sizeof;
|
|
|
|
LONG queryValue(Char)()
|
|
{
|
|
static if (is(Char == wchar))
|
|
auto keyname = toUTF16z(name);
|
|
else
|
|
auto keyname = toMBSz(name);
|
|
LONG res = RegQueryValueEx!Char(hkey, keyname, null, cast(LPDWORD) &type, data, &cbData);
|
|
if (res == ERROR_MORE_DATA)
|
|
{ data = (new Char[cbData]).ptr;
|
|
res = RegQueryValueEx!Char(hkey, keyname, null, cast(LPDWORD) &type, data, &cbData);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
LONG res = useWfuncs ? queryValue!wchar() : queryValue!char();
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Cannot read the requested value", res);
|
|
|
|
if (type != reqType)
|
|
throw new RegistryException("Value type has been changed since the value was acquired");
|
|
|
|
switch (type)
|
|
{
|
|
default:
|
|
case REG_VALUE_TYPE.REG_BINARY:
|
|
case REG_VALUE_TYPE.REG_MULTI_SZ:
|
|
throw new RegistryException("Cannot read the given value as a string");
|
|
|
|
case REG_VALUE_TYPE.REG_SZ:
|
|
case REG_VALUE_TYPE.REG_EXPAND_SZ:
|
|
if (useWfuncs)
|
|
{
|
|
auto wstr = (cast(immutable(wchar)*)data)[0 .. cbData / wchar.sizeof];
|
|
assert(wstr.length > 0 && wstr[$-1] == '\0');
|
|
if (wstr.length && wstr[$-1] == '\0')
|
|
wstr.length = wstr.length - 1;
|
|
assert(wstr.length == 0 || wstr[$-1] != '\0');
|
|
value = toUTF8(wstr);
|
|
}
|
|
else
|
|
{
|
|
auto cstr = (cast(immutable(char)*)data)[0 .. cbData];
|
|
assert(cstr.length > 0 && cstr[$-1] == '\0');
|
|
assert(cstr.length == 1 || cstr[$-2] != '\0');
|
|
value = fromMBSz(cstr.ptr);
|
|
if (value.ptr == cast(immutable(char)*)&u.qw)
|
|
value = value.idup; // don't point into the stack
|
|
}
|
|
break;
|
|
version(LittleEndian)
|
|
{
|
|
case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
|
|
value = to!string(u.dw);
|
|
break;
|
|
case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
|
|
value = to!string(core.bitop.bswap(u.dw));
|
|
break;
|
|
}
|
|
version(BigEndian)
|
|
{
|
|
case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
|
|
value = to!string(core.bitop.bswap(u.dw));
|
|
break;
|
|
case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
|
|
value = to!string(u.dw);
|
|
break;
|
|
}
|
|
case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN:
|
|
value = to!string(u.qw);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void regQueryValue(in HKEY hkey, in string name, out string[] value, REG_VALUE_TYPE reqType)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
|
|
void queryValue(Char)()
|
|
{
|
|
static if (is(Char == wchar))
|
|
auto keyname = toUTF16z(name);
|
|
else
|
|
auto keyname = toMBSz(name);
|
|
Char[] data = new Char[256];
|
|
DWORD cbData = data.length / Char.sizeof;
|
|
LONG res = RegQueryValueEx!Char(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
if (res == ERROR_MORE_DATA)
|
|
{ data.length = cbData / Char.sizeof;
|
|
res = RegQueryValueEx!Char(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
}
|
|
else if (res == ERROR_SUCCESS)
|
|
{
|
|
data.length = cbData / Char.sizeof;
|
|
}
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Cannot read the requested value", res);
|
|
|
|
if (type != REG_VALUE_TYPE.REG_MULTI_SZ)
|
|
throw new RegistryException("Cannot read the given value as a string");
|
|
|
|
if (type != reqType)
|
|
throw new RegistryException("Value type has been changed since the value was acquired");
|
|
|
|
// Remove last two (or one) null terminator
|
|
assert(data.length > 0 && data[$-1] == '\0');
|
|
data.length = data.length - 1;
|
|
if (data.length > 0 && data[$-1] == '\0')
|
|
data.length = data.length - 1;
|
|
|
|
auto list = std.array.split(data[], "\0");
|
|
value.length = list.length;
|
|
foreach (i, ref v; value)
|
|
{
|
|
static if (is(Char == wchar))
|
|
v = toUTF8(list[i]);
|
|
else
|
|
v = fromMBSz(cast(immutable(char)*)list[i].ptr); // assume unique
|
|
}
|
|
}
|
|
|
|
useWfuncs ? queryValue!wchar() : queryValue!char();
|
|
}
|
|
|
|
private void regQueryValue(in HKEY hkey, in string name, out uint value, REG_VALUE_TYPE reqType)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
|
|
DWORD cbData = value.sizeof;
|
|
LONG res = useWfuncs
|
|
? RegQueryValueExW(hkey, toUTF16z(name), null, cast(LPDWORD) &type, &value, &cbData)
|
|
: RegQueryValueExA(hkey, toMBSz(name), null, cast(LPDWORD) &type, &value, &cbData);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Cannot read the requested value", res);
|
|
|
|
if (type != reqType)
|
|
throw new RegistryException("Value type has been changed since the value was acquired");
|
|
|
|
switch (type)
|
|
{
|
|
default:
|
|
throw new RegistryException("Cannot read the given value as a 32-bit integer");
|
|
|
|
version(LittleEndian)
|
|
{
|
|
case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
|
|
assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN);
|
|
break;
|
|
case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
|
|
}
|
|
version(BigEndian)
|
|
{
|
|
case REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN:
|
|
assert(REG_VALUE_TYPE.REG_DWORD == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN);
|
|
break;
|
|
case REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN:
|
|
}
|
|
value = core.bitop.bswap(value);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void regQueryValue(in HKEY hkey, in string name, out ulong value, REG_VALUE_TYPE reqType)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
|
|
DWORD cbData = value.sizeof;
|
|
LONG res = useWfuncs
|
|
? RegQueryValueExW(hkey, toUTF16z(name), null, cast(LPDWORD) &type, &value, &cbData)
|
|
: RegQueryValueExA(hkey, toMBSz(name), null, cast(LPDWORD) &type, &value, &cbData);
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Cannot read the requested value", res);
|
|
|
|
if (type != reqType)
|
|
throw new RegistryException("Value type has been changed since the value was acquired");
|
|
|
|
switch (type)
|
|
{
|
|
default:
|
|
throw new RegistryException("Cannot read the given value as a 64-bit integer");
|
|
|
|
case REG_VALUE_TYPE.REG_QWORD_LITTLE_ENDIAN:
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void regQueryValue(in HKEY hkey, in string name, out byte[] value, REG_VALUE_TYPE reqType)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
REG_VALUE_TYPE type;
|
|
|
|
byte[] data = new byte[100];
|
|
DWORD cbData = data.length;
|
|
LONG res;
|
|
if (useWfuncs)
|
|
{
|
|
auto keyname = toUTF16z(name);
|
|
res = RegQueryValueExW(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
if (res == ERROR_MORE_DATA)
|
|
{ data.length = cbData;
|
|
res = RegQueryValueExW(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto keyname = toMBSz(name);
|
|
res = RegQueryValueExA(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
if (res == ERROR_MORE_DATA)
|
|
{ data.length = cbData;
|
|
res = RegQueryValueExA(hkey, keyname, null, cast(LPDWORD) &type, data.ptr, &cbData);
|
|
}
|
|
}
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Cannot read the requested value", res);
|
|
|
|
if (type != reqType)
|
|
throw new RegistryException("Value type has been changed since the value was acquired");
|
|
|
|
switch (type)
|
|
{
|
|
default:
|
|
throw new RegistryException("Cannot read the given value as a string");
|
|
|
|
case REG_VALUE_TYPE.REG_BINARY:
|
|
data.length = cbData;
|
|
value = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void regSetValue(in HKEY hkey, in string subKey, in REG_VALUE_TYPE type, in LPCVOID lpData, in DWORD cbData)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
LONG res = useWfuncs
|
|
? RegSetValueExW(hkey, toUTF16z(subKey), 0, type, cast(BYTE*) lpData, cbData)
|
|
: RegSetValueExA(hkey, toMBSz(subKey), 0, type, cast(BYTE*) lpData, cbData);
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value cannot be set: \"" ~ subKey ~ "\"", res);
|
|
}
|
|
|
|
private void regProcessNthKey(HKEY hkey, scope void delegate(scope LONG delegate(DWORD, out string)) dg)
|
|
{
|
|
void impl(Char)()
|
|
{
|
|
DWORD cSubKeys;
|
|
DWORD cchSubKeyMaxLen;
|
|
|
|
LONG res = regGetNumSubKeys!Char(hkey, cSubKeys, cchSubKeyMaxLen);
|
|
assert(res == ERROR_SUCCESS);
|
|
|
|
Char[] sName = new Char[cchSubKeyMaxLen + 1];
|
|
|
|
dg((DWORD index, out string name)
|
|
{
|
|
DWORD cchName;
|
|
res = regEnumKeyName!Char(hkey, index, sName, cchName);
|
|
if (res == ERROR_SUCCESS)
|
|
{
|
|
static if (is(Char == wchar))
|
|
name = toUTF8(sName[0 .. cchName]);
|
|
else
|
|
name = fromMBSz(cast(immutable(char)*) sName.ptr);
|
|
}
|
|
return res;
|
|
});
|
|
}
|
|
|
|
useWfuncs ? impl!wchar() : impl!char();
|
|
}
|
|
|
|
private void regProcessNthValue(HKEY hkey, scope void delegate(scope LONG delegate(DWORD, out string)) dg)
|
|
{
|
|
void impl(Char)()
|
|
{
|
|
DWORD cValues;
|
|
DWORD cchValueMaxLen;
|
|
|
|
LONG res = regGetNumValues!Char(hkey, cValues, cchValueMaxLen);
|
|
assert(res == ERROR_SUCCESS);
|
|
|
|
Char[] sName = new Char[cchValueMaxLen + 1];
|
|
|
|
dg((DWORD index, out string name)
|
|
{
|
|
DWORD cchName;
|
|
res = regEnumValueName!Char(hkey, index, sName, cchName);
|
|
if (res == ERROR_SUCCESS)
|
|
{
|
|
static if (is(Char == wchar))
|
|
name = toUTF8(sName[0 .. cchName]);
|
|
else
|
|
name = fromMBSz(cast(immutable(char)*) sName.ptr);
|
|
}
|
|
return res;
|
|
});
|
|
}
|
|
|
|
useWfuncs ? impl!wchar() : impl!char();
|
|
}
|
|
|
|
/* ************* public classes *************** */
|
|
|
|
/**
|
|
This class represents a registry key.
|
|
*/
|
|
class Key
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_hkey !is null);
|
|
}
|
|
|
|
private:
|
|
this(HKEY hkey, string name, bool created)
|
|
in
|
|
{
|
|
assert(hkey !is null);
|
|
}
|
|
body
|
|
{
|
|
m_hkey = hkey;
|
|
m_name = name;
|
|
m_created = created;
|
|
}
|
|
|
|
~this()
|
|
{
|
|
regCloseKey(m_hkey);
|
|
|
|
// Even though this is horried waste-of-cycles programming
|
|
// we're doing it here so that the
|
|
m_hkey = null;
|
|
}
|
|
|
|
public:
|
|
/// The name of the key
|
|
@property string name() @safe nothrow const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
// Indicates whether this key was created, rather than opened, by the client
|
|
// bool Created()
|
|
// {
|
|
// return m_created;
|
|
// }
|
|
|
|
/**
|
|
The number of sub keys.
|
|
*/
|
|
@property uint keyCount() const
|
|
{
|
|
uint cSubKeys;
|
|
uint cchSubKeyMaxLen;
|
|
LONG res = useWfuncs
|
|
? regGetNumSubKeys!wchar(m_hkey, cSubKeys, cchSubKeyMaxLen)
|
|
: regGetNumSubKeys!char(m_hkey, cSubKeys, cchSubKeyMaxLen);
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Number of sub-keys cannot be determined", res);
|
|
|
|
return cSubKeys;
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence of all the sub-keys of this key.
|
|
*/
|
|
@property KeySequence keys()
|
|
{
|
|
return new KeySequence(this);
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence of the names of all the sub-keys of this key.
|
|
*/
|
|
@property KeyNameSequence keyNames()
|
|
{
|
|
return new KeyNameSequence(this);
|
|
}
|
|
|
|
/**
|
|
The number of values.
|
|
*/
|
|
@property uint valueCount() const
|
|
{
|
|
uint cValues;
|
|
uint cchValueMaxLen;
|
|
LONG res = useWfuncs
|
|
? regGetNumValues!wchar(m_hkey, cValues, cchValueMaxLen)
|
|
: regGetNumValues!char(m_hkey, cValues, cchValueMaxLen);
|
|
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Number of values cannot be determined", res);
|
|
|
|
return cValues;
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence of all the values of this key.
|
|
*/
|
|
@property ValueSequence values()
|
|
{
|
|
return new ValueSequence(this);
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence of the names of all the values of this key.
|
|
*/
|
|
@property ValueNameSequence valueNames()
|
|
{
|
|
return new ValueNameSequence(this);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
Returns the named sub-key of this key.
|
|
|
|
Params:
|
|
name = The name of the subkey to create. May not be $(D null).
|
|
Returns:
|
|
The created key.
|
|
Throws:
|
|
$(D RegistryException) is thrown if the key cannot be created.
|
|
*/
|
|
Key createKey(string name, REGSAM access = REGSAM.KEY_ALL_ACCESS)
|
|
{
|
|
if (name.length == 0)
|
|
throw new RegistryException("Key name is invalid");
|
|
|
|
DWORD disposition;
|
|
HKEY hkey = regCreateKey(m_hkey, name, 0, access, null, disposition);
|
|
assert(hkey !is null);
|
|
|
|
// Potential resource leak here!!
|
|
//
|
|
// If the allocation of the memory for Key fails, the HKEY could be
|
|
// lost. Hence, we catch such a failure by the finally, and release
|
|
// the HKEY there. If the creation of
|
|
try
|
|
{
|
|
Key key = new Key(hkey, name, disposition == REG_CREATED_NEW_KEY);
|
|
hkey = null;
|
|
return key;
|
|
}
|
|
finally
|
|
{
|
|
if (hkey !is null)
|
|
{
|
|
regCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Returns the named sub-key of this key.
|
|
|
|
Params:
|
|
name = The name of the subkey to aquire. If name is the empty
|
|
string, then the called key is duplicated.
|
|
access = The desired access; one of the $(D REGSAM) enumeration.
|
|
Returns:
|
|
The aquired key.
|
|
Throws:
|
|
This function never returns null. If a key corresponding to the
|
|
requested name is not found, $(D RegistryException) is thrown.
|
|
*/
|
|
Key getKey(string name, REGSAM access = REGSAM.KEY_READ)
|
|
{
|
|
if (name is null || name.length == 0)
|
|
{
|
|
return new Key(regDup(m_hkey), m_name, false);
|
|
}
|
|
else
|
|
{
|
|
HKEY hkey = regOpenKey(m_hkey, name, access);
|
|
assert(hkey !is null);
|
|
|
|
// Potential resource leak here!!
|
|
//
|
|
// If the allocation of the memory for Key fails, the HKEY could be
|
|
// lost. Hence, we catch such a failure by the finally, and release
|
|
// the HKEY there. If the creation of
|
|
try
|
|
{
|
|
Key key = new Key(hkey, name, false);
|
|
hkey = null;
|
|
return key;
|
|
}
|
|
finally
|
|
{
|
|
if (hkey != null)
|
|
{
|
|
regCloseKey(hkey);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Deletes the named key.
|
|
|
|
Params:
|
|
name = The name of the key to delete. May not be $(D null).
|
|
*/
|
|
void deleteKey(string name, REGSAM access = cast(REGSAM)0)
|
|
{
|
|
if (name.length == 0)
|
|
throw new RegistryException("Key name is invalid");
|
|
|
|
regDeleteKey(m_hkey, name, access);
|
|
}
|
|
|
|
/**
|
|
Returns the named value.
|
|
If $(D name) is the empty string, then the default value is returned.
|
|
|
|
Returns:
|
|
This function never returns $(D null). If a value corresponding
|
|
to the requested name is not found, $(D RegistryException) is thrown.
|
|
*/
|
|
Value getValue(string name)
|
|
{
|
|
return new Value(this, name, regGetValueType(m_hkey, name));
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given 32-bit unsigned integer value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The 32-bit unsigned value to set.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, uint value)
|
|
{
|
|
setValue(name, value, endian);
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given 32-bit unsigned integer value,
|
|
according to the desired byte-ordering.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The 32-bit unsigned value to set.
|
|
endian = Can be $(D Endian.BigEndian) or $(D Endian.LittleEndian).
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, uint value, Endian endian)
|
|
{
|
|
REG_VALUE_TYPE type = _RVT_from_Endian(endian);
|
|
|
|
assert(type == REG_VALUE_TYPE.REG_DWORD_BIG_ENDIAN ||
|
|
type == REG_VALUE_TYPE.REG_DWORD_LITTLE_ENDIAN);
|
|
|
|
regSetValue(m_hkey, name, type, &value, value.sizeof);
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given 64-bit unsigned integer value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The 64-bit unsigned value to set.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, ulong value)
|
|
{
|
|
regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_QWORD, &value, value.sizeof);
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given string value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The string value to set.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, string value)
|
|
{
|
|
setValue(name, value, false);
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given string value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The string value to set.
|
|
asEXPAND_SZ = If $(D true), the value will be stored as an
|
|
expandable environment string, otherwise as a normal string.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, string value, bool asEXPAND_SZ)
|
|
{
|
|
const(void)* data;
|
|
DWORD len;
|
|
if (useWfuncs)
|
|
{
|
|
auto psz = toUTF16z(value);
|
|
data = psz;
|
|
len = lstrlenW(psz) * wchar.sizeof;
|
|
}
|
|
else
|
|
{
|
|
auto psz = toMBSz(value);
|
|
data = psz;
|
|
len = lstrlenA(psz);
|
|
}
|
|
|
|
regSetValue(m_hkey, name,
|
|
asEXPAND_SZ ? REG_VALUE_TYPE.REG_EXPAND_SZ
|
|
: REG_VALUE_TYPE.REG_SZ,
|
|
data, len);
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given multiple-strings value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The multiple-strings value to set.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, string[] value)
|
|
{
|
|
wstring[] data = new wstring[value.length+1];
|
|
foreach (i, ref s; data[0..$-1])
|
|
{
|
|
s = toUTF16(value[i]);
|
|
}
|
|
data[$-1] = "\0";
|
|
auto ws = std.array.join(data, "\0"w);
|
|
|
|
if (useWfuncs)
|
|
{
|
|
regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, ws.ptr, ws.length * wchar.sizeof);
|
|
}
|
|
else
|
|
{
|
|
char[] cs;
|
|
int readLen;
|
|
cs.length = WideCharToMultiByte(/*CP_ACP*/ 0, 0, ws.ptr, ws.length, null, 0, null, null);
|
|
if (cs.length)
|
|
{
|
|
readLen = WideCharToMultiByte(/*CP_ACP*/ 0, 0, ws.ptr, ws.length, cs.ptr, cs.length, null, null);
|
|
}
|
|
if (!readLen || readLen != cs.length)
|
|
throw new Win32Exception("Couldn't convert string: " ~ sysErrorString(GetLastError()));
|
|
|
|
regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_MULTI_SZ, cs.ptr, cs.length);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Sets the named value with the given binary value.
|
|
|
|
Params:
|
|
name = The name of the value to set. If it is the empty string,
|
|
sets the default value.
|
|
value = The binary value to set.
|
|
Throws:
|
|
If a value corresponding to the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void setValue(string name, byte[] value)
|
|
{
|
|
regSetValue(m_hkey, name, REG_VALUE_TYPE.REG_BINARY, value.ptr, to!DWORD(value.length));
|
|
}
|
|
|
|
/**
|
|
Deletes the named value.
|
|
|
|
Params:
|
|
name = The name of the value to delete. May not be $(D null).
|
|
Throws:
|
|
If a value of the requested name is not found,
|
|
$(D RegistryException) is thrown.
|
|
*/
|
|
void deleteValue(string name)
|
|
{
|
|
regDeleteValue(m_hkey, name);
|
|
}
|
|
|
|
/**
|
|
Flushes any changes to the key to disk.
|
|
*/
|
|
void flush()
|
|
{
|
|
regFlushKey(m_hkey);
|
|
}
|
|
|
|
private:
|
|
HKEY m_hkey;
|
|
string m_name;
|
|
bool m_created;
|
|
}
|
|
|
|
/**
|
|
This class represents a value of a registry key.
|
|
*/
|
|
class Value
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_key !is null);
|
|
}
|
|
|
|
private:
|
|
this(Key key, string name, REG_VALUE_TYPE type)
|
|
in
|
|
{
|
|
assert(null !is key);
|
|
}
|
|
body
|
|
{
|
|
m_key = key;
|
|
m_type = type;
|
|
m_name = name;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
The name of the value.
|
|
If the value represents a default value of a key, which has no name,
|
|
the returned string will be of zero length.
|
|
*/
|
|
@property string name() pure nothrow const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
/**
|
|
The type of value.
|
|
*/
|
|
@property REG_VALUE_TYPE type() pure nothrow const
|
|
{
|
|
return m_type;
|
|
}
|
|
|
|
/**
|
|
Obtains the current value of the value as a string.
|
|
If the value's type is REG_EXPAND_SZ the returned value is <b>not</b>
|
|
expanded; $(D value_EXPAND_SZ) should be called
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
Throws:
|
|
$(D RegistryException) if the type of the value is not REG_SZ,
|
|
REG_EXPAND_SZ, REG_DWORD, or REG_QWORD.
|
|
*/
|
|
@property string value_SZ() const
|
|
{
|
|
string value;
|
|
|
|
regQueryValue(m_key.m_hkey, m_name, value, m_type);
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
Obtains the current value as a string, within which any environment
|
|
variables have undergone expansion.
|
|
This function works with the same value-types as $(D value_SZ).
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
*/
|
|
@property string value_EXPAND_SZ() const
|
|
{
|
|
string value = value_SZ;
|
|
|
|
// value = expand_environment_strings(value);
|
|
// return value;
|
|
|
|
// ExpandEnvironemntStrings():
|
|
// http://msdn2.microsoft.com/en-us/library/ms724265.aspx
|
|
LPCSTR lpSrc = toMBSz(value);
|
|
DWORD cchRequired = ExpandEnvironmentStringsA(lpSrc, null, 0);
|
|
char[] newValue = new char[cchRequired];
|
|
|
|
if (!ExpandEnvironmentStringsA(lpSrc, newValue.ptr, to!DWORD(newValue.length)))
|
|
throw new Win32Exception("Failed to expand environment variables");
|
|
|
|
return fromMBSz(cast(immutable(char)*) newValue.ptr); // remove trailing 0
|
|
}
|
|
|
|
/**
|
|
Obtains the current value as an array of strings.
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
Throws:
|
|
$(D RegistryException) if the type of the value is not REG_MULTI_SZ.
|
|
*/
|
|
@property string[] value_MULTI_SZ() const
|
|
{
|
|
string[] value;
|
|
|
|
regQueryValue(m_key.m_hkey, m_name, value, m_type);
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
Obtains the current value as a 32-bit unsigned integer, ordered
|
|
correctly according to the current architecture.
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
Throws:
|
|
$(D RegistryException) is thrown for all types other than
|
|
REG_DWORD, REG_DWORD_LITTLE_ENDIAN and REG_DWORD_BIG_ENDIAN.
|
|
*/
|
|
@property uint value_DWORD() const
|
|
{
|
|
uint value;
|
|
|
|
regQueryValue(m_key.m_hkey, m_name, value, m_type);
|
|
|
|
return value;
|
|
}
|
|
|
|
deprecated uint value_DWORD_LITTLEENDIAN()
|
|
{
|
|
return value_DWORD;
|
|
}
|
|
|
|
deprecated uint value_DWORD_BIGENDIAN()
|
|
{
|
|
return value_DWORD;
|
|
}
|
|
|
|
/**
|
|
Obtains the value as a 64-bit unsigned integer, ordered correctly
|
|
according to the current architecture.
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
Throws:
|
|
$(D RegistryException) if the type of the value is not REG_QWORD.
|
|
*/
|
|
@property ulong value_QWORD() const
|
|
{
|
|
ulong value;
|
|
|
|
regQueryValue(m_key.m_hkey, m_name, value, m_type);
|
|
|
|
return value;
|
|
}
|
|
|
|
deprecated ulong value_QWORD_LITTLEENDIAN()
|
|
{
|
|
return value_QWORD;
|
|
}
|
|
|
|
/**
|
|
Obtains the value as a binary blob.
|
|
|
|
Returns:
|
|
The contents of the value.
|
|
Throws:
|
|
$(D RegistryException) if the type of the value is not REG_BINARY.
|
|
*/
|
|
@property byte[] value_BINARY() const
|
|
{
|
|
byte[] value;
|
|
|
|
regQueryValue(m_key.m_hkey, m_name, value, m_type);
|
|
|
|
return value;
|
|
}
|
|
|
|
private:
|
|
Key m_key;
|
|
REG_VALUE_TYPE m_type;
|
|
string m_name;
|
|
}
|
|
|
|
/**
|
|
Represents the local system registry.
|
|
*/
|
|
abstract final class Registry
|
|
{
|
|
private:
|
|
shared static this()
|
|
{
|
|
sm_keyClassesRoot = new Key(regDup(HKEY_CLASSES_ROOT), "HKEY_CLASSES_ROOT", false);
|
|
sm_keyCurrentUser = new Key(regDup(HKEY_CURRENT_USER), "HKEY_CURRENT_USER", false);
|
|
sm_keyLocalMachine = new Key(regDup(HKEY_LOCAL_MACHINE), "HKEY_LOCAL_MACHINE", false);
|
|
sm_keyUsers = new Key(regDup(HKEY_USERS), "HKEY_USERS", false);
|
|
sm_keyPerformanceData = new Key(regDup(HKEY_PERFORMANCE_DATA), "HKEY_PERFORMANCE_DATA", false);
|
|
sm_keyCurrentConfig = new Key(regDup(HKEY_CURRENT_CONFIG), "HKEY_CURRENT_CONFIG", false);
|
|
sm_keyDynData = new Key(regDup(HKEY_DYN_DATA), "HKEY_DYN_DATA", false);
|
|
}
|
|
|
|
public:
|
|
/// Returns the root key for the HKEY_CLASSES_ROOT hive
|
|
static @property Key classesRoot() { return sm_keyClassesRoot; }
|
|
/// Returns the root key for the HKEY_CURRENT_USER hive
|
|
static @property Key currentUser() { return sm_keyCurrentUser; }
|
|
/// Returns the root key for the HKEY_LOCAL_MACHINE hive
|
|
static @property Key localMachine() { return sm_keyLocalMachine; }
|
|
/// Returns the root key for the HKEY_USERS hive
|
|
static @property Key users() { return sm_keyUsers; }
|
|
/// Returns the root key for the HKEY_PERFORMANCE_DATA hive
|
|
static @property Key performanceData() { return sm_keyPerformanceData; }
|
|
/// Returns the root key for the HKEY_CURRENT_CONFIG hive
|
|
static @property Key currentConfig() { return sm_keyCurrentConfig; }
|
|
/// Returns the root key for the HKEY_DYN_DATA hive
|
|
static @property Key dynData() { return sm_keyDynData; }
|
|
|
|
private:
|
|
__gshared Key sm_keyClassesRoot;
|
|
__gshared Key sm_keyCurrentUser;
|
|
__gshared Key sm_keyLocalMachine;
|
|
__gshared Key sm_keyUsers;
|
|
__gshared Key sm_keyPerformanceData;
|
|
__gshared Key sm_keyCurrentConfig;
|
|
__gshared Key sm_keyDynData;
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence representing the names of the sub-keys of a registry Key.
|
|
|
|
Example:
|
|
----
|
|
Key key = ...
|
|
foreach (string subkeyName; key.keyNames)
|
|
{
|
|
// using subkeyName
|
|
}
|
|
----
|
|
*/
|
|
class KeyNameSequence
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_key !is null);
|
|
}
|
|
|
|
private:
|
|
this(Key key)
|
|
{
|
|
m_key = key;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
The number of keys.
|
|
*/
|
|
@property uint count() const
|
|
{
|
|
return m_key.keyCount;
|
|
}
|
|
|
|
/**
|
|
The name of the key at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the key to retrieve.
|
|
Returns:
|
|
The name of the key corresponding to the given index.
|
|
Throws:
|
|
RegistryException if no corresponding key is retrieved.
|
|
*/
|
|
string getKeyName(uint index)
|
|
{
|
|
string name;
|
|
regProcessNthKey(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getKeyName)
|
|
{
|
|
auto res = getKeyName(index, name);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Invalid key", res);
|
|
});
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
The name of the key at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the key to retrieve.
|
|
Returns:
|
|
The name of the key corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding key is retrieved.
|
|
*/
|
|
string opIndex(uint index)
|
|
{
|
|
return getKeyName(index);
|
|
}
|
|
|
|
public:
|
|
///
|
|
int opApply(scope int delegate(ref string name) dg)
|
|
{
|
|
int result;
|
|
regProcessNthKey(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getKeyName)
|
|
{
|
|
for (DWORD index = 0; result == 0; ++index)
|
|
{
|
|
string name;
|
|
LONG res = getKeyName(index, name);
|
|
if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
|
|
break;
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Key name enumeration incomplete", res);
|
|
|
|
result = dg(name);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
Key m_key;
|
|
}
|
|
|
|
|
|
/**
|
|
An enumerable sequence representing the sub-keys of a registry Key.
|
|
|
|
Example:
|
|
----
|
|
Key key = ...
|
|
foreach (Key subkey; key.keys)
|
|
{
|
|
// using subkey
|
|
}
|
|
----
|
|
*/
|
|
class KeySequence
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_key !is null);
|
|
}
|
|
|
|
private:
|
|
this(Key key)
|
|
{
|
|
m_key = key;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
The number of keys.
|
|
*/
|
|
@property uint count() const
|
|
{
|
|
return m_key.keyCount;
|
|
}
|
|
|
|
/**
|
|
The key at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the key to retrieve.
|
|
Returns:
|
|
The key corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding key is retrieved.
|
|
*/
|
|
Key getKey(uint index)
|
|
{
|
|
string name;
|
|
regProcessNthKey(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getKeyName)
|
|
{
|
|
auto res = getKeyName(index, name);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Invalid key", res);
|
|
});
|
|
return m_key.getKey(name);
|
|
}
|
|
|
|
/**
|
|
The key at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the key to retrieve.
|
|
Returns:
|
|
The key corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding key is retrieved.
|
|
*/
|
|
Key opIndex(uint index)
|
|
{
|
|
return getKey(index);
|
|
}
|
|
|
|
public:
|
|
///
|
|
int opApply(scope int delegate(ref Key key) dg)
|
|
{
|
|
int result = 0;
|
|
regProcessNthKey(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getKeyName)
|
|
{
|
|
for (DWORD index = 0; result == 0; ++index)
|
|
{
|
|
string name;
|
|
LONG res = getKeyName(index, name);
|
|
if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
|
|
break;
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Key enumeration incomplete", res);
|
|
|
|
try
|
|
{
|
|
Key key = m_key.getKey(name);
|
|
result = dg(key);
|
|
}
|
|
catch (RegistryException e)
|
|
{
|
|
// Skip inaccessible keys; they are
|
|
// accessible via the KeyNameSequence
|
|
if (e.error == ERROR_ACCESS_DENIED)
|
|
continue;
|
|
|
|
throw e;
|
|
}
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
Key m_key;
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence representing the names of the values of a registry Key.
|
|
|
|
Example:
|
|
----
|
|
Key key = ...
|
|
foreach (string valueName; key.valueNames)
|
|
{
|
|
// using valueName
|
|
}
|
|
----
|
|
*/
|
|
class ValueNameSequence
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_key !is null);
|
|
}
|
|
|
|
private:
|
|
this(Key key)
|
|
{
|
|
m_key = key;
|
|
}
|
|
|
|
public:
|
|
/**
|
|
The number of values.
|
|
*/
|
|
@property uint count() const
|
|
{
|
|
return m_key.valueCount;
|
|
}
|
|
|
|
/**
|
|
The name of the value at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the value to retrieve.
|
|
Returns:
|
|
The name of the value corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding value is retrieved.
|
|
*/
|
|
string getValueName(uint index)
|
|
{
|
|
string name;
|
|
regProcessNthValue(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getValueName)
|
|
{
|
|
auto res = getValueName(index, name);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Invalid value", res);
|
|
});
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
The name of the value at the given index.
|
|
|
|
Params:
|
|
index = The 0-based index of the value to retrieve.
|
|
Returns:
|
|
The name of the value corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding value is retrieved.
|
|
*/
|
|
string opIndex(uint index)
|
|
{
|
|
return getValueName(index);
|
|
}
|
|
|
|
public:
|
|
///
|
|
int opApply(scope int delegate(ref string name) dg)
|
|
{
|
|
int result = 0;
|
|
regProcessNthValue(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getValueName)
|
|
{
|
|
for(DWORD index = 0; 0 == result; ++index)
|
|
{
|
|
string name;
|
|
auto res = getValueName(index, name);
|
|
if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
|
|
break;
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value name enumeration incomplete", res);
|
|
|
|
result = dg(name);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
Key m_key;
|
|
}
|
|
|
|
/**
|
|
An enumerable sequence representing the values of a registry Key.
|
|
|
|
Example:
|
|
----
|
|
Key key = ...
|
|
foreach (Value value; key.values)
|
|
{
|
|
// using value
|
|
}
|
|
----
|
|
*/
|
|
class ValueSequence
|
|
{
|
|
invariant()
|
|
{
|
|
assert(m_key !is null);
|
|
}
|
|
|
|
private:
|
|
this(Key key)
|
|
{
|
|
m_key = key;
|
|
}
|
|
|
|
public:
|
|
/// The number of values
|
|
@property uint count() const
|
|
{
|
|
return m_key.valueCount;
|
|
}
|
|
|
|
/**
|
|
The value at the given $(D index).
|
|
|
|
Params:
|
|
index = The 0-based index of the value to retrieve
|
|
Returns:
|
|
The value corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding value is retrieved
|
|
*/
|
|
Value getValue(uint index)
|
|
{
|
|
string name;
|
|
regProcessNthValue(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getValueName)
|
|
{
|
|
auto res = getValueName(index, name);
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Invalid value", res);
|
|
});
|
|
return m_key.getValue(name);
|
|
}
|
|
|
|
/**
|
|
The value at the given $(D index).
|
|
|
|
Params:
|
|
index = The 0-based index of the value to retrieve.
|
|
Returns:
|
|
The value corresponding to the given index.
|
|
Throws:
|
|
$(D RegistryException) if no corresponding value is retrieved.
|
|
*/
|
|
Value opIndex(uint index)
|
|
{
|
|
return getValue(index);
|
|
}
|
|
|
|
public:
|
|
///
|
|
int opApply(scope int delegate(ref Value value) dg)
|
|
{
|
|
int result = 0;
|
|
regProcessNthValue(m_key.m_hkey, (scope LONG delegate(DWORD, out string) getValueName)
|
|
{
|
|
for(DWORD index = 0; 0 == result; ++index)
|
|
{
|
|
string name;
|
|
auto res = getValueName(index, name);
|
|
if (res == ERROR_NO_MORE_ITEMS) // Enumeration complete
|
|
break;
|
|
if (res != ERROR_SUCCESS)
|
|
throw new RegistryException("Value enumeration incomplete", res);
|
|
|
|
Value value = m_key.getValue(name);
|
|
result = dg(value);
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
Key m_key;
|
|
}
|
|
|
|
|
|
unittest
|
|
{
|
|
debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
|
|
debug(winreg) writefln("std.windows.registry.unittest read");
|
|
|
|
//synchronized(useWfuncs)
|
|
{
|
|
auto save_useWfuncs = useWfuncs;
|
|
|
|
foreach (loop; 0..2)
|
|
{
|
|
if (loop == 0) useWfuncs = false;
|
|
else useWfuncs = true;
|
|
|
|
/+
|
|
// Mask for test speed up
|
|
|
|
Key HKCR = Registry.classesRoot;
|
|
Key CLSID = HKCR.getKey("CLSID");
|
|
|
|
foreach (Key key; CLSID.keys)
|
|
{
|
|
foreach (Value val; key.values)
|
|
{
|
|
}
|
|
}
|
|
+/
|
|
Key HKCU = Registry.currentUser;
|
|
assert(HKCU);
|
|
|
|
// Enumerate all subkeys of key Software
|
|
Key softwareKey = HKCU.getKey("Software");
|
|
assert(softwareKey);
|
|
foreach (Key key; softwareKey.keys)
|
|
{
|
|
//writefln("Key %s", key.name);
|
|
foreach (Value val; key.values)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
useWfuncs = save_useWfuncs;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
debug(winreg) scope(success) writeln("unittest @", __FILE__, ":", __LINE__, " succeeded.");
|
|
debug(winreg) writefln("std.windows.registry.unittest write");
|
|
|
|
//synchronized(useWfuncs)
|
|
{
|
|
auto save_useWfuncs = useWfuncs;
|
|
|
|
foreach (loop; 0..2)
|
|
{
|
|
if (loop == 0) useWfuncs = false;
|
|
else useWfuncs = true;
|
|
|
|
if (useWfuncs == false)
|
|
continue;
|
|
|
|
// Warning: This unit test writes to the registry.
|
|
// The test can fail if you don't have sufficient rights
|
|
|
|
Key HKCU = Registry.currentUser;
|
|
assert(HKCU);
|
|
|
|
// Create a new key
|
|
string unittestKeyName = "Temporary key for a D UnitTest which can be deleted afterwards";
|
|
Key unittestKey = HKCU.createKey(unittestKeyName);
|
|
assert(unittestKey);
|
|
Key cityKey = unittestKey.createKey("CityCollection using foreign names with umlauts and accents: \u00f6\u00e4\u00fc\u00d6\u00c4\u00dc\u00e0\u00e1\u00e2\u00df");
|
|
cityKey.setValue("K\u00f6ln", "Germany"); // Cologne
|
|
cityKey.setValue("\u041c\u0438\u043d\u0441\u043a", "Belarus"); // Minsk
|
|
cityKey.setValue("\u5317\u4eac", "China"); // Bejing
|
|
bool foundCologne, foundMinsk, foundBeijing;
|
|
foreach (Value v; cityKey.values)
|
|
{
|
|
auto vname = v.name;
|
|
auto vvalue_SZ = v.value_SZ;
|
|
if (v.name == "K\u00f6ln")
|
|
{
|
|
foundCologne = true;
|
|
assert(v.value_SZ == "Germany");
|
|
}
|
|
if (v.name == "\u041c\u0438\u043d\u0441\u043a")
|
|
{
|
|
foundMinsk = true;
|
|
assert(v.value_SZ == "Belarus");
|
|
}
|
|
if (v.name == "\u5317\u4eac")
|
|
{
|
|
foundBeijing = true;
|
|
assert(v.value_SZ == "China");
|
|
}
|
|
}
|
|
assert(foundCologne);
|
|
assert(foundMinsk);
|
|
assert(foundBeijing);
|
|
|
|
Key stateKey = unittestKey.createKey("StateCollection");
|
|
stateKey.setValue("Germany", ["D\u00fcsseldorf", "K\u00f6ln", "Hamburg"]);
|
|
Value v = stateKey.getValue("Germany");
|
|
string[] actual = v.value_MULTI_SZ;
|
|
assert(actual.length == 3);
|
|
assert(actual[0] == "D\u00fcsseldorf");
|
|
assert(actual[1] == "K\u00f6ln");
|
|
assert(actual[2] == "Hamburg");
|
|
|
|
Key numberKey = unittestKey.createKey("Number");
|
|
numberKey.setValue("One", 1);
|
|
Value one = numberKey.getValue("One");
|
|
assert(one.value_SZ == "1");
|
|
assert(one.value_DWORD == 1);
|
|
|
|
unittestKey.deleteKey(numberKey.name);
|
|
unittestKey.deleteKey(stateKey.name);
|
|
unittestKey.deleteKey(cityKey.name);
|
|
HKCU.deleteKey(unittestKeyName);
|
|
}
|
|
|
|
useWfuncs = save_useWfuncs;
|
|
}
|
|
}
|