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