New test.
[mono.git] / mcs / class / corlib / Microsoft.Win32 / RegistryKey.cs
1 //
2 // RegistryKey.cs: a single node in the Windows registry
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Erik LeBel (eriklebel@yahoo.ca)
7 //   Gert Driesen (drieseng@users.sourceforge.net)
8 //
9 //
10 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.IO;
34 using System.Collections;
35 using System.Diagnostics;
36 using System.Runtime.InteropServices;
37 using System.Security;
38 using System.Text;
39
40 namespace Microsoft.Win32
41 {
42         /// <summary>
43         ///     Wrapper class for Windows Registry Entry.
44         /// </summary>
45         public sealed class RegistryKey : MarshalByRefObject, IDisposable 
46         {
47                 //
48                 // This represents the backend data, used when creating the
49                 // RegistryKey object
50                 //
51                 object handle;
52
53                 object hive; // the RegistryHive if the key represents a base key
54                 readonly string qname;  // the fully qualified registry key name
55                 readonly bool isRemoteRoot;     // is an instance of a remote root key?
56                 readonly bool isWritable;       // is the key openen in writable mode
57
58                 static readonly IRegistryApi RegistryApi;
59
60                 static RegistryKey ()
61                 {
62                         if (Path.DirectorySeparatorChar == '\\')
63                                 RegistryApi = new Win32RegistryApi ();
64                         else
65                                 RegistryApi = new UnixRegistryApi ();
66                 }
67
68                 /// <summary>
69                 ///     Construct an instance of a root registry key entry.
70                 /// </summary>
71                 internal RegistryKey (RegistryHive hiveId) : this (hiveId, 
72                         new IntPtr ((int) hiveId), false)
73                 {
74                 }
75
76                 /// <summary>
77                 ///     Construct an instance of a root registry key entry.
78                 /// </summary>
79                 internal RegistryKey (RegistryHive hiveId, IntPtr keyHandle, bool remoteRoot)
80                 {
81                         hive = hiveId;
82                         handle = keyHandle;
83                         qname = GetHiveName (hiveId);
84                         isRemoteRoot = remoteRoot;
85                         isWritable = true; // always consider root writable
86                 }
87
88                 /// <summary>
89                 ///     Construct an instance of a registry key entry.
90                 /// </summary>
91                 internal RegistryKey (object data, string keyName, bool writable)
92                 {
93                         handle = data;
94                         qname = keyName;
95                         isWritable = writable;
96                 }
97
98                 #region PublicAPI
99
100                 /// <summary>
101                 ///     Dispose of registry key object. Close the 
102                 ///     key if it's still open.
103                 /// </summary>
104                 void IDisposable.Dispose ()
105                 {
106                         GC.SuppressFinalize (this);
107                         Close ();
108                 }
109
110                 
111                 /// <summary>
112                 ///     Final cleanup of registry key object. Close the 
113                 ///     key if it's still open.
114                 /// </summary>
115                 ~RegistryKey ()
116                 {
117                         Close ();
118                 }
119
120                 
121                 /// <summary>
122                 ///     Get the fully qualified registry key name.
123                 /// </summary>
124                 public string Name {
125                         get { return qname; }
126                 }
127         
128                 
129                 /// <summary>
130                 ///     Flush the current registry state to disk.
131                 /// </summary>
132                 public void Flush()
133                 {
134                         RegistryApi.Flush (this);
135                 }
136                 
137                 
138                 /// <summary>
139                 ///     Close the current registry key and flushes the state of the registry
140                 /// right away.
141                 /// </summary>
142                 public void Close()
143                 {
144                         Flush ();
145
146                         // a handle to a remote hive must be closed, while one to a local
147                         // hive should not be closed
148                         if (!isRemoteRoot && IsRoot)
149                                 return;
150                         
151                         RegistryApi.Close (this);
152                         handle = null;
153                 }
154                 
155                 
156                 /// <summary>
157                 ///     get the number of sub-keys for this key
158                 /// </summary>
159                 public int SubKeyCount {
160                         get {
161                                 AssertKeyStillValid ();
162
163                                 return RegistryApi.SubKeyCount (this);
164                         }
165                 }
166
167                 
168                 /// <summary>
169                 ///     get the number of values for this key
170                 /// </summary>
171                 public int ValueCount {
172                         get {
173                                 AssertKeyStillValid ();
174
175                                 return RegistryApi.ValueCount (this);
176                         }
177                 }
178
179                 
180                 /// <summary>
181                 ///     Set a registry value.
182                 /// </summary>
183                 public void SetValue (string name, object value)
184                 {
185                         AssertKeyStillValid ();
186
187                         if (value == null)
188                                 throw new ArgumentNullException ("value");
189
190                         if (!IsWritable)
191                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
192
193                         RegistryApi.SetValue (this, name, value);
194                 }
195
196 #if NET_2_0
197                 [ComVisible (false)]
198                 public void SetValue (string name, object value, RegistryValueKind valueKind)
199                 {
200                         AssertKeyStillValid ();
201                         
202                         if (value == null)
203                                 throw new ArgumentNullException ();
204
205                         if (!IsWritable)
206                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
207
208                         RegistryApi.SetValue (this, name, value, valueKind);
209                 }
210 #endif
211
212                 /// <summary>
213                 ///     Open the sub key specified, for read access.
214                 /// </summary>
215                 public RegistryKey OpenSubKey (string keyName)
216                 {
217                         return OpenSubKey (keyName, false);
218                 }
219
220                 
221                 /// <summary>
222                 ///     Open the sub key specified.
223                 /// </summary>
224                 public RegistryKey OpenSubKey (string keyName, bool writtable)
225                 {
226                         AssertKeyStillValid ();
227                         AssertKeyNameNotNull (keyName);
228
229                         return RegistryApi.OpenSubKey (this, keyName, writtable);
230                 }
231                 
232                 
233                 /// <summary>
234                 ///     Get a registry value.
235                 /// </summary>
236                 public object GetValue (string name)
237                 {
238                         return GetValue (name, null);
239                 }
240
241                 
242                 /// <summary>
243                 ///     Get a registry value.
244                 /// </summary>
245                 public object GetValue (string name, object defaultValue)
246                 {
247                         AssertKeyStillValid ();
248                         
249                         return RegistryApi.GetValue (this, name, defaultValue,
250                                 RegistryValueOptions.None);
251                 }
252
253 #if NET_2_0
254                 [ComVisible (false)]
255                 public object GetValue (string name, object defaultValue, RegistryValueOptions options)
256                 {
257                         AssertKeyStillValid ();
258
259                         return RegistryApi.GetValue (this, name, defaultValue, options);
260                 }
261 #endif
262
263                 /// <summary>
264                 ///     Create a sub key.
265                 /// </summary>
266                 [MonoTODO("RegistryPermission")]
267                 public RegistryKey CreateSubKey (string subkey)
268                 {
269                         AssertKeyStillValid ();
270                         AssertKeyNameNotNull (subkey);
271                         if (subkey.Length > 255)
272                                 throw new ArgumentException ("keyName length is larger than 255 characters", subkey);
273
274                         if (!IsWritable)
275                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
276                         return RegistryApi.CreateSubKey (this, subkey);
277                 }
278                 
279                 
280                 /// <summary>
281                 ///     Delete the specified subkey.
282                 /// </summary>
283                 public void DeleteSubKey(string subkey)
284                 {
285                         DeleteSubKey (subkey, true);
286                 }
287                 
288                 
289                 /// <summary>
290                 ///     Delete the specified subkey.
291                 /// </summary>
292                 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
293                 {
294                         AssertKeyStillValid ();
295                         AssertKeyNameNotNull (subkey);
296
297                         if (!IsWritable)
298                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
299
300                         RegistryKey child = OpenSubKey (subkey);
301                         
302                         if (child == null) {
303                                 if (throwOnMissingSubKey)
304                                         throw new ArgumentException ("Cannot delete a subkey tree"
305                                                 + " because the subkey does not exist.");
306                                 return;
307                         }
308
309                         if (child.SubKeyCount > 0){
310                                 throw new InvalidOperationException ("Registry key has subkeys"
311                                         + " and recursive removes are not supported by this method.");
312                         }
313                         
314                         child.Close ();
315
316                         RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
317                 }
318                 
319                 
320                 /// <summary>
321                 ///     Delete a sub tree (node, and values alike).
322                 /// </summary>
323                 public void DeleteSubKeyTree(string keyName)
324                 {
325                         // Note: this is done by deleting sub-nodes recursively.
326                         // The preformance is not very good. There may be a 
327                         // better way to implement this.
328                         
329                         AssertKeyStillValid ();
330                         AssertKeyNameNotNull (keyName);
331                         
332                         RegistryKey child = OpenSubKey (keyName, true);
333                         if (child == null)
334                                 throw new ArgumentException ("Cannot delete a subkey tree"
335                                         + " because the subkey does not exist.");
336
337                         child.DeleteChildKeysAndValues ();
338                         child.Close ();
339                         DeleteSubKey (keyName, false);
340                 }
341                 
342
343                 /// <summary>
344                 ///     Delete a value from the registry.
345                 /// </summary>
346                 public void DeleteValue(string value)
347                 {
348                         DeleteValue (value, true);
349                 }
350                 
351                 
352                 /// <summary>
353                 ///     Delete a value from the registry.
354                 /// </summary>
355                 public void DeleteValue(string value, bool shouldThrowWhenKeyMissing)
356                 {
357                         AssertKeyStillValid ();
358                         AssertKeyNameNotNull (value);
359
360                         if (!IsWritable)
361                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
362
363                         RegistryApi.DeleteValue (this, value, shouldThrowWhenKeyMissing);
364                 }
365                 
366                 
367                 /// <summary>
368                 ///     Get the names of the sub keys.
369                 /// </summary>
370                 public string[] GetSubKeyNames()
371                 {
372                         AssertKeyStillValid ();
373
374                         return RegistryApi.GetSubKeyNames (this);
375                 }
376                 
377                 
378                 /// <summary>
379                 ///     Get the names of values contained in this key.
380                 /// </summary>
381                 public string[] GetValueNames()
382                 {
383                         AssertKeyStillValid ();
384                         return RegistryApi.GetValueNames (this);
385                 }
386                 
387                 
388                 [MonoTODO ("Not implemented on unix")]
389                 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
390                 {
391                         if (machineName == null)
392                                 throw new ArgumentNullException ("machineName");
393                         return RegistryApi.OpenRemoteBaseKey (hKey, machineName);
394                 }
395                 
396                 
397                 /// <summary>
398                 ///     Build a string representation of the registry key.
399                 ///     Conatins the fully qualified key name, and the Hex
400                 ///     representation of the registry key handle.
401                 /// </summary>
402                 public override string ToString()
403                 {
404                         AssertKeyStillValid ();
405
406                         return RegistryApi.ToString (this);
407                 }
408
409                 #endregion // PublicAPI
410
411                 internal bool IsRoot {
412                         get { return hive != null; }
413                 }
414
415                 private bool IsWritable {
416                         get { return isWritable; }
417                 }
418
419                 internal RegistryHive Hive {
420                         get {
421                                 if (!IsRoot)
422                                         throw new NotSupportedException ();
423                                 return (RegistryHive) hive;
424                         }
425                 }
426
427                 // returns the key handle for the win32 implementation and the
428                 // KeyHandler for the unix implementation
429                 internal object Handle {
430                         get { return handle; }
431                 }
432
433                 /// <summary>
434                 /// validate that the registry key handle is still usable.
435                 /// </summary>
436                 private void AssertKeyStillValid ()
437                 {
438                         if (handle == null)
439                                 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
440                 }
441
442                 
443                 /// <summary>
444                 /// validate that the registry key handle is still usable, and
445                 /// that the 'subKeyName' is not null.
446                 /// </summary>
447                 private void AssertKeyNameNotNull (string subKeyName)
448                 {
449                         if (subKeyName == null)
450                                 throw new ArgumentNullException ();
451                 }
452                 
453
454                 /// <summary>
455                 ///     Utility method to delelte a key's sub keys and values.
456                 ///     This method removes a level of indirection when deleting
457                 ///     key node trees.
458                 /// </summary>
459                 private void DeleteChildKeysAndValues ()
460                 {
461                         if (IsRoot)
462                                 return;
463                         
464                         string[] subKeys = GetSubKeyNames ();
465                         foreach (string subKey in subKeys)
466                         {
467                                 RegistryKey sub = OpenSubKey (subKey, true);
468                                 sub.DeleteChildKeysAndValues ();
469                                 sub.Close ();
470                                 DeleteSubKey (subKey, false);
471                         }
472
473                         string[] values = GetValueNames ();
474                         foreach (string value in values) {
475                                 DeleteValue (value, false);
476                         }
477                 }
478
479                 /// <summary>
480                 ///     decode a byte array as a string, and strip trailing nulls
481                 /// </summary>
482                 static internal string DecodeString (byte[] data)
483                 {
484                         string stringRep = Encoding.Unicode.GetString (data);
485                         int idx = stringRep.IndexOf ('\0');
486                         if (idx != -1)
487                                 stringRep = stringRep.TrimEnd ('\0');
488                         return stringRep;
489                 }
490
491                 static internal IOException CreateMarkedForDeletionException ()
492                 {
493                         throw new IOException ("Illegal operation attempted on a"
494                                 + " registry key that has been marked for deletion.");
495                 }
496
497                 static string GetHiveName (RegistryHive hive)
498                 {
499                         switch (hive) {
500                         case RegistryHive.ClassesRoot:
501                                 return "HKEY_CLASSES_ROOT";
502                         case RegistryHive.CurrentConfig:
503                                 return "HKEY_CURRENT_CONFIG";
504                         case RegistryHive.CurrentUser:
505                                 return "HKEY_CURRENT_USER";
506                         case RegistryHive.DynData:
507                                 return "HKEY_DYN_DATA";
508                         case RegistryHive.LocalMachine:
509                                 return "HKEY_LOCAL_MACHINE";
510                         case RegistryHive.PerformanceData:
511                                 return "HKEY_PERFORMANCE_DATA";
512                         case RegistryHive.Users:
513                                 return "HKEY_USERS";
514                         }
515
516                         throw new NotImplementedException (string.Format (
517                                 "Registry hive '{0}' is not implemented.", hive.ToString ()));
518                 }
519
520         }
521 }
522