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 internal enum RegistryType {
62 EnvironmentString = 2,
68 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
69 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
71 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
72 static extern int RegCloseKey (IntPtr keyHandle);
74 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
75 private static extern int RegFlushKey (IntPtr keyHandle);
77 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
78 private static extern int RegOpenKeyEx (IntPtr keyBase,
79 string keyName, IntPtr reserved, int access,
80 out IntPtr keyHandle);
82 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
83 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
85 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
86 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
88 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
89 private static extern int RegEnumKey (IntPtr keyBase, int index, [Out] byte[] nameBuffer, int bufferLength);
91 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
92 private static extern int RegEnumValue (IntPtr keyBase,
93 int index, StringBuilder nameBuffer,
94 ref int nameLength, IntPtr reserved,
95 ref RegistryType type, IntPtr data, IntPtr dataLength);
97 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
98 private static extern int RegSetValueEx (IntPtr keyBase,
99 string valueName, IntPtr reserved, RegistryType type,
100 StringBuilder data, int rawDataLength);
102 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
103 private static extern int RegSetValueEx (IntPtr keyBase,
104 string valueName, IntPtr reserved, RegistryType type,
105 string 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, RegistryType type,
110 byte[] rawData, int rawDataLength);
112 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
113 private static extern int RegSetValueEx (IntPtr keyBase,
114 string valueName, IntPtr reserved, RegistryType type,
115 ref int data, int rawDataLength);
117 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
118 private static extern int RegQueryValueEx (IntPtr keyBase,
119 string valueName, IntPtr reserved, ref RegistryType type,
120 IntPtr zero, ref int dataSize);
122 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
123 private static extern int RegQueryValueEx (IntPtr keyBase,
124 string valueName, IntPtr reserved, ref RegistryType type,
125 [Out] byte[] data, 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 RegistryType type,
130 ref int data, ref int dataSize);
132 // Returns our handle from the RegistryKey
133 static IntPtr GetHandle (RegistryKey key)
135 return key.IsRoot ? new IntPtr ((int) key.Data)
140 /// Acctually read a registry value. Requires knoledge of the
141 /// value's type and size.
143 public object GetValue (RegistryKey rkey, string name, bool returnDefaultValue, object defaultValue)
145 RegistryType type = 0;
148 IntPtr handle = GetHandle (rkey);
149 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
151 if (result == Win32ResultCode.FileNotFound) {
152 if (returnDefaultValue) {
158 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
159 GenerateException (result);
162 if (type == RegistryType.String || type == RegistryType.EnvironmentString) {
164 result = GetBinaryValue (rkey, name, type, out data, size);
165 obj = RegistryKey.DecodeString (data);
166 } else if (type == RegistryType.Dword) {
168 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
170 } else if (type == RegistryType.Binary) {
172 result = GetBinaryValue (rkey, name, type, out data, size);
174 } else if (type == RegistryType.StringArray) {
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);
196 public void SetValue (RegistryKey rkey, string name, object value)
198 Type type = value.GetType ();
200 IntPtr handle = GetHandle (rkey);
202 if (type == typeof (int)) {
203 int rawValue = (int)value;
204 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Dword, ref rawValue, Int32ByteSize);
205 } else if (type == typeof (byte[])) {
206 byte[] rawValue = (byte[]) value;
207 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Binary, rawValue, rawValue.Length);
208 } else if (type == typeof (string[])) {
209 string[] vals = (string[]) value;
210 StringBuilder fullStringValue = new StringBuilder ();
211 foreach (string v in vals)
213 fullStringValue.Append (v);
214 fullStringValue.Append ('\0');
216 fullStringValue.Append ('\0');
218 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
220 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.StringArray, rawValue, rawValue.Length);
221 } else if (type.IsArray) {
222 throw new ArgumentException ("Only string and byte arrays can written as registry values");
224 string rawValue = String.Format ("{0}{1}", value, '\0');
225 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.String, rawValue,
226 rawValue.Length * NativeBytesPerCharacter);
229 // handle the result codes
230 if (result != Win32ResultCode.Success)
232 GenerateException (result);
237 /// Get a binary value.
239 private int GetBinaryValue (RegistryKey rkey, string name, RegistryType type, out byte[] data, int size)
241 byte[] internalData = new byte [size];
242 IntPtr handle = GetHandle (rkey);
243 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
249 // Arbitrary max size for key/values names that can be fetched.
250 // .NET framework SDK docs say that the max name length that can
251 // be used is 255 characters, we'll allow for a bit more.
252 const int BufferMaxLength = 1024;
254 public int SubKeyCount (RegistryKey rkey)
257 byte[] stringBuffer = new byte [BufferMaxLength];
258 IntPtr handle = GetHandle (rkey);
260 for (index = 0; true; index ++) {
261 result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
263 if (result == Win32ResultCode.Success)
266 if (result == Win32ResultCode.NoMoreEntries)
269 // something is wrong!!
270 GenerateException (result);
275 public int ValueCount (RegistryKey rkey)
277 int index, result, bufferCapacity;
279 StringBuilder buffer = new StringBuilder (BufferMaxLength);
281 IntPtr handle = GetHandle (rkey);
282 for (index = 0; true; index ++) {
284 bufferCapacity = buffer.Capacity;
285 result = RegEnumValue (handle, index,
286 buffer, ref bufferCapacity,
287 IntPtr.Zero, ref type,
288 IntPtr.Zero, IntPtr.Zero);
290 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
293 if (result == Win32ResultCode.NoMoreEntries)
296 // something is wrong
297 GenerateException (result);
302 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writtable)
304 int access = OpenRegKeyRead;
305 if (writtable) access |= OpenRegKeyWrite;
306 IntPtr handle = GetHandle (rkey);
309 int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
311 if (result == Win32ResultCode.FileNotFound)
314 if (result != Win32ResultCode.Success)
315 GenerateException (result);
317 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
320 public void Flush (RegistryKey rkey)
322 IntPtr handle = GetHandle (rkey);
323 RegFlushKey (handle);
326 public void Close (RegistryKey rkey)
328 IntPtr handle = GetHandle (rkey);
329 RegCloseKey (handle);
332 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
334 IntPtr handle = GetHandle (rkey);
336 int result = RegCreateKey (handle , keyName, out subKeyHandle);
338 if (result != Win32ResultCode.Success) {
339 GenerateException (result);
342 RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
347 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
349 IntPtr handle = GetHandle (rkey);
350 int result = RegDeleteKey (handle, keyName);
352 if (result == Win32ResultCode.FileNotFound) {
353 if (shouldThrowWhenKeyMissing)
354 throw new ArgumentException ("key " + keyName);
358 if (result != Win32ResultCode.Success)
359 GenerateException (result);
362 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
364 IntPtr handle = GetHandle (rkey);
365 int result = RegDeleteValue (handle, value);
367 if (result == Win32ResultCode.FileNotFound){
368 if (shouldThrowWhenKeyMissing)
369 throw new ArgumentException ("value " + value);
373 if (result != Win32ResultCode.Success)
374 GenerateException (result);
377 public string [] GetSubKeyNames (RegistryKey rkey)
379 IntPtr handle = GetHandle (rkey);
380 byte[] buffer = new byte [BufferMaxLength];
381 int bufferCapacity = BufferMaxLength;
382 ArrayList keys = new ArrayList ();
384 for (int index = 0; true; index ++) {
385 int result = RegEnumKey (handle, index, buffer, bufferCapacity);
387 if (result == Win32ResultCode.Success) {
388 keys.Add (RegistryKey.DecodeString (buffer));
392 if (result == Win32ResultCode.NoMoreEntries)
395 // should not be here!
396 GenerateException (result);
398 return (string []) keys.ToArray (typeof(String));
402 public string [] GetValueNames (RegistryKey rkey)
404 IntPtr handle = GetHandle (rkey);
405 ArrayList values = new ArrayList ();
407 for (int index = 0; true; index ++)
409 StringBuilder buffer = new StringBuilder (BufferMaxLength);
410 int bufferCapacity = buffer.Capacity;
411 RegistryType type = 0;
413 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
414 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
416 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
417 values.Add (buffer.ToString ());
421 if (result == Win32ResultCode.NoMoreEntries)
424 GenerateException (result);
427 return (string []) values.ToArray (typeof(String));
431 /// convert a win32 error code into an appropriate exception.
433 private void GenerateException (int errorCode)
436 case Win32ResultCode.FileNotFound:
437 case Win32ResultCode.InvalidParameter:
438 throw new ArgumentException ();
440 case Win32ResultCode.AccessDenied:
441 throw new SecurityException ();
444 // unidentified system exception
445 throw new SystemException ();
449 public string ToString (RegistryKey rkey)
451 IntPtr handle = GetHandle (rkey);
453 return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
457 /// utility: Combine the sub key name to the current name to produce a
458 /// fully qualified sub key name.
460 internal static string CombineName (RegistryKey rkey, string localName)
462 return String.Concat (rkey.Name, "\\", localName);