Merge pull request #439 from mono-soc-2012/garyb/iconfix
[mono.git] / mcs / class / corlib / Microsoft.Win32 / RegistryKey.cs
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 //   Gert Driesen (drieseng@users.sourceforge.net)
8 //
9 //
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 //
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.
30 //
31
32 #if !NET_2_1
33
34 using System;
35 using System.IO;
36 using System.Collections;
37 using System.Diagnostics;
38 using System.Runtime.InteropServices;
39 using System.Security;
40 using System.Text;
41 using System.Security.AccessControl;
42 using System.Security.Permissions;
43 using Microsoft.Win32.SafeHandles;
44
45 namespace Microsoft.Win32
46 {
47         /// <summary>
48         ///     Wrapper class for Windows Registry Entry.
49         /// </summary>
50         [ComVisible (true)]
51         public sealed class RegistryKey : MarshalByRefObject, IDisposable 
52         {
53                 //
54                 // This represents the backend data, used when creating the
55                 // RegistryKey object
56                 //
57                 object handle;
58 #if NET_4_0
59                 SafeRegistryHandle safe_handle;
60 #endif
61
62                 object hive; // the RegistryHive if the key represents a base key
63                 readonly string qname;  // the fully qualified registry key name
64                 readonly bool isRemoteRoot;     // is an instance of a remote root key?
65                 readonly bool isWritable;       // is the key openen in writable mode
66
67                 static readonly IRegistryApi RegistryApi;
68
69                 static RegistryKey ()
70                 {
71                         if (Path.DirectorySeparatorChar == '\\')
72                                 RegistryApi = new Win32RegistryApi ();
73                         else
74                                 RegistryApi = new UnixRegistryApi ();
75                 }
76
77                 /// <summary>
78                 ///     Construct an instance of a root registry key entry.
79                 /// </summary>
80                 internal RegistryKey (RegistryHive hiveId) : this (hiveId, 
81                         new IntPtr ((int) hiveId), false)
82                 {
83                 }
84
85                 /// <summary>
86                 ///     Construct an instance of a root registry key entry.
87                 /// </summary>
88                 internal RegistryKey (RegistryHive hiveId, IntPtr keyHandle, bool remoteRoot)
89                 {
90                         hive = hiveId;
91                         handle = keyHandle;
92                         qname = GetHiveName (hiveId);
93                         isRemoteRoot = remoteRoot;
94                         isWritable = true; // always consider root writable
95                 }
96
97                 /// <summary>
98                 ///     Construct an instance of a registry key entry.
99                 /// </summary>
100                 internal RegistryKey (object data, string keyName, bool writable)
101                 {
102                         handle = data;
103                         qname = keyName;
104                         isWritable = writable;
105                 }
106
107                 #region PublicAPI
108
109                 /// <summary>
110                 ///     Dispose of registry key object. Close the 
111                 ///     key if it's still open.
112                 /// </summary>
113 #if NET_4_0
114                 public void Dispose ()
115 #else
116                 void IDisposable.Dispose ()
117 #endif
118                 {
119                         GC.SuppressFinalize (this);
120                         Close ();
121                 }
122
123                 
124                 /// <summary>
125                 ///     Final cleanup of registry key object. Close the 
126                 ///     key if it's still open.
127                 /// </summary>
128                 ~RegistryKey ()
129                 {
130                         Close ();
131                 }
132
133                 
134                 /// <summary>
135                 ///     Get the fully qualified registry key name.
136                 /// </summary>
137                 public string Name {
138                         get { return qname; }
139                 }
140         
141                 
142                 /// <summary>
143                 ///     Flush the current registry state to disk.
144                 /// </summary>
145                 public void Flush()
146                 {
147                         RegistryApi.Flush (this);
148                 }
149                 
150                 
151                 /// <summary>
152                 ///     Close the current registry key and flushes the state of the registry
153                 /// right away.
154                 /// </summary>
155                 public void Close()
156                 {
157                         Flush ();
158
159                         // a handle to a remote hive must be closed, while one to a local
160                         // hive should not be closed
161                         if (!isRemoteRoot && IsRoot)
162                                 return;
163                         
164                         RegistryApi.Close (this);
165                         handle = null;
166 #if NET_4_0
167                         safe_handle = null;
168 #endif
169                 }
170                 
171                 
172                 /// <summary>
173                 ///     get the number of sub-keys for this key
174                 /// </summary>
175                 public int SubKeyCount {
176                         get {
177                                 AssertKeyStillValid ();
178
179                                 return RegistryApi.SubKeyCount (this);
180                         }
181                 }
182
183                 
184                 /// <summary>
185                 ///     get the number of values for this key
186                 /// </summary>
187                 public int ValueCount {
188                         get {
189                                 AssertKeyStillValid ();
190
191                                 return RegistryApi.ValueCount (this);
192                         }
193                 }
194
195 #if NET_4_0
196                 [ComVisible (false)]
197                 [MonoTODO ("Not implemented in Unix")]
198                 public SafeRegistryHandle Handle {
199                         get {
200                                 AssertKeyStillValid ();
201
202                                 if (safe_handle == null) {
203                                         IntPtr h = RegistryApi.GetHandle (this);
204                                         safe_handle = new SafeRegistryHandle (h, true);
205                                 }
206
207                                 return safe_handle;
208                         }
209                 }
210
211                 [ComVisible (false)]
212                 [MonoLimitation ("View is ignored in Mono.")]
213                 public RegistryView View {
214                         get {
215                                 return RegistryView.Default;
216                         }
217                 }
218 #endif
219
220                 
221                 /// <summary>
222                 ///     Set a registry value.
223                 /// </summary>
224                 public void SetValue (string name, object value)
225                 {
226                         AssertKeyStillValid ();
227
228                         if (value == null)
229                                 throw new ArgumentNullException ("value");
230
231                         if (name != null)
232                                 AssertKeyNameLength (name);
233
234                         if (!IsWritable)
235                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
236
237                         RegistryApi.SetValue (this, name, value);
238                 }
239
240                 [ComVisible (false)]
241                 public void SetValue (string name, object value, RegistryValueKind valueKind)
242                 {
243                         AssertKeyStillValid ();
244                         
245                         if (value == null)
246                                 throw new ArgumentNullException ("value");
247
248                         if (name != null)
249                                 AssertKeyNameLength (name);
250
251                         if (!IsWritable)
252                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
253
254                         RegistryApi.SetValue (this, name, value, valueKind);
255                 }
256
257                 /// <summary>
258                 ///     Open the sub key specified, for read access.
259                 /// </summary>
260                 public RegistryKey OpenSubKey (string name)
261                 {
262                         return OpenSubKey (name, false);
263                 }
264
265                 
266                 /// <summary>
267                 ///     Open the sub key specified.
268                 /// </summary>
269                 public RegistryKey OpenSubKey (string name, bool writable)
270                 {
271                         AssertKeyStillValid ();
272
273                         if (name == null)
274                                 throw new ArgumentNullException ("name");
275
276                         AssertKeyNameLength (name);
277
278                         return RegistryApi.OpenSubKey (this, name, writable);
279                 }
280                 
281                 
282                 /// <summary>
283                 ///     Get a registry value.
284                 /// </summary>
285                 public object GetValue (string name)
286                 {
287                         return GetValue (name, null);
288                 }
289
290                 
291                 /// <summary>
292                 ///     Get a registry value.
293                 /// </summary>
294                 public object GetValue (string name, object defaultValue)
295                 {
296                         AssertKeyStillValid ();
297                         
298                         return RegistryApi.GetValue (this, name, defaultValue,
299                                 RegistryValueOptions.None);
300                 }
301
302                 [ComVisible (false)]
303                 public object GetValue (string name, object defaultValue, RegistryValueOptions options)
304                 {
305                         AssertKeyStillValid ();
306
307                         return RegistryApi.GetValue (this, name, defaultValue, options);
308                 }
309
310                 [ComVisible (false)]
311                 public RegistryValueKind GetValueKind (string name)
312                 {
313                         return RegistryApi.GetValueKind (this, name);
314                 }
315
316                 /// <summary>
317                 ///     Create a sub key.
318                 /// </summary>
319                 public RegistryKey CreateSubKey (string subkey)
320                 {
321                         AssertKeyStillValid ();
322                         AssertKeyNameNotNull (subkey);
323                         AssertKeyNameLength (subkey);
324
325                         if (!IsWritable)
326                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
327                         return RegistryApi.CreateSubKey (this, subkey);
328                 }
329
330                 [ComVisible (false)]
331                 [MonoLimitation ("permissionCheck is ignored in Mono")]
332                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck)
333                 {
334                         return CreateSubKey (subkey);
335                 }
336
337                 [ComVisible (false)]
338                 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
339                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)
340                 {
341                         return CreateSubKey (subkey);
342                 }
343
344 #if NET_4_0
345                 [ComVisible (false)]
346                 [MonoLimitation ("permissionCheck is ignored in Mono")]
347                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
348                 {
349                         AssertKeyStillValid ();
350                         AssertKeyNameNotNull (subkey);
351                         AssertKeyNameLength (subkey);
352
353                         if (!IsWritable)
354                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
355
356                         return RegistryApi.CreateSubKey (this, subkey, options);
357                 }
358
359                 [ComVisible (false)]
360                 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
361                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions registryOptions,
362                         RegistrySecurity registrySecurity)
363                 {
364                         return CreateSubKey (subkey, permissionCheck, registryOptions);
365                 }
366 #endif
367
368                 
369                 /// <summary>
370                 ///     Delete the specified subkey.
371                 /// </summary>
372                 public void DeleteSubKey(string subkey)
373                 {
374                         DeleteSubKey (subkey, true);
375                 }
376                 
377                 
378                 /// <summary>
379                 ///     Delete the specified subkey.
380                 /// </summary>
381                 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
382                 {
383                         AssertKeyStillValid ();
384                         AssertKeyNameNotNull (subkey);
385                         AssertKeyNameLength (subkey);
386
387                         if (!IsWritable)
388                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
389
390                         RegistryKey child = OpenSubKey (subkey);
391                         
392                         if (child == null) {
393                                 if (throwOnMissingSubKey)
394                                         throw new ArgumentException ("Cannot delete a subkey tree"
395                                                 + " because the subkey does not exist.");
396                                 return;
397                         }
398
399                         if (child.SubKeyCount > 0){
400                                 throw new InvalidOperationException ("Registry key has subkeys"
401                                         + " and recursive removes are not supported by this method.");
402                         }
403                         
404                         child.Close ();
405
406                         RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
407                 }
408                 
409                 
410                 /// <summary>
411                 ///     Delete a sub tree (node, and values alike).
412                 /// </summary>
413                 public void DeleteSubKeyTree(string subkey)
414                 {
415                         DeleteSubKeyTree (subkey, true);
416                 }
417
418 #if NET_4_0
419                 public
420 #endif
421                 void DeleteSubKeyTree (string subkey, bool throwOnMissingSubKey)
422                 {
423                         // Note: this is done by deleting sub-nodes recursively.
424                         // The preformance is not very good. There may be a 
425                         // better way to implement this.
426                         
427                         AssertKeyStillValid ();
428                         AssertKeyNameNotNull (subkey);
429                         AssertKeyNameLength (subkey);
430                         
431                         RegistryKey child = OpenSubKey (subkey, true);
432                         if (child == null) {
433                                 if (!throwOnMissingSubKey)
434                                         return;
435
436                                 throw new ArgumentException ("Cannot delete a subkey tree"
437                                         + " because the subkey does not exist.");
438                         }
439
440                         child.DeleteChildKeysAndValues ();
441                         child.Close ();
442                         DeleteSubKey (subkey, false);
443                 }
444                 
445
446                 /// <summary>
447                 ///     Delete a value from the registry.
448                 /// </summary>
449                 public void DeleteValue(string name)
450                 {
451                         DeleteValue (name, true);
452                 }
453                 
454                 
455                 /// <summary>
456                 ///     Delete a value from the registry.
457                 /// </summary>
458                 public void DeleteValue(string name, bool throwOnMissingValue)
459                 {
460                         AssertKeyStillValid ();
461
462                         if (name == null)
463                                 throw new ArgumentNullException ("name");
464
465                         if (!IsWritable)
466                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
467
468                         RegistryApi.DeleteValue (this, name, throwOnMissingValue);
469                 }
470
471                 public RegistrySecurity GetAccessControl ()
472                 {
473                         return GetAccessControl (AccessControlSections.Owner |
474                                                  AccessControlSections.Group |
475                                                  AccessControlSections.Access);
476                 }
477                 
478                 public RegistrySecurity GetAccessControl (AccessControlSections includeSections)
479                 {
480                         return new RegistrySecurity (Name, includeSections);
481                 }
482                 
483                 
484                 /// <summary>
485                 ///     Get the names of the sub keys.
486                 /// </summary>
487                 public string[] GetSubKeyNames()
488                 {
489                         AssertKeyStillValid ();
490
491                         return RegistryApi.GetSubKeyNames (this);
492                 }
493                 
494                 
495                 /// <summary>
496                 ///     Get the names of values contained in this key.
497                 /// </summary>
498                 public string[] GetValueNames()
499                 {
500                         AssertKeyStillValid ();
501                         return RegistryApi.GetValueNames (this);
502                 }
503
504 #if NET_4_0
505                 [ComVisible (false)]
506                 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
507                 [MonoTODO ("Not implemented on unix")]
508                 public static RegistryKey FromHandle (SafeRegistryHandle handle)
509                 {
510                         if (handle == null)
511                                 throw new ArgumentNullException ("handle");
512
513                         return RegistryApi.FromHandle (handle);
514                 }
515
516                 [ComVisible (false)]
517                 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
518                 [MonoTODO ("Not implemented on unix")]
519                 public static RegistryKey FromHandle (SafeRegistryHandle handle, RegistryView view)
520                 {
521                         return FromHandle (handle);
522                 }
523 #endif
524                 
525                 
526                 [MonoTODO ("Not implemented on unix")]
527                 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
528                 {
529                         if (machineName == null)
530                                 throw new ArgumentNullException ("machineName");
531                         return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
532                 }
533
534 #if NET_4_0
535                 [ComVisible (false)]
536                 [MonoTODO ("Not implemented on unix")]
537                 public static RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName, RegistryView view)
538                 {
539                         if (machineName == null)
540                                 throw new ArgumentNullException ("machineName");
541                         return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
542                 }
543
544                 [ComVisible (false)]
545                 [MonoLimitation ("View is ignored in Mono")]
546                 public static RegistryKey OpenBaseKey (RegistryHive hKey, RegistryView view)
547                 {
548                         switch (hKey) {
549                                 case RegistryHive.ClassesRoot:
550                                         return Registry.ClassesRoot;
551                                 case RegistryHive.CurrentConfig:
552                                         return Registry.CurrentConfig;
553                                 case RegistryHive.CurrentUser:
554                                         return Registry.CurrentUser;
555                                 case RegistryHive.DynData:
556                                         return Registry.DynData;
557                                 case RegistryHive.LocalMachine:
558                                         return Registry.LocalMachine;
559                                 case RegistryHive.PerformanceData:
560                                         return Registry.PerformanceData;
561                                 case RegistryHive.Users:
562                                         return Registry.Users;
563                         }
564
565                         throw new ArgumentException ("hKey");
566                 }
567 #endif
568
569                 [ComVisible (false)]
570                 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck)
571                 {
572                         return OpenSubKey (name, permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree);
573                 }
574                 
575                 [ComVisible (false)]
576                 [MonoLimitation ("rights are ignored in Mono")]
577                 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights)
578                 {
579                         return OpenSubKey (name, permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree);
580                 }
581                 
582                 public void SetAccessControl (RegistrySecurity registrySecurity)
583                 {
584                         if (null == registrySecurity)
585                                 throw new ArgumentNullException ("registrySecurity");
586                                 
587                         registrySecurity.PersistModifications (Name);
588                 }
589                 
590                 
591                 /// <summary>
592                 ///     Build a string representation of the registry key.
593                 ///     Conatins the fully qualified key name, and the Hex
594                 ///     representation of the registry key handle.
595                 /// </summary>
596                 public override string ToString()
597                 {
598                         AssertKeyStillValid ();
599
600                         return RegistryApi.ToString (this);
601                 }
602
603                 #endregion // PublicAPI
604
605                 internal bool IsRoot {
606                         get { return hive != null; }
607                 }
608
609                 private bool IsWritable {
610                         get { return isWritable; }
611                 }
612
613                 internal RegistryHive Hive {
614                         get {
615                                 if (!IsRoot)
616                                         throw new NotSupportedException ();
617                                 return (RegistryHive) hive;
618                         }
619                 }
620
621                 // returns the key handle for the win32 implementation and the
622                 // KeyHandler for the unix implementation
623                 internal object InternalHandle {
624                         get { return handle; }
625                 }
626
627                 /// <summary>
628                 /// validate that the registry key handle is still usable.
629                 /// </summary>
630                 private void AssertKeyStillValid ()
631                 {
632                         if (handle == null)
633                                 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
634                 }
635
636                 
637                 /// <summary>
638                 /// validate that the registry key handle is still usable, and
639                 /// that the 'subKeyName' is not null.
640                 /// </summary>
641                 private void AssertKeyNameNotNull (string subKeyName)
642                 {
643                         if (subKeyName == null)
644                                 throw new ArgumentNullException ("name");
645                 }
646
647                 private void AssertKeyNameLength (string name)
648                 {
649                         if (name.Length > 255)
650                                 throw new ArgumentException ("Name of registry key cannot be greater than 255 characters");
651                 }
652
653                 /// <summary>
654                 ///     Utility method to delelte a key's sub keys and values.
655                 ///     This method removes a level of indirection when deleting
656                 ///     key node trees.
657                 /// </summary>
658                 private void DeleteChildKeysAndValues ()
659                 {
660                         if (IsRoot)
661                                 return;
662                         
663                         string[] subKeys = GetSubKeyNames ();
664                         foreach (string subKey in subKeys)
665                         {
666                                 RegistryKey sub = OpenSubKey (subKey, true);
667                                 sub.DeleteChildKeysAndValues ();
668                                 sub.Close ();
669                                 DeleteSubKey (subKey, false);
670                         }
671
672                         string[] values = GetValueNames ();
673                         foreach (string value in values) {
674                                 DeleteValue (value, false);
675                         }
676                 }
677
678                 /// <summary>
679                 ///     decode a byte array as a string, and strip trailing nulls
680                 /// </summary>
681                 static internal string DecodeString (byte[] data)
682                 {
683                         string stringRep = Encoding.Unicode.GetString (data);
684                         int idx = stringRep.IndexOf ('\0');
685                         if (idx != -1)
686                                 stringRep = stringRep.TrimEnd ('\0');
687                         return stringRep;
688                 }
689
690                 static internal IOException CreateMarkedForDeletionException ()
691                 {
692                         throw new IOException ("Illegal operation attempted on a"
693                                 + " registry key that has been marked for deletion.");
694                 }
695
696                 static string GetHiveName (RegistryHive hive)
697                 {
698                         switch (hive) {
699                         case RegistryHive.ClassesRoot:
700                                 return "HKEY_CLASSES_ROOT";
701                         case RegistryHive.CurrentConfig:
702                                 return "HKEY_CURRENT_CONFIG";
703                         case RegistryHive.CurrentUser:
704                                 return "HKEY_CURRENT_USER";
705                         case RegistryHive.DynData:
706                                 return "HKEY_DYN_DATA";
707                         case RegistryHive.LocalMachine:
708                                 return "HKEY_LOCAL_MACHINE";
709                         case RegistryHive.PerformanceData:
710                                 return "HKEY_PERFORMANCE_DATA";
711                         case RegistryHive.Users:
712                                 return "HKEY_USERS";
713                         }
714
715                         throw new NotImplementedException (string.Format (
716                                 "Registry hive '{0}' is not implemented.", hive.ToString ()));
717                 }
718
719         }
720 }
721
722 #endif // NET_2_1
723