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