1 /** 2 * Copyright: Copyright (c) 2011 Nick Sabalausky. All rights reserved. 3 * Authors: Nick Sabalausky 4 * Version: Initial created: Jun 1, 2011 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dvm.sys.Registry; 8 9 version (Windows): 10 11 import std.utf : toUTF16z; 12 13 import tango.sys.win32.Types; 14 import tango.sys.win32.UserGdi; 15 import tango.util.Convert; 16 17 import dvm.dvm.Exceptions; 18 import dvm.util.Windows; 19 20 /// Low-Level Registry Wrappers 21 /// 22 /// WARNING: REG_MULTI_SZ, REG_BINARY and REG_NONE are untested. 23 24 /// Types and Constants /////////////////////////// 25 26 public import tango.sys.win32.Types : 27 HKEY, 28 KEY_ALL_ACCESS, 29 KEY_CREATE_LINK, 30 KEY_CREATE_SUB_KEY, 31 KEY_ENUMERATE_SUB_KEYS, 32 KEY_EXECUTE, 33 KEY_NOTIFY, 34 KEY_QUERY_VALUE, 35 KEY_READ, 36 KEY_SET_VALUE, 37 KEY_WRITE, 38 REG_OPTION_NON_VOLATILE, 39 REG_OPTION_VOLATILE; 40 41 enum : DWORD 42 { 43 REG_OPTION_CREATE_LINK = 2, 44 REG_OPTION_BACKUP_RESTORE = 4, 45 } 46 47 enum RegRoot : DWORD 48 { 49 HKEY_CLASSES_ROOT = (0x80000000), 50 HKEY_CURRENT_USER = (0x80000001), 51 HKEY_LOCAL_MACHINE = (0x80000002), 52 HKEY_USERS = (0x80000003), 53 HKEY_PERFORMANCE_DATA = (0x80000004), 54 HKEY_CURRENT_CONFIG = (0x80000005), 55 HKEY_DYN_DATA = (0x80000006), 56 } 57 58 HKEY HKEY_CLASSES_ROOT () 59 { 60 return cast(HKEY) RegRoot.HKEY_CLASSES_ROOT; 61 } 62 63 HKEY HKEY_CURRENT_USER () 64 { 65 return cast(HKEY) RegRoot.HKEY_CURRENT_USER; 66 } 67 68 HKEY HKEY_LOCAL_MACHINE () 69 { 70 return cast(HKEY) RegRoot.HKEY_LOCAL_MACHINE; 71 } 72 73 HKEY HKEY_USERS () 74 { 75 return cast(HKEY) RegRoot.HKEY_USERS; 76 } 77 78 HKEY HKEY_PERFORMANCE_DATA () 79 { 80 return cast(HKEY) RegRoot.HKEY_PERFORMANCE_DATA; 81 } 82 83 HKEY HKEY_CURRENT_CONFIG () 84 { 85 return cast(HKEY) RegRoot.HKEY_CURRENT_CONFIG; 86 } 87 88 HKEY HKEY_DYN_DATA () 89 { 90 return cast(HKEY) RegRoot.HKEY_DYN_DATA; 91 } 92 93 enum RegKeyAccess 94 { 95 Read, Write, All 96 } 97 98 enum RegValueType : DWORD 99 { 100 Unknown = .DWORD.max, 101 102 BINARY = REG_BINARY, 103 DWORD = REG_DWORD, 104 EXPAND_SZ = REG_EXPAND_SZ, 105 LINK = REG_LINK, 106 MULTI_SZ = REG_MULTI_SZ, 107 NONE = REG_NONE, 108 SZ = REG_SZ, 109 110 // Beware, these are reported to not work on all versions 111 // of Windows from Win2K and up: 112 //QWORD = REG_QWORD, 113 //QWORD_LITTLE_ENDIAN = REG_QWORD_LITTLE_ENDIAN, 114 DWORD_LITTLE_ENDIAN = REG_DWORD_LITTLE_ENDIAN, 115 DWORD_BIG_ENDIAN = REG_DWORD_BIG_ENDIAN, 116 } 117 118 struct RegQueryResult 119 { 120 RegValueType type; 121 string asString; 122 string[] asStringArray; 123 uint asUInt; 124 ubyte[] asBinary; 125 } 126 127 template DataTypeOf (RegValueType type) 128 { 129 static if(type == RegValueType.DWORD) 130 alias uint DataTypeOf; 131 132 else static if(type == RegValueType.DWORD_LITTLE_ENDIAN) 133 alias uint DataTypeOf; 134 135 else static if(type == RegValueType.SZ) 136 alias string DataTypeOf; 137 138 else static if(type == RegValueType.EXPAND_SZ) 139 alias string DataTypeOf; 140 141 else static if(type == RegValueType.MULTI_SZ) 142 alias string[] DataTypeOf; 143 144 else 145 alias ubyte[] DataTypeOf; 146 } 147 148 /// Exception /////////////////////////// 149 150 class RegistryException : WinAPIException 151 { 152 string registryMsg; 153 string path; 154 bool isKey; // Is path a key or a value? 155 156 this (LONG code, string path, bool isKey, string registryMsg="") 157 { 158 this.registryMsg = registryMsg; 159 this.path = path; 160 this.isKey = isKey; 161 162 string keyInfo; 163 if(isKey) 164 keyInfo = "Registry Key '" ~ path ~ "': "; 165 else 166 keyInfo = "Registry Value '" ~ path ~ "': "; 167 168 string regMsgInfo = registryMsg; 169 string windowsMsg = ""; 170 171 if(code != ERROR_SUCCESS) 172 windowsMsg = WinAPIException.getMessage(code); 173 174 if(regMsgInfo != "" && windowsMsg != "") 175 regMsgInfo ~= ": "; 176 177 super(code, keyInfo ~ regMsgInfo ~ windowsMsg); 178 } 179 180 this (string path, bool isKey, string registryMsg = "") 181 { 182 this(ERROR_SUCCESS, path, isKey, registryMsg); 183 } 184 } 185 186 /// Conversion Functions /////////////////////////// 187 188 ubyte[] toRegDWord (ref uint val) 189 { 190 return (cast(ubyte*) &val)[0..4]; 191 } 192 193 ubyte[] toRegSZ (string str) 194 { 195 auto wstr = to!(wstring)(str); 196 197 if(wstr.length == 0 || wstr[$-1] != '\0') 198 wstr ~= '\0'; 199 200 return cast(ubyte[])wstr; 201 } 202 203 ubyte[] toRegMultiSZ (string[] arr) 204 { 205 ushort[] result; 206 207 foreach(str; arr) 208 { 209 if(str.length == 0) 210 throw new DvmException("Cannot store empty strings in a REG_MULTI_SZ", __FILE__, __LINE__); 211 212 auto wstr = to!(wstring)(str); 213 result ~= cast(ushort[]) wstr; 214 result ~= 0; 215 } 216 217 result ~= 0; 218 219 return cast(ubyte[]) result; 220 } 221 222 REGSAM toRegSam (RegKeyAccess access) 223 { 224 switch(access) 225 { 226 case RegKeyAccess.Read: return KEY_READ; 227 case RegKeyAccess.Write: return KEY_WRITE; 228 case RegKeyAccess.All: return KEY_READ | KEY_WRITE; 229 230 default: 231 throw new Exception("Internal Error: Unhandled RegKeyAccess: '" ~ to!(string)(access) ~ "'", __FILE__, __LINE__); 232 } 233 } 234 235 string toString (RegRoot root) 236 { 237 switch(root) 238 { 239 case RegRoot.HKEY_CLASSES_ROOT: return "HKEY_CLASSES_ROOT"; 240 case RegRoot.HKEY_CURRENT_USER: return "HKEY_CURRENT_USER"; 241 case RegRoot.HKEY_LOCAL_MACHINE: return "HKEY_LOCAL_MACHINE"; 242 case RegRoot.HKEY_USERS: return "HKEY_USERS"; 243 case RegRoot.HKEY_PERFORMANCE_DATA: return "HKEY_PERFORMANCE_DATA"; 244 case RegRoot.HKEY_CURRENT_CONFIG: return "HKEY_CURRENT_CONFIG"; 245 case RegRoot.HKEY_DYN_DATA: return "HKEY_DYN_DATA"; 246 247 default: 248 throw new Exception("Internal Error: Unhandled RegRoot '" ~ to!(string)(root) ~ "'", __FILE__, __LINE__); 249 } 250 } 251 252 string toString (RegValueType type) 253 { 254 switch(type) 255 { 256 case RegValueType.BINARY: return "REG_BINARY"; 257 case RegValueType.DWORD: return "REG_DWORD"; 258 case RegValueType.EXPAND_SZ: return "REG_EXPAND_SZ"; 259 case RegValueType.LINK: return "REG_LINK"; 260 case RegValueType.MULTI_SZ: return "REG_MULTI_SZ"; 261 case RegValueType.NONE: return "REG_NONE"; 262 case RegValueType.SZ: return "REG_SZ"; 263 case RegValueType.DWORD_BIG_ENDIAN: return "REG_DWORD_BIG_ENDIAN"; 264 case RegValueType.Unknown: return "(Unknown KeyValueType)"; 265 266 default: 267 return "(KeyValueType #" ~ to!(string)(cast(DWORD) type) ~ ")"; 268 } 269 } 270 271 /// Private Error Handling Utilities /////////////////////////// 272 273 private void ensureSuccess (LONG code, string path, bool isKey, string registryMsg="") 274 { 275 if (code != ERROR_SUCCESS) 276 error(code, path, isKey, registryMsg); 277 } 278 279 private void ensureSuccessKey (LONG code, string path, string registryMsg="") 280 { 281 ensureSuccess(code, path, true, registryMsg); 282 } 283 284 private void ensureSuccessValue (LONG code, string path, string registryMsg="") 285 { 286 ensureSuccess(code, path, false, registryMsg); 287 } 288 289 private void error (LONG code, string path, bool isKey, string registryMsg="") 290 { 291 throw new RegistryException(code, `{Unknown Path}\` ~ path, isKey, registryMsg); 292 } 293 294 private void errorKey (LONG code, string path, string registryMsg="") 295 { 296 error(code, path, true, registryMsg); 297 } 298 299 private void errorValue (LONG code, string path, string registryMsg="") 300 { 301 error(code, path, false, registryMsg); 302 } 303 304 /// Registry Functions /////////////////////////// 305 306 HKEY regOpenKey (HKEY hKey, string subKey, RegKeyAccess access) 307 { 308 HKEY outKey; 309 310 auto result = RegOpenKeyExW(hKey, subKey.toUTF16z(),0, toRegSam(access), &outKey); 311 ensureSuccessKey(result, subKey, "Couldn't open key"); 312 313 return outKey; 314 } 315 316 HKEY regCreateKey (HKEY hKey, string subKey, DWORD dwOptions, RegKeyAccess access, out bool wasCreated) 317 { 318 HKEY outKey; 319 DWORD disposition; 320 auto result = RegCreateKeyExW(hKey, subKey.toUTF16z(), 0, null, dwOptions, toRegSam(access), null, &outKey, &disposition); 321 wasCreated = (disposition == REG_CREATED_NEW_KEY); 322 ensureSuccessKey(result, subKey, "Couldn't open or create key"); 323 324 return outKey; 325 } 326 327 HKEY regCreateKey (HKEY hKey, string subKey, DWORD dwOptions, RegKeyAccess access) 328 { 329 bool wasCreated; 330 return regCreateKey(hKey, subKey, dwOptions, access, wasCreated); 331 } 332 333 void regCloseKey (HKEY hKey) 334 { 335 auto result = RegCloseKey(hKey); 336 ensureSuccessKey(result, "{Unknown Key}", "Couldn't close key"); 337 } 338 339 bool regValueExists (HKEY hKey, string valueName) 340 { 341 auto result = RegQueryValueExW(hKey, valueName.toUTF16z(), null, null, null, null); 342 343 if(result == ERROR_FILE_NOT_FOUND) 344 return false; 345 346 if(result == ERROR_SUCCESS) 347 return true; 348 349 errorValue(result, valueName, "Couldn't check if value exists"); 350 return false; 351 } 352 353 void regDeleteKey (HKEY hKey, string subKey) 354 { 355 auto result = RegDeleteKeyW(hKey, subKey.toUTF16z()); 356 ensureSuccessKey(result, subKey, "Couldn't delete key"); 357 } 358 359 void regDeleteValue (HKEY hKey, string valueName) 360 { 361 auto result = RegDeleteValueW(hKey, valueName.toUTF16z()); 362 ensureSuccessValue(result, valueName, "Couldn't delete value"); 363 } 364 365 /// Registry Functions: regSetValue /////////////////////////// 366 367 /// Be very careful with this particuler version. 368 /// Make sure to follow all the rules in MS's documentation. 369 /// The other overloads of regSetValue are recommended over 370 /// this one, since they already handle all the proper rules. 371 void regSetValue (HKEY hKey, string valueName, RegValueType type, ubyte[] data) 372 { 373 if(type == RegValueType.Unknown) 374 errorValue(ERROR_SUCCESS, valueName, "Can't set a key value of type 'Unknown'"); 375 376 auto ptr = (data is null)? null : data.ptr; 377 auto len = (data is null)? 0 : data.length; 378 379 auto result = RegSetValueExW(hKey, valueName.toUTF16z(), 0, type, ptr, len); 380 ensureSuccessValue(result, valueName, "Couldn't set "~toString(type)~" value"); 381 } 382 383 void regSetValue (HKEY hKey, string valueName, string data) 384 { 385 regSetValue(hKey, valueName, data, false); 386 } 387 388 void regSetValueExpand (HKEY hKey, string valueName, string data) 389 { 390 regSetValue(hKey, valueName, data, true); 391 } 392 393 void regSetValue (HKEY hKey, string valueName, string data, bool expand) 394 { 395 auto type = expand? RegValueType.EXPAND_SZ : RegValueType.SZ; 396 regSetValue(hKey, valueName, type, data.toRegSZ()); 397 } 398 399 void regSetValue (HKEY hKey, string valueName, string[] data) 400 { 401 regSetValue(hKey, valueName, RegValueType.MULTI_SZ, data.toRegMultiSZ()); 402 } 403 404 void regSetValue (HKEY hKey, string valueName, ubyte[] data) 405 { 406 regSetValue(hKey, valueName, RegValueType.BINARY, data); 407 } 408 409 void regSetValue (HKEY hKey, string valueName, uint data) 410 { 411 regSetValue(hKey, valueName, RegValueType.DWORD, toRegDWord(data)); 412 } 413 414 void regSetValue (HKEY hKey, string valueName) 415 { 416 regSetValue(hKey, valueName, RegValueType.NONE, null); 417 } 418 419 void regSetValue (HKEY hKey, string valueName, RegQueryResult data) 420 { 421 switch(data.type) 422 { 423 case RegValueType.DWORD: 424 regSetValue(hKey, valueName, data.type, toRegDWord(data.asUInt)); 425 break; 426 427 case RegValueType.SZ, RegValueType.EXPAND_SZ: 428 regSetValue(hKey, valueName, data.type, data.asString.toRegSZ()); 429 break; 430 431 case RegValueType.MULTI_SZ: 432 regSetValue(hKey, valueName, data.type, data.asStringArray.toRegMultiSZ()); 433 break; 434 435 default: 436 regSetValue(hKey, valueName, data.type, data.asBinary); 437 break; 438 } 439 } 440 441 /// Registry Functions: regQueryValue /////////////////////////// 442 443 RegQueryResult regQueryValue () (HKEY hKey, string valueName) 444 { 445 RegQueryResult ret; 446 DWORD dataSize; 447 auto valueNameZ = valueName.toUTF16z(); 448 449 auto result = RegQueryValueExW(hKey, valueNameZ, null, null, null, &dataSize); 450 ensureSuccessValue(result, valueName, "Couldn't check length of value's data"); 451 452 ubyte[] data; 453 data.length = dataSize; 454 455 result = RegQueryValueExW(hKey, valueNameZ, null, &(cast(DWORD)(ret.type)), data.ptr, &dataSize); 456 ensureSuccessValue(result, valueName, "Couldn't get value"); 457 458 switch(ret.type) 459 { 460 case RegValueType.DWORD: 461 ret.asUInt = (cast(uint[])data)[0]; 462 break; 463 464 case RegValueType.SZ, RegValueType.EXPAND_SZ: 465 ret.asString = to!(string)( (cast(wstring) data)[0 .. $-1] ); 466 break; 467 468 case RegValueType.MULTI_SZ: 469 ret.asStringArray = null; 470 auto wstrArr = split(cast(wstring)data, "\0"w)[0 .. $-1]; 471 472 foreach(wstr; wstrArr) 473 ret.asStringArray ~= to!(string)(wstr); 474 break; 475 476 default: 477 ret.asBinary = data; 478 break; 479 } 480 481 return ret; 482 } 483 484 DataTypeOf!(type) regQueryValue (RegValueType type) (HKEY hKey, string valueName) 485 { 486 auto result = regQueryValue(hKey, valueName); 487 488 if(result.type != type) 489 errorValue(ERROR_SUCCESS, valueName, "Expected key type '" ~ toString(type) ~ "', " ~"not '"~toString(result.type)~"'"); 490 491 alias DataTypeOf!(type) T; 492 493 static if (is(T == uint)) 494 return result.asUInt; 495 496 else static if (is(T == string)) 497 return result.asString; 498 499 else static if (is(T == string[])) 500 return result.asStringArray; 501 502 else 503 return result.asBinary; 504 }