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