03629c99c632573e6617120f9f070115eb943ad2
[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                 /// <summary>
133                 ///     Acctually read a registry value. Requires knoledge of the
134                 ///     value's type and size.
135                 /// </summary>
136                 public object GetValue (RegistryKey rkey, string name, bool returnDefaultValue, object defaultValue)
137                 {
138                         RegistryType type = 0;
139                         int size = 0;
140                         object obj = null;
141                         IntPtr handle = (IntPtr)rkey.Data;
142                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
143
144                         if (result == Win32ResultCode.FileNotFound) {
145                                 if (returnDefaultValue) {
146                                         return defaultValue;
147                                 }
148                                 return null;
149                         }
150                         
151                         if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
152                                 GenerateException (result);
153                         }
154                         
155                         if (type == RegistryType.String || type == RegistryType.EnvironmentString) {
156                                 byte[] data;
157                                 result = GetBinaryValue (rkey, name, type, out data, size);
158                                 obj = RegistryKey.DecodeString (data);
159                         } else if (type == RegistryType.Dword) {
160                                 int data = 0;
161                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
162                                 obj = data;
163                         } else if (type == RegistryType.Binary) {
164                                 byte[] data;
165                                 result = GetBinaryValue (rkey, name, type, out data, size);
166                                 obj = data;
167                         } else if (type == RegistryType.StringArray) {
168                                 obj = null;
169                                 byte[] data;
170                                 result = GetBinaryValue (rkey, name, type, out data, size);
171                                 
172                                 if (result == Win32ResultCode.Success)
173                                         obj = RegistryKey.DecodeString (data).Split ('\0');
174                         } else {
175                                 // should never get here
176                                 throw new SystemException ();
177                         }
178
179                         // check result codes again:
180                         if (result != Win32ResultCode.Success)
181                         {
182                                 GenerateException (result);
183                         }
184                         
185
186                         return obj;
187                 }
188
189                 public void SetValue (RegistryKey rkey, string name, object value)
190                 {
191                         Type type = value.GetType ();
192                         int result;
193                         IntPtr handle = (IntPtr)rkey.Data;
194
195                         if (type == typeof (int)) {
196                                 int rawValue = (int)value;
197                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Dword, ref rawValue, Int32ByteSize); 
198                         } else if (type == typeof (byte[])) {
199                                 byte[] rawValue = (byte[]) value;
200                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Binary, rawValue, rawValue.Length);
201                         } else if (type == typeof (string[])) {
202                                 string[] vals = (string[]) value;
203                                 StringBuilder fullStringValue = new StringBuilder ();
204                                 foreach (string v in vals)
205                                 {
206                                         fullStringValue.Append (v);
207                                         fullStringValue.Append ('\0');
208                                 }
209                                 fullStringValue.Append ('\0');
210
211                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
212                         
213                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.StringArray, rawValue, rawValue.Length); 
214                         } else if (type.IsArray) {
215                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
216                         } else {
217                                 string rawValue = String.Format ("{0}{1}", value, '\0');
218                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.String, rawValue,
219                                                         rawValue.Length * NativeBytesPerCharacter);
220                         }
221
222                         // handle the result codes
223                         if (result != Win32ResultCode.Success)
224                         {
225                                 GenerateException (result);
226                         }
227                 }
228
229                 /// <summary>
230                 ///     Get a binary value.
231                 /// </summary>
232                 private int GetBinaryValue (RegistryKey rkey, string name, RegistryType type, out byte[] data, int size)
233                 {
234                         byte[] internalData = new byte [size];
235                         IntPtr handle = (IntPtr)rkey.Data;
236                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
237                         data = internalData;
238                         return result;
239                 }
240
241                 
242                 // Arbitrary max size for key/values names that can be fetched.
243                 // .NET framework SDK docs say that the max name length that can 
244                 // be used is 255 characters, we'll allow for a bit more.
245                 const int BufferMaxLength = 1024;
246                 
247                 public int SubKeyCount (RegistryKey rkey)
248                 {
249                         int index, result;
250                         byte[] stringBuffer = new byte [BufferMaxLength];
251                         IntPtr handle = (IntPtr)rkey.Data;
252                         
253                         for (index = 0; true; index ++) {
254                                 result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
255                                 
256                                 if (result == Win32ResultCode.Success)
257                                         continue;
258                                 
259                                 if (result == Win32ResultCode.NoMoreEntries)
260                                         break;
261                                 
262                                 // something is wrong!!
263                                 GenerateException (result);
264                         }
265                         return index;
266                 }
267
268                 public int ValueCount (RegistryKey rkey)
269                 {
270                         int index, result, bufferCapacity;
271                         RegistryType type;
272                         StringBuilder buffer = new StringBuilder (BufferMaxLength);
273                         
274                         IntPtr handle = (IntPtr)rkey.Data;
275                         for (index = 0; true; index ++) {
276                                 type = 0;
277                                 bufferCapacity = buffer.Capacity;
278                                 result = RegEnumValue (handle, index, 
279                                                        buffer, ref bufferCapacity,
280                                                        IntPtr.Zero, ref type, 
281                                                        IntPtr.Zero, IntPtr.Zero);
282                                 
283                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
284                                         continue;
285                                 
286                                 if (result == Win32ResultCode.NoMoreEntries)
287                                         break;
288                                 
289                                 // something is wrong
290                                 GenerateException (result);
291                         }
292                         return index;
293                 }
294                 
295                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writtable)
296                 {
297                         int access = OpenRegKeyRead;
298                         if (writtable) access |= OpenRegKeyWrite;
299                         IntPtr handle = new IntPtr ((int) rkey.Data);
300                         
301                         IntPtr subKeyHandle;
302                         int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
303
304                         if (result == Win32ResultCode.FileNotFound)
305                                 return null;
306                         
307                         if (result != Win32ResultCode.Success)
308                                 GenerateException (result);
309                         
310                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
311                 }
312
313                 public void Flush (RegistryKey rkey)
314                 {
315                         IntPtr handle = (IntPtr)rkey.Data;
316                         RegFlushKey (handle);
317                 }
318
319                 public void Close (RegistryKey rkey)
320                 {
321                         IntPtr handle = (IntPtr)rkey.Data;
322                         RegCloseKey (handle);
323                 }
324
325                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
326                 {
327                         IntPtr handle = (IntPtr)rkey.Data;
328                         IntPtr subKeyHandle;
329                         int result = RegCreateKey (handle , keyName, out subKeyHandle);
330
331                         if (result != Win32ResultCode.Success) {
332                                 GenerateException (result);
333                         }
334                         
335                         RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
336
337                         return subKey;
338                 }
339
340                 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
341                 {
342                         IntPtr handle = (IntPtr)rkey.Data;
343                         int result = RegDeleteKey (handle, keyName);
344
345                         if (result == Win32ResultCode.FileNotFound) {
346                                 if (shouldThrowWhenKeyMissing)
347                                         throw new ArgumentException ("key " + keyName);
348                                 return;
349                         }
350                         
351                         if (result != Win32ResultCode.Success)
352                                 GenerateException (result);
353                 }
354
355                 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
356                 {
357                         IntPtr handle = (IntPtr)rkey.Data;
358                         int result = RegDeleteValue (handle, value);
359                         
360                         if (result == Win32ResultCode.FileNotFound){
361                                 if (shouldThrowWhenKeyMissing)
362                                         throw new ArgumentException ("value " + value);
363                                 return;
364                         }
365                         
366                         if (result != Win32ResultCode.Success)
367                                 GenerateException (result);
368                 }
369
370                 public string [] GetSubKeyNames (RegistryKey rkey)
371                 {
372                         IntPtr handle = (IntPtr)rkey.Data;
373                         byte[] buffer = new byte [BufferMaxLength];
374                         int bufferCapacity = BufferMaxLength;
375                         ArrayList keys = new ArrayList ();
376                                 
377                         for (int index = 0; true; index ++) {
378                                 int result = RegEnumKey (handle, index, buffer, bufferCapacity);
379
380                                 if (result == Win32ResultCode.Success) {
381                                         keys.Add (RegistryKey.DecodeString (buffer));
382                                         continue;
383                                 }
384
385                                 if (result == Win32ResultCode.NoMoreEntries)
386                                         break;
387
388                                 // should not be here!
389                                 GenerateException (result);
390                         }
391                         return (string []) keys.ToArray (typeof(String));
392                 }
393
394
395                 public string [] GetValueNames (RegistryKey rkey)
396                 {
397                         IntPtr handle = (IntPtr)rkey.Data;
398                         ArrayList values = new ArrayList ();
399                         
400                         for (int index = 0; true; index ++)
401                         {
402                                 StringBuilder buffer = new StringBuilder (BufferMaxLength);
403                                 int bufferCapacity = buffer.Capacity;
404                                 RegistryType type = 0;
405                                 
406                                 int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
407                                                         IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
408
409                                 if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
410                                         values.Add (buffer.ToString ());
411                                         continue;
412                                 }
413                                 
414                                 if (result == Win32ResultCode.NoMoreEntries)
415                                         break;
416                                         
417                                 GenerateException (result);
418                         }
419
420                         return (string []) values.ToArray (typeof(String));
421                 }
422
423                 /// <summary>
424                 /// convert a win32 error code into an appropriate exception.
425                 /// </summary>
426                 private void GenerateException (int errorCode)
427                 {
428                         switch (errorCode) {
429                                 case Win32ResultCode.FileNotFound:
430                                 case Win32ResultCode.InvalidParameter:
431                                         throw new ArgumentException ();
432                                 
433                                 case Win32ResultCode.AccessDenied:
434                                         throw new SecurityException ();
435
436                                 default:
437                                         // unidentified system exception
438                                         throw new SystemException ();
439                         }
440                 }
441
442                 public string ToString (RegistryKey rkey)
443                 {
444                         IntPtr handle = (IntPtr)rkey.Data;
445                         
446                         return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
447                 }
448
449                 /// <summary>
450                 ///     utility: Combine the sub key name to the current name to produce a 
451                 ///     fully qualified sub key name.
452                 /// </summary>
453                 internal static string CombineName (RegistryKey rkey, string localName)
454                 {
455                         return String.Concat (rkey.Name, "\\", localName);
456                 }
457         }
458 }
459