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;
40 using System.Collections.Generic;
42 using System.Runtime.InteropServices;
43 using System.Security;
45 using Microsoft.Win32.SafeHandles;
47 namespace Microsoft.Win32
50 /// Function stubs, constants and helper functions for
51 /// the Win32 registry manipulation utilities.
53 internal class Win32RegistryApi : IRegistryApi
55 // bit masks for registry key open access permissions
56 const int OpenRegKeyRead = 0x00020019;
57 const int OpenRegKeyWrite = 0x00020006;
59 // FIXME must be a way to determin this dynamically?
60 const int Int32ByteSize = 4;
61 const int Int64ByteSize = 8;
63 // FIXME this is hard coded on Mono, can it be determined dynamically?
64 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
66 const int RegOptionsNonVolatile = 0x00000000;
67 const int RegOptionsVolatile = 0x00000001;
69 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKeyEx")]
70 static extern int RegCreateKeyEx (IntPtr keyBase, string keyName, int reserved,
71 IntPtr lpClass, int options, int access, IntPtr securityAttrs,
72 out IntPtr keyHandle, out int disposition);
74 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
75 static extern int RegCloseKey (IntPtr keyHandle);
77 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
78 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
79 out IntPtr keyHandle);
81 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
82 private static extern int RegFlushKey (IntPtr keyHandle);
84 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
85 private static extern int RegOpenKeyEx (IntPtr keyBase,
86 string keyName, IntPtr reserved, int access,
87 out IntPtr keyHandle);
89 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
90 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
92 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
93 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
95 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
96 private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
98 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
99 private static extern int RegEnumValue (IntPtr keyBase,
100 int index, StringBuilder nameBuffer,
101 ref int nameLength, IntPtr reserved,
102 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
104 // [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
105 // private static extern int RegSetValueEx (IntPtr keyBase,
106 // string valueName, IntPtr reserved, RegistryValueKind type,
107 // StringBuilder data, int rawDataLength);
109 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
110 private static extern int RegSetValueEx (IntPtr keyBase,
111 string valueName, IntPtr reserved, RegistryValueKind type,
112 string data, int rawDataLength);
114 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
115 private static extern int RegSetValueEx (IntPtr keyBase,
116 string valueName, IntPtr reserved, RegistryValueKind type,
117 byte[] rawData, int rawDataLength);
119 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
120 private static extern int RegSetValueEx (IntPtr keyBase,
121 string valueName, IntPtr reserved, RegistryValueKind type,
122 ref int data, int rawDataLength);
124 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
125 private static extern int RegSetValueEx (IntPtr keyBase,
126 string valueName, IntPtr reserved, RegistryValueKind type,
127 ref long data, int rawDataLength);
129 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
130 private static extern int RegQueryValueEx (IntPtr keyBase,
131 string valueName, IntPtr reserved, ref RegistryValueKind type,
132 IntPtr zero, ref int dataSize);
134 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
135 private static extern int RegQueryValueEx (IntPtr keyBase,
136 string valueName, IntPtr reserved, ref RegistryValueKind type,
137 [Out] byte[] data, ref int dataSize);
139 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
140 private static extern int RegQueryValueEx (IntPtr keyBase,
141 string valueName, IntPtr reserved, ref RegistryValueKind type,
142 ref int data, ref int dataSize);
144 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
145 private static extern int RegQueryValueEx (IntPtr keyBase,
146 string valueName, IntPtr reserved, ref RegistryValueKind type,
147 ref long data, ref int dataSize);
149 // Returns our handle from the RegistryKey
150 public IntPtr GetHandle (RegistryKey key)
152 return (IntPtr) key.InternalHandle;
155 static bool IsHandleValid (RegistryKey key)
157 return key.InternalHandle != null;
160 public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
162 RegistryValueKind type = 0;
164 IntPtr handle = GetHandle (rkey);
165 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
167 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
168 return RegistryValueKind.Unknown;
174 /// Acctually read a registry value. Requires knowledge of the
175 /// value's type and size.
177 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
179 RegistryValueKind type = 0;
182 IntPtr handle = GetHandle (rkey);
183 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
185 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
189 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
190 GenerateException (result);
193 if (type == RegistryValueKind.String) {
195 result = GetBinaryValue (rkey, name, type, out data, size);
196 obj = RegistryKey.DecodeString (data);
197 } else if (type == RegistryValueKind.ExpandString) {
199 result = GetBinaryValue (rkey, name, type, out data, size);
200 obj = RegistryKey.DecodeString (data);
201 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
202 obj = Environment.ExpandEnvironmentVariables ((string) obj);
203 } else if (type == RegistryValueKind.DWord) {
205 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
207 } else if (type == RegistryValueKind.QWord) {
209 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
211 } else if (type == RegistryValueKind.Binary) {
213 result = GetBinaryValue (rkey, name, type, out data, size);
215 } else if (type == RegistryValueKind.MultiString) {
218 result = GetBinaryValue (rkey, name, type, out data, size);
220 if (result == Win32ResultCode.Success)
221 obj = RegistryKey.DecodeString (data).Split ('\0');
223 // should never get here
224 throw new SystemException ();
227 // check result codes again:
228 if (result != Win32ResultCode.Success)
230 GenerateException (result);
238 // This version has to do extra checking, make sure that the requested
239 // valueKind matches the type of the value being stored
241 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
243 Type type = value.GetType ();
245 IntPtr handle = GetHandle (rkey);
247 if (valueKind == RegistryValueKind.QWord && type == typeof (long)) {
248 long rawValue = (long)value;
249 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.QWord, ref rawValue, Int64ByteSize);
250 } else if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
251 int rawValue = (int)value;
252 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
253 } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
254 byte[] rawValue = (byte[]) value;
255 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
256 } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
257 string[] vals = (string[]) value;
258 StringBuilder fullStringValue = new StringBuilder ();
259 foreach (string v in vals)
261 fullStringValue.Append (v);
262 fullStringValue.Append ('\0');
264 fullStringValue.Append ('\0');
266 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
268 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
269 } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
270 type == typeof (string)){
271 string rawValue = String.Format ("{0}{1}", value, '\0');
272 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
273 rawValue.Length * NativeBytesPerCharacter);
275 } else if (type.IsArray) {
276 throw new ArgumentException ("Only string and byte arrays can written as registry values");
278 throw new ArgumentException ("Type does not match the valueKind");
281 // handle the result codes
282 if (result != Win32ResultCode.Success)
284 GenerateException (result);
288 public void SetValue (RegistryKey rkey, string name, object value)
290 Type type = value.GetType ();
292 IntPtr handle = GetHandle (rkey);
294 if (type == typeof (int)) {
295 int rawValue = (int)value;
296 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
297 } else if (type == typeof (byte[])) {
298 byte[] rawValue = (byte[]) value;
299 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
300 } else if (type == typeof (string[])) {
301 string[] vals = (string[]) value;
302 StringBuilder fullStringValue = new StringBuilder ();
303 foreach (string v in vals)
305 fullStringValue.Append (v);
306 fullStringValue.Append ('\0');
308 fullStringValue.Append ('\0');
310 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
312 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
313 } else if (type.IsArray) {
314 throw new ArgumentException ("Only string and byte arrays can written as registry values");
316 string rawValue = String.Format ("{0}{1}", value, '\0');
317 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
318 rawValue.Length * NativeBytesPerCharacter);
321 if (result == Win32ResultCode.MarkedForDeletion)
322 throw RegistryKey.CreateMarkedForDeletionException ();
324 // handle the result codes
325 if (result != Win32ResultCode.Success)
327 GenerateException (result);
332 /// Get a binary value.
334 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
336 byte[] internalData = new byte [size];
337 IntPtr handle = GetHandle (rkey);
338 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
344 // Arbitrary max size for key/values names that can be fetched.
345 // .NET framework SDK docs say that the max name length that can
346 // be used is 255 characters, we'll allow for a bit more.
347 const int BufferMaxLength = 1024;
349 public int SubKeyCount (RegistryKey rkey)
352 StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
353 IntPtr handle = GetHandle (rkey);
355 for (index = 0; true; index ++) {
356 int result = RegEnumKey (handle, index, stringBuffer,
357 stringBuffer.Capacity);
359 if (result == Win32ResultCode.MarkedForDeletion)
360 throw RegistryKey.CreateMarkedForDeletionException ();
362 if (result == Win32ResultCode.Success)
365 if (result == Win32ResultCode.NoMoreEntries)
368 // something is wrong!!
369 GenerateException (result);
374 public int ValueCount (RegistryKey rkey)
376 int index, result, bufferCapacity;
377 RegistryValueKind type;
378 StringBuilder buffer = new StringBuilder (BufferMaxLength);
380 IntPtr handle = GetHandle (rkey);
381 for (index = 0; true; index ++) {
383 bufferCapacity = buffer.Capacity;
384 result = RegEnumValue (handle, index,
385 buffer, ref bufferCapacity,
386 IntPtr.Zero, ref type,
387 IntPtr.Zero, IntPtr.Zero);
389 if (result == Win32ResultCode.MarkedForDeletion)
390 throw RegistryKey.CreateMarkedForDeletionException ();
392 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
395 if (result == Win32ResultCode.NoMoreEntries)
398 // something is wrong
399 GenerateException (result);
404 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
406 IntPtr handle = new IntPtr ((int) hKey);
409 int result = RegConnectRegistry (machineName, handle, out keyHandle);
410 if (result != Win32ResultCode.Success)
411 GenerateException (result);
413 return new RegistryKey (hKey, keyHandle, true);
416 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
418 int access = OpenRegKeyRead;
419 if (writable) access |= OpenRegKeyWrite;
420 IntPtr handle = GetHandle (rkey);
423 int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
425 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
428 if (result != Win32ResultCode.Success)
429 GenerateException (result);
431 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
434 public void Flush (RegistryKey rkey)
436 if (!IsHandleValid (rkey))
438 IntPtr handle = GetHandle (rkey);
439 RegFlushKey (handle);
442 public void Close (RegistryKey rkey)
444 if (!IsHandleValid (rkey))
447 SafeRegistryHandle safe_handle = rkey.Handle;
448 if (safe_handle != null) {
449 // closes the unmanaged pointer for us.
450 safe_handle.Close ();
454 IntPtr handle = GetHandle (rkey);
455 RegCloseKey (handle);
459 public RegistryKey FromHandle (SafeRegistryHandle handle)
461 // At this point we can't tell whether the key is writable
462 // or not (nor the name), so we let the error check code handle it later, as
464 return new RegistryKey (handle.DangerousGetHandle (), String.Empty, true);
468 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
470 IntPtr handle = GetHandle (rkey);
473 int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
474 RegOptionsNonVolatile,
475 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
477 if (result == Win32ResultCode.MarkedForDeletion)
478 throw RegistryKey.CreateMarkedForDeletionException ();
480 if (result != Win32ResultCode.Success) {
481 GenerateException (result);
484 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
489 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName, RegistryOptions options)
491 IntPtr handle = GetHandle (rkey);
494 int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
495 options == RegistryOptions.Volatile ? RegOptionsVolatile : RegOptionsNonVolatile,
496 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
498 if (result == Win32ResultCode.MarkedForDeletion)
499 throw RegistryKey.CreateMarkedForDeletionException ();
501 if (result != Win32ResultCode.Success)
502 GenerateException (result);
504 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
509 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
511 IntPtr handle = GetHandle (rkey);
512 int result = RegDeleteKey (handle, keyName);
514 if (result == Win32ResultCode.FileNotFound) {
515 if (shouldThrowWhenKeyMissing)
516 throw new ArgumentException ("key " + keyName);
520 if (result != Win32ResultCode.Success)
521 GenerateException (result);
524 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
526 IntPtr handle = GetHandle (rkey);
527 int result = RegDeleteValue (handle, value);
529 if (result == Win32ResultCode.MarkedForDeletion)
532 if (result == Win32ResultCode.FileNotFound){
533 if (shouldThrowWhenKeyMissing)
534 throw new ArgumentException ("value " + value);
538 if (result != Win32ResultCode.Success)
539 GenerateException (result);
542 public string [] GetSubKeyNames (RegistryKey rkey)
544 IntPtr handle = GetHandle (rkey);
545 StringBuilder buffer = new StringBuilder (BufferMaxLength);
546 var keys = new List<string> ();
548 for (int index = 0; true; index ++) {
549 int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
551 if (result == Win32ResultCode.Success) {
552 keys.Add (buffer.ToString ());
557 if (result == Win32ResultCode.NoMoreEntries)
560 // should not be here!
561 GenerateException (result);
563 return keys.ToArray ();
567 public string [] GetValueNames (RegistryKey rkey)
569 IntPtr handle = GetHandle (rkey);
570 var values = new List<string> ();
572 for (int index = 0; true; index ++)
574 StringBuilder buffer = new StringBuilder (BufferMaxLength);
575 int bufferCapacity = buffer.Capacity;
576 RegistryValueKind type = 0;
578 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
579 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
581 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
582 values.Add (buffer.ToString ());
586 if (result == Win32ResultCode.NoMoreEntries)
589 if (result == Win32ResultCode.MarkedForDeletion)
590 throw RegistryKey.CreateMarkedForDeletionException ();
592 GenerateException (result);
595 return values.ToArray ();
599 /// convert a win32 error code into an appropriate exception.
601 private void GenerateException (int errorCode)
604 case Win32ResultCode.FileNotFound:
605 case Win32ResultCode.InvalidParameter:
606 throw new ArgumentException ();
607 case Win32ResultCode.AccessDenied:
608 throw new SecurityException ();
609 case Win32ResultCode.NetworkPathNotFound:
610 throw new IOException ("The network path was not found.");
611 case Win32ResultCode.InvalidHandle:
612 throw new IOException ("Invalid handle.");
614 // unidentified system exception
615 throw new SystemException ();
619 public string ToString (RegistryKey rkey)
625 /// utility: Combine the sub key name to the current name to produce a
626 /// fully qualified sub key name.
628 internal static string CombineName (RegistryKey rkey, string localName)
630 return String.Concat (rkey.Name, "\\", localName);