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