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.
39 using System
.Collections
;
41 using System
.Runtime
.InteropServices
;
42 using System
.Security
;
45 namespace Microsoft
.Win32
48 /// Function stubs, constants and helper functions for
49 /// the Win32 registry manipulation utilities.
51 internal class Win32RegistryApi
: IRegistryApi
53 // bit masks for registry key open access permissions
54 const int OpenRegKeyRead
= 0x00020019;
55 const int OpenRegKeyWrite
= 0x00020006;
57 // FIXME must be a way to determin this dynamically?
58 const int Int32ByteSize
= 4;
60 // FIXME this is hard coded on Mono, can it be determined dynamically?
61 readonly int NativeBytesPerCharacter
= Marshal
.SystemDefaultCharSize
;
63 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegCreateKey")]
64 static extern int RegCreateKey (IntPtr keyBase
, string keyName
, out IntPtr keyHandle
);
66 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegCloseKey")]
67 static extern int RegCloseKey (IntPtr keyHandle
);
69 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
)]
70 static extern int RegConnectRegistry (string machineName
, IntPtr hKey
,
71 out IntPtr keyHandle
);
73 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegFlushKey")]
74 private static extern int RegFlushKey (IntPtr keyHandle
);
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
);
81 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegDeleteKey")]
82 private static extern int RegDeleteKey (IntPtr keyHandle
, string valueName
);
84 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegDeleteValue")]
85 private static extern int RegDeleteValue (IntPtr keyHandle
, string valueName
);
87 [DllImport ("advapi32.dll", CharSet
=CharSet
.Unicode
, EntryPoint
="RegEnumKey")]
88 private static extern int RegEnumKey (IntPtr keyBase
, int index
, StringBuilder nameBuffer
, int bufferLength
);
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
);
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);
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
);
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
);
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
);
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
);
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
);
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
);
131 // Returns our handle from the RegistryKey
132 static IntPtr
GetHandle (RegistryKey key
)
134 return (IntPtr
) key
.Handle
;
137 static bool IsHandleValid (RegistryKey key
)
139 return key
.Handle
!= null;
142 public RegistryValueKind
GetValueKind (RegistryKey rkey
, string name
)
144 RegistryValueKind type
= 0;
146 IntPtr handle
= GetHandle (rkey
);
147 int result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, IntPtr
.Zero
, ref size
);
149 if (result
== Win32ResultCode
.FileNotFound
|| result
== Win32ResultCode
.MarkedForDeletion
)
150 return RegistryValueKind
.Unknown
;
156 /// Acctually read a registry value. Requires knowledge of the
157 /// value's type and size.
159 public object GetValue (RegistryKey rkey
, string name
, object defaultValue
, RegistryValueOptions options
)
161 RegistryValueKind type
= 0;
164 IntPtr handle
= GetHandle (rkey
);
165 int result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, IntPtr
.Zero
, ref size
);
167 if (result
== Win32ResultCode
.FileNotFound
|| result
== Win32ResultCode
.MarkedForDeletion
) {
171 if (result
!= Win32ResultCode
.MoreData
&& result
!= Win32ResultCode
.Success
) {
172 GenerateException (result
);
175 if (type
== RegistryValueKind
.String
) {
177 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
178 obj
= RegistryKey
.DecodeString (data
);
179 } else if (type
== RegistryValueKind
.ExpandString
) {
181 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
182 obj
= RegistryKey
.DecodeString (data
);
183 if ((options
& RegistryValueOptions
.DoNotExpandEnvironmentNames
) == 0)
184 obj
= Environment
.ExpandEnvironmentVariables ((string) obj
);
185 } else if (type
== RegistryValueKind
.DWord
) {
187 result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, ref data
, ref size
);
189 } else if (type
== RegistryValueKind
.Binary
) {
191 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
193 } else if (type
== RegistryValueKind
.MultiString
) {
196 result
= GetBinaryValue (rkey
, name
, type
, out data
, size
);
198 if (result
== Win32ResultCode
.Success
)
199 obj
= RegistryKey
.DecodeString (data
).Split ('\0');
201 // should never get here
202 throw new SystemException ();
205 // check result codes again:
206 if (result
!= Win32ResultCode
.Success
)
208 GenerateException (result
);
216 // This version has to do extra checking, make sure that the requested
217 // valueKind matches the type of the value being stored
219 public void SetValue (RegistryKey rkey
, string name
, object value, RegistryValueKind valueKind
)
221 Type type
= value.GetType ();
223 IntPtr handle
= GetHandle (rkey
);
225 if (valueKind
== RegistryValueKind
.DWord
&& type
== typeof (int)) {
226 int rawValue
= (int)value;
227 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.DWord
, ref rawValue
, Int32ByteSize
);
228 } else if (valueKind
== RegistryValueKind
.Binary
&& type
== typeof (byte[])) {
229 byte[] rawValue
= (byte[]) value;
230 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.Binary
, rawValue
, rawValue
.Length
);
231 } else if (valueKind
== RegistryValueKind
.MultiString
&& type
== typeof (string[])) {
232 string[] vals
= (string[]) value;
233 StringBuilder fullStringValue
= new StringBuilder ();
234 foreach (string v
in vals
)
236 fullStringValue
.Append (v
);
237 fullStringValue
.Append ('\0');
239 fullStringValue
.Append ('\0');
241 byte[] rawValue
= Encoding
.Unicode
.GetBytes (fullStringValue
.ToString ());
243 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.MultiString
, rawValue
, rawValue
.Length
);
244 } else if ((valueKind
== RegistryValueKind
.String
|| valueKind
== RegistryValueKind
.ExpandString
) &&
245 type
== typeof (string)){
246 string rawValue
= String
.Format ("{0}{1}", value, '\0');
247 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, valueKind
, rawValue
,
248 rawValue
.Length
* NativeBytesPerCharacter
);
250 } else if (type
.IsArray
) {
251 throw new ArgumentException ("Only string and byte arrays can written as registry values");
253 throw new ArgumentException ("Type does not match the valueKind");
256 // handle the result codes
257 if (result
!= Win32ResultCode
.Success
)
259 GenerateException (result
);
263 public void SetValue (RegistryKey rkey
, string name
, object value)
265 Type type
= value.GetType ();
267 IntPtr handle
= GetHandle (rkey
);
269 if (type
== typeof (int)) {
270 int rawValue
= (int)value;
271 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.DWord
, ref rawValue
, Int32ByteSize
);
272 } else if (type
== typeof (byte[])) {
273 byte[] rawValue
= (byte[]) value;
274 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.Binary
, rawValue
, rawValue
.Length
);
275 } else if (type
== typeof (string[])) {
276 string[] vals
= (string[]) value;
277 StringBuilder fullStringValue
= new StringBuilder ();
278 foreach (string v
in vals
)
280 fullStringValue
.Append (v
);
281 fullStringValue
.Append ('\0');
283 fullStringValue
.Append ('\0');
285 byte[] rawValue
= Encoding
.Unicode
.GetBytes (fullStringValue
.ToString ());
287 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.MultiString
, rawValue
, rawValue
.Length
);
288 } else if (type
.IsArray
) {
289 throw new ArgumentException ("Only string and byte arrays can written as registry values");
291 string rawValue
= String
.Format ("{0}{1}", value, '\0');
292 result
= RegSetValueEx (handle
, name
, IntPtr
.Zero
, RegistryValueKind
.String
, rawValue
,
293 rawValue
.Length
* NativeBytesPerCharacter
);
296 if (result
== Win32ResultCode
.MarkedForDeletion
)
297 throw RegistryKey
.CreateMarkedForDeletionException ();
299 // handle the result codes
300 if (result
!= Win32ResultCode
.Success
)
302 GenerateException (result
);
307 /// Get a binary value.
309 private int GetBinaryValue (RegistryKey rkey
, string name
, RegistryValueKind type
, out byte[] data
, int size
)
311 byte[] internalData
= new byte [size
];
312 IntPtr handle
= GetHandle (rkey
);
313 int result
= RegQueryValueEx (handle
, name
, IntPtr
.Zero
, ref type
, internalData
, ref size
);
319 // Arbitrary max size for key/values names that can be fetched.
320 // .NET framework SDK docs say that the max name length that can
321 // be used is 255 characters, we'll allow for a bit more.
322 const int BufferMaxLength
= 1024;
324 public int SubKeyCount (RegistryKey rkey
)
327 StringBuilder stringBuffer
= new StringBuilder (BufferMaxLength
);
328 IntPtr handle
= GetHandle (rkey
);
330 for (index
= 0; true; index
++) {
331 int result
= RegEnumKey (handle
, index
, stringBuffer
,
332 stringBuffer
.Capacity
);
334 if (result
== Win32ResultCode
.MarkedForDeletion
)
335 throw RegistryKey
.CreateMarkedForDeletionException ();
337 if (result
== Win32ResultCode
.Success
)
340 if (result
== Win32ResultCode
.NoMoreEntries
)
343 // something is wrong!!
344 GenerateException (result
);
349 public int ValueCount (RegistryKey rkey
)
351 int index
, result
, bufferCapacity
;
352 RegistryValueKind type
;
353 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
355 IntPtr handle
= GetHandle (rkey
);
356 for (index
= 0; true; index
++) {
358 bufferCapacity
= buffer
.Capacity
;
359 result
= RegEnumValue (handle
, index
,
360 buffer
, ref bufferCapacity
,
361 IntPtr
.Zero
, ref type
,
362 IntPtr
.Zero
, IntPtr
.Zero
);
364 if (result
== Win32ResultCode
.MarkedForDeletion
)
365 throw RegistryKey
.CreateMarkedForDeletionException ();
367 if (result
== Win32ResultCode
.Success
|| result
== Win32ResultCode
.MoreData
)
370 if (result
== Win32ResultCode
.NoMoreEntries
)
373 // something is wrong
374 GenerateException (result
);
379 public RegistryKey
OpenRemoteBaseKey (RegistryHive hKey
, string machineName
)
381 IntPtr handle
= new IntPtr ((int) hKey
);
384 int result
= RegConnectRegistry (machineName
, handle
, out keyHandle
);
385 if (result
!= Win32ResultCode
.Success
)
386 GenerateException (result
);
388 return new RegistryKey (hKey
, keyHandle
, true);
391 public RegistryKey
OpenSubKey (RegistryKey rkey
, string keyName
, bool writable
)
393 int access
= OpenRegKeyRead
;
394 if (writable
) access
|= OpenRegKeyWrite
;
395 IntPtr handle
= GetHandle (rkey
);
398 int result
= RegOpenKeyEx (handle
, keyName
, IntPtr
.Zero
, access
, out subKeyHandle
);
400 if (result
== Win32ResultCode
.FileNotFound
|| result
== Win32ResultCode
.MarkedForDeletion
)
403 if (result
!= Win32ResultCode
.Success
)
404 GenerateException (result
);
406 return new RegistryKey (subKeyHandle
, CombineName (rkey
, keyName
), writable
);
409 public void Flush (RegistryKey rkey
)
411 if (!IsHandleValid (rkey
))
413 IntPtr handle
= GetHandle (rkey
);
414 RegFlushKey (handle
);
417 public void Close (RegistryKey rkey
)
419 if (!IsHandleValid (rkey
))
421 IntPtr handle
= GetHandle (rkey
);
422 RegCloseKey (handle
);
425 public RegistryKey
CreateSubKey (RegistryKey rkey
, string keyName
)
427 IntPtr handle
= GetHandle (rkey
);
429 int result
= RegCreateKey (handle
, keyName
, out subKeyHandle
);
431 if (result
== Win32ResultCode
.MarkedForDeletion
)
432 throw RegistryKey
.CreateMarkedForDeletionException ();
434 if (result
!= Win32ResultCode
.Success
) {
435 GenerateException (result
);
438 return new RegistryKey (subKeyHandle
, CombineName (rkey
, keyName
),
442 public void DeleteKey (RegistryKey rkey
, string keyName
, bool shouldThrowWhenKeyMissing
)
444 IntPtr handle
= GetHandle (rkey
);
445 int result
= RegDeleteKey (handle
, keyName
);
447 if (result
== Win32ResultCode
.FileNotFound
) {
448 if (shouldThrowWhenKeyMissing
)
449 throw new ArgumentException ("key " + keyName
);
453 if (result
!= Win32ResultCode
.Success
)
454 GenerateException (result
);
457 public void DeleteValue (RegistryKey rkey
, string value, bool shouldThrowWhenKeyMissing
)
459 IntPtr handle
= GetHandle (rkey
);
460 int result
= RegDeleteValue (handle
, value);
462 if (result
== Win32ResultCode
.MarkedForDeletion
)
465 if (result
== Win32ResultCode
.FileNotFound
){
466 if (shouldThrowWhenKeyMissing
)
467 throw new ArgumentException ("value " + value);
471 if (result
!= Win32ResultCode
.Success
)
472 GenerateException (result
);
475 public string [] GetSubKeyNames (RegistryKey rkey
)
477 IntPtr handle
= GetHandle (rkey
);
478 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
479 ArrayList keys
= new ArrayList ();
481 for (int index
= 0; true; index
++) {
482 int result
= RegEnumKey (handle
, index
, buffer
, buffer
.Capacity
);
484 if (result
== Win32ResultCode
.Success
) {
485 keys
.Add (buffer
.ToString ());
490 if (result
== Win32ResultCode
.NoMoreEntries
)
493 // should not be here!
494 GenerateException (result
);
496 return (string []) keys
.ToArray (typeof(String
));
500 public string [] GetValueNames (RegistryKey rkey
)
502 IntPtr handle
= GetHandle (rkey
);
503 ArrayList values
= new ArrayList ();
505 for (int index
= 0; true; index
++)
507 StringBuilder buffer
= new StringBuilder (BufferMaxLength
);
508 int bufferCapacity
= buffer
.Capacity
;
509 RegistryValueKind type
= 0;
511 int result
= RegEnumValue (handle
, index
, buffer
, ref bufferCapacity
,
512 IntPtr
.Zero
, ref type
, IntPtr
.Zero
, IntPtr
.Zero
);
514 if (result
== Win32ResultCode
.Success
|| result
== Win32ResultCode
.MoreData
) {
515 values
.Add (buffer
.ToString ());
519 if (result
== Win32ResultCode
.NoMoreEntries
)
522 if (result
== Win32ResultCode
.MarkedForDeletion
)
523 throw RegistryKey
.CreateMarkedForDeletionException ();
525 GenerateException (result
);
528 return (string []) values
.ToArray (typeof(String
));
532 /// convert a win32 error code into an appropriate exception.
534 private void GenerateException (int errorCode
)
537 case Win32ResultCode
.FileNotFound
:
538 case Win32ResultCode
.InvalidParameter
:
539 throw new ArgumentException ();
540 case Win32ResultCode
.AccessDenied
:
541 throw new SecurityException ();
542 case Win32ResultCode
.NetworkPathNotFound
:
543 throw new IOException ("The network path was not found.");
545 // unidentified system exception
546 throw new SystemException ();
550 public string ToString (RegistryKey rkey
)
556 /// utility: Combine the sub key name to the current name to produce a
557 /// fully qualified sub key name.
559 internal static string CombineName (RegistryKey rkey
, string localName
)
561 return String
.Concat (rkey
.Name
, "\\", localName
);