More removal of old profile
[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 #if !NET_2_1
37
38 using System;
39 using System.Collections;
40 using System.IO;
41 using System.Runtime.InteropServices;
42 using System.Security;
43 using System.Text;
44
45 namespace Microsoft.Win32
46 {
47         /// <summary>
48         ///     Function stubs, constants and helper functions for
49         ///     the Win32 registry manipulation utilities.
50         /// </summary>
51         internal class Win32RegistryApi : IRegistryApi
52         {
53                 // bit masks for registry key open access permissions
54                 const int OpenRegKeyRead = 0x00020019; 
55                 const int OpenRegKeyWrite = 0x00020006; 
56
57                 // FIXME must be a way to determin this dynamically?
58                 const int Int32ByteSize = 4;
59
60                 // FIXME this is hard coded on Mono, can it be determined dynamically? 
61                 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
62
63                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
64                 static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
65                
66                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
67                 static extern int RegCloseKey (IntPtr keyHandle);
68
69                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
70                 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
71                                 out IntPtr keyHandle);
72
73                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
74                 private static extern int RegFlushKey (IntPtr keyHandle);
75
76                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
77                 private static extern int RegOpenKeyEx (IntPtr keyBase,
78                                 string keyName, IntPtr reserved, int access,
79                                 out IntPtr keyHandle);
80
81                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
82                 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
83
84                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
85                 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
86
87                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
88                 private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
89
90                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
91                 private static extern int RegEnumValue (IntPtr keyBase, 
92                                 int index, StringBuilder nameBuffer, 
93                                 ref int nameLength, IntPtr reserved, 
94                                 ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
95
96 //              [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
97 //              private static extern int RegSetValueEx (IntPtr keyBase, 
98 //                              string valueName, IntPtr reserved, RegistryValueKind type,
99 //                              StringBuilder data, int rawDataLength);
100
101                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
102                 private static extern int RegSetValueEx (IntPtr keyBase, 
103                                 string valueName, IntPtr reserved, RegistryValueKind type,
104                                 string data, int rawDataLength);
105
106                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
107                 private static extern int RegSetValueEx (IntPtr keyBase, 
108                                 string valueName, IntPtr reserved, RegistryValueKind type,
109                                 byte[] rawData, int rawDataLength);
110
111                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
112                 private static extern int RegSetValueEx (IntPtr keyBase, 
113                                 string valueName, IntPtr reserved, RegistryValueKind type,
114                                 ref int data, int rawDataLength);
115
116                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
117                 private static extern int RegQueryValueEx (IntPtr keyBase,
118                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
119                                 IntPtr zero, ref int dataSize);
120
121                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
122                 private static extern int RegQueryValueEx (IntPtr keyBase,
123                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
124                                 [Out] byte[] data, ref int dataSize);
125
126                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
127                 private static extern int RegQueryValueEx (IntPtr keyBase,
128                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
129                                 ref int data, ref int dataSize);
130
131                 // Returns our handle from the RegistryKey
132                 static IntPtr GetHandle (RegistryKey key)
133                 {
134                         return (IntPtr) key.Handle;
135                 }
136
137                 static bool IsHandleValid (RegistryKey key)
138                 {
139                         return key.Handle != null;
140                 }
141
142                 /// <summary>
143                 /// Acctually read a registry value. Requires knowledge of the
144                 /// value's type and size.
145                 /// </summary>
146                 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
147                 {
148                         RegistryValueKind type = 0;
149                         int size = 0;
150                         object obj = null;
151                         IntPtr handle = GetHandle (rkey);
152                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
153
154                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
155                                 return defaultValue;
156                         }
157                         
158                         if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
159                                 GenerateException (result);
160                         }
161                         
162                         if (type == RegistryValueKind.String) {
163                                 byte[] data;
164                                 result = GetBinaryValue (rkey, name, type, out data, size);
165                                 obj = RegistryKey.DecodeString (data);
166                         } else if (type == RegistryValueKind.ExpandString) {
167                                 byte [] data;
168                                 result = GetBinaryValue (rkey, name, type, out data, size);
169                                 obj = RegistryKey.DecodeString (data);
170                                 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
171                                         obj = Environment.ExpandEnvironmentVariables ((string) obj);
172                         } else if (type == RegistryValueKind.DWord) {
173                                 int data = 0;
174                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
175                                 obj = data;
176                         } else if (type == RegistryValueKind.Binary) {
177                                 byte[] data;
178                                 result = GetBinaryValue (rkey, name, type, out data, size);
179                                 obj = data;
180                         } else if (type == RegistryValueKind.MultiString) {
181                                 obj = null;
182                                 byte[] data;
183                                 result = GetBinaryValue (rkey, name, type, out data, size);
184                                 
185                                 if (result == Win32ResultCode.Success)
186                                         obj = RegistryKey.DecodeString (data).Split ('\0');
187                         } else {
188                                 // should never get here
189                                 throw new SystemException ();
190                         }
191
192                         // check result codes again:
193                         if (result != Win32ResultCode.Success)
194                         {
195                                 GenerateException (result);
196                         }
197                         
198
199                         return obj;
200                 }
201
202                 //
203                 // This version has to do extra checking, make sure that the requested
204                 // valueKind matches the type of the value being stored
205                 //
206                 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
207                 {
208                         Type type = value.GetType ();
209                         int result;
210                         IntPtr handle = GetHandle (rkey);
211
212                         if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
213                                 int rawValue = (int)value;
214                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
215                         } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
216                                 byte[] rawValue = (byte[]) value;
217                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
218                         } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
219                                 string[] vals = (string[]) value;
220                                 StringBuilder fullStringValue = new StringBuilder ();
221                                 foreach (string v in vals)
222                                 {
223                                         fullStringValue.Append (v);
224                                         fullStringValue.Append ('\0');
225                                 }
226                                 fullStringValue.Append ('\0');
227
228                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
229                         
230                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
231                         } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
232                                    type == typeof (string)){
233                                 string rawValue = String.Format ("{0}{1}", value, '\0');
234                                 result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
235                                                         rawValue.Length * NativeBytesPerCharacter);
236                                 
237                         } else if (type.IsArray) {
238                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
239                         } else {
240                                 throw new ArgumentException ("Type does not match the valueKind");
241                         }
242
243                         // handle the result codes
244                         if (result != Win32ResultCode.Success)
245                         {
246                                 GenerateException (result);
247                         }
248                 }
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                         return rkey.Name;
540                 }
541
542                 /// <summary>
543                 ///     utility: Combine the sub key name to the current name to produce a 
544                 ///     fully qualified sub key name.
545                 /// </summary>
546                 internal static string CombineName (RegistryKey rkey, string localName)
547                 {
548                         return String.Concat (rkey.Name, "\\", localName);
549                 }
550         }
551 }
552
553 #endif // NET_2_1
554