(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / class / corlib / Microsoft.Win32 / RegistryKey.cs
blob38bda255021b16920ef8684db560ebdeae06858b
1 //
2 // RegistryKey.cs: a single node in the Windows registry
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Erik LeBel (eriklebel@yahoo.ca)
7 //
9 //
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System;
33 using System.IO;
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Runtime.InteropServices;
37 using System.Security;
38 using System.Text;
40 namespace Microsoft.Win32
42 /// <summary>
43 /// Wrapper class for Windows Registry Entry.
44 /// </summary>
45 public sealed class RegistryKey : MarshalByRefObject, IDisposable
47 const char NullChar = '\0';
49 // Arbitrary max size for key/values names that can be fetched.
50 // .NET framework SDK docs say that the max name length that can
51 // be used is 255 characters, we'll allow for a bit more.
52 const int BufferMaxLength = 1024;
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 // FIXME this should be determined dynamically.
61 // It will be used to decode some return strings
62 // for which embeded '\0' must be preserved.
63 readonly Encoding Decoder = Encoding.Unicode;
66 IntPtr hkey; // the reg key handle
67 string qname; // the fully qualified registry key name
68 bool isRoot; // is the an instance of a root key?
70 IRegistryApi reg_api;
72 /// <summary>
73 /// Construct an instance of a root registry key entry.
74 /// </summary>
75 internal RegistryKey (RegistryHive hiveId, string keyName)
77 hkey = new IntPtr ((int)hiveId);
78 qname = keyName;
79 isRoot = true;
81 InitRegistryApi ();
84 /// <summary>
85 /// Construct an instance of a registry key entry.
86 /// </summary>
87 internal RegistryKey (IntPtr hkey, string keyName)
89 this.hkey = hkey;
90 qname = keyName;
91 isRoot = false;
93 InitRegistryApi ();
96 internal void InitRegistryApi ()
98 if (Path.DirectorySeparatorChar == '\\')
99 reg_api = new Win32RegistryApi ();
102 private IRegistryApi RegistryApi {
103 get {
104 if (reg_api == null)
105 throw new NotImplementedException ("The registry is" +
106 " only available on Windows.");
107 return reg_api;
111 /// <summary>
112 /// Fetch the inetrnal registry key.
113 /// </summary>
114 private IntPtr Handle {
115 get { return hkey; }
119 #region PublicAPI
121 /// <summary>
122 /// Dispose of registry key object. Close the
123 /// key if it's still open.
124 /// </summary>
125 void IDisposable.Dispose ()
127 Close ();
128 GC.SuppressFinalize (this);
132 /// <summary>
133 /// Final cleanup of registry key object. Close the
134 /// key if it's still open.
135 /// </summary>
136 ~RegistryKey ()
138 Close ();
142 /// <summary>
143 /// Get the fully qualified registry key name.
144 /// </summary>
145 public string Name {
146 get { return qname; }
150 /// <summary>
151 /// Flush the current registry state to disk.
152 /// </summary>
153 public void Flush()
155 RegTrace (" +Flush");
156 RegistryApi.RegFlushKey (Handle);
157 RegTrace (" -Flush");
161 /// <summary>
162 /// Close the current registry key. This may not
163 /// flush the state of the registry right away.
164 /// </summary>
165 public void Close()
167 if (isRoot)
168 return;
170 RegTrace (" +Close");
171 RegistryApi.RegCloseKey (Handle);
172 hkey = IntPtr.Zero;
173 RegTrace (" -Close");
177 /// <summary>
178 /// get the number of sub-keys for this key
179 /// </summary>
180 public int SubKeyCount {
181 get {
182 RegTrace (" +SubKeyCount");
183 AssertKeyStillValid ();
185 int index, result;
186 byte[] stringBuffer = new byte [BufferMaxLength];
188 for (index = 0; true; index ++)
190 result = RegistryApi.RegEnumKey (Handle, index,
191 stringBuffer, BufferMaxLength);
193 if (result == Win32ResultCode.Success)
194 continue;
196 if (result == Win32ResultCode.NoMoreEntries)
197 break;
199 // something is wrong!!
200 RegTrace ("Win32Api::ReEnumKey result='{0}' name='{1}'",
201 result, Name);
202 GenerateException (result);
205 RegTrace (" -SubKeyCount");
206 return index;
211 /// <summary>
212 /// get the number of values for this key
213 /// </summary>
214 public int ValueCount {
215 get {
216 RegTrace (" +ValueCount");
217 AssertKeyStillValid ();
219 int index, result, type, bufferCapacity;
220 StringBuilder buffer = new StringBuilder (BufferMaxLength);
222 for (index = 0; true; index ++)
224 type = 0;
225 bufferCapacity = buffer.Capacity;
226 result = RegistryApi.RegEnumValue (Handle, index,
227 buffer, ref bufferCapacity,
228 IntPtr.Zero, ref type,
229 IntPtr.Zero, IntPtr.Zero);
231 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
232 continue;
234 if (result == Win32ResultCode.NoMoreEntries)
235 break;
237 // something is wrong
238 RegTrace ("Win32Api::RegEnumValue result='{0}' name='{1}'",
239 result, Name);
240 GenerateException (result);
243 RegTrace (" -ValueCount");
244 return index;
249 /// <summary>
250 /// Set a registry value.
251 /// </summary>
252 public void SetValue (string name, object value)
254 RegTrace (" +SetValue");
255 AssertKeyStillValid ();
257 if (value == null)
258 throw new ArgumentNullException ();
260 Type type = value.GetType ();
261 int result;
263 if (type == typeof (int))
265 int rawValue = (int)value;
266 result = RegistryApi.RegSetValueEx (Handle, name,
267 IntPtr.Zero, RegistryApi.RegDwordType,
268 ref rawValue, Int32ByteSize);
270 else if (type == typeof (byte[]))
272 byte[] rawValue = (byte[]) value;
273 result = RegistryApi.RegSetValueEx (Handle, name,
274 IntPtr.Zero, RegistryApi.RegBinaryType,
275 rawValue, rawValue.Length);
277 else if (type == typeof (string[]))
279 string[] vals = (string[]) value;
280 StringBuilder fullStringValue = new StringBuilder ();
281 foreach (string v in vals)
283 fullStringValue.Append (v);
284 fullStringValue.Append (NullChar);
286 fullStringValue.Append (NullChar);
288 byte[] rawValue = Decoder.GetBytes (fullStringValue.ToString ());
290 result = RegistryApi.RegSetValueEx (Handle, name,
291 IntPtr.Zero, RegistryApi.RegStringArrayType,
292 rawValue, rawValue.Length);
294 else if (type.IsArray)
296 throw new ArgumentException ("Only string and byte arrays can written as registry values");
298 else
300 string rawValue = String.Format ("{0}{1}", value, NullChar);
301 result = RegistryApi.RegSetValueEx (Handle, name,
302 IntPtr.Zero, RegistryApi.RegStringType,
303 rawValue, rawValue.Length * NativeBytesPerCharacter);
306 // handle the result codes
307 if (result != Win32ResultCode.Success)
309 RegTrace ("Win32Api::RegSetValueEx: result: {0}", result);
310 GenerateException (result);
313 RegTrace (" -SetValue");
317 /// <summary>
318 /// Open the sub key specified, for read access.
319 /// </summary>
320 public RegistryKey OpenSubKey (string keyName)
322 return OpenSubKey (keyName, false);
326 /// <summary>
327 /// Open the sub key specified.
328 /// </summary>
329 public RegistryKey OpenSubKey (string keyName, bool writtable)
331 RegTrace (" +OpenSubKey");
332 AssertKeyStillValid ();
333 AssertKeyNameNotNull (keyName);
335 int access = RegistryApi.OpenRegKeyRead;
336 if (writtable) access |= RegistryApi.OpenRegKeyWrite;
338 IntPtr subKeyHandle;
339 int result = RegistryApi.RegOpenKeyEx (Handle, keyName, IntPtr.Zero,
340 access, out subKeyHandle);
342 if (result == Win32ResultCode.FileNotFound)
344 RegTrace (" -OpenSubKey");
345 return null;
348 if (result != Win32ResultCode.Success)
350 RegTrace ("Win32Api::RegOpenKeyEx result='{0}' key name='{1}'",
351 result, CombineName (keyName));
352 GenerateException (result);
355 RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (keyName));
356 RegTrace (" -OpenSubKey");
357 return subKey;
361 /// <summary>
362 /// Get a registry value.
363 /// </summary>
364 public object GetValue (string name)
366 RegTrace (" +GetValue");
367 object obj = GetValueImpl (name, false, null);
368 RegTrace (" -GetValue");
369 return obj;
373 /// <summary>
374 /// Get a registry value.
375 /// </summary>
376 public object GetValue (string name, object defaultValue)
378 RegTrace (" +GetValue");
379 object obj = GetValueImpl (name, true, defaultValue);
380 RegTrace (" -GetValue");
381 return obj;
385 /// <summary>
386 /// Create a sub key.
387 /// </summary>
388 public RegistryKey CreateSubKey (string keyName)
390 RegTrace (" +CreateSubKey");
391 AssertKeyStillValid ();
392 AssertKeyNameNotNull (keyName);
394 IntPtr subKeyHandle;
395 int result = RegistryApi.RegCreateKey (Handle , keyName, out subKeyHandle);
397 if (result != Win32ResultCode.Success)
399 RegTrace ("Win32Api::RegCreateKey: result='{0}' key name='{1}'",
400 result, CombineName (keyName));
401 GenerateException (result);
404 RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (keyName));
405 RegTrace (" -CreateSubKey");
406 return subKey;
410 /// <summary>
411 /// Delete the specified subkey.
412 /// </summary>
413 public void DeleteSubKey(string subkey)
415 DeleteSubKey (subkey, true);
419 /// <summary>
420 /// Delete the specified subkey.
421 /// </summary>
422 public void DeleteSubKey(string keyName, bool shouldThrowWhenKeyMissing)
424 RegTrace (" +DeleteSubKey");
425 AssertKeyStillValid ();
426 AssertKeyNameNotNull (keyName);
428 RegistryKey child = OpenSubKey (keyName);
430 if (child == null)
432 if (shouldThrowWhenKeyMissing)
433 throw new ArgumentException ("key " + keyName);
434 RegTrace (" -DeleteSubKey");
435 return;
438 if (child.SubKeyCount > 0)
439 throw new InvalidOperationException ("key " + keyName + " has sub keys");
441 child.Close ();
443 int result = RegistryApi.RegDeleteKey (Handle, keyName);
444 if (result == Win32ResultCode.FileNotFound)
446 if (shouldThrowWhenKeyMissing)
447 throw new ArgumentException ("key " + keyName);
448 RegTrace (" -DeleteSubKey");
449 return;
452 if (result != Win32ResultCode.Success)
454 RegTrace ("Win32Api::RegDeleteKey: result='{0}' key name='{1}'",
455 result, CombineName (keyName));
456 GenerateException (result);
459 RegTrace (" -DeleteSubKey");
463 /// <summary>
464 /// Delete a sub tree (node, and values alike).
465 /// </summary>
466 public void DeleteSubKeyTree(string keyName)
468 // Note: this is done by deleting sub-nodes recursively.
469 // The preformance is not very good. There may be a
470 // better way to implement this.
471 RegTrace (" +DeleteSubKeyTree");
472 AssertKeyStillValid ();
473 AssertKeyNameNotNull (keyName);
475 RegistryKey child = OpenSubKey (keyName, true);
476 if (child == null)
477 throw new ArgumentException ("key " + keyName);
479 child.DeleteChildKeysAndValues ();
480 child.Close ();
481 DeleteSubKey (keyName, false);
482 RegTrace (" -DeleteSubKeyTree");
486 /// <summary>
487 /// Delete a value from the registry.
488 /// </summary>
489 public void DeleteValue(string value)
491 DeleteValue (value, true);
495 /// <summary>
496 /// Delete a value from the registry.
497 /// </summary>
498 public void DeleteValue(string value, bool shouldThrowWhenKeyMissing)
500 RegTrace (" +DeleteValue");
501 AssertKeyStillValid ();
502 AssertKeyNameNotNull (value);
504 int result = RegistryApi.RegDeleteValue (Handle, value);
506 if (result == Win32ResultCode.FileNotFound)
508 if (shouldThrowWhenKeyMissing)
509 throw new ArgumentException ("value " + value);
510 RegTrace (" -DeleteValue");
511 return;
514 if (result != Win32ResultCode.Success)
516 RegTrace ("Win32Api::RegDeleteValue: result='{0}' value name='{1}'",
517 result, CombineName (value));
518 GenerateException (result);
521 RegTrace (" -DeleteValue");
525 /// <summary>
526 /// Get the names of the sub keys.
527 /// </summary>
528 public string[] GetSubKeyNames()
530 RegTrace (" +GetSubKeyNames");
531 AssertKeyStillValid ();
533 byte[] buffer = new byte [BufferMaxLength];
534 int bufferCapacity = BufferMaxLength;
535 ArrayList keys = new ArrayList ();
537 for (int index = 0; true; index ++)
539 int result = RegistryApi.RegEnumKey (Handle, index, buffer, bufferCapacity);
541 if (result == Win32ResultCode.Success)
543 keys.Add (DecodeString (buffer));
544 continue;
547 if (result == Win32ResultCode.NoMoreEntries)
548 break;
550 // should not be here!
551 RegTrace ("Win32Api::RegEnumKey: result='{0}' value name='{1}'",
552 result, CombineName (Name));
553 GenerateException (result);
556 RegTrace (" -GetSubKeyNames");
557 return (string []) keys.ToArray (typeof(String));
561 /// <summary>
562 /// Get the names of values contained in this key.
563 /// </summary>
564 public string[] GetValueNames()
566 RegTrace (" +GetValueNames");
567 AssertKeyStillValid ();
569 ArrayList values = new ArrayList ();
571 for (int index = 0; true; index ++)
573 StringBuilder buffer = new StringBuilder (BufferMaxLength);
574 int bufferCapacity = buffer.Capacity;
575 int type = 0;
577 int result = RegistryApi.RegEnumValue (Handle, index, buffer, ref bufferCapacity,
578 IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
580 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
582 values.Add (buffer.ToString ());
583 continue;
586 if (result == Win32ResultCode.NoMoreEntries)
587 break;
589 // should not be here!
590 RegTrace ("RegistryApi.RegEnumValue: result code='{0}' name='{1}'",
591 result, CombineName (Name));
592 GenerateException (result);
595 RegTrace (" -GetValueNames");
596 return (string []) values.ToArray (typeof(String));
600 [MonoTODO]
601 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
603 throw new NotImplementedException ();
607 /// <summary>
608 /// Build a string representation of the registry key.
609 /// Conatins the fully qualified key name, and the Hex
610 /// representation of the registry key handle.
611 /// </summary>
612 public override string ToString()
614 return String.Format ("{0} [0x{1:X}]", Name, Handle.ToInt32 ());
617 #endregion // PublicAPI
620 /// <summary>
621 /// validate that the registry key handle is still usable.
622 /// </summary>
623 private void AssertKeyStillValid ()
625 if (Handle == IntPtr.Zero)
626 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
630 /// <summary>
631 /// validate that the registry key handle is still usable, and
632 /// that the 'subKeyName' is not null.
633 /// </summary>
634 private void AssertKeyNameNotNull (string subKeyName)
636 if (subKeyName == null)
637 throw new ArgumentNullException ();
641 /// <summary>
642 /// Utility method to delelte a key's sub keys and values.
643 /// This method removes a level of indirection when deleting
644 /// key node trees.
645 /// </summary>
646 private void DeleteChildKeysAndValues ()
648 RegTrace (" +DeleteChildKeysAndValues");
649 if (isRoot)
651 RegTrace (" -DeleteChildKeysAndValues");
652 return;
655 string[] subKeys = GetSubKeyNames ();
656 foreach (string subKey in subKeys)
658 RegistryKey sub = OpenSubKey (subKey, true);
659 sub.DeleteChildKeysAndValues ();
660 sub.Close ();
661 DeleteSubKey (subKey, false);
664 string[] values = GetValueNames ();
665 foreach (string value in values)
667 DeleteValue (value, false);
670 RegTrace (" -DeleteChildKeysAndValues");
674 /// <summary>
675 /// Acctually read a registry value. Requires knoledge of the
676 /// value's type and size.
677 /// </summary>
678 private object GetValueImpl (string name, bool returnDefaultValue, object defaultValue)
680 RegTrace (" +GetValueImpl");
681 AssertKeyStillValid ();
683 int type = 0;
684 int size = 0;
685 object obj = null;
687 int result = RegistryApi.RegQueryValueEx (Handle, name, IntPtr.Zero,
688 ref type, IntPtr.Zero, ref size);
690 if (returnDefaultValue && result == Win32ResultCode.FileNotFound)
692 RegTrace (" -GetValueImpl");
693 return defaultValue;
696 if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success )
698 RegTrace ("Win32Api::RegQueryValueEx: result='{0}' name='{1}' type='{2}' size='{3}'",
699 result, name, type, size);
700 GenerateException (result);
703 if (type == RegistryApi.RegStringType || type == RegistryApi.RegEnvironmentString)
705 byte[] data;
706 result = GetBinaryValue (name, type, out data, size);
707 obj = DecodeString (data);
709 else if (type == RegistryApi.RegDwordType)
711 int data = 0;
712 result = RegistryApi.RegQueryValueEx (Handle, name, IntPtr.Zero,
713 ref type, ref data, ref size);
714 obj = data;
716 else if (type == RegistryApi.RegBinaryType)
718 byte[] data;
719 result = GetBinaryValue (name, type, out data, size);
720 obj = data;
722 else if (type == RegistryApi.RegStringArrayType)
724 obj = null;
725 byte[] data;
726 result = GetBinaryValue (name, type, out data, size);
728 if (result == Win32ResultCode.Success)
729 obj = DecodeString (data).Split (NullChar);
731 else
733 // should never get here
734 throw new SystemException ();
737 // check result codes again:
738 if (result != Win32ResultCode.Success)
740 RegTrace ("Win32Api::RegQueryValueEx: result='{0}' name='{1}'",
741 result, name);
742 GenerateException (result);
745 RegTrace (" -ReadValueImpl");
746 return obj;
750 /// <summary>
751 /// Get a binary value.
752 /// </summary>
753 private int GetBinaryValue (string name, int type, out byte[] data, int size)
755 byte[] internalData = new byte [size];
756 int result = RegistryApi.RegQueryValueEx (Handle, name,
757 IntPtr.Zero, ref type, internalData, ref size);
758 data = internalData;
759 return result;
763 /// <summary>
764 /// decode a byte array as a string, and strip trailing nulls
765 /// </summary>
766 private string DecodeString (byte[] data)
768 string stringRep = Decoder.GetString (data);
769 return stringRep.TrimEnd (NullChar);
773 /// <summary>
774 /// utility: Combine the sub key name to the current name to produce a
775 /// fully qualified sub key name.
776 /// </summary>
777 private string CombineName (string localName)
779 return String.Format ("{0}\\{1}", Name, localName);
783 /// <summary>
784 /// convert a win32 error code into an appropriate exception.
785 /// </summary>
786 private void GenerateException (int errorCode)
788 switch (errorCode) {
789 case Win32ResultCode.FileNotFound:
790 case Win32ResultCode.InvalidParameter:
791 throw new ArgumentException ();
793 case Win32ResultCode.AccessDenied:
794 throw new SecurityException ();
796 default:
797 // unidentified system exception
798 throw new SystemException ();
802 #if (false)
803 /// <summary>
804 /// dump trace messages if this code was compiled with tracing enabled.
805 /// </summary>
806 [Conditional("TRACE")]
807 private static void RegTrace (string message, params object[] args)
809 message = "REG " + message;
810 if (args.Length > 0)
811 message = String.Format (message, args);
813 Trace.WriteLine (message);
814 //Console.WriteLine (message);
816 #endif
817 private static void RegTrace (string message, params object[] args)