2 // Microsoft.Win32/Win32RegistryApi.cs: wrapper for win32 registry API
5 // Erik LeBel (eriklebel@yahoo.ca)
6 // Jackson Harper (jackson@ximian.com)
7 // Miguel de Icaza (miguel@gnome.org)
9 // Copyright (C) Erik LeBel 2004
10 // (C) 2004, 2005 Novell, Inc (http://www.novell.com)
14 // Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 using System.Collections;
41 using System.Runtime.InteropServices;
42 using System.Security;
44 using Microsoft.Win32.SafeHandles;
46 namespace Microsoft.Win32
49 /// Function stubs, constants and helper functions for
50 /// the Win32 registry manipulation utilities.
52 internal class Win32RegistryApi : IRegistryApi
54 // bit masks for registry key open access permissions
55 const int OpenRegKeyRead = 0x00020019;
56 const int OpenRegKeyWrite = 0x00020006;
58 // FIXME must be a way to determin this dynamically?
59 const int Int32ByteSize = 4;
61 // FIXME this is hard coded on Mono, can it be determined dynamically?
62 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
64 const int RegOptionsNonVolatile = 0x00000000;
65 const int RegOptionsVolatile = 0x00000001;
67 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKeyEx")]
68 static extern int RegCreateKeyEx (IntPtr keyBase, string keyName, int reserved,
69 IntPtr lpClass, int options, int access, IntPtr securityAttrs,
70 out IntPtr keyHandle, out int disposition);
72 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
73 static extern int RegCloseKey (IntPtr keyHandle);
75 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
76 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
77 out IntPtr keyHandle);
79 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
80 private static extern int RegFlushKey (IntPtr keyHandle);
82 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
83 private static extern int RegOpenKeyEx (IntPtr keyBase,
84 string keyName, IntPtr reserved, int access,
85 out IntPtr keyHandle);
87 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
88 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
90 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
91 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
93 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
94 private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
96 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
97 private static extern int RegEnumValue (IntPtr keyBase,
98 int index, StringBuilder nameBuffer,
99 ref int nameLength, IntPtr reserved,
100 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
102 // [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
103 // private static extern int RegSetValueEx (IntPtr keyBase,
104 // string valueName, IntPtr reserved, RegistryValueKind type,
105 // StringBuilder data, int rawDataLength);
107 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
108 private static extern int RegSetValueEx (IntPtr keyBase,
109 string valueName, IntPtr reserved, RegistryValueKind type,
110 string data, int rawDataLength);
112 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
113 private static extern int RegSetValueEx (IntPtr keyBase,
114 string valueName, IntPtr reserved, RegistryValueKind type,
115 byte[] rawData, int rawDataLength);
117 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
118 private static extern int RegSetValueEx (IntPtr keyBase,
119 string valueName, IntPtr reserved, RegistryValueKind type,
120 ref int data, int rawDataLength);
122 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
123 private static extern int RegQueryValueEx (IntPtr keyBase,
124 string valueName, IntPtr reserved, ref RegistryValueKind type,
125 IntPtr zero, ref int dataSize);
127 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
128 private static extern int RegQueryValueEx (IntPtr keyBase,
129 string valueName, IntPtr reserved, ref RegistryValueKind type,
130 [Out] byte[] data, ref int dataSize);
132 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
133 private static extern int RegQueryValueEx (IntPtr keyBase,
134 string valueName, IntPtr reserved, ref RegistryValueKind type,
135 ref int data, ref int dataSize);
137 // Returns our handle from the RegistryKey
138 public IntPtr GetHandle (RegistryKey key)
140 return (IntPtr) key.InternalHandle;
143 static bool IsHandleValid (RegistryKey key)
145 return key.InternalHandle != null;
148 public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
150 RegistryValueKind type = 0;
152 IntPtr handle = GetHandle (rkey);
153 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
155 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
156 return RegistryValueKind.Unknown;
162 /// Acctually read a registry value. Requires knowledge of the
163 /// value's type and size.
165 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
167 RegistryValueKind type = 0;
170 IntPtr handle = GetHandle (rkey);
171 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
173 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
177 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
178 GenerateException (result);
181 if (type == RegistryValueKind.String) {
183 result = GetBinaryValue (rkey, name, type, out data, size);
184 obj = RegistryKey.DecodeString (data);
185 } else if (type == RegistryValueKind.ExpandString) {
187 result = GetBinaryValue (rkey, name, type, out data, size);
188 obj = RegistryKey.DecodeString (data);
189 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
190 obj = Environment.ExpandEnvironmentVariables ((string) obj);
191 } else if (type == RegistryValueKind.DWord) {
193 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
195 } else if (type == RegistryValueKind.Binary) {
197 result = GetBinaryValue (rkey, name, type, out data, size);
199 } else if (type == RegistryValueKind.MultiString) {
202 result = GetBinaryValue (rkey, name, type, out data, size);
204 if (result == Win32ResultCode.Success)
205 obj = RegistryKey.DecodeString (data).Split ('\0');
207 // should never get here
208 throw new SystemException ();
211 // check result codes again:
212 if (result != Win32ResultCode.Success)
214 GenerateException (result);
222 // This version has to do extra checking, make sure that the requested
223 // valueKind matches the type of the value being stored
225 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
227 Type type = value.GetType ();
229 IntPtr handle = GetHandle (rkey);
231 if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
232 int rawValue = (int)value;
233 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
234 } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
235 byte[] rawValue = (byte[]) value;
236 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
237 } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
238 string[] vals = (string[]) value;
239 StringBuilder fullStringValue = new StringBuilder ();
240 foreach (string v in vals)
242 fullStringValue.Append (v);
243 fullStringValue.Append ('\0');
245 fullStringValue.Append ('\0');
247 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
249 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
250 } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
251 type == typeof (string)){
252 string rawValue = String.Format ("{0}{1}", value, '\0');
253 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
254 rawValue.Length * NativeBytesPerCharacter);
256 } else if (type.IsArray) {
257 throw new ArgumentException ("Only string and byte arrays can written as registry values");
259 throw new ArgumentException ("Type does not match the valueKind");
262 // handle the result codes
263 if (result != Win32ResultCode.Success)
265 GenerateException (result);
269 public void SetValue (RegistryKey rkey, string name, object value)
271 Type type = value.GetType ();
273 IntPtr handle = GetHandle (rkey);
275 if (type == typeof (int)) {
276 int rawValue = (int)value;
277 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
278 } else if (type == typeof (byte[])) {
279 byte[] rawValue = (byte[]) value;
280 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
281 } else if (type == typeof (string[])) {
282 string[] vals = (string[]) value;
283 StringBuilder fullStringValue = new StringBuilder ();
284 foreach (string v in vals)
286 fullStringValue.Append (v);
287 fullStringValue.Append ('\0');
289 fullStringValue.Append ('\0');
291 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
293 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
294 } else if (type.IsArray) {
295 throw new ArgumentException ("Only string and byte arrays can written as registry values");
297 string rawValue = String.Format ("{0}{1}", value, '\0');
298 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
299 rawValue.Length * NativeBytesPerCharacter);
302 if (result == Win32ResultCode.MarkedForDeletion)
303 throw RegistryKey.CreateMarkedForDeletionException ();
305 // handle the result codes
306 if (result != Win32ResultCode.Success)
308 GenerateException (result);
313 /// Get a binary value.
315 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
317 byte[] internalData = new byte [size];
318 IntPtr handle = GetHandle (rkey);
319 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
325 // Arbitrary max size for key/values names that can be fetched.
326 // .NET framework SDK docs say that the max name length that can
327 // be used is 255 characters, we'll allow for a bit more.
328 const int BufferMaxLength = 1024;
330 public int SubKeyCount (RegistryKey rkey)
333 StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
334 IntPtr handle = GetHandle (rkey);
336 for (index = 0; true; index ++) {
337 int result = RegEnumKey (handle, index, stringBuffer,
338 stringBuffer.Capacity);
340 if (result == Win32ResultCode.MarkedForDeletion)
341 throw RegistryKey.CreateMarkedForDeletionException ();
343 if (result == Win32ResultCode.Success)
346 if (result == Win32ResultCode.NoMoreEntries)
349 // something is wrong!!
350 GenerateException (result);
355 public int ValueCount (RegistryKey rkey)
357 int index, result, bufferCapacity;
358 RegistryValueKind type;
359 StringBuilder buffer = new StringBuilder (BufferMaxLength);
361 IntPtr handle = GetHandle (rkey);
362 for (index = 0; true; index ++) {
364 bufferCapacity = buffer.Capacity;
365 result = RegEnumValue (handle, index,
366 buffer, ref bufferCapacity,
367 IntPtr.Zero, ref type,
368 IntPtr.Zero, IntPtr.Zero);
370 if (result == Win32ResultCode.MarkedForDeletion)
371 throw RegistryKey.CreateMarkedForDeletionException ();
373 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
376 if (result == Win32ResultCode.NoMoreEntries)
379 // something is wrong
380 GenerateException (result);
385 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
387 IntPtr handle = new IntPtr ((int) hKey);
390 int result = RegConnectRegistry (machineName, handle, out keyHandle);
391 if (result != Win32ResultCode.Success)
392 GenerateException (result);
394 return new RegistryKey (hKey, keyHandle, true);
397 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
399 int access = OpenRegKeyRead;
400 if (writable) access |= OpenRegKeyWrite;
401 IntPtr handle = GetHandle (rkey);
404 int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
406 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
409 if (result != Win32ResultCode.Success)
410 GenerateException (result);
412 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
415 public void Flush (RegistryKey rkey)
417 if (!IsHandleValid (rkey))
419 IntPtr handle = GetHandle (rkey);
420 RegFlushKey (handle);
423 public void Close (RegistryKey rkey)
425 if (!IsHandleValid (rkey))
427 IntPtr handle = GetHandle (rkey);
428 RegCloseKey (handle);
432 public RegistryKey FromHandle (SafeRegistryHandle handle)
434 // At this point we can't tell whether the key is writable
435 // or not (nor the name), so we let the error check code handle it later, as
437 return new RegistryKey (handle.DangerousGetHandle (), String.Empty, true);
441 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
443 IntPtr handle = GetHandle (rkey);
446 int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
447 RegOptionsNonVolatile,
448 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
450 if (result == Win32ResultCode.MarkedForDeletion)
451 throw RegistryKey.CreateMarkedForDeletionException ();
453 if (result != Win32ResultCode.Success) {
454 GenerateException (result);
457 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
462 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName, RegistryOptions options)
464 IntPtr handle = GetHandle (rkey);
467 int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
468 options == RegistryOptions.Volatile ? RegOptionsVolatile : RegOptionsNonVolatile,
469 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
471 if (result == Win32ResultCode.MarkedForDeletion)
472 throw RegistryKey.CreateMarkedForDeletionException ();
474 if (result != Win32ResultCode.Success)
475 GenerateException (result);
477 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
482 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
484 IntPtr handle = GetHandle (rkey);
485 int result = RegDeleteKey (handle, keyName);
487 if (result == Win32ResultCode.FileNotFound) {
488 if (shouldThrowWhenKeyMissing)
489 throw new ArgumentException ("key " + keyName);
493 if (result != Win32ResultCode.Success)
494 GenerateException (result);
497 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
499 IntPtr handle = GetHandle (rkey);
500 int result = RegDeleteValue (handle, value);
502 if (result == Win32ResultCode.MarkedForDeletion)
505 if (result == Win32ResultCode.FileNotFound){
506 if (shouldThrowWhenKeyMissing)
507 throw new ArgumentException ("value " + value);
511 if (result != Win32ResultCode.Success)
512 GenerateException (result);
515 public string [] GetSubKeyNames (RegistryKey rkey)
517 IntPtr handle = GetHandle (rkey);
518 StringBuilder buffer = new StringBuilder (BufferMaxLength);
519 ArrayList keys = new ArrayList ();
521 for (int index = 0; true; index ++) {
522 int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
524 if (result == Win32ResultCode.Success) {
525 keys.Add (buffer.ToString ());
530 if (result == Win32ResultCode.NoMoreEntries)
533 // should not be here!
534 GenerateException (result);
536 return (string []) keys.ToArray (typeof(String));
540 public string [] GetValueNames (RegistryKey rkey)
542 IntPtr handle = GetHandle (rkey);
543 ArrayList values = new ArrayList ();
545 for (int index = 0; true; index ++)
547 StringBuilder buffer = new StringBuilder (BufferMaxLength);
548 int bufferCapacity = buffer.Capacity;
549 RegistryValueKind type = 0;
551 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
552 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
554 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
555 values.Add (buffer.ToString ());
559 if (result == Win32ResultCode.NoMoreEntries)
562 if (result == Win32ResultCode.MarkedForDeletion)
563 throw RegistryKey.CreateMarkedForDeletionException ();
565 GenerateException (result);
568 return (string []) values.ToArray (typeof(String));
572 /// convert a win32 error code into an appropriate exception.
574 private void GenerateException (int errorCode)
577 case Win32ResultCode.FileNotFound:
578 case Win32ResultCode.InvalidParameter:
579 throw new ArgumentException ();
580 case Win32ResultCode.AccessDenied:
581 throw new SecurityException ();
582 case Win32ResultCode.NetworkPathNotFound:
583 throw new IOException ("The network path was not found.");
584 case Win32ResultCode.InvalidHandle:
585 throw new IOException ("Invalid handle.");
587 // unidentified system exception
588 throw new SystemException ();
592 public string ToString (RegistryKey rkey)
598 /// utility: Combine the sub key name to the current name to produce a
599 /// fully qualified sub key name.
601 internal static string CombineName (RegistryKey rkey, string localName)
603 return String.Concat (rkey.Name, "\\", localName);