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