Implement mono_gc_alloc_fixed on Boehm to be uncollectable. This matches SGen behavio...
[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 !MOBILE
37
38 using System;
39 using System.Collections;
40 using System.Collections.Generic;
41 using System.IO;
42 using System.Runtime.InteropServices;
43 using System.Security;
44 using System.Text;
45 using Microsoft.Win32.SafeHandles;
46
47 namespace Microsoft.Win32
48 {
49         /// <summary>
50         ///     Function stubs, constants and helper functions for
51         ///     the Win32 registry manipulation utilities.
52         /// </summary>
53         internal class Win32RegistryApi : IRegistryApi
54         {
55                 // bit masks for registry key open access permissions
56                 const int OpenRegKeyRead = 0x00020019; 
57                 const int OpenRegKeyWrite = 0x00020006; 
58
59                 // FIXME must be a way to determin this dynamically?
60                 const int Int32ByteSize = 4;
61                 const int Int64ByteSize = 8;
62
63                 // FIXME this is hard coded on Mono, can it be determined dynamically? 
64                 readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
65
66                 const int RegOptionsNonVolatile = 0x00000000;
67                 const int RegOptionsVolatile = 0x00000001;
68
69                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKeyEx")]
70                 static extern int RegCreateKeyEx (IntPtr keyBase, string keyName, int reserved, 
71                         IntPtr lpClass, int options, int access, IntPtr securityAttrs,
72                         out IntPtr keyHandle, out int disposition);
73                
74                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
75                 static extern int RegCloseKey (IntPtr keyHandle);
76
77                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
78                 static extern int RegConnectRegistry (string machineName, IntPtr hKey,
79                                 out IntPtr keyHandle);
80
81                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
82                 private static extern int RegFlushKey (IntPtr keyHandle);
83
84                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
85                 private static extern int RegOpenKeyEx (IntPtr keyBase,
86                                 string keyName, IntPtr reserved, int access,
87                                 out IntPtr keyHandle);
88
89                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
90                 private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
91
92                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
93                 private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
94
95                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKeyExW")]
96                 internal unsafe static extern int RegEnumKeyEx (IntPtr keyHandle, int dwIndex,
97                                         char* lpName, ref int lpcbName, int[] lpReserved,
98                                         [Out]StringBuilder lpClass, int[] lpcbClass,
99                                         long[] lpftLastWriteTime);
100
101                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
102                 internal unsafe static extern int RegEnumValue (IntPtr hKey, int dwIndex,
103                                         char* lpValueName, ref int lpcbValueName,
104                                         IntPtr lpReserved_MustBeZero, int[] lpType, byte[] lpData,
105                                         int[] lpcbData);
106
107 //              [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
108 //              private static extern int RegSetValueEx (IntPtr keyBase, 
109 //                              string valueName, IntPtr reserved, RegistryValueKind type,
110 //                              StringBuilder data, 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, RegistryValueKind type,
115                                 string data, int rawDataLength);
116
117                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
118                 private static extern int RegSetValueEx (IntPtr keyBase, 
119                                 string valueName, IntPtr reserved, RegistryValueKind type,
120                                 byte[] rawData, int rawDataLength);
121
122                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
123                 private static extern int RegSetValueEx (IntPtr keyBase, 
124                                 string valueName, IntPtr reserved, RegistryValueKind type,
125                                 ref int data, int rawDataLength);
126
127                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
128                 private static extern int RegSetValueEx (IntPtr keyBase, 
129                                 string valueName, IntPtr reserved, RegistryValueKind type,
130                                 ref long data, int rawDataLength);
131
132                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
133                 private static extern int RegQueryValueEx (IntPtr keyBase,
134                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
135                                 IntPtr zero, ref int dataSize);
136
137                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
138                 private static extern int RegQueryValueEx (IntPtr keyBase,
139                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
140                                 [Out] byte[] data, ref int dataSize);
141
142                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
143                 private static extern int RegQueryValueEx (IntPtr keyBase,
144                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
145                                 ref int data, ref int dataSize);
146
147                 [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
148                 private static extern int RegQueryValueEx (IntPtr keyBase,
149                                 string valueName, IntPtr reserved, ref RegistryValueKind type,
150                                 ref long data, ref int dataSize);
151
152                 [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint="RegQueryInfoKeyW")]
153                 internal static extern int RegQueryInfoKey (IntPtr hKey, [Out]StringBuilder lpClass,
154                         int[] lpcbClass, IntPtr lpReserved_MustBeZero, ref int lpcSubKeys,
155                         int[] lpcbMaxSubKeyLen, int[] lpcbMaxClassLen,
156                         ref int lpcValues, int[] lpcbMaxValueNameLen,
157                         int[] lpcbMaxValueLen, int[] lpcbSecurityDescriptor,
158                         int[] lpftLastWriteTime);
159
160                 // Returns our handle from the RegistryKey
161                 public IntPtr GetHandle (RegistryKey key)
162                 {
163                         return (IntPtr) key.InternalHandle;
164                 }
165
166                 static bool IsHandleValid (RegistryKey key)
167                 {
168                         return key.InternalHandle != null;
169                 }
170
171                 public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
172                 {
173                         RegistryValueKind type = 0;
174                         int size = 0;
175                         IntPtr handle = GetHandle (rkey);
176                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
177
178                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) 
179                                 return RegistryValueKind.Unknown;
180
181                         return type;
182                 }
183                 
184                 /// <summary>
185                 /// Acctually read a registry value. Requires knowledge of the
186                 /// value's type and size.
187                 /// </summary>
188                 public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
189                 {
190                         RegistryValueKind type = 0;
191                         int size = 0;
192                         object obj = null;
193                         IntPtr handle = GetHandle (rkey);
194                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
195
196                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
197                                 return defaultValue;
198                         }
199                         
200                         if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
201                                 GenerateException (result);
202                         }
203                         
204                         if (type == RegistryValueKind.String) {
205                                 byte[] data;
206                                 result = GetBinaryValue (rkey, name, type, out data, size);
207                                 obj = RegistryKey.DecodeString (data);
208                         } else if (type == RegistryValueKind.ExpandString) {
209                                 byte [] data;
210                                 result = GetBinaryValue (rkey, name, type, out data, size);
211                                 obj = RegistryKey.DecodeString (data);
212                                 if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
213                                         obj = Environment.ExpandEnvironmentVariables ((string) obj);
214                         } else if (type == RegistryValueKind.DWord) {
215                                 int data = 0;
216                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
217                                 obj = data;
218                         } else if (type == RegistryValueKind.QWord) {
219                                 long data = 0;
220                                 result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
221                                 obj = data;
222                         } else if (type == RegistryValueKind.Binary) {
223                                 byte[] data;
224                                 result = GetBinaryValue (rkey, name, type, out data, size);
225                                 obj = data;
226                         } else if (type == RegistryValueKind.MultiString) {
227                                 obj = null;
228                                 byte[] data;
229                                 result = GetBinaryValue (rkey, name, type, out data, size);
230                                 
231                                 if (result == Win32ResultCode.Success)
232                                         obj = RegistryKey.DecodeString (data).Split ('\0');
233                         } else {
234                                 // should never get here
235                                 throw new SystemException ();
236                         }
237
238                         // check result codes again:
239                         if (result != Win32ResultCode.Success)
240                         {
241                                 GenerateException (result);
242                         }
243                         
244
245                         return obj;
246                 }
247
248                 //
249                 // This version has to do extra checking, make sure that the requested
250                 // valueKind matches the type of the value being stored
251                 //
252                 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
253                 {
254                         Type type = value.GetType ();
255                         IntPtr handle = GetHandle (rkey);
256
257                         switch (valueKind) {
258                         case RegistryValueKind.QWord:
259                                 try {
260                                         long rawValue = Convert.ToInt64 (value);
261                                         CheckResult (RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.QWord, ref rawValue, Int64ByteSize));
262                                         return;
263                                 } catch (OverflowException) {
264                                 }
265                                 break;
266                         case RegistryValueKind.DWord:
267                                 try {
268                                         int rawValue = Convert.ToInt32 (value);
269                                         CheckResult (RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize));
270                                         return;
271                                 } catch (OverflowException) {
272                                 }
273                                 break;
274                         case RegistryValueKind.Binary:
275                                 if (type == typeof (byte[])) {
276                                         byte[] rawValue = (byte[]) value;
277                                         CheckResult (RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length));
278                                         return;
279                                 }
280                                 break;
281                         case RegistryValueKind.MultiString:
282                                 if (type == typeof (string[])) {
283                                         string[] vals = (string[]) value;
284                                         StringBuilder fullStringValue = new StringBuilder ();
285                                         foreach (string v in vals)
286                                         {
287                                                 fullStringValue.Append (v);
288                                                 fullStringValue.Append ('\0');
289                                         }
290                                         fullStringValue.Append ('\0');
291
292                                         byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
293                         
294                                         CheckResult (RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length));
295                                         return;
296                                 }
297                                 break;
298                         case RegistryValueKind.String:
299                         case RegistryValueKind.ExpandString:
300                                 if (type == typeof (string)) {
301                                         string rawValue = String.Format ("{0}{1}", value, '\0');
302                                         CheckResult (RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
303                                                                 rawValue.Length * NativeBytesPerCharacter));
304                                         return;
305                                 }
306                                 break;
307                         default:
308                                 if (type.IsArray) {
309                                         throw new ArgumentException ("Only string and byte arrays can written as registry values");
310                                 }
311                                 break;
312                         }
313
314                         throw new ArgumentException ("Type does not match the valueKind");
315                 }
316         
317                 public void SetValue (RegistryKey rkey, string name, object value)
318                 {
319                         Type type = value.GetType ();
320                         int result;
321                         IntPtr handle = GetHandle (rkey);
322
323                         if (type == typeof (int)) {
324                                 int rawValue = (int)value;
325                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
326                         } else if (type == typeof (byte[])) {
327                                 byte[] rawValue = (byte[]) value;
328                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
329                         } else if (type == typeof (string[])) {
330                                 string[] vals = (string[]) value;
331                                 StringBuilder fullStringValue = new StringBuilder ();
332                                 foreach (string v in vals)
333                                 {
334                                         fullStringValue.Append (v);
335                                         fullStringValue.Append ('\0');
336                                 }
337                                 fullStringValue.Append ('\0');
338
339                                 byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
340                         
341                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
342                         } else if (type.IsArray) {
343                                 throw new ArgumentException ("Only string and byte arrays can written as registry values");
344                         } else {
345                                 string rawValue = String.Format ("{0}{1}", value, '\0');
346                                 result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
347                                                         rawValue.Length * NativeBytesPerCharacter);
348                         }
349
350                         // handle the result codes
351                         if (result != Win32ResultCode.Success)
352                         {
353                                 GenerateException (result);
354                         }
355                 }
356
357                 /// <summary>
358                 ///     Get a binary value.
359                 /// </summary>
360                 private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
361                 {
362                         byte[] internalData = new byte [size];
363                         IntPtr handle = GetHandle (rkey);
364                         int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
365                         data = internalData;
366                         return result;
367                 }
368
369                 // MSDN defines the following limits for registry key names & values:
370                 // Key Name: 255 characters
371                 // Value name:  16,383 Unicode characters
372                 // Value: either 1 MB or current available memory, depending on registry format.
373                 private const int MaxKeyLength = 255;
374                 private const int MaxValueLength = 16383;
375                 
376                 public int SubKeyCount (RegistryKey rkey)
377                 {
378                         int subkeys = 0;
379                         int junk = 0;
380                         int ret = RegQueryInfoKey (GetHandle (rkey),
381                                                                            null,
382                                                                            null,
383                                                                            IntPtr.Zero,
384                                                                            ref subkeys,  // subkeys
385                                                                            null,
386                                                                            null,
387                                                                            ref junk,     // values
388                                                                            null,
389                                                                            null,
390                                                                            null,
391                                                                            null);
392
393                         if (ret != Win32ResultCode.Success)
394                                 GenerateException (ret);
395                         return subkeys;
396                 }
397
398                 public int ValueCount (RegistryKey rkey)
399                 {
400                         int values = 0;
401                         int junk = 0;
402                         int ret = RegQueryInfoKey (GetHandle (rkey),
403                                                                            null,
404                                                                            null,
405                                                                            IntPtr.Zero,
406                                                                            ref junk,     // subkeys
407                                                                            null,
408                                                                            null,
409                                                                            ref values,   // values
410                                                                            null,
411                                                                            null,
412                                                                            null,
413                                                                            null);
414                         if (ret != Win32ResultCode.Success)
415                                 GenerateException (ret);
416                         return values;
417                 }
418
419                 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
420                 {
421                         IntPtr handle = new IntPtr ((int) hKey);
422
423                         IntPtr keyHandle;
424                         int result = RegConnectRegistry (machineName, handle, out keyHandle);
425                         if (result != Win32ResultCode.Success)
426                                 GenerateException (result);
427
428                         return new RegistryKey (hKey, keyHandle, true);
429                 }
430
431                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
432                 {
433                         int access = OpenRegKeyRead;
434                         if (writable) access |= OpenRegKeyWrite;
435                         IntPtr handle = GetHandle (rkey);
436                         
437                         IntPtr subKeyHandle;
438                         int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
439
440                         if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
441                                 return null;
442                         
443                         if (result != Win32ResultCode.Success)
444                                 GenerateException (result);
445                         
446                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
447                 }
448
449                 public void Flush (RegistryKey rkey)
450                 {
451                         if (!IsHandleValid (rkey))
452                                 return;
453                         IntPtr handle = GetHandle (rkey);
454                         RegFlushKey (handle);
455                 }
456
457                 public void Close (RegistryKey rkey)
458                 {
459                         if (!IsHandleValid (rkey))
460                                 return;
461                         SafeRegistryHandle safe_handle = rkey.Handle;
462                         if (safe_handle != null) {
463                                 // closes the unmanaged pointer for us.
464                                 safe_handle.Close ();
465                                 return;
466                         }
467                         IntPtr handle = GetHandle (rkey);
468                         RegCloseKey (handle);
469                 }
470
471                 public RegistryKey FromHandle (SafeRegistryHandle handle)
472                 {
473                         // At this point we can't tell whether the key is writable
474                         // or not (nor the name), so we let the error check code handle it later, as
475                         // .Net seems to do.
476                         return new RegistryKey (handle.DangerousGetHandle (), String.Empty, true);
477                 }
478
479                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
480                 {
481                         IntPtr handle = GetHandle (rkey);
482                         IntPtr subKeyHandle;
483                         int disposition;
484                         int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
485                                 RegOptionsNonVolatile,
486                                 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
487
488                         if (result != Win32ResultCode.Success) {
489                                 GenerateException (result);
490                         }
491                         
492                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
493                                 true);
494                 }
495
496                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyName, RegistryOptions options)
497                 {
498                         IntPtr handle = GetHandle (rkey);
499                         IntPtr subKeyHandle;
500                         int disposition;
501                         int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
502                                 options == RegistryOptions.Volatile ? RegOptionsVolatile : RegOptionsNonVolatile,
503                                 OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
504
505                         if (result != Win32ResultCode.Success)
506                                 GenerateException (result);
507                         
508                         return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
509                                 true);
510                 }
511
512                 public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
513                 {
514                         IntPtr handle = GetHandle (rkey);
515                         int result = RegDeleteKey (handle, keyName);
516
517                         if (result == Win32ResultCode.FileNotFound) {
518                                 if (shouldThrowWhenKeyMissing)
519                                         throw new ArgumentException ("key " + keyName);
520                                 return;
521                         }
522                         
523                         if (result != Win32ResultCode.Success)
524                                 GenerateException (result);
525                 }
526
527                 public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
528                 {
529                         IntPtr handle = GetHandle (rkey);
530                         int result = RegDeleteValue (handle, value);
531
532                         if (result == Win32ResultCode.MarkedForDeletion)
533                                 return;
534
535                         if (result == Win32ResultCode.FileNotFound){
536                                 if (shouldThrowWhenKeyMissing)
537                                         throw new ArgumentException ("value " + value);
538                                 return;
539                         }
540                         
541                         if (result != Win32ResultCode.Success)
542                                 GenerateException (result);
543                 }
544
545                 public unsafe string [] GetSubKeyNames (RegistryKey rkey)
546                 {
547                         int subkeys = SubKeyCount (rkey);
548                         var names = new string [subkeys];  // Returns 0-length array if empty.
549
550                         if (subkeys > 0) {
551                                 var hkey = GetHandle (rkey);
552                                 char[] name = new char [MaxKeyLength + 1];
553                                 int namelen;
554
555                                 fixed (char* namePtr = &name [0]) {
556                                         for (int i = 0; i < subkeys; i++) {
557                                                 namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialised.
558                                                 int ret = RegEnumKeyEx (hkey,
559                                                         i,
560                                                         namePtr,
561                                                         ref namelen,
562                                                         null,
563                                                         null,
564                                                         null,
565                                                         null);
566
567                                                 if (ret != 0)
568                                                         GenerateException (ret);
569                                                 names [i] = new String (namePtr);
570                                         }
571                                 }
572                         }
573
574                         return names;
575                 }
576
577                 public unsafe string [] GetValueNames (RegistryKey rkey)
578                 {
579                         int values = ValueCount (rkey);
580                         String[] names = new String [values];
581
582                         if (values > 0) {
583                                 IntPtr hkey = GetHandle (rkey);
584                                 char[] name = new char [MaxValueLength + 1];
585                                 int namelen;
586
587                                 fixed (char* namePtr = &name [0]) {
588                                         for (int i = 0; i < values; i++) {
589                                                 namelen = name.Length;
590
591                                                 int ret = RegEnumValue (hkey,
592                                                         i,
593                                                         namePtr,
594                                                         ref namelen,
595                                                         IntPtr.Zero,
596                                                         null,
597                                                         null,
598                                                         null);
599
600                                                 if (ret != Win32ResultCode.Success && ret != Win32Native.ERROR_MORE_DATA)
601                                                         GenerateException (ret);
602
603                                                 names [i] = new String (namePtr);
604                                         }
605                                 }
606                         }
607
608                         return names;
609                 }
610
611                 private void CheckResult (int result)
612                 {
613                         if (result != Win32ResultCode.Success) {
614                                 GenerateException (result);
615                         }
616                 }
617
618                 /// <summary>
619                 /// convert a win32 error code into an appropriate exception.
620                 /// </summary>
621                 private void GenerateException (int errorCode)
622                 {
623                         switch (errorCode) {
624                                 case Win32ResultCode.FileNotFound:
625                                 case Win32ResultCode.InvalidParameter:
626                                         throw new ArgumentException ();
627                                 case Win32ResultCode.AccessDenied:
628                                         throw new SecurityException ();
629                                 case Win32ResultCode.NetworkPathNotFound:
630                                         throw new IOException ("The network path was not found.");
631                                 case Win32ResultCode.InvalidHandle:
632                                         throw new IOException ("Invalid handle.");
633                                 case Win32ResultCode.MarkedForDeletion:
634                                         throw RegistryKey.CreateMarkedForDeletionException ();
635                                 case Win32ResultCode.ChildMustBeVolatile:
636                                         throw new IOException ("Cannot create a stable subkey under a volatile parent key.");
637                                 default:
638                                         // unidentified system exception
639                                         throw new SystemException ();
640                         }
641                 }
642
643                 public string ToString (RegistryKey rkey)
644                 {
645                         return rkey.Name;
646                 }
647
648                 /// <summary>
649                 ///     utility: Combine the sub key name to the current name to produce a 
650                 ///     fully qualified sub key name.
651                 /// </summary>
652                 internal static string CombineName (RegistryKey rkey, string localName)
653                 {
654                         return String.Concat (rkey.Name, "\\", localName);
655                 }
656         }
657 }
658
659 #endif // MOBILE
660