1849a46f4e1e4a2c6d5fc81d6a5e6488e203f346
[mono.git] / mcs / class / corlib / Microsoft.Win32 / Win32RegistryApi.cs
1 //
2 // Microsoft.Win32/Win32RegistryApi.cs: wrapper for win32 registry API
3 //
4 // Authos:
5 //      Erik LeBel (eriklebel@yahoo.ca)
6 //      Jackson Harper (jackson@ximian.com)
7 //      Miguel de Icaza (miguel@gnome.org)
8 //
9 // Copyright (C) Erik LeBel 2004
10 // (C) 2004, 2005 Novell, Inc (http://www.novell.com)
11 // 
12
13 //
14 // Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Collections;
38 using System.IO;
39 using System.Runtime.InteropServices;
40 using System.Security;
41 using System.Text;
42
43 namespace Microsoft.Win32
44 {
45         /// <summary>
46         ///     Function stubs, constants and helper functions for
47         ///     the Win32 registry manipulation utilities.
48         /// </summary>
49         internal class Win32RegistryApi : IRegistryApi
50         {
51                 // bit masks for registry key open access permissions
52                 const int OpenRegKeyRead = 0x00020019; 
53                 const int OpenRegKeyWrite = 0x00020006; 
54
55                 // FIXME must be a way to determin this dynamically?
56                 const int Int32ByteSize = 4;
57
58                 // FIXME this is hard coded on Mono, can it be determined dynamically? 
59                 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
60
61                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
62                 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
63                
64                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
65                 static extern int RegCloseKey (IntPtr keyHandle);
66
67                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
68                 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
69                                 out IntPtr keyHandle);
70
71                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
72                 private static extern int RegFlushKey (IntPtr keyHandle);
73
74                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
75                 private static extern int RegOpenKeyEx (IntPtr keyBase,
76                                 string keyName, IntPtr reserved, int access,
77                                 out IntPtr keyHandle);
78
79                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
80                 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
81
82                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
83                 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
84
85                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
86                 private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
87
88                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
89                 private static extern int RegEnumValue (IntPtr keyBase, 
90                                 int index, StringBuilder nameBuffer, 
91                                 ref int nameLength, IntPtr reserved, 
92                                 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
93
94                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
95                 private static extern int RegSetValueEx (IntPtr keyBase, 
96                                 string valueName, IntPtr reserved, RegistryValueKind type,
97                                 StringBuilder data, int rawDataLength);
98
99                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
100                 private static extern int RegSetValueEx (IntPtr keyBase, 
101                                 string valueName, IntPtr reserved, RegistryValueKind type,
102                                 string data, int rawDataLength);
103
104                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
105                 private static extern int RegSetValueEx (IntPtr keyBase, 
106                                 string valueName, IntPtr reserved, RegistryValueKind type,
107                                 byte[] rawData, int rawDataLength);
108
109                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
110                 private static extern int RegSetValueEx (IntPtr keyBase, 
111                                 string valueName, IntPtr reserved, RegistryValueKind type,
112                                 ref int data, int rawDataLength);
113
114                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
115                 private static extern int RegQueryValueEx (IntPtr keyBase,
116                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
117                                 IntPtr zero, ref int dataSize);
118
119                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
120                 private static extern int RegQueryValueEx (IntPtr keyBase,
121                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
122                                 [Out] byte[] data, ref int dataSize);
123
124                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
125                 private static extern int RegQueryValueEx (IntPtr keyBase,
126                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
127                                 ref int data, ref int dataSize);
128
129                 // Returns our handle from the RegistryKey
130                 static IntPtr GetHandle (RegistryKey key)
131                 {
132                         return (IntPtr) key.Handle;
133                 }
134
135                 static bool IsHandleValid (RegistryKey key)
136                 {
137                         return key.Handle != null;
138                 }
139
140                 /// <summary>
141                 /// Acctually read a registry value. Requires knowledge of the
142                 /// value's type and size.
143                 /// </summary>
144                 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
145                 {
146                         RegistryValueKind type = 0;
147                         int size = 0;
148                         object obj = null;
149                         IntPtr handle = GetHandle (rkey);
150                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
151
152                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
153                                 return defaultValue;
154                         }
155                         
156                         if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
157                                 GenerateException (result);
158                         }
159                         
160                         if (type == RegistryValueKind.String) {
161                                 byte[] data;
162                                 result = GetBinaryValue (rkey, name, type, out data, size);
163                                 obj = RegistryKey.DecodeString (data);
164                         } else if (type == RegistryValueKind.ExpandString) {
165                                 byte [] data;
166                                 result = GetBinaryValue (rkey, name, type, out data, size);
167                                 obj = RegistryKey.DecodeString (data);
168                                 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
169                                         obj = Environment.ExpandEnvironmentVariables ((string) obj);
170                         } else if (type == RegistryValueKind.DWord) {
171                                 int data = 0;
172                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
173                                 obj = data;
174                         } else if (type == RegistryValueKind.Binary) {
175                                 byte[] data;
176                                 result = GetBinaryValue (rkey, name, type, out data, size);
177                                 obj = data;
178                         } else if (type == RegistryValueKind.MultiString) {
179                                 obj = null;
180                                 byte[] data;
181                                 result = GetBinaryValue (rkey, name, type, out data, size);
182                                 
183                                 if (result == Win32ResultCode.Success)
184                                         obj = RegistryKey.DecodeString (data).Split ('\0');
185                         } else {
186                                 // should never get here
187                                 throw new SystemException ();
188                         }
189
190                         // check result codes again:
191                         if (result != Win32ResultCode.Success)
192                         {
193                                 GenerateException (result);
194                         }
195                         
196
197                         return obj;
198                 }
199
200 #if NET_2_0
201                 //
202                 // This version has to do extra checking, make sure that the requested
203                 // valueKind matches the type of the value being stored
204                 //
205                 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
206                 {
207                         Type type = value.GetType ();
208                         int result;
209                         IntPtr handle = GetHandle (rkey);
210
211                         if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
212                                 int rawValue = (int)value;
213                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
214                         } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
215                                 byte[] rawValue = (byte[]) value;
216                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
217                         } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
218                                 string[] vals = (string[]) value;
219                                 StringBuilder fullStringValue = new StringBuilder ();
220                                 foreach (string v in vals)
221                                 {
222                                         fullStringValue.Append (v);
223                                         fullStringValue.Append ('\0');
224                                 }
225                                 fullStringValue.Append ('\0');
226
227                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
228                         
229                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
230                         } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
231                                    type == typeof (string)){
232                                 string rawValue = String.Format ("{0}{1}", value, '\0');
233                                 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
234                                                         rawValue.Length * NativeBytesPerCharacter);
235                                 
236                         } else if (type.IsArray) {
237                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
238                         } else {
239                                 throw new ArgumentException ("Type does not match the valueKind");
240                         }
241
242                         // handle the result codes
243                         if (result != Win32ResultCode.Success)
244                         {
245                                 GenerateException (result);
246                         }
247                 }
248 #endif
249         
250                 public void SetValue (RegistryKey rkey, string name, object value)
251                 {
252                         Type type = value.GetType ();
253                         int result;
254                         IntPtr handle = GetHandle (rkey);
255
256                         if (type == typeof (int)) {
257                                 int rawValue = (int)value;
258                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
259                         } else if (type == typeof (byte[])) {
260                                 byte[] rawValue = (byte[]) value;
261                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
262                         } else if (type == typeof (string[])) {
263                                 string[] vals = (string[]) value;
264                                 StringBuilder fullStringValue = new StringBuilder ();
265                                 foreach (string v in vals)
266                                 {
267                                         fullStringValue.Append (v);
268                                         fullStringValue.Append ('\0');
269                                 }
270                                 fullStringValue.Append ('\0');
271
272                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
273                         
274                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
275                         } else if (type.IsArray) {
276                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
277                         } else {
278                                 string rawValue = String.Format ("{0}{1}", value, '\0');
279                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
280                                                         rawValue.Length * NativeBytesPerCharacter);
281                         }
282
283                         if (result == Win32ResultCode.MarkedForDeletion)
284                                 throw RegistryKey.CreateMarkedForDeletionException ();
285
286                         // handle the result codes
287                         if (result != Win32ResultCode.Success)
288                         {
289                                 GenerateException (result);
290                         }
291                 }
292
293                 /// <summary>
294                 ///     Get a binary value.
295                 /// </summary>
296                 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
297                 {
298                         byte[] internalData = new byte [size];
299                         IntPtr handle = GetHandle (rkey);
300                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
301                         data = internalData;
302                         return result;
303                 }
304
305                 
306                 // Arbitrary max size for key/values names that can be fetched.
307                 // .NET framework SDK docs say that the max name length that can 
308                 // be used is 255 characters, we'll allow for a bit more.
309                 const int BufferMaxLength = 1024;
310                 
311                 public int SubKeyCount (RegistryKey rkey)
312                 {
313                         int index;
314                         StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
315                         IntPtr handle = GetHandle (rkey);
316                         
317                         for (index = 0; true; index ++) {
318                                 int result = RegEnumKey (handle, index, stringBuffer,
319                                         stringBuffer.Capacity);
320
321                                 if (result == Win32ResultCode.MarkedForDeletion)
322                                         throw RegistryKey.CreateMarkedForDeletionException ();
323
324                                 if (result == Win32ResultCode.Success)
325                                         continue;
326                                 
327                                 if (result == Win32ResultCode.NoMoreEntries)
328                                         break;
329                                 
330                                 // something is wrong!!
331                                 GenerateException (result);
332                         }
333                         return index;
334                 }
335
336                 public int ValueCount (RegistryKey rkey)
337                 {
338                         int index, result, bufferCapacity;
339                         RegistryValueKind type;
340                         StringBuilder buffer = new StringBuilder (BufferMaxLength);
341                         
342                         IntPtr handle = GetHandle (rkey);
343                         for (index = 0; true; index ++) {
344                                 type = 0;
345                                 bufferCapacity = buffer.Capacity;
346                                 result = RegEnumValue (handle, index, 
347                                                        buffer, ref bufferCapacity,
348                                                        IntPtr.Zero, ref type, 
349                                                        IntPtr.Zero, IntPtr.Zero);
350
351                                 if (result == Win32ResultCode.MarkedForDeletion)
352                                         throw RegistryKey.CreateMarkedForDeletionException ();
353
354                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
355                                         continue;
356                                 
357                                 if (result == Win32ResultCode.NoMoreEntries)
358                                         break;
359                                 
360                                 // something is wrong
361                                 GenerateException (result);
362                         }
363                         return index;
364                 }
365
366                 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
367                 {
368                         IntPtr handle = new IntPtr ((int) hKey);
369
370                         IntPtr keyHandle;
371                         int result = RegConnectRegistry (machineName, handle, out keyHandle);
372                         if (result != Win32ResultCode.Success)
373                                 GenerateException (result);
374
375                         return new RegistryKey (hKey, keyHandle, true);
376                 }
377
378                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
379                 {
380                         int access = OpenRegKeyRead;
381                         if (writable) access |= OpenRegKeyWrite;
382                         IntPtr handle = GetHandle (rkey);
383                         
384                         IntPtr subKeyHandle;
385                         int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
386
387                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
388                                 return null;
389                         
390                         if (result != Win32ResultCode.Success)
391                                 GenerateException (result);
392                         
393                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
394                 }
395
396                 public void Flush (RegistryKey rkey)
397                 {
398                         if (!IsHandleValid (rkey))
399                                 return;
400                         IntPtr handle = GetHandle (rkey);
401                         RegFlushKey (handle);
402                 }
403
404                 public void Close (RegistryKey rkey)
405                 {
406                         if (!IsHandleValid (rkey))
407                                 return;
408                         IntPtr handle = GetHandle (rkey);
409                         RegCloseKey (handle);
410                 }
411
412                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
413                 {
414                         IntPtr handle = GetHandle (rkey);
415                         IntPtr subKeyHandle;
416                         int result = RegCreateKey (handle , keyName, out subKeyHandle);
417
418                         if (result == Win32ResultCode.MarkedForDeletion)
419                                 throw RegistryKey.CreateMarkedForDeletionException ();
420
421                         if (result != Win32ResultCode.Success) {
422                                 GenerateException (result);
423                         }
424                         
425                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
426                                 true);
427                 }
428
429                 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
430                 {
431                         IntPtr handle = GetHandle (rkey);
432                         int result = RegDeleteKey (handle, keyName);
433
434                         if (result == Win32ResultCode.FileNotFound) {
435                                 if (shouldThrowWhenKeyMissing)
436                                         throw new ArgumentException ("key " + keyName);
437                                 return;
438                         }
439                         
440                         if (result != Win32ResultCode.Success)
441                                 GenerateException (result);
442                 }
443
444                 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
445                 {
446                         IntPtr handle = GetHandle (rkey);
447                         int result = RegDeleteValue (handle, value);
448
449                         if (result == Win32ResultCode.MarkedForDeletion)
450                                 return;
451
452                         if (result == Win32ResultCode.FileNotFound){
453                                 if (shouldThrowWhenKeyMissing)
454                                         throw new ArgumentException ("value " + value);
455                                 return;
456                         }
457                         
458                         if (result != Win32ResultCode.Success)
459                                 GenerateException (result);
460                 }
461
462                 public string [] GetSubKeyNames (RegistryKey rkey)
463                 {
464                         IntPtr handle = GetHandle (rkey);
465                         StringBuilder buffer = new StringBuilder (BufferMaxLength);
466                         ArrayList keys = new ArrayList ();
467                                 
468                         for (int index = 0; true; index ++) {
469                                 int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
470
471                                 if (result == Win32ResultCode.Success) {
472                                         keys.Add (buffer.ToString ());
473                                         buffer.Length = 0;
474                                         continue;
475                                 }
476
477                                 if (result == Win32ResultCode.NoMoreEntries)
478                                         break;
479
480                                 // should not be here!
481                                 GenerateException (result);
482                         }
483                         return (string []) keys.ToArray (typeof(String));
484                 }
485
486
487                 public string [] GetValueNames (RegistryKey rkey)
488                 {
489                         IntPtr handle = GetHandle (rkey);
490                         ArrayList values = new ArrayList ();
491                         
492                         for (int index = 0; true; index ++)
493                         {
494                                 StringBuilder buffer = new StringBuilder (BufferMaxLength);
495                                 int bufferCapacity = buffer.Capacity;
496                                 RegistryValueKind type = 0;
497                                 
498                                 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
499                                                         IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
500
501                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
502                                         values.Add (buffer.ToString ());
503                                         continue;
504                                 }
505                                 
506                                 if (result == Win32ResultCode.NoMoreEntries)
507                                         break;
508
509                                 if (result == Win32ResultCode.MarkedForDeletion)
510                                         throw RegistryKey.CreateMarkedForDeletionException ();
511
512                                 GenerateException (result);
513                         }
514
515                         return (string []) values.ToArray (typeof(String));
516                 }
517
518                 /// <summary>
519                 /// convert a win32 error code into an appropriate exception.
520                 /// </summary>
521                 private void GenerateException (int errorCode)
522                 {
523                         switch (errorCode) {
524                                 case Win32ResultCode.FileNotFound:
525                                 case Win32ResultCode.InvalidParameter:
526                                         throw new ArgumentException ();
527                                 case Win32ResultCode.AccessDenied:
528                                         throw new SecurityException ();
529                                 case Win32ResultCode.NetworkPathNotFound:
530                                         throw new IOException ("The network path was not found.");
531                                 default:
532                                         // unidentified system exception
533                                         throw new SystemException ();
534                         }
535                 }
536
537                 public string ToString (RegistryKey rkey)
538                 {
539                         IntPtr handle = GetHandle (rkey);
540                         
541                         return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
542                 }
543
544                 /// <summary>
545                 ///     utility: Combine the sub key name to the current name to produce a 
546                 ///     fully qualified sub key name.
547                 /// </summary>
548                 internal static string CombineName (RegistryKey rkey, string localName)
549                 {
550                         return String.Concat (rkey.Name, "\\", localName);
551                 }
552         }
553 }
554