* RegistryKeyTest.cs: Added large batch of tests.
[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                 internal object Data;
52                 
53                 readonly string qname;  // the fully qualified registry key name
54                 readonly bool isRoot;   // is the an instance of a root key?
55                 readonly bool isWritable;       // is the key openen in writable mode
56
57                 internal bool IsRoot {
58                         get { return isRoot; }
59                 }
60
61                 internal bool IsWritable {
62                         get { return isWritable; }
63                 }
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, string keyName)
79                 {
80                         Data = hiveId;
81                         qname = keyName;
82                         isRoot = true;
83                         isWritable = true; // always consider root writable
84                 }
85                 
86                 /// <summary>
87                 ///     Construct an instance of a registry key entry.
88                 /// </summary>
89                 internal RegistryKey (object data, string keyName, bool writable)
90                 {
91                         Data = data;
92                         qname = keyName;
93                         isRoot = false;
94                         isWritable = writable;
95                 }
96
97                 #region PublicAPI
98
99                 /// <summary>
100                 ///     Dispose of registry key object. Close the 
101                 ///     key if it's still open.
102                 /// </summary>
103                 void IDisposable.Dispose ()
104                 {
105                         GC.SuppressFinalize (this);
106                         Close ();
107                 }
108
109                 
110                 /// <summary>
111                 ///     Final cleanup of registry key object. Close the 
112                 ///     key if it's still open.
113                 /// </summary>
114                 ~RegistryKey ()
115                 {
116                         Close ();
117                 }
118
119                 
120                 /// <summary>
121                 ///     Get the fully qualified registry key name.
122                 /// </summary>
123                 public string Name {
124                         get { return qname; }
125                 }
126         
127                 
128                 /// <summary>
129                 ///     Flush the current registry state to disk.
130                 /// </summary>
131                 public void Flush()
132                 {
133                         RegistryApi.Flush (this);
134                 }
135                 
136                 
137                 /// <summary>
138                 ///     Close the current registry key and flushes the state of the registry
139                 /// right away.
140                 /// </summary>
141                 public void Close()
142                 {
143                         Flush ();
144
145                         if (isRoot)
146                                 return;
147                         
148                         RegistryApi.Close (this);
149                         Data = null;
150                 }
151                 
152                 
153                 /// <summary>
154                 ///     get the number of sub-keys for this key
155                 /// </summary>
156                 public int SubKeyCount {
157                         get {
158                                 AssertKeyStillValid ();
159
160                                 return RegistryApi.SubKeyCount (this);
161                         }
162                 }
163
164                 
165                 /// <summary>
166                 ///     get the number of values for this key
167                 /// </summary>
168                 public int ValueCount {
169                         get {
170                                 AssertKeyStillValid ();
171
172                                 return RegistryApi.ValueCount (this);
173                         }
174                 }
175
176                 
177                 /// <summary>
178                 ///     Set a registry value.
179                 /// </summary>
180                 public void SetValue (string name, object value)
181                 {
182                         AssertKeyStillValid ();
183
184                         if (value == null)
185                                 throw new ArgumentNullException ("value");
186
187                         if (!IsWritable)
188                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
189
190                         RegistryApi.SetValue (this, name, value);
191                 }
192
193 #if NET_2_0
194                 public void SetValue (string name, object value, RegistryValueKind valueKind)
195                 {
196                         AssertKeyStillValid ();
197                         
198                         if (value == null)
199                                 throw new ArgumentNullException ();
200
201                         if (!IsWritable)
202                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
203
204                         RegistryApi.SetValue (this, name, value, valueKind);
205                 }
206 #endif
207
208                 /// <summary>
209                 ///     Open the sub key specified, for read access.
210                 /// </summary>
211                 public RegistryKey OpenSubKey (string keyName)
212                 {
213                         return OpenSubKey (keyName, false);
214                 }
215
216                 
217                 /// <summary>
218                 ///     Open the sub key specified.
219                 /// </summary>
220                 public RegistryKey OpenSubKey (string keyName, bool writtable)
221                 {
222                         AssertKeyStillValid ();
223                         AssertKeyNameNotNull (keyName);
224
225                         return RegistryApi.OpenSubKey (this, keyName, writtable);
226                 }
227                 
228                 
229                 /// <summary>
230                 ///     Get a registry value.
231                 /// </summary>
232                 public object GetValue (string name)
233                 {
234                         return RegistryApi.GetValue (this, name, false, null);
235                 }
236
237                 
238                 /// <summary>
239                 ///     Get a registry value.
240                 /// </summary>
241                 public object GetValue (string name, object defaultValue)
242                 {
243                         AssertKeyStillValid ();
244                         
245                         return RegistryApi.GetValue (this, name, true, defaultValue);
246                 }
247
248                 
249                 /// <summary>
250                 ///     Create a sub key.
251                 /// </summary>
252                 [MonoTODO("RegistryPermission")]
253                 public RegistryKey CreateSubKey (string subkey)
254                 {
255                         AssertKeyStillValid ();
256                         AssertKeyNameNotNull (subkey);
257                         if (subkey.Length > 255)
258                                 throw new ArgumentException ("keyName length is larger than 255 characters", subkey);
259
260                         if (!IsWritable)
261                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
262                         return RegistryApi.CreateSubKey (this, subkey);
263                 }
264                 
265                 
266                 /// <summary>
267                 ///     Delete the specified subkey.
268                 /// </summary>
269                 public void DeleteSubKey(string subkey)
270                 {
271                         DeleteSubKey (subkey, true);
272                 }
273                 
274                 
275                 /// <summary>
276                 ///     Delete the specified subkey.
277                 /// </summary>
278                 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
279                 {
280                         AssertKeyStillValid ();
281                         AssertKeyNameNotNull (subkey);
282
283                         if (!IsWritable)
284                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
285
286                         RegistryKey child = OpenSubKey (subkey);
287                         
288                         if (child == null) {
289                                 if (throwOnMissingSubKey)
290                                         throw new ArgumentException ("Cannot delete a subkey tree"
291                                                 + " because the subkey does not exist.");
292                                 return;
293                         }
294
295                         if (child.SubKeyCount > 0){
296                                 throw new InvalidOperationException ("Registry key has subkeys"
297                                         + " and recursive removes are not supported by this method.");
298                         }
299                         
300                         child.Close ();
301
302                         RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
303                 }
304                 
305                 
306                 /// <summary>
307                 ///     Delete a sub tree (node, and values alike).
308                 /// </summary>
309                 public void DeleteSubKeyTree(string keyName)
310                 {
311                         // Note: this is done by deleting sub-nodes recursively.
312                         // The preformance is not very good. There may be a 
313                         // better way to implement this.
314                         
315                         AssertKeyStillValid ();
316                         AssertKeyNameNotNull (keyName);
317                         
318                         RegistryKey child = OpenSubKey (keyName, true);
319                         if (child == null)
320                                 throw new ArgumentException ("Cannot delete a subkey tree"
321                                         + " because the subkey does not exist.");
322
323                         child.DeleteChildKeysAndValues ();
324                         child.Close ();
325                         DeleteSubKey (keyName, false);
326                 }
327                 
328
329                 /// <summary>
330                 ///     Delete a value from the registry.
331                 /// </summary>
332                 public void DeleteValue(string value)
333                 {
334                         DeleteValue (value, true);
335                 }
336                 
337                 
338                 /// <summary>
339                 ///     Delete a value from the registry.
340                 /// </summary>
341                 public void DeleteValue(string value, bool shouldThrowWhenKeyMissing)
342                 {
343                         AssertKeyStillValid ();
344                         AssertKeyNameNotNull (value);
345
346                         if (!IsWritable)
347                                 throw new UnauthorizedAccessException ("Cannot write to the registry key.");
348
349                         RegistryApi.DeleteValue (this, value, shouldThrowWhenKeyMissing);
350                 }
351                 
352                 
353                 /// <summary>
354                 ///     Get the names of the sub keys.
355                 /// </summary>
356                 public string[] GetSubKeyNames()
357                 {
358                         AssertKeyStillValid ();
359
360                         return RegistryApi.GetSubKeyNames (this);
361                 }
362                 
363                 
364                 /// <summary>
365                 ///     Get the names of values contained in this key.
366                 /// </summary>
367                 public string[] GetValueNames()
368                 {
369                         AssertKeyStillValid ();
370                         return RegistryApi.GetValueNames (this);
371                 }
372                 
373                 
374                 [MonoTODO]
375                 public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey,string machineName)
376                 {
377                         throw new NotImplementedException ();
378                 }
379                 
380                 
381                 /// <summary>
382                 ///     Build a string representation of the registry key.
383                 ///     Conatins the fully qualified key name, and the Hex
384                 ///     representation of the registry key handle.
385                 /// </summary>
386                 public override string ToString()
387                 {
388                         return RegistryApi.ToString (this);
389                 }
390
391                 #endregion // PublicAPI
392
393                 
394                 /// <summary>
395                 /// validate that the registry key handle is still usable.
396                 /// </summary>
397                 private void AssertKeyStillValid ()
398                 {
399                         if (Data == null)
400                                 throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
401                 }
402
403                 
404                 /// <summary>
405                 /// validate that the registry key handle is still usable, and
406                 /// that the 'subKeyName' is not null.
407                 /// </summary>
408                 private void AssertKeyNameNotNull (string subKeyName)
409                 {
410                         if (subKeyName == null)
411                                 throw new ArgumentNullException ();
412                 }
413                 
414
415                 /// <summary>
416                 ///     Utility method to delelte a key's sub keys and values.
417                 ///     This method removes a level of indirection when deleting
418                 ///     key node trees.
419                 /// </summary>
420                 private void DeleteChildKeysAndValues ()
421                 {
422                         if (isRoot)
423                                 return;
424                         
425                         string[] subKeys = GetSubKeyNames ();
426                         foreach (string subKey in subKeys)
427                         {
428                                 RegistryKey sub = OpenSubKey (subKey, true);
429                                 sub.DeleteChildKeysAndValues ();
430                                 sub.Close ();
431                                 DeleteSubKey (subKey, false);
432                         }
433
434                         string[] values = GetValueNames ();
435                         foreach (string value in values) {
436                                 DeleteValue (value, false);
437                         }
438                 }
439
440                 /// <summary>
441                 ///     decode a byte array as a string, and strip trailing nulls
442                 /// </summary>
443                 static internal string DecodeString (byte[] data)
444                 {
445                         string stringRep = Encoding.Unicode.GetString (data);
446                         int idx = stringRep.IndexOf ('\0');
447                         if (idx != -1)
448                                 stringRep = stringRep.TrimEnd ('\0');
449                         return stringRep;
450                 }
451
452                 static internal IOException CreateMarkedForDeletionException ()
453                 {
454                         throw new IOException ("Illegal operation attempted on a"
455                                 + " registry key that has been marked for deletion.");
456                 }
457
458         }
459 }
460