2 // Microsoft.Win32/Win32RegistryApi.cs: wrapper for win32 registry API
5 // Erik LeBel (eriklebel@yahoo.ca)
6 // Jackson Harper (jackson@ximian.com)
7 // Miguel de Icaza (miguel@gnome.org)
9 // Copyright (C) Erik LeBel 2004
10 // (C) 2004, 2005 Novell, Inc (http://www.novell.com)
14 // Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
37 using System
.Collections
;
39 using System
.Runtime
.InteropServices
;
40 using System
.Security
;
43 namespace Microsoft
.Win32
46 /// Function stubs, constants and helper functions for
47 /// the Win32 registry manipulation utilities.
49 internal class Win32RegistryApi
: IRegistryApi
51 // bit masks for registry key open access permissions
52 const int OpenRegKeyRead
= 0x00020019;
53 const int OpenRegKeyWrite
= 0x00020006;
55 // FIXME must be a way to determin this dynamically?
56 const int Int32ByteSize
= 4;
58 // FIXME this is hard coded on Mono, can it be determined dynamically?
59 readonly int NativeBytesPerCharacter
= Marshal
.SystemDefaultCharSize
;
61 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegCreateKey")]
62 static extern int RegCreateKey (IntPtr keyBase
, string keyName
, out IntPtr keyHandle
);
64 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegCloseKey")]
65 static extern int RegCloseKey (IntPtr keyHandle
);
67 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
)]
68 static extern int RegConnectRegistry (string machineName
, IntPtr hKey
,
69 out IntPtr keyHandle
);
71 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegFlushKey")]
72 private static extern int RegFlushKey (IntPtr keyHandle
);
74 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegOpenKeyEx")]
75 private static extern int RegOpenKeyEx (IntPtr keyBase
,
76 string keyName
, IntPtr reserved
, int access
,
77 out IntPtr keyHandle
);
79 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegDeleteKey")]
80 private static extern int RegDeleteKey (IntPtr keyHandle
, string valueName
);
82 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegDeleteValue")]
83 private static extern int RegDeleteValue (IntPtr keyHandle
, string valueName
);
85 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegEnumKey")]
86 private static extern int RegEnumKey (IntPtr keyBase
, int index
, StringBuilder nameBuffer
, int bufferLength
);
88 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegEnumValue")]
89 private static extern int RegEnumValue (IntPtr keyBase
,
90 int index
, StringBuilder nameBuffer
,
91 ref int nameLength
, IntPtr reserved
,
92 ref RegistryValueKind type
, IntPtr data
, IntPtr dataLength
);
94 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegSetValueEx")]
95 private static extern int RegSetValueEx (IntPtr keyBase
,
96 string valueName
, IntPtr reserved
, RegistryValueKind type
,
97 StringBuilder data
, int rawDataLength
);
99 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegSetValueEx")]
100 private static extern int RegSetValueEx (IntPtr keyBase
,
101 string valueName
, IntPtr reserved
, RegistryValueKind type
,
102 string data
, int rawDataLength
);
104 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegSetValueEx")]
105 private static extern int RegSetValueEx (IntPtr keyBase
,
106 string valueName
, IntPtr reserved
, RegistryValueKind type
,
107 byte[] rawData
, int rawDataLength
);
109 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegSetValueEx")]
110 private static extern int RegSetValueEx (IntPtr keyBase
,
111 string valueName
, IntPtr reserved
, RegistryValueKind type
,
112 ref int data
, int rawDataLength
);
114 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegQueryValueEx")]
115 private static extern int RegQueryValueEx (IntPtr keyBase
,
116 string valueName
, IntPtr reserved
, ref RegistryValueKind type
,
117 IntPtr zero
, ref int dataSize
);
119 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegQueryValueEx")]
120 private static extern int RegQueryValueEx (IntPtr keyBase
,
121 string valueName
, IntPtr reserved
, ref RegistryValueKind type
,
122 [Out
] byte[] data
, ref int dataSize
);
124 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegQueryValueEx")]
125 private static extern int RegQueryValueEx (IntPtr keyBase
,
126 string valueName
, IntPtr reserved
, ref RegistryValueKind type
,
127 ref int data
, ref int dataSize
);
129 // Returns our handle from the RegistryKey
130 static IntPtr
GetHandle (RegistryKey key
)
132 return (IntPtr
) key
.Handle
;
135 static bool IsHandleValid (RegistryKey key
)
137 return key
.Handle
!= null;
141 /// Acctually read a registry value. Requires knowledge of the
142 /// value's type and size.
144 public object GetValue (RegistryKey rkey
, string name
, object defaultValue
, RegistryValueOptions options
)
146 RegistryValueKind type
= 0;
149 IntPtr handle
= GetHandle (rkey
);
150 int result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, IntPtr
.Zero
, ref size
);
152 if (result
== Win32ResultCode
.FileNotFound
|| result
== Win32ResultCode
.MarkedForDeletion
) {
156 if (result
!= Win32ResultCode
.MoreData
&& result
!= Win32ResultCode
.Success
) {
157 GenerateException (result
);
160 if (type
== RegistryValueKind
.String
) {
162 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
163 obj
= RegistryKey
.DecodeString (data
);
164 } else if (type
== RegistryValueKind
.ExpandString
) {
166 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
167 obj
= RegistryKey
.DecodeString (data
);
168 if ((options
& RegistryValueOptions
.DoNotExpandEnvironmentNames
) == 0)
169 obj
= Environment
.ExpandEnvironmentVariables ((string) obj
);
170 } else if (type
== RegistryValueKind
.DWord
) {
172 result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, ref data
, ref size
);
174 } else if (type
== RegistryValueKind
.Binary
) {
176 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
178 } else if (type
== RegistryValueKind
.MultiString
) {
181 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
183 if (result
== Win32ResultCode
.Success
)
184 obj
= RegistryKey
.DecodeString (data
).Split ('\0');
186 // should never get here
187 throw new SystemException ();
190 // check result codes again:
191 if (result
!= Win32ResultCode
.Success
)
193 GenerateException (result
);
202 // This version has to do extra checking, make sure that the requested
203 // valueKind matches the type of the value being stored
205 public void SetValue (RegistryKey rkey
, string name
, object value, RegistryValueKind valueKind
)
207 Type type
= value.GetType ();
209 IntPtr handle
= GetHandle (rkey
);
211 if (valueKind
== RegistryValueKind
.DWord
&& type
== typeof (int)) {
212 int rawValue
= (int)value;
213 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.DWord
, ref rawValue
, Int32ByteSize
);
214 } else if (valueKind
== RegistryValueKind
.Binary
&& type
== typeof (byte[])) {
215 byte[] rawValue
= (byte[]) value;
216 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.Binary
, rawValue
, rawValue
.Length
);
217 } else if (valueKind
== RegistryValueKind
.MultiString
&& type
== typeof (string[])) {
218 string[] vals
= (string[]) value;
219 StringBuilder fullStringValue
= new StringBuilder ();
220 foreach (string v
in vals
)
222 fullStringValue
.Append (v
);
223 fullStringValue
.Append ('\0');
225 fullStringValue
.Append ('\0');
227 byte[] rawValue
= Encoding
.Unicode
.GetBytes (fullStringValue
.ToString ());
229 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.MultiString
, rawValue
, rawValue
.Length
);
230 } else if ((valueKind
== RegistryValueKind
.String
|| valueKind
== RegistryValueKind
.ExpandString
) &&
231 type
== typeof (string)){
232 string rawValue
= String
.Format ("{0}{1}", value, '\0');
233 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, valueKind
, rawValue
,
234 rawValue
.Length
* NativeBytesPerCharacter
);
236 } else if (type
.IsArray
) {
237 throw new ArgumentException ("Only string and byte arrays can written as registry values");
239 throw new ArgumentException ("Type does not match the valueKind");
242 // handle the result codes
243 if (result
!= Win32ResultCode
.Success
)
245 GenerateException (result
);
250 public void SetValue (RegistryKey rkey
, string name
, object value)
252 Type type
= value.GetType ();
254 IntPtr handle
= GetHandle (rkey
);
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
)
267 fullStringValue
.Append (v
);
268 fullStringValue
.Append ('\0');
270 fullStringValue
.Append ('\0');
272 byte[] rawValue
= Encoding
.Unicode
.GetBytes (fullStringValue
.ToString ());
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");
278 string rawValue
= String
.Format ("{0}{1}", value, '\0');
279 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.String
, rawValue
,
280 rawValue
.Length
* NativeBytesPerCharacter
);
283 if (result
== Win32ResultCode
.MarkedForDeletion
)
284 throw RegistryKey
.CreateMarkedForDeletionException ();
286 // handle the result codes
287 if (result
!= Win32ResultCode
.Success
)
289 GenerateException (result
);
294 /// Get a binary value.
296 private int GetBinaryValue (RegistryKey rkey
, string name
, RegistryValueKind type
, out byte[] data
, int size
)
298 byte[] internalData
= new byte [size
];
299 IntPtr handle
= GetHandle (rkey
);
300 int result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, internalData
, ref size
);
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;
311 public int SubKeyCount (RegistryKey rkey
)
314 StringBuilder stringBuffer
= new StringBuilder (BufferMaxLength
);
315 IntPtr handle
= GetHandle (rkey
);
317 for (index
= 0; true; index
++) {
318 int result
= RegEnumKey (handle
, index
, stringBuffer
,
319 stringBuffer
.Capacity
);
321 if (result
== Win32ResultCode
.MarkedForDeletion
)
322 throw RegistryKey
.CreateMarkedForDeletionException ();
324 if (result
== Win32ResultCode
.Success
)
327 if (result
== Win32ResultCode
.NoMoreEntries
)
330 // something is wrong!!
331 GenerateException (result
);
336 public int ValueCount (RegistryKey rkey
)
338 int index
, result
, bufferCapacity
;
339 RegistryValueKind type
;
340 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
342 IntPtr handle
= GetHandle (rkey
);
343 for (index
= 0; true; index
++) {
345 bufferCapacity
= buffer
.Capacity
;
346 result
= RegEnumValue (handle
, index
,
347 buffer
, ref bufferCapacity
,
348 IntPtr
.Zero
, ref type
,
349 IntPtr
.Zero
, IntPtr
.Zero
);
351 if (result
== Win32ResultCode
.MarkedForDeletion
)
352 throw RegistryKey
.CreateMarkedForDeletionException ();
354 if (result
== Win32ResultCode
.Success
|| result
== Win32ResultCode
.MoreData
)
357 if (result
== Win32ResultCode
.NoMoreEntries
)
360 // something is wrong
361 GenerateException (result
);
366 public RegistryKey
OpenRemoteBaseKey (RegistryHive hKey
, string machineName
)
368 IntPtr handle
= new IntPtr ((int) hKey
);
371 int result
= RegConnectRegistry (machineName
, handle
, out keyHandle
);
372 if (result
!= Win32ResultCode
.Success
)
373 GenerateException (result
);
375 return new RegistryKey (hKey
, keyHandle
, true);
378 public RegistryKey
OpenSubKey (RegistryKey rkey
, string keyName
, bool writable
)
380 int access
= OpenRegKeyRead
;
381 if (writable
) access
|= OpenRegKeyWrite
;
382 IntPtr handle
= GetHandle (rkey
);
385 int result
= RegOpenKeyEx (handle
, keyName
, IntPtr
.Zero
, access
, out subKeyHandle
);
387 if (result
== Win32ResultCode
.FileNotFound
|| result
== Win32ResultCode
.MarkedForDeletion
)
390 if (result
!= Win32ResultCode
.Success
)
391 GenerateException (result
);
393 return new RegistryKey (subKeyHandle
, CombineName (rkey
, keyName
), writable
);
396 public void Flush (RegistryKey rkey
)
398 if (!IsHandleValid (rkey
))
400 IntPtr handle
= GetHandle (rkey
);
401 RegFlushKey (handle
);
404 public void Close (RegistryKey rkey
)
406 if (!IsHandleValid (rkey
))
408 IntPtr handle
= GetHandle (rkey
);
409 RegCloseKey (handle
);
412 public RegistryKey
CreateSubKey (RegistryKey rkey
, string keyName
)
414 IntPtr handle
= GetHandle (rkey
);
416 int result
= RegCreateKey (handle
, keyName
, out subKeyHandle
);
418 if (result
== Win32ResultCode
.MarkedForDeletion
)
419 throw RegistryKey
.CreateMarkedForDeletionException ();
421 if (result
!= Win32ResultCode
.Success
) {
422 GenerateException (result
);
425 return new RegistryKey (subKeyHandle
, CombineName (rkey
, keyName
),
429 public void DeleteKey (RegistryKey rkey
, string keyName
, bool shouldThrowWhenKeyMissing
)
431 IntPtr handle
= GetHandle (rkey
);
432 int result
= RegDeleteKey (handle
, keyName
);
434 if (result
== Win32ResultCode
.FileNotFound
) {
435 if (shouldThrowWhenKeyMissing
)
436 throw new ArgumentException ("key " + keyName
);
440 if (result
!= Win32ResultCode
.Success
)
441 GenerateException (result
);
444 public void DeleteValue (RegistryKey rkey
, string value, bool shouldThrowWhenKeyMissing
)
446 IntPtr handle
= GetHandle (rkey
);
447 int result
= RegDeleteValue (handle
, value);
449 if (result
== Win32ResultCode
.MarkedForDeletion
)
452 if (result
== Win32ResultCode
.FileNotFound
){
453 if (shouldThrowWhenKeyMissing
)
454 throw new ArgumentException ("value " + value);
458 if (result
!= Win32ResultCode
.Success
)
459 GenerateException (result
);
462 public string [] GetSubKeyNames (RegistryKey rkey
)
464 IntPtr handle
= GetHandle (rkey
);
465 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
466 ArrayList keys
= new ArrayList ();
468 for (int index
= 0; true; index
++) {
469 int result
= RegEnumKey (handle
, index
, buffer
, buffer
.Capacity
);
471 if (result
== Win32ResultCode
.Success
) {
472 keys
.Add (buffer
.ToString ());
477 if (result
== Win32ResultCode
.NoMoreEntries
)
480 // should not be here!
481 GenerateException (result
);
483 return (string []) keys
.ToArray (typeof(String
));
487 public string [] GetValueNames (RegistryKey rkey
)
489 IntPtr handle
= GetHandle (rkey
);
490 ArrayList values
= new ArrayList ();
492 for (int index
= 0; true; index
++)
494 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
495 int bufferCapacity
= buffer
.Capacity
;
496 RegistryValueKind type
= 0;
498 int result
= RegEnumValue (handle
, index
, buffer
, ref bufferCapacity
,
499 IntPtr
.Zero
, ref type
, IntPtr
.Zero
, IntPtr
.Zero
);
501 if (result
== Win32ResultCode
.Success
|| result
== Win32ResultCode
.MoreData
) {
502 values
.Add (buffer
.ToString ());
506 if (result
== Win32ResultCode
.NoMoreEntries
)
509 if (result
== Win32ResultCode
.MarkedForDeletion
)
510 throw RegistryKey
.CreateMarkedForDeletionException ();
512 GenerateException (result
);
515 return (string []) values
.ToArray (typeof(String
));
519 /// convert a win32 error code into an appropriate exception.
521 private void GenerateException (int 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.");
532 // unidentified system exception
533 throw new SystemException ();
537 public string ToString (RegistryKey rkey
)
539 IntPtr handle
= GetHandle (rkey
);
541 return String
.Format ("{0} [0x{1:X}]", rkey
.Name
, handle
.ToInt32 ());
545 /// utility: Combine the sub key name to the current name to produce a
546 /// fully qualified sub key name.
548 internal static string CombineName (RegistryKey rkey
, string localName
)
550 return String
.Concat (rkey
.Name
, "\\", localName
);