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.
37 using System.Collections;
38 using System.Runtime.InteropServices;
39 using System.Security;
42 namespace Microsoft.Win32
45 /// Function stubs, constants and helper functions for
46 /// the Win32 registry manipulation utilities.
48 internal class Win32RegistryApi : IRegistryApi
50 // bit masks for registry key open access permissions
51 const int OpenRegKeyRead = 0x00020019;
52 const int OpenRegKeyWrite = 0x00020006;
54 // FIXME must be a way to determin this dynamically?
55 const int Int32ByteSize = 4;
57 // FIXME this is hard coded on Mono, can it be determined dynamically?
58 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
60 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
61 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
63 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
64 static extern int RegCloseKey (IntPtr keyHandle);
66 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
67 private static extern int RegFlushKey (IntPtr keyHandle);
69 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
70 private static extern int RegOpenKeyEx (IntPtr keyBase,
71 string keyName, IntPtr reserved, int access,
72 out IntPtr keyHandle);
74 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
75 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
77 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
78 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
80 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
81 private static extern int RegEnumKey (IntPtr keyBase, int index, [Out] byte[] nameBuffer, int bufferLength);
83 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
84 private static extern int RegEnumValue (IntPtr keyBase,
85 int index, StringBuilder nameBuffer,
86 ref int nameLength, IntPtr reserved,
87 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
89 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
90 private static extern int RegSetValueEx (IntPtr keyBase,
91 string valueName, IntPtr reserved, RegistryValueKind type,
92 StringBuilder data, int rawDataLength);
94 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
95 private static extern int RegSetValueEx (IntPtr keyBase,
96 string valueName, IntPtr reserved, RegistryValueKind type,
97 string data, int rawDataLength);
99 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
100 private static extern int RegSetValueEx (IntPtr keyBase,
101 string valueName, IntPtr reserved, RegistryValueKind type,
102 byte[] rawData, int rawDataLength);
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 ref int data, int rawDataLength);
109 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
110 private static extern int RegQueryValueEx (IntPtr keyBase,
111 string valueName, IntPtr reserved, ref RegistryValueKind type,
112 IntPtr zero, ref int dataSize);
114 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
115 private static extern int RegQueryValueEx (IntPtr keyBase,
116 string valueName, IntPtr reserved, ref RegistryValueKind type,
117 [Out] byte[] data, ref int dataSize);
119 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
120 private static extern int RegQueryValueEx (IntPtr keyBase,
121 string valueName, IntPtr reserved, ref RegistryValueKind type,
122 ref int data, ref int dataSize);
124 // Returns our handle from the RegistryKey
125 static IntPtr GetHandle (RegistryKey key)
127 return key.IsRoot ? new IntPtr ((int) key.Data)
131 static bool IsHandleValid (RegistryKey key)
133 return key.Data != null;
137 /// Acctually read a registry value. Requires knowledge of the
138 /// value's type and size.
140 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
142 RegistryValueKind type = 0;
145 IntPtr handle = GetHandle (rkey);
146 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
148 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
152 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
153 GenerateException (result);
156 if (type == RegistryValueKind.String) {
158 result = GetBinaryValue (rkey, name, type, out data, size);
159 obj = RegistryKey.DecodeString (data);
160 } else if (type == RegistryValueKind.ExpandString) {
162 result = GetBinaryValue (rkey, name, type, out data, size);
163 obj = RegistryKey.DecodeString (data);
164 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
165 obj = Environment.ExpandEnvironmentVariables ((string) obj);
166 } else if (type == RegistryValueKind.DWord) {
168 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
170 } else if (type == RegistryValueKind.Binary) {
172 result = GetBinaryValue (rkey, name, type, out data, size);
174 } else if (type == RegistryValueKind.MultiString) {
177 result = GetBinaryValue (rkey, name, type, out data, size);
179 if (result == Win32ResultCode.Success)
180 obj = RegistryKey.DecodeString (data).Split ('\0');
182 // should never get here
183 throw new SystemException ();
186 // check result codes again:
187 if (result != Win32ResultCode.Success)
189 GenerateException (result);
198 // This version has to do extra checking, make sure that the requested
199 // valueKind matches the type of the value being stored
201 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
203 Type type = value.GetType ();
205 IntPtr handle = GetHandle (rkey);
207 if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
208 int rawValue = (int)value;
209 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
210 } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
211 byte[] rawValue = (byte[]) value;
212 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
213 } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
214 string[] vals = (string[]) value;
215 StringBuilder fullStringValue = new StringBuilder ();
216 foreach (string v in vals)
218 fullStringValue.Append (v);
219 fullStringValue.Append ('\0');
221 fullStringValue.Append ('\0');
223 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
225 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
226 } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
227 type == typeof (string)){
228 string rawValue = String.Format ("{0}{1}", value, '\0');
229 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
230 rawValue.Length * NativeBytesPerCharacter);
232 } else if (type.IsArray) {
233 throw new ArgumentException ("Only string and byte arrays can written as registry values");
235 throw new ArgumentException ("Type does not match the valueKind");
238 // handle the result codes
239 if (result != Win32ResultCode.Success)
241 GenerateException (result);
246 public void SetValue (RegistryKey rkey, string name, object value)
248 Type type = value.GetType ();
250 IntPtr handle = GetHandle (rkey);
252 if (type == typeof (int)) {
253 int rawValue = (int)value;
254 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
255 } else if (type == typeof (byte[])) {
256 byte[] rawValue = (byte[]) value;
257 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
258 } else if (type == typeof (string[])) {
259 string[] vals = (string[]) value;
260 StringBuilder fullStringValue = new StringBuilder ();
261 foreach (string v in vals)
263 fullStringValue.Append (v);
264 fullStringValue.Append ('\0');
266 fullStringValue.Append ('\0');
268 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
270 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
271 } else if (type.IsArray) {
272 throw new ArgumentException ("Only string and byte arrays can written as registry values");
274 string rawValue = String.Format ("{0}{1}", value, '\0');
275 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
276 rawValue.Length * NativeBytesPerCharacter);
279 if (result == Win32ResultCode.MarkedForDeletion)
280 throw RegistryKey.CreateMarkedForDeletionException ();
282 // handle the result codes
283 if (result != Win32ResultCode.Success)
285 GenerateException (result);
290 /// Get a binary value.
292 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
294 byte[] internalData = new byte [size];
295 IntPtr handle = GetHandle (rkey);
296 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
302 // Arbitrary max size for key/values names that can be fetched.
303 // .NET framework SDK docs say that the max name length that can
304 // be used is 255 characters, we'll allow for a bit more.
305 const int BufferMaxLength = 1024;
307 public int SubKeyCount (RegistryKey rkey)
310 byte[] stringBuffer = new byte [BufferMaxLength];
311 IntPtr handle = GetHandle (rkey);
313 for (index = 0; true; index ++) {
314 result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
316 if (result == Win32ResultCode.MarkedForDeletion)
317 throw RegistryKey.CreateMarkedForDeletionException ();
319 if (result == Win32ResultCode.Success)
322 if (result == Win32ResultCode.NoMoreEntries)
325 // something is wrong!!
326 GenerateException (result);
331 public int ValueCount (RegistryKey rkey)
333 int index, result, bufferCapacity;
334 RegistryValueKind type;
335 StringBuilder buffer = new StringBuilder (BufferMaxLength);
337 IntPtr handle = GetHandle (rkey);
338 for (index = 0; true; index ++) {
340 bufferCapacity = buffer.Capacity;
341 result = RegEnumValue (handle, index,
342 buffer, ref bufferCapacity,
343 IntPtr.Zero, ref type,
344 IntPtr.Zero, IntPtr.Zero);
346 if (result == Win32ResultCode.MarkedForDeletion)
347 throw RegistryKey.CreateMarkedForDeletionException ();
349 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
352 if (result == Win32ResultCode.NoMoreEntries)
355 // something is wrong
356 GenerateException (result);
361 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
363 int access = OpenRegKeyRead;
364 if (writable) access |= OpenRegKeyWrite;
365 IntPtr handle = GetHandle (rkey);
368 int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
370 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
373 if (result != Win32ResultCode.Success)
374 GenerateException (result);
376 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
379 public void Flush (RegistryKey rkey)
381 if (!IsHandleValid (rkey))
383 IntPtr handle = GetHandle (rkey);
384 RegFlushKey (handle);
387 public void Close (RegistryKey rkey)
389 if (!IsHandleValid (rkey))
391 IntPtr handle = GetHandle (rkey);
392 RegCloseKey (handle);
395 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
397 IntPtr handle = GetHandle (rkey);
399 int result = RegCreateKey (handle , keyName, out subKeyHandle);
401 if (result == Win32ResultCode.MarkedForDeletion)
402 throw RegistryKey.CreateMarkedForDeletionException ();
404 if (result != Win32ResultCode.Success) {
405 GenerateException (result);
408 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
412 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
414 IntPtr handle = GetHandle (rkey);
415 int result = RegDeleteKey (handle, keyName);
417 if (result == Win32ResultCode.FileNotFound) {
418 if (shouldThrowWhenKeyMissing)
419 throw new ArgumentException ("key " + keyName);
423 if (result != Win32ResultCode.Success)
424 GenerateException (result);
427 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
429 IntPtr handle = GetHandle (rkey);
430 int result = RegDeleteValue (handle, value);
432 if (result == Win32ResultCode.MarkedForDeletion)
435 if (result == Win32ResultCode.FileNotFound){
436 if (shouldThrowWhenKeyMissing)
437 throw new ArgumentException ("value " + value);
441 if (result != Win32ResultCode.Success)
442 GenerateException (result);
445 public string [] GetSubKeyNames (RegistryKey rkey)
447 IntPtr handle = GetHandle (rkey);
448 byte[] buffer = new byte [BufferMaxLength];
449 int bufferCapacity = BufferMaxLength;
450 ArrayList keys = new ArrayList ();
452 for (int index = 0; true; index ++) {
453 int result = RegEnumKey (handle, index, buffer, bufferCapacity);
455 if (result == Win32ResultCode.Success) {
456 keys.Add (RegistryKey.DecodeString (buffer));
460 if (result == Win32ResultCode.NoMoreEntries)
463 // should not be here!
464 GenerateException (result);
466 return (string []) keys.ToArray (typeof(String));
470 public string [] GetValueNames (RegistryKey rkey)
472 IntPtr handle = GetHandle (rkey);
473 ArrayList values = new ArrayList ();
475 for (int index = 0; true; index ++)
477 StringBuilder buffer = new StringBuilder (BufferMaxLength);
478 int bufferCapacity = buffer.Capacity;
479 RegistryValueKind type = 0;
481 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
482 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
484 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
485 values.Add (buffer.ToString ());
489 if (result == Win32ResultCode.NoMoreEntries)
492 if (result == Win32ResultCode.MarkedForDeletion)
493 throw RegistryKey.CreateMarkedForDeletionException ();
495 GenerateException (result);
498 return (string []) values.ToArray (typeof(String));
502 /// convert a win32 error code into an appropriate exception.
504 private void GenerateException (int errorCode)
507 case Win32ResultCode.FileNotFound:
508 case Win32ResultCode.InvalidParameter:
509 throw new ArgumentException ();
511 case Win32ResultCode.AccessDenied:
512 throw new SecurityException ();
515 // unidentified system exception
516 throw new SystemException ();
520 public string ToString (RegistryKey rkey)
522 IntPtr handle = GetHandle (rkey);
524 return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
528 /// utility: Combine the sub key name to the current name to produce a
529 /// fully qualified sub key name.
531 internal static string CombineName (RegistryKey rkey, string localName)
533 return String.Concat (rkey.Name, "\\", localName);