2 // RegistryKey.cs: a single node in the Windows registry
5 // Miguel de Icaza (miguel@ximian.com)
6 // Erik LeBel (eriklebel@yahoo.ca)
7 // Gert Driesen (drieseng@users.sourceforge.net)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Runtime.InteropServices;
37 using System.Security;
39 using System.Security.AccessControl;
40 using System.Security.Permissions;
41 using Microsoft.Win32.SafeHandles;
43 namespace Microsoft.Win32
47 public sealed class RegistryKey : IDisposable
49 internal RegistryKey (RegistryHive hiveId)
51 throw new PlatformNotSupportedException ();
54 public void Dispose ()
58 public RegistryKey CreateSubKey (string subkey)
60 throw new PlatformNotSupportedException ();
63 public object GetValue (string name, object defaultValue)
65 throw new PlatformNotSupportedException ();
68 public static object GetValue (string keyName, string valueName, object defaultValue)
70 throw new PlatformNotSupportedException ();
73 public RegistryKey OpenSubKey (string name, bool writable)
75 throw new PlatformNotSupportedException ();
78 public void SetValue (string name, object value)
82 public void SetValue (string name, object value, RegistryValueKind valueKind)
86 // TODO: Finish full contract API
90 /// Wrapper class for Windows Registry Entry.
93 public sealed class RegistryKey : MarshalByRefObject, IDisposable
96 // This represents the backend data, used when creating the
100 SafeRegistryHandle safe_handle;
102 object hive; // the RegistryHive if the key represents a base key
103 readonly string qname; // the fully qualified registry key name
104 readonly bool isRemoteRoot; // is an instance of a remote root key?
105 readonly bool isWritable; // is the key openen in writable mode
107 static readonly IRegistryApi RegistryApi;
109 static RegistryKey ()
111 if (Path.DirectorySeparatorChar == '\\')
112 RegistryApi = new Win32RegistryApi ();
114 RegistryApi = new UnixRegistryApi ();
118 /// Construct an instance of a root registry key entry.
120 internal RegistryKey (RegistryHive hiveId) : this (hiveId,
121 new IntPtr ((int) hiveId), false)
126 /// Construct an instance of a root registry key entry.
128 internal RegistryKey (RegistryHive hiveId, IntPtr keyHandle, bool remoteRoot)
132 qname = GetHiveName (hiveId);
133 isRemoteRoot = remoteRoot;
134 isWritable = true; // always consider root writable
138 /// Construct an instance of a registry key entry.
140 internal RegistryKey (object data, string keyName, bool writable)
144 isWritable = writable;
147 static internal bool IsEquals (RegistryKey a, RegistryKey b)
149 return a.hive == b.hive && a.handle == b.handle && a.qname == b.qname && a.isRemoteRoot == b.isRemoteRoot && a.isWritable == b.isWritable;
155 /// Dispose of registry key object. Close the
156 /// key if it's still open.
158 public void Dispose ()
160 GC.SuppressFinalize (this);
165 /// Get the fully qualified registry key name.
168 get { return qname; }
173 /// Flush the current registry state to disk.
177 RegistryApi.Flush (this);
182 /// Close the current registry key and flushes the state of the registry
189 // a handle to a remote hive must be closed, while one to a local
190 // hive should not be closed
191 if (!isRemoteRoot && IsRoot)
194 RegistryApi.Close (this);
201 /// get the number of sub-keys for this key
203 public int SubKeyCount {
205 AssertKeyStillValid ();
207 return RegistryApi.SubKeyCount (this);
213 /// get the number of values for this key
215 public int ValueCount {
217 AssertKeyStillValid ();
219 return RegistryApi.ValueCount (this);
224 [MonoTODO ("Not implemented in Unix")]
225 public SafeRegistryHandle Handle {
227 AssertKeyStillValid ();
229 if (safe_handle == null) {
230 IntPtr h = RegistryApi.GetHandle (this);
231 safe_handle = new SafeRegistryHandle (h, true);
239 [MonoLimitation ("View is ignored in Mono.")]
240 public RegistryView View {
242 return RegistryView.Default;
248 /// Set a registry value.
250 public void SetValue (string name, object value)
252 AssertKeyStillValid ();
255 throw new ArgumentNullException ("value");
258 AssertKeyNameLength (name);
261 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
263 RegistryApi.SetValue (this, name, value);
267 public void SetValue (string name, object value, RegistryValueKind valueKind)
269 AssertKeyStillValid ();
272 throw new ArgumentNullException ("value");
275 AssertKeyNameLength (name);
278 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
280 RegistryApi.SetValue (this, name, value, valueKind);
284 /// Open the sub key specified, for read access.
286 public RegistryKey OpenSubKey (string name)
288 return OpenSubKey (name, false);
293 /// Open the sub key specified.
295 public RegistryKey OpenSubKey (string name, bool writable)
297 AssertKeyStillValid ();
300 throw new ArgumentNullException ("name");
302 AssertKeyNameLength (name);
304 return RegistryApi.OpenSubKey (this, name, writable);
309 /// Get a registry value.
311 public object GetValue (string name)
313 return GetValue (name, null);
318 /// Get a registry value.
320 public object GetValue (string name, object defaultValue)
322 AssertKeyStillValid ();
324 return RegistryApi.GetValue (this, name, defaultValue,
325 RegistryValueOptions.None);
329 public object GetValue (string name, object defaultValue, RegistryValueOptions options)
331 AssertKeyStillValid ();
333 return RegistryApi.GetValue (this, name, defaultValue, options);
337 public RegistryValueKind GetValueKind (string name)
339 return RegistryApi.GetValueKind (this, name);
343 /// Create a sub key.
345 public RegistryKey CreateSubKey (string subkey)
347 AssertKeyStillValid ();
348 AssertKeyNameNotNull (subkey);
349 AssertKeyNameLength (subkey);
352 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
353 return RegistryApi.CreateSubKey (this, subkey);
357 [MonoLimitation ("permissionCheck is ignored in Mono")]
358 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck)
360 return CreateSubKey (subkey);
364 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
365 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)
367 return CreateSubKey (subkey);
371 [MonoLimitation ("permissionCheck is ignored in Mono")]
372 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
374 AssertKeyStillValid ();
375 AssertKeyNameNotNull (subkey);
376 AssertKeyNameLength (subkey);
379 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
381 return RegistryApi.CreateSubKey (this, subkey, options);
385 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
386 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions registryOptions,
387 RegistrySecurity registrySecurity)
389 return CreateSubKey (subkey, permissionCheck, registryOptions);
394 /// Delete the specified subkey.
396 public void DeleteSubKey(string subkey)
398 DeleteSubKey (subkey, true);
403 /// Delete the specified subkey.
405 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
407 AssertKeyStillValid ();
408 AssertKeyNameNotNull (subkey);
409 AssertKeyNameLength (subkey);
412 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
414 RegistryKey child = OpenSubKey (subkey);
417 if (throwOnMissingSubKey)
418 throw new ArgumentException ("Cannot delete a subkey tree"
419 + " because the subkey does not exist.");
423 if (child.SubKeyCount > 0){
424 throw new InvalidOperationException ("Registry key has subkeys"
425 + " and recursive removes are not supported by this method.");
430 RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
435 /// Delete a sub tree (node, and values alike).
437 public void DeleteSubKeyTree(string subkey)
439 DeleteSubKeyTree (subkey, true);
443 void DeleteSubKeyTree (string subkey, bool throwOnMissingSubKey)
445 // Note: this is done by deleting sub-nodes recursively.
446 // The preformance is not very good. There may be a
447 // better way to implement this.
449 AssertKeyStillValid ();
450 AssertKeyNameNotNull (subkey);
451 AssertKeyNameLength (subkey);
453 RegistryKey child = OpenSubKey (subkey, true);
455 if (!throwOnMissingSubKey)
458 throw new ArgumentException ("Cannot delete a subkey tree"
459 + " because the subkey does not exist.");
462 child.DeleteChildKeysAndValues ();
464 DeleteSubKey (subkey, false);
469 /// Delete a value from the registry.
471 public void DeleteValue(string name)
473 DeleteValue (name, true);
478 /// Delete a value from the registry.
480 public void DeleteValue(string name, bool throwOnMissingValue)
482 AssertKeyStillValid ();
485 throw new ArgumentNullException ("name");
488 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
490 RegistryApi.DeleteValue (this, name, throwOnMissingValue);
493 public RegistrySecurity GetAccessControl ()
495 return GetAccessControl (AccessControlSections.Owner |
496 AccessControlSections.Group |
497 AccessControlSections.Access);
500 public RegistrySecurity GetAccessControl (AccessControlSections includeSections)
502 return new RegistrySecurity (Name, includeSections);
507 /// Get the names of the sub keys.
509 public string[] GetSubKeyNames()
511 AssertKeyStillValid ();
513 return RegistryApi.GetSubKeyNames (this);
518 /// Get the names of values contained in this key.
520 public string[] GetValueNames()
522 AssertKeyStillValid ();
523 return RegistryApi.GetValueNames (this);
527 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
528 [MonoTODO ("Not implemented on unix")]
529 public static RegistryKey FromHandle (SafeRegistryHandle handle)
532 throw new ArgumentNullException ("handle");
534 return RegistryApi.FromHandle (handle);
538 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
539 [MonoTODO ("Not implemented on unix")]
540 public static RegistryKey FromHandle (SafeRegistryHandle handle, RegistryView view)
542 return FromHandle (handle);
546 [MonoTODO ("Not implemented on unix")]
547 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
549 if (machineName == null)
550 throw new ArgumentNullException ("machineName");
551 return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
555 [MonoTODO ("Not implemented on unix")]
556 public static RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName, RegistryView view)
558 if (machineName == null)
559 throw new ArgumentNullException ("machineName");
560 return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
564 [MonoLimitation ("View is ignored in Mono")]
565 public static RegistryKey OpenBaseKey (RegistryHive hKey, RegistryView view)
568 case RegistryHive.ClassesRoot:
569 return Registry.ClassesRoot;
570 case RegistryHive.CurrentConfig:
571 return Registry.CurrentConfig;
572 case RegistryHive.CurrentUser:
573 return Registry.CurrentUser;
574 case RegistryHive.DynData:
575 return Registry.DynData;
576 case RegistryHive.LocalMachine:
577 return Registry.LocalMachine;
578 case RegistryHive.PerformanceData:
579 return Registry.PerformanceData;
580 case RegistryHive.Users:
581 return Registry.Users;
584 throw new ArgumentException ("hKey");
588 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck)
590 return OpenSubKey (name, permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree);
594 [MonoLimitation ("rights are ignored in Mono")]
595 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights)
597 return OpenSubKey (name, permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree);
600 public void SetAccessControl (RegistrySecurity registrySecurity)
602 if (null == registrySecurity)
603 throw new ArgumentNullException ("registrySecurity");
605 registrySecurity.PersistModifications (Name);
610 /// Build a string representation of the registry key.
611 /// Conatins the fully qualified key name, and the Hex
612 /// representation of the registry key handle.
614 public override string ToString()
616 AssertKeyStillValid ();
618 return RegistryApi.ToString (this);
621 #endregion // PublicAPI
623 internal bool IsRoot {
624 get { return hive != null; }
627 private bool IsWritable {
628 get { return isWritable; }
631 internal RegistryHive Hive {
634 throw new NotSupportedException ();
635 return (RegistryHive) hive;
639 // returns the key handle for the win32 implementation and the
640 // KeyHandler for the unix implementation
641 internal object InternalHandle {
642 get { return handle; }
646 /// validate that the registry key handle is still usable.
648 private void AssertKeyStillValid ()
651 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
656 /// validate that the registry key handle is still usable, and
657 /// that the 'subKeyName' is not null.
659 private void AssertKeyNameNotNull (string subKeyName)
661 if (subKeyName == null)
662 throw new ArgumentNullException ("name");
665 private void AssertKeyNameLength (string name)
667 if (name.Length > 255)
668 throw new ArgumentException ("Name of registry key cannot be greater than 255 characters");
672 /// Utility method to delelte a key's sub keys and values.
673 /// This method removes a level of indirection when deleting
676 private void DeleteChildKeysAndValues ()
681 string[] subKeys = GetSubKeyNames ();
682 foreach (string subKey in subKeys)
684 RegistryKey sub = OpenSubKey (subKey, true);
685 sub.DeleteChildKeysAndValues ();
687 DeleteSubKey (subKey, false);
690 string[] values = GetValueNames ();
691 foreach (string value in values) {
692 DeleteValue (value, false);
697 /// decode a byte array as a string, and strip trailing nulls
699 static internal string DecodeString (byte[] data)
701 string stringRep = Encoding.Unicode.GetString (data);
702 int idx = stringRep.IndexOf ('\0');
704 stringRep = stringRep.TrimEnd ('\0');
708 static internal IOException CreateMarkedForDeletionException ()
710 throw new IOException ("Illegal operation attempted on a"
711 + " registry key that has been marked for deletion.");
714 static string GetHiveName (RegistryHive hive)
717 case RegistryHive.ClassesRoot:
718 return "HKEY_CLASSES_ROOT";
719 case RegistryHive.CurrentConfig:
720 return "HKEY_CURRENT_CONFIG";
721 case RegistryHive.CurrentUser:
722 return "HKEY_CURRENT_USER";
723 case RegistryHive.DynData:
724 return "HKEY_DYN_DATA";
725 case RegistryHive.LocalMachine:
726 return "HKEY_LOCAL_MACHINE";
727 case RegistryHive.PerformanceData:
728 return "HKEY_PERFORMANCE_DATA";
729 case RegistryHive.Users:
733 throw new NotImplementedException (string.Format (
734 "Registry hive '{0}' is not implemented.", hive.ToString ()));