merge -r 60439:60440
[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.Runtime.InteropServices;
39 using System.Security;
40 using System.Text;
41
42 namespace Microsoft.Win32
43 {
44         /// <summary>
45         ///     Function stubs, constants and helper functions for
46         ///     the Win32 registry manipulation utilities.
47         /// </summary>
48         internal class Win32RegistryApi : IRegistryApi
49         {
50                 // bit masks for registry key open access permissions
51                 const int OpenRegKeyRead = 0x00020019; 
52                 const int OpenRegKeyWrite = 0x00020006; 
53
54                 // FIXME must be a way to determin this dynamically?
55                 const int Int32ByteSize = 4;
56
57                 // FIXME this is hard coded on Mono, can it be determined dynamically? 
58                 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
59
60                 internal enum RegistryType {
61                         String = 1,
62                         EnvironmentString = 2,
63                         Binary = 3,
64                         Dword = 4,
65                         StringArray = 7,
66                 }
67                 
68                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
69                 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
70                
71                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
72                 static extern int RegCloseKey (IntPtr keyHandle);
73
74                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
75                 private static extern int RegFlushKey (IntPtr keyHandle);
76
77                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
78                 private static extern int RegOpenKeyEx (IntPtr keyBase,
79                                 string keyName, IntPtr reserved, int access,
80                                 out IntPtr keyHandle);
81
82                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
83                 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
84
85                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
86                 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
87
88                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
89                 private static extern int RegEnumKey (IntPtr keyBase, int index, [Out] byte[] nameBuffer, int bufferLength);
90
91                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
92                 private static extern int RegEnumValue (IntPtr keyBase, 
93                                 int index, StringBuilder nameBuffer, 
94                                 ref int nameLength, IntPtr reserved, 
95                                 ref RegistryType type, IntPtr data, IntPtr dataLength);
96
97                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
98                 private static extern int RegSetValueEx (IntPtr keyBase, 
99                                 string valueName, IntPtr reserved, RegistryType type,
100                                 StringBuilder data, int rawDataLength);
101
102                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
103                 private static extern int RegSetValueEx (IntPtr keyBase, 
104                                 string valueName, IntPtr reserved, RegistryType type,
105                                 string data, int rawDataLength);
106
107                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
108                 private static extern int RegSetValueEx (IntPtr keyBase, 
109                                 string valueName, IntPtr reserved, RegistryType type,
110                                 byte[] rawData, int rawDataLength);
111
112                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
113                 private static extern int RegSetValueEx (IntPtr keyBase, 
114                                 string valueName, IntPtr reserved, RegistryType type,
115                                 ref int data, int rawDataLength);
116
117                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
118                 private static extern int RegQueryValueEx (IntPtr keyBase,
119                                 string valueName, IntPtr reserved, ref RegistryType type,
120                                 IntPtr zero, ref int dataSize);
121
122                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
123                 private static extern int RegQueryValueEx (IntPtr keyBase,
124                                 string valueName, IntPtr reserved, ref RegistryType type,
125                                 [Out] byte[] data, ref int dataSize);
126
127                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
128                 private static extern int RegQueryValueEx (IntPtr keyBase,
129                                 string valueName, IntPtr reserved, ref RegistryType type,
130                                 ref int data, ref int dataSize);
131
132                 // Returns our handle from the RegistryKey
133                 static IntPtr GetHandle (RegistryKey key)
134                 {
135                         return key.IsRoot ? new IntPtr ((int) key.Data)
136                                 : (IntPtr) key.Data;
137                 }
138
139                 /// <summary>
140                 ///     Acctually read a registry value. Requires knoledge of the
141                 ///     value's type and size.
142                 /// </summary>
143                 public object GetValue (RegistryKey rkey, string name, bool returnDefaultValue, object defaultValue)
144                 {
145                         RegistryType type = 0;
146                         int size = 0;
147                         object obj = null;
148                         IntPtr handle = GetHandle (rkey);
149                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
150
151                         if (result == Win32ResultCode.FileNotFound) {
152                                 if (returnDefaultValue) {
153                                         return defaultValue;
154                                 }
155                                 return null;
156                         }
157                         
158                         if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
159                                 GenerateException (result);
160                         }
161                         
162                         if (type == RegistryType.String || type == RegistryType.EnvironmentString) {
163                                 byte[] data;
164                                 result = GetBinaryValue (rkey, name, type, out data, size);
165                                 obj = RegistryKey.DecodeString (data);
166                         } else if (type == RegistryType.Dword) {
167                                 int data = 0;
168                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
169                                 obj = data;
170                         } else if (type == RegistryType.Binary) {
171                                 byte[] data;
172                                 result = GetBinaryValue (rkey, name, type, out data, size);
173                                 obj = data;
174                         } else if (type == RegistryType.StringArray) {
175                                 obj = null;
176                                 byte[] data;
177                                 result = GetBinaryValue (rkey, name, type, out data, size);
178                                 
179                                 if (result == Win32ResultCode.Success)
180                                         obj = RegistryKey.DecodeString (data).Split ('\0');
181                         } else {
182                                 // should never get here
183                                 throw new SystemException ();
184                         }
185
186                         // check result codes again:
187                         if (result != Win32ResultCode.Success)
188                         {
189                                 GenerateException (result);
190                         }
191                         
192
193                         return obj;
194                 }
195
196                 public void SetValue (RegistryKey rkey, string name, object value)
197                 {
198                         Type type = value.GetType ();
199                         int result;
200                         IntPtr handle = GetHandle (rkey);
201
202                         if (type == typeof (int)) {
203                                 int rawValue = (int)value;
204                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Dword, ref rawValue, Int32ByteSize); 
205                         } else if (type == typeof (byte[])) {
206                                 byte[] rawValue = (byte[]) value;
207                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Binary, rawValue, rawValue.Length);
208                         } else if (type == typeof (string[])) {
209                                 string[] vals = (string[]) value;
210                                 StringBuilder fullStringValue = new StringBuilder ();
211                                 foreach (string v in vals)
212                                 {
213                                         fullStringValue.Append (v);
214                                         fullStringValue.Append ('\0');
215                                 }
216                                 fullStringValue.Append ('\0');
217
218                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
219                         
220                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.StringArray, rawValue, rawValue.Length); 
221                         } else if (type.IsArray) {
222                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
223                         } else {
224                                 string rawValue = String.Format ("{0}{1}", value, '\0');
225                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.String, rawValue,
226                                                         rawValue.Length * NativeBytesPerCharacter);
227                         }
228
229                         // handle the result codes
230                         if (result != Win32ResultCode.Success)
231                         {
232                                 GenerateException (result);
233                         }
234                 }
235
236                 /// <summary>
237                 ///     Get a binary value.
238                 /// </summary>
239                 private int GetBinaryValue (RegistryKey rkey, string name, RegistryType type, out byte[] data, int size)
240                 {
241                         byte[] internalData = new byte [size];
242                         IntPtr handle = GetHandle (rkey);
243                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
244                         data = internalData;
245                         return result;
246                 }
247
248                 
249                 // Arbitrary max size for key/values names that can be fetched.
250                 // .NET framework SDK docs say that the max name length that can 
251                 // be used is 255 characters, we'll allow for a bit more.
252                 const int BufferMaxLength = 1024;
253                 
254                 public int SubKeyCount (RegistryKey rkey)
255                 {
256                         int index, result;
257                         byte[] stringBuffer = new byte [BufferMaxLength];
258                         IntPtr handle = GetHandle (rkey);
259                         
260                         for (index = 0; true; index ++) {
261                                 result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
262                                 
263                                 if (result == Win32ResultCode.Success)
264                                         continue;
265                                 
266                                 if (result == Win32ResultCode.NoMoreEntries)
267                                         break;
268                                 
269                                 // something is wrong!!
270                                 GenerateException (result);
271                         }
272                         return index;
273                 }
274
275                 public int ValueCount (RegistryKey rkey)
276                 {
277                         int index, result, bufferCapacity;
278                         RegistryType type;
279                         StringBuilder buffer = new StringBuilder (BufferMaxLength);
280                         
281                         IntPtr handle = GetHandle (rkey);
282                         for (index = 0; true; index ++) {
283                                 type = 0;
284                                 bufferCapacity = buffer.Capacity;
285                                 result = RegEnumValue (handle, index, 
286                                                        buffer, ref bufferCapacity,
287                                                        IntPtr.Zero, ref type, 
288                                                        IntPtr.Zero, IntPtr.Zero);
289                                 
290                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
291                                         continue;
292                                 
293                                 if (result == Win32ResultCode.NoMoreEntries)
294                                         break;
295                                 
296                                 // something is wrong
297                                 GenerateException (result);
298                         }
299                         return index;
300                 }
301                 
302                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writtable)
303                 {
304                         int access = OpenRegKeyRead;
305                         if (writtable) access |= OpenRegKeyWrite;
306                         IntPtr handle = GetHandle (rkey);
307                         
308                         IntPtr subKeyHandle;
309                         int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
310
311                         if (result == Win32ResultCode.FileNotFound)
312                                 return null;
313                         
314                         if (result != Win32ResultCode.Success)
315                                 GenerateException (result);
316                         
317                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
318                 }
319
320                 public void Flush (RegistryKey rkey)
321                 {
322                         IntPtr handle = GetHandle (rkey);
323                         RegFlushKey (handle);
324                 }
325
326                 public void Close (RegistryKey rkey)
327                 {
328                         IntPtr handle = GetHandle (rkey);
329                         RegCloseKey (handle);
330                 }
331
332                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
333                 {
334                         IntPtr handle = GetHandle (rkey);
335                         IntPtr subKeyHandle;
336                         int result = RegCreateKey (handle , keyName, out subKeyHandle);
337
338                         if (result != Win32ResultCode.Success) {
339                                 GenerateException (result);
340                         }
341                         
342                         RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
343
344                         return subKey;
345                 }
346
347                 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
348                 {
349                         IntPtr handle = GetHandle (rkey);
350                         int result = RegDeleteKey (handle, keyName);
351
352                         if (result == Win32ResultCode.FileNotFound) {
353                                 if (shouldThrowWhenKeyMissing)
354                                         throw new ArgumentException ("key " + keyName);
355                                 return;
356                         }
357                         
358                         if (result != Win32ResultCode.Success)
359                                 GenerateException (result);
360                 }
361
362                 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
363                 {
364                         IntPtr handle = GetHandle (rkey);
365                         int result = RegDeleteValue (handle, value);
366                         
367                         if (result == Win32ResultCode.FileNotFound){
368                                 if (shouldThrowWhenKeyMissing)
369                                         throw new ArgumentException ("value " + value);
370                                 return;
371                         }
372                         
373                         if (result != Win32ResultCode.Success)
374                                 GenerateException (result);
375                 }
376
377                 public string [] GetSubKeyNames (RegistryKey rkey)
378                 {
379                         IntPtr handle = GetHandle (rkey);
380                         byte[] buffer = new byte [BufferMaxLength];
381                         int bufferCapacity = BufferMaxLength;
382                         ArrayList keys = new ArrayList ();
383                                 
384                         for (int index = 0; true; index ++) {
385                                 int result = RegEnumKey (handle, index, buffer, bufferCapacity);
386
387                                 if (result == Win32ResultCode.Success) {
388                                         keys.Add (RegistryKey.DecodeString (buffer));
389                                         continue;
390                                 }
391
392                                 if (result == Win32ResultCode.NoMoreEntries)
393                                         break;
394
395                                 // should not be here!
396                                 GenerateException (result);
397                         }
398                         return (string []) keys.ToArray (typeof(String));
399                 }
400
401
402                 public string [] GetValueNames (RegistryKey rkey)
403                 {
404                         IntPtr handle = GetHandle (rkey);
405                         ArrayList values = new ArrayList ();
406                         
407                         for (int index = 0; true; index ++)
408                         {
409                                 StringBuilder buffer = new StringBuilder (BufferMaxLength);
410                                 int bufferCapacity = buffer.Capacity;
411                                 RegistryType type = 0;
412                                 
413                                 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
414                                                         IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
415
416                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
417                                         values.Add (buffer.ToString ());
418                                         continue;
419                                 }
420                                 
421                                 if (result == Win32ResultCode.NoMoreEntries)
422                                         break;
423                                         
424                                 GenerateException (result);
425                         }
426
427                         return (string []) values.ToArray (typeof(String));
428                 }
429
430                 /// <summary>
431                 /// convert a win32 error code into an appropriate exception.
432                 /// </summary>
433                 private void GenerateException (int errorCode)
434                 {
435                         switch (errorCode) {
436                                 case Win32ResultCode.FileNotFound:
437                                 case Win32ResultCode.InvalidParameter:
438                                         throw new ArgumentException ();
439                                 
440                                 case Win32ResultCode.AccessDenied:
441                                         throw new SecurityException ();
442
443                                 default:
444                                         // unidentified system exception
445                                         throw new SystemException ();
446                         }
447                 }
448
449                 public string ToString (RegistryKey rkey)
450                 {
451                         IntPtr handle = GetHandle (rkey);
452                         
453                         return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
454                 }
455
456                 /// <summary>
457                 ///     utility: Combine the sub key name to the current name to produce a 
458                 ///     fully qualified sub key name.
459                 /// </summary>
460                 internal static string CombineName (RegistryKey rkey, string localName)
461                 {
462                         return String.Concat (rkey.Name, "\\", localName);
463                 }
464         }
465 }
466