Merge branch 'master' of github.com:mono/mono
[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                         if (safe_handle != null) {
168                                 safe_handle.Close ();
169                                 safe_handle = null;
170                         }
171 #endif
172                 }
173                 
174                 
175                 /// <summary>
176                 ///     get the number of sub-keys for this key
177                 /// </summary>
178                 public int SubKeyCount {
179                         get {
180                                 AssertKeyStillValid ();
181
182                                 return RegistryApi.SubKeyCount (this);
183                         }
184                 }
185
186                 
187                 /// <summary>
188                 ///     get the number of values for this key
189                 /// </summary>
190                 public int ValueCount {
191                         get {
192                                 AssertKeyStillValid ();
193
194                                 return RegistryApi.ValueCount (this);
195                         }
196                 }
197
198 #if NET_4_0
199                 [ComVisible (false)]
200                 [MonoTODO ("Not implemented in Unix")]
201                 public SafeRegistryHandle Handle {
202                         get {
203                                 AssertKeyStillValid ();
204
205                                 if (safe_handle == null) {
206                                         IntPtr h = RegistryApi.GetHandle (this);
207                                         safe_handle = new SafeRegistryHandle (h, true);
208                                 }
209
210                                 return safe_handle;
211                         }
212                 }
213
214                 [ComVisible (false)]
215                 [MonoLimitation ("View is ignored in Mono.")]
216                 public RegistryView View {
217                         get {
218                                 return RegistryView.Default;
219                         }
220                 }
221 #endif
222
223                 
224                 /// <summary>
225                 ///     Set a registry value.
226                 /// </summary>
227                 public void SetValue (string name, object value)
228                 {
229                         AssertKeyStillValid ();
230
231                         if (value == null)
232                                 throw new ArgumentNullException ("value");
233
234                         if (name != null)
235                                 AssertKeyNameLength (name);
236
237                         if (!IsWritable)
238                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
239
240                         RegistryApi.SetValue (this, name, value);
241                 }
242
243                 [ComVisible (false)]
244                 public void SetValue (string name, object value, RegistryValueKind valueKind)
245                 {
246                         AssertKeyStillValid ();
247                         
248                         if (value == null)
249                                 throw new ArgumentNullException ("value");
250
251                         if (name != null)
252                                 AssertKeyNameLength (name);
253
254                         if (!IsWritable)
255                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
256
257                         RegistryApi.SetValue (this, name, value, valueKind);
258                 }
259
260                 /// <summary>
261                 ///     Open the sub key specified, for read access.
262                 /// </summary>
263                 public RegistryKey OpenSubKey (string name)
264                 {
265                         return OpenSubKey (name, false);
266                 }
267
268                 
269                 /// <summary>
270                 ///     Open the sub key specified.
271                 /// </summary>
272                 public RegistryKey OpenSubKey (string name, bool writable)
273                 {
274                         AssertKeyStillValid ();
275
276                         if (name == null)
277                                 throw new ArgumentNullException ("name");
278
279                         AssertKeyNameLength (name);
280
281                         return RegistryApi.OpenSubKey (this, name, writable);
282                 }
283                 
284                 
285                 /// <summary>
286                 ///     Get a registry value.
287                 /// </summary>
288                 public object GetValue (string name)
289                 {
290                         return GetValue (name, null);
291                 }
292
293                 
294                 /// <summary>
295                 ///     Get a registry value.
296                 /// </summary>
297                 public object GetValue (string name, object defaultValue)
298                 {
299                         AssertKeyStillValid ();
300                         
301                         return RegistryApi.GetValue (this, name, defaultValue,
302                                 RegistryValueOptions.None);
303                 }
304
305                 [ComVisible (false)]
306                 public object GetValue (string name, object defaultValue, RegistryValueOptions options)
307                 {
308                         AssertKeyStillValid ();
309
310                         return RegistryApi.GetValue (this, name, defaultValue, options);
311                 }
312
313                 [ComVisible (false)]
314                 public RegistryValueKind GetValueKind (string name)
315                 {
316                         return RegistryApi.GetValueKind (this, name);
317                 }
318
319                 /// <summary>
320                 ///     Create a sub key.
321                 /// </summary>
322                 public RegistryKey CreateSubKey (string subkey)
323                 {
324                         AssertKeyStillValid ();
325                         AssertKeyNameNotNull (subkey);
326                         AssertKeyNameLength (subkey);
327
328                         if (!IsWritable)
329                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
330                         return RegistryApi.CreateSubKey (this, subkey);
331                 }
332
333                 [ComVisible (false)]
334                 [MonoLimitation ("permissionCheck is ignored in Mono")]
335                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck)
336                 {
337                         return CreateSubKey (subkey);
338                 }
339
340                 [ComVisible (false)]
341                 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
342                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)
343                 {
344                         return CreateSubKey (subkey);
345                 }
346
347 #if NET_4_0
348                 [ComVisible (false)]
349                 [MonoLimitation ("permissionCheck is ignored in Mono")]
350                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
351                 {
352                         AssertKeyStillValid ();
353                         AssertKeyNameNotNull (subkey);
354                         AssertKeyNameLength (subkey);
355
356                         if (!IsWritable)
357                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
358
359                         return RegistryApi.CreateSubKey (this, subkey, options);
360                 }
361
362                 [ComVisible (false)]
363                 [MonoLimitation ("permissionCheck and registrySecurity are ignored in Mono")]
364                 public RegistryKey CreateSubKey (string subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options,
365                         RegistrySecurity registrySecurity)
366                 {
367                         return CreateSubKey (subkey, permissionCheck, options);
368                 }
369 #endif
370
371                 
372                 /// <summary>
373                 ///     Delete the specified subkey.
374                 /// </summary>
375                 public void DeleteSubKey(string subkey)
376                 {
377                         DeleteSubKey (subkey, true);
378                 }
379                 
380                 
381                 /// <summary>
382                 ///     Delete the specified subkey.
383                 /// </summary>
384                 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
385                 {
386                         AssertKeyStillValid ();
387                         AssertKeyNameNotNull (subkey);
388                         AssertKeyNameLength (subkey);
389
390                         if (!IsWritable)
391                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
392
393                         RegistryKey child = OpenSubKey (subkey);
394                         
395                         if (child == null) {
396                                 if (throwOnMissingSubKey)
397                                         throw new ArgumentException ("Cannot delete a subkey tree"
398                                                 + " because the subkey does not exist.");
399                                 return;
400                         }
401
402                         if (child.SubKeyCount > 0){
403                                 throw new InvalidOperationException ("Registry key has subkeys"
404                                         + " and recursive removes are not supported by this method.");
405                         }
406                         
407                         child.Close ();
408
409                         RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
410                 }
411                 
412                 
413                 /// <summary>
414                 ///     Delete a sub tree (node, and values alike).
415                 /// </summary>
416                 public void DeleteSubKeyTree(string subkey)
417                 {
418                         DeleteSubKeyTree (subkey, true);
419                 }
420
421 #if NET_4_0
422                 public
423 #endif
424                 void DeleteSubKeyTree (string subkey, bool throwOnMissingSubKey)
425                 {
426                         // Note: this is done by deleting sub-nodes recursively.
427                         // The preformance is not very good. There may be a 
428                         // better way to implement this.
429                         
430                         AssertKeyStillValid ();
431                         AssertKeyNameNotNull (subkey);
432                         AssertKeyNameLength (subkey);
433                         
434                         RegistryKey child = OpenSubKey (subkey, true);
435                         if (child == null) {
436                                 if (!throwOnMissingSubKey)
437                                         return;
438
439                                 throw new ArgumentException ("Cannot delete a subkey tree"
440                                         + " because the subkey does not exist.");
441                         }
442
443                         child.DeleteChildKeysAndValues ();
444                         child.Close ();
445                         DeleteSubKey (subkey, false);
446                 }
447                 
448
449                 /// <summary>
450                 ///     Delete a value from the registry.
451                 /// </summary>
452                 public void DeleteValue(string name)
453                 {
454                         DeleteValue (name, true);
455                 }
456                 
457                 
458                 /// <summary>
459                 ///     Delete a value from the registry.
460                 /// </summary>
461                 public void DeleteValue(string name, bool throwOnMissingValue)
462                 {
463                         AssertKeyStillValid ();
464
465                         if (name == null)
466                                 throw new ArgumentNullException ("name");
467
468                         if (!IsWritable)
469                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
470
471                         RegistryApi.DeleteValue (this, name, throwOnMissingValue);
472                 }
473
474                 public RegistrySecurity GetAccessControl ()
475                 {
476                         throw new NotImplementedException ();
477                 }
478                 
479                 public RegistrySecurity GetAccessControl (AccessControlSections includeSections)
480                 {
481                         throw new NotImplementedException ();
482                 }
483                 
484                 
485                 /// <summary>
486                 ///     Get the names of the sub keys.
487                 /// </summary>
488                 public string[] GetSubKeyNames()
489                 {
490                         AssertKeyStillValid ();
491
492                         return RegistryApi.GetSubKeyNames (this);
493                 }
494                 
495                 
496                 /// <summary>
497                 ///     Get the names of values contained in this key.
498                 /// </summary>
499                 public string[] GetValueNames()
500                 {
501                         AssertKeyStillValid ();
502                         return RegistryApi.GetValueNames (this);
503                 }
504
505 #if NET_4_0
506                 [ComVisible (false)]
507                 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
508                 [MonoTODO ("Not implemented on unix")]
509                 public static RegistryKey FromHandle (SafeRegistryHandle handle)
510                 {
511                         if (handle == null)
512                                 throw new ArgumentNullException ("handle");
513
514                         return RegistryApi.FromHandle (handle);
515                 }
516
517                 [ComVisible (false)]
518                 [SecurityPermission (SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
519                 [MonoTODO ("Not implemented on unix")]
520                 public static RegistryKey FromHandle (SafeRegistryHandle handle, RegistryKey view)
521                 {
522                         return FromHandle (handle);
523                 }
524 #endif
525                 
526                 
527                 [MonoTODO ("Not implemented on unix")]
528                 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
529                 {
530                         if (machineName == null)
531                                 throw new ArgumentNullException ("machineName");
532                         return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
533                 }
534
535 #if NET_4_0
536                 [ComVisible (false)]
537                 [MonoTODO ("Not implemented on unix")]
538                 public static RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName, RegistryView view)
539                 {
540                         if (machineName == null)
541                                 throw new ArgumentNullException ("machineName");
542                         return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
543                 }
544
545                 [ComVisible (false)]
546                 [MonoLimitation ("View is ignored in Mono")]
547                 public static RegistryKey OpenBaseKey (RegistryHive hKey, RegistryView view)
548                 {
549                         switch (hKey) {
550                                 case RegistryHive.ClassesRoot:
551                                         return Registry.ClassesRoot;
552                                 case RegistryHive.CurrentConfig:
553                                         return Registry.CurrentConfig;
554                                 case RegistryHive.CurrentUser:
555                                         return Registry.CurrentUser;
556                                 case RegistryHive.DynData:
557                                         return Registry.DynData;
558                                 case RegistryHive.LocalMachine:
559                                         return Registry.LocalMachine;
560                                 case RegistryHive.PerformanceData:
561                                         return Registry.PerformanceData;
562                                 case RegistryHive.Users:
563                                         return Registry.Users;
564                         }
565
566                         throw new ArgumentException ("hKey");
567                 }
568 #endif
569
570                 [ComVisible (false)]
571                 [MonoLimitation ("permissionCheck is ignored in Mono")]
572                 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck)
573                 {
574                         return OpenSubKey (name);
575                 }
576                 
577                 [ComVisible (false)]
578                 [MonoLimitation ("permissionCheck and rights are ignored in Mono")]
579                 public RegistryKey OpenSubKey (string name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights)
580                 {
581                         return OpenSubKey (name);
582                 }
583                 
584                 public void SetAccessControl (RegistrySecurity registrySecurity)
585                 {
586                         throw new NotImplementedException ();
587                 }
588                 
589                 
590                 /// <summary>
591                 ///     Build a string representation of the registry key.
592                 ///     Conatins the fully qualified key name, and the Hex
593                 ///     representation of the registry key handle.
594                 /// </summary>
595                 public override string ToString()
596                 {
597                         AssertKeyStillValid ();
598
599                         return RegistryApi.ToString (this);
600                 }
601
602                 #endregion // PublicAPI
603
604                 internal bool IsRoot {
605                         get { return hive != null; }
606                 }
607
608                 private bool IsWritable {
609                         get { return isWritable; }
610                 }
611
612                 internal RegistryHive Hive {
613                         get {
614                                 if (!IsRoot)
615                                         throw new NotSupportedException ();
616                                 return (RegistryHive) hive;
617                         }
618                 }
619
620                 // returns the key handle for the win32 implementation and the
621                 // KeyHandler for the unix implementation
622                 internal object InternalHandle {
623                         get { return handle; }
624                 }
625
626                 /// <summary>
627                 /// validate that the registry key handle is still usable.
628                 /// </summary>
629                 private void AssertKeyStillValid ()
630                 {
631                         if (handle == null)
632                                 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
633                 }
634
635                 
636                 /// <summary>
637                 /// validate that the registry key handle is still usable, and
638                 /// that the 'subKeyName' is not null.
639                 /// </summary>
640                 private void AssertKeyNameNotNull (string subKeyName)
641                 {
642                         if (subKeyName == null)
643                                 throw new ArgumentNullException ("name");
644                 }
645
646                 private void AssertKeyNameLength (string name)
647                 {
648                         if (name.Length > 255)
649                                 throw new ArgumentException ("Name of registry key cannot be greater than 255 characters");
650                 }
651
652                 /// <summary>
653                 ///     Utility method to delelte a key's sub keys and values.
654                 ///     This method removes a level of indirection when deleting
655                 ///     key node trees.
656                 /// </summary>
657                 private void DeleteChildKeysAndValues ()
658                 {
659                         if (IsRoot)
660                                 return;
661                         
662                         string[] subKeys = GetSubKeyNames ();
663                         foreach (string subKey in subKeys)
664                         {
665                                 RegistryKey sub = OpenSubKey (subKey, true);
666                                 sub.DeleteChildKeysAndValues ();
667                                 sub.Close ();
668                                 DeleteSubKey (subKey, false);
669                         }
670
671                         string[] values = GetValueNames ();
672                         foreach (string value in values) {
673                                 DeleteValue (value, false);
674                         }
675                 }
676
677                 /// <summary>
678                 ///     decode a byte array as a string, and strip trailing nulls
679                 /// </summary>
680                 static internal string DecodeString (byte[] data)
681                 {
682                         string stringRep = Encoding.Unicode.GetString (data);
683                         int idx = stringRep.IndexOf ('\0');
684                         if (idx != -1)
685                                 stringRep = stringRep.TrimEnd ('\0');
686                         return stringRep;
687                 }
688
689                 static internal IOException CreateMarkedForDeletionException ()
690                 {
691                         throw new IOException ("Illegal operation attempted on a"
692                                 + " registry key that has been marked for deletion.");
693                 }
694
695                 static string GetHiveName (RegistryHive hive)
696                 {
697                         switch (hive) {
698                         case RegistryHive.ClassesRoot:
699                                 return "HKEY_CLASSES_ROOT";
700                         case RegistryHive.CurrentConfig:
701                                 return "HKEY_CURRENT_CONFIG";
702                         case RegistryHive.CurrentUser:
703                                 return "HKEY_CURRENT_USER";
704                         case RegistryHive.DynData:
705                                 return "HKEY_DYN_DATA";
706                         case RegistryHive.LocalMachine:
707                                 return "HKEY_LOCAL_MACHINE";
708                         case RegistryHive.PerformanceData:
709                                 return "HKEY_PERFORMANCE_DATA";
710                         case RegistryHive.Users:
711                                 return "HKEY_USERS";
712                         }
713
714                         throw new NotImplementedException (string.Format (
715                                 "Registry hive '{0}' is not implemented.", hive.ToString ()));
716                 }
717
718         }
719 }
720
721 #endif // NET_2_1
722