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