In Test/System.Windows.Forms:
[mono-project.git] / mcs / class / corlib / Microsoft.Win32 / Win32RegistryApi.cs
blob1849a46f4e1e4a2c6d5fc81d6a5e6488e203f346
1 //
2 // Microsoft.Win32/Win32RegistryApi.cs: wrapper for win32 registry API
3 //
4 // Authos:
5 // Erik LeBel (eriklebel@yahoo.ca)
6 // Jackson Harper (jackson@ximian.com)
7 // Miguel de Icaza (miguel@gnome.org)
8 //
9 // Copyright (C) Erik LeBel 2004
10 // (C) 2004, 2005 Novell, Inc (http://www.novell.com)
11 //
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:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
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.
36 using System;
37 using System.Collections;
38 using System.IO;
39 using System.Runtime.InteropServices;
40 using System.Security;
41 using System.Text;
43 namespace Microsoft.Win32
45 /// <summary>
46 /// Function stubs, constants and helper functions for
47 /// the Win32 registry manipulation utilities.
48 /// </summary>
49 internal class Win32RegistryApi : IRegistryApi
51 // bit masks for registry key open access permissions
52 const int OpenRegKeyRead = 0x00020019;
53 const int OpenRegKeyWrite = 0x00020006;
55 // FIXME must be a way to determin this dynamically?
56 const int Int32ByteSize = 4;
58 // FIXME this is hard coded on Mono, can it be determined dynamically?
59 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
61 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
62 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
64 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
65 static extern int RegCloseKey (IntPtr keyHandle);
67 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
68 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
69 out IntPtr keyHandle);
71 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
72 private static extern int RegFlushKey (IntPtr keyHandle);
74 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
75 private static extern int RegOpenKeyEx (IntPtr keyBase,
76 string keyName, IntPtr reserved, int access,
77 out IntPtr keyHandle);
79 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
80 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
82 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
83 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
85 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
86 private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
88 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
89 private static extern int RegEnumValue (IntPtr keyBase,
90 int index, StringBuilder nameBuffer,
91 ref int nameLength, IntPtr reserved,
92 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
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 StringBuilder 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 string data, 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 byte[] rawData, 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 ref int data, int rawDataLength);
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 IntPtr zero, 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 [Out] byte[] data, ref int dataSize);
124 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
125 private static extern int RegQueryValueEx (IntPtr keyBase,
126 string valueName, IntPtr reserved, ref RegistryValueKind type,
127 ref int data, ref int dataSize);
129 // Returns our handle from the RegistryKey
130 static IntPtr GetHandle (RegistryKey key)
132 return (IntPtr) key.Handle;
135 static bool IsHandleValid (RegistryKey key)
137 return key.Handle != null;
140 /// <summary>
141 /// Acctually read a registry value. Requires knowledge of the
142 /// value's type and size.
143 /// </summary>
144 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
146 RegistryValueKind type = 0;
147 int size = 0;
148 object obj = null;
149 IntPtr handle = GetHandle (rkey);
150 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
152 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
153 return defaultValue;
156 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
157 GenerateException (result);
160 if (type == RegistryValueKind.String) {
161 byte[] data;
162 result = GetBinaryValue (rkey, name, type, out data, size);
163 obj = RegistryKey.DecodeString (data);
164 } else if (type == RegistryValueKind.ExpandString) {
165 byte [] data;
166 result = GetBinaryValue (rkey, name, type, out data, size);
167 obj = RegistryKey.DecodeString (data);
168 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
169 obj = Environment.ExpandEnvironmentVariables ((string) obj);
170 } else if (type == RegistryValueKind.DWord) {
171 int data = 0;
172 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
173 obj = data;
174 } else if (type == RegistryValueKind.Binary) {
175 byte[] data;
176 result = GetBinaryValue (rkey, name, type, out data, size);
177 obj = data;
178 } else if (type == RegistryValueKind.MultiString) {
179 obj = null;
180 byte[] data;
181 result = GetBinaryValue (rkey, name, type, out data, size);
183 if (result == Win32ResultCode.Success)
184 obj = RegistryKey.DecodeString (data).Split ('\0');
185 } else {
186 // should never get here
187 throw new SystemException ();
190 // check result codes again:
191 if (result != Win32ResultCode.Success)
193 GenerateException (result);
197 return obj;
200 #if NET_2_0
202 // This version has to do extra checking, make sure that the requested
203 // valueKind matches the type of the value being stored
205 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
207 Type type = value.GetType ();
208 int result;
209 IntPtr handle = GetHandle (rkey);
211 if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
212 int rawValue = (int)value;
213 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
214 } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
215 byte[] rawValue = (byte[]) value;
216 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
217 } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
218 string[] vals = (string[]) value;
219 StringBuilder fullStringValue = new StringBuilder ();
220 foreach (string v in vals)
222 fullStringValue.Append (v);
223 fullStringValue.Append ('\0');
225 fullStringValue.Append ('\0');
227 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
229 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
230 } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
231 type == typeof (string)){
232 string rawValue = String.Format ("{0}{1}", value, '\0');
233 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
234 rawValue.Length * NativeBytesPerCharacter);
236 } else if (type.IsArray) {
237 throw new ArgumentException ("Only string and byte arrays can written as registry values");
238 } else {
239 throw new ArgumentException ("Type does not match the valueKind");
242 // handle the result codes
243 if (result != Win32ResultCode.Success)
245 GenerateException (result);
248 #endif
250 public void SetValue (RegistryKey rkey, string name, object value)
252 Type type = value.GetType ();
253 int result;
254 IntPtr handle = GetHandle (rkey);
256 if (type == typeof (int)) {
257 int rawValue = (int)value;
258 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize);
259 } else if (type == typeof (byte[])) {
260 byte[] rawValue = (byte[]) value;
261 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
262 } else if (type == typeof (string[])) {
263 string[] vals = (string[]) value;
264 StringBuilder fullStringValue = new StringBuilder ();
265 foreach (string v in vals)
267 fullStringValue.Append (v);
268 fullStringValue.Append ('\0');
270 fullStringValue.Append ('\0');
272 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
274 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length);
275 } else if (type.IsArray) {
276 throw new ArgumentException ("Only string and byte arrays can written as registry values");
277 } else {
278 string rawValue = String.Format ("{0}{1}", value, '\0');
279 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
280 rawValue.Length * NativeBytesPerCharacter);
283 if (result == Win32ResultCode.MarkedForDeletion)
284 throw RegistryKey.CreateMarkedForDeletionException ();
286 // handle the result codes
287 if (result != Win32ResultCode.Success)
289 GenerateException (result);
293 /// <summary>
294 /// Get a binary value.
295 /// </summary>
296 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
298 byte[] internalData = new byte [size];
299 IntPtr handle = GetHandle (rkey);
300 int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
301 data = internalData;
302 return result;
306 // Arbitrary max size for key/values names that can be fetched.
307 // .NET framework SDK docs say that the max name length that can
308 // be used is 255 characters, we'll allow for a bit more.
309 const int BufferMaxLength = 1024;
311 public int SubKeyCount (RegistryKey rkey)
313 int index;
314 StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
315 IntPtr handle = GetHandle (rkey);
317 for (index = 0; true; index ++) {
318 int result = RegEnumKey (handle, index, stringBuffer,
319 stringBuffer.Capacity);
321 if (result == Win32ResultCode.MarkedForDeletion)
322 throw RegistryKey.CreateMarkedForDeletionException ();
324 if (result == Win32ResultCode.Success)
325 continue;
327 if (result == Win32ResultCode.NoMoreEntries)
328 break;
330 // something is wrong!!
331 GenerateException (result);
333 return index;
336 public int ValueCount (RegistryKey rkey)
338 int index, result, bufferCapacity;
339 RegistryValueKind type;
340 StringBuilder buffer = new StringBuilder (BufferMaxLength);
342 IntPtr handle = GetHandle (rkey);
343 for (index = 0; true; index ++) {
344 type = 0;
345 bufferCapacity = buffer.Capacity;
346 result = RegEnumValue (handle, index,
347 buffer, ref bufferCapacity,
348 IntPtr.Zero, ref type,
349 IntPtr.Zero, IntPtr.Zero);
351 if (result == Win32ResultCode.MarkedForDeletion)
352 throw RegistryKey.CreateMarkedForDeletionException ();
354 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
355 continue;
357 if (result == Win32ResultCode.NoMoreEntries)
358 break;
360 // something is wrong
361 GenerateException (result);
363 return index;
366 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
368 IntPtr handle = new IntPtr ((int) hKey);
370 IntPtr keyHandle;
371 int result = RegConnectRegistry (machineName, handle, out keyHandle);
372 if (result != Win32ResultCode.Success)
373 GenerateException (result);
375 return new RegistryKey (hKey, keyHandle, true);
378 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
380 int access = OpenRegKeyRead;
381 if (writable) access |= OpenRegKeyWrite;
382 IntPtr handle = GetHandle (rkey);
384 IntPtr subKeyHandle;
385 int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
387 if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
388 return null;
390 if (result != Win32ResultCode.Success)
391 GenerateException (result);
393 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
396 public void Flush (RegistryKey rkey)
398 if (!IsHandleValid (rkey))
399 return;
400 IntPtr handle = GetHandle (rkey);
401 RegFlushKey (handle);
404 public void Close (RegistryKey rkey)
406 if (!IsHandleValid (rkey))
407 return;
408 IntPtr handle = GetHandle (rkey);
409 RegCloseKey (handle);
412 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
414 IntPtr handle = GetHandle (rkey);
415 IntPtr subKeyHandle;
416 int result = RegCreateKey (handle , keyName, out subKeyHandle);
418 if (result == Win32ResultCode.MarkedForDeletion)
419 throw RegistryKey.CreateMarkedForDeletionException ();
421 if (result != Win32ResultCode.Success) {
422 GenerateException (result);
425 return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
426 true);
429 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
431 IntPtr handle = GetHandle (rkey);
432 int result = RegDeleteKey (handle, keyName);
434 if (result == Win32ResultCode.FileNotFound) {
435 if (shouldThrowWhenKeyMissing)
436 throw new ArgumentException ("key " + keyName);
437 return;
440 if (result != Win32ResultCode.Success)
441 GenerateException (result);
444 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
446 IntPtr handle = GetHandle (rkey);
447 int result = RegDeleteValue (handle, value);
449 if (result == Win32ResultCode.MarkedForDeletion)
450 return;
452 if (result == Win32ResultCode.FileNotFound){
453 if (shouldThrowWhenKeyMissing)
454 throw new ArgumentException ("value " + value);
455 return;
458 if (result != Win32ResultCode.Success)
459 GenerateException (result);
462 public string [] GetSubKeyNames (RegistryKey rkey)
464 IntPtr handle = GetHandle (rkey);
465 StringBuilder buffer = new StringBuilder (BufferMaxLength);
466 ArrayList keys = new ArrayList ();
468 for (int index = 0; true; index ++) {
469 int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
471 if (result == Win32ResultCode.Success) {
472 keys.Add (buffer.ToString ());
473 buffer.Length = 0;
474 continue;
477 if (result == Win32ResultCode.NoMoreEntries)
478 break;
480 // should not be here!
481 GenerateException (result);
483 return (string []) keys.ToArray (typeof(String));
487 public string [] GetValueNames (RegistryKey rkey)
489 IntPtr handle = GetHandle (rkey);
490 ArrayList values = new ArrayList ();
492 for (int index = 0; true; index ++)
494 StringBuilder buffer = new StringBuilder (BufferMaxLength);
495 int bufferCapacity = buffer.Capacity;
496 RegistryValueKind type = 0;
498 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
499 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
501 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
502 values.Add (buffer.ToString ());
503 continue;
506 if (result == Win32ResultCode.NoMoreEntries)
507 break;
509 if (result == Win32ResultCode.MarkedForDeletion)
510 throw RegistryKey.CreateMarkedForDeletionException ();
512 GenerateException (result);
515 return (string []) values.ToArray (typeof(String));
518 /// <summary>
519 /// convert a win32 error code into an appropriate exception.
520 /// </summary>
521 private void GenerateException (int errorCode)
523 switch (errorCode) {
524 case Win32ResultCode.FileNotFound:
525 case Win32ResultCode.InvalidParameter:
526 throw new ArgumentException ();
527 case Win32ResultCode.AccessDenied:
528 throw new SecurityException ();
529 case Win32ResultCode.NetworkPathNotFound:
530 throw new IOException ("The network path was not found.");
531 default:
532 // unidentified system exception
533 throw new SystemException ();
537 public string ToString (RegistryKey rkey)
539 IntPtr handle = GetHandle (rkey);
541 return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
544 /// <summary>
545 /// utility: Combine the sub key name to the current name to produce a
546 /// fully qualified sub key name.
547 /// </summary>
548 internal static string CombineName (RegistryKey rkey, string localName)
550 return String.Concat (rkey.Name, "\\", localName);