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