Implement 4.0 RegistryKey.Handle property.
[mono.git] / mcs / class / corlib / Microsoft.Win32 / Win32RegistryApi.cs
index 39b5e8d4deb594d971902d7c665919842960f90d..472f4a73c5b9a4d076e0f205851f92d90d4b372c 100644 (file)
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
+#if !NET_2_1
+
 using System;
 using System.Collections;
+using System.IO;
 using System.Runtime.InteropServices;
 using System.Security;
 using System.Text;
+using Microsoft.Win32.SafeHandles;
 
 namespace Microsoft.Win32
 {
@@ -57,20 +61,21 @@ namespace Microsoft.Win32
                // FIXME this is hard coded on Mono, can it be determined dynamically? 
                readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
 
-               internal enum RegistryType {
-                       String = 1,
-                       EnvironmentString = 2,
-                       Binary = 3,
-                       Dword = 4,
-                       StringArray = 7,
-               }
-               
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
-               static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
+               const int RegOptionsNonVolatile = 0x00000000;
+               const int RegOptionsVolatile = 0x00000001;
+
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKeyEx")]
+               static extern int RegCreateKeyEx (IntPtr keyBase, string keyName, int reserved, 
+                       IntPtr lpClass, int options, int access, IntPtr securityAttrs,
+                       out IntPtr keyHandle, out int disposition);
               
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
                static extern int RegCloseKey (IntPtr keyHandle);
 
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode)]
+               static extern int RegConnectRegistry (string machineName, IntPtr hKey,
+                               out IntPtr keyHandle);
+
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
                private static extern int RegFlushKey (IntPtr keyHandle);
 
@@ -86,92 +91,112 @@ namespace Microsoft.Win32
                private static extern int RegDeleteValue (IntPtr keyHandle, string valueName);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
-               private static extern int RegEnumKey (IntPtr keyBase, int index, [Out] byte[] nameBuffer, int bufferLength);
+               private static extern int RegEnumKey (IntPtr keyBase, int index, StringBuilder nameBuffer, int bufferLength);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
                private static extern int RegEnumValue (IntPtr keyBase, 
                                int index, StringBuilder nameBuffer, 
                                ref int nameLength, IntPtr reserved, 
-                               ref RegistryType type, IntPtr data, IntPtr dataLength);
+                               ref RegistryValueKind type, IntPtr data, IntPtr dataLength);
 
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
-               private static extern int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, RegistryType type,
-                               StringBuilder data, int rawDataLength);
+//             [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
+//             private static extern int RegSetValueEx (IntPtr keyBase, 
+//                             string valueName, IntPtr reserved, RegistryValueKind type,
+//                             StringBuilder data, int rawDataLength);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
                private static extern int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, RegistryType type,
+                               string valueName, IntPtr reserved, RegistryValueKind type,
                                string data, int rawDataLength);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
                private static extern int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, RegistryType type,
+                               string valueName, IntPtr reserved, RegistryValueKind type,
                                byte[] rawData, int rawDataLength);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
                private static extern int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, RegistryType type,
+                               string valueName, IntPtr reserved, RegistryValueKind type,
                                ref int data, int rawDataLength);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
                private static extern int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref RegistryType type,
+                               string valueName, IntPtr reserved, ref RegistryValueKind type,
                                IntPtr zero, ref int dataSize);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
                private static extern int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref RegistryType type,
+                               string valueName, IntPtr reserved, ref RegistryValueKind type,
                                [Out] byte[] data, ref int dataSize);
 
                [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
                private static extern int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref RegistryType type,
+                               string valueName, IntPtr reserved, ref RegistryValueKind type,
                                ref int data, ref int dataSize);
 
                // Returns our handle from the RegistryKey
-               static IntPtr GetHandle (RegistryKey key)
+               public IntPtr GetHandle (RegistryKey key)
+               {
+                       return (IntPtr) key.InternalHandle;
+               }
+
+               static bool IsHandleValid (RegistryKey key)
                {
-                       return key.IsRoot ? new IntPtr ((int) key.Data)
-                               : (IntPtr) key.Data;
+                       return key.InternalHandle != null;
                }
 
+               public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
+               {
+                       RegistryValueKind type = 0;
+                       int size = 0;
+                       IntPtr handle = GetHandle (rkey);
+                       int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
+
+                       if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) 
+                               return RegistryValueKind.Unknown;
+
+                       return type;
+               }
+               
                /// <summary>
-               ///     Acctually read a registry value. Requires knoledge of the
-               ///     value's type and size.
+               /// Acctually read a registry value. Requires knowledge of the
+               /// value's type and size.
                /// </summary>
-               public object GetValue (RegistryKey rkey, string name, bool returnDefaultValue, object defaultValue)
+               public object GetValue (RegistryKey rkey, string name, object defaultValue, RegistryValueOptions options)
                {
-                       RegistryType type = 0;
+                       RegistryValueKind type = 0;
                        int size = 0;
                        object obj = null;
                        IntPtr handle = GetHandle (rkey);
                        int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, IntPtr.Zero, ref size);
 
-                       if (result == Win32ResultCode.FileNotFound) {
-                               if (returnDefaultValue) {
-                                       return defaultValue;
-                               }
-                               return null;
+                       if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion) {
+                               return defaultValue;
                        }
                        
                        if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success ) {
                                GenerateException (result);
                        }
                        
-                       if (type == RegistryType.String || type == RegistryType.EnvironmentString) {
+                       if (type == RegistryValueKind.String) {
                                byte[] data;
                                result = GetBinaryValue (rkey, name, type, out data, size);
                                obj = RegistryKey.DecodeString (data);
-                       } else if (type == RegistryType.Dword) {
+                       } else if (type == RegistryValueKind.ExpandString) {
+                               byte [] data;
+                               result = GetBinaryValue (rkey, name, type, out data, size);
+                               obj = RegistryKey.DecodeString (data);
+                               if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
+                                       obj = Environment.ExpandEnvironmentVariables ((string) obj);
+                       } else if (type == RegistryValueKind.DWord) {
                                int data = 0;
                                result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
                                obj = data;
-                       } else if (type == RegistryType.Binary) {
+                       } else if (type == RegistryValueKind.Binary) {
                                byte[] data;
                                result = GetBinaryValue (rkey, name, type, out data, size);
                                obj = data;
-                       } else if (type == RegistryType.StringArray) {
+                       } else if (type == RegistryValueKind.MultiString) {
                                obj = null;
                                byte[] data;
                                result = GetBinaryValue (rkey, name, type, out data, size);
@@ -193,6 +218,54 @@ namespace Microsoft.Win32
                        return obj;
                }
 
+               //
+               // This version has to do extra checking, make sure that the requested
+               // valueKind matches the type of the value being stored
+               //
+               public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
+               {
+                       Type type = value.GetType ();
+                       int result;
+                       IntPtr handle = GetHandle (rkey);
+
+                       if (valueKind == RegistryValueKind.DWord && type == typeof (int)) {
+                               int rawValue = (int)value;
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
+                       } else if (valueKind == RegistryValueKind.Binary && type == typeof (byte[])) {
+                               byte[] rawValue = (byte[]) value;
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
+                       } else if (valueKind == RegistryValueKind.MultiString && type == typeof (string[])) {
+                               string[] vals = (string[]) value;
+                               StringBuilder fullStringValue = new StringBuilder ();
+                               foreach (string v in vals)
+                               {
+                                       fullStringValue.Append (v);
+                                       fullStringValue.Append ('\0');
+                               }
+                               fullStringValue.Append ('\0');
+
+                               byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
+                       
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
+                       } else if ((valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.ExpandString) &&
+                                  type == typeof (string)){
+                               string rawValue = String.Format ("{0}{1}", value, '\0');
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, valueKind, rawValue,
+                                                       rawValue.Length * NativeBytesPerCharacter);
+                               
+                       } else if (type.IsArray) {
+                               throw new ArgumentException ("Only string and byte arrays can written as registry values");
+                       } else {
+                               throw new ArgumentException ("Type does not match the valueKind");
+                       }
+
+                       // handle the result codes
+                       if (result != Win32ResultCode.Success)
+                       {
+                               GenerateException (result);
+                       }
+               }
+       
                public void SetValue (RegistryKey rkey, string name, object value)
                {
                        Type type = value.GetType ();
@@ -201,10 +274,10 @@ namespace Microsoft.Win32
 
                        if (type == typeof (int)) {
                                int rawValue = (int)value;
-                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Dword, ref rawValue, Int32ByteSize); 
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.DWord, ref rawValue, Int32ByteSize); 
                        } else if (type == typeof (byte[])) {
                                byte[] rawValue = (byte[]) value;
-                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Binary, rawValue, rawValue.Length);
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.Binary, rawValue, rawValue.Length);
                        } else if (type == typeof (string[])) {
                                string[] vals = (string[]) value;
                                StringBuilder fullStringValue = new StringBuilder ();
@@ -217,15 +290,18 @@ namespace Microsoft.Win32
 
                                byte[] rawValue = Encoding.Unicode.GetBytes (fullStringValue.ToString ());
                        
-                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.StringArray, rawValue, rawValue.Length); 
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.MultiString, rawValue, rawValue.Length); 
                        } else if (type.IsArray) {
                                throw new ArgumentException ("Only string and byte arrays can written as registry values");
                        } else {
                                string rawValue = String.Format ("{0}{1}", value, '\0');
-                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.String, rawValue,
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryValueKind.String, rawValue,
                                                        rawValue.Length * NativeBytesPerCharacter);
                        }
 
+                       if (result == Win32ResultCode.MarkedForDeletion)
+                               throw RegistryKey.CreateMarkedForDeletionException ();
+
                        // handle the result codes
                        if (result != Win32ResultCode.Success)
                        {
@@ -236,7 +312,7 @@ namespace Microsoft.Win32
                /// <summary>
                ///     Get a binary value.
                /// </summary>
-               private int GetBinaryValue (RegistryKey rkey, string name, RegistryType type, out byte[] data, int size)
+               private int GetBinaryValue (RegistryKey rkey, string name, RegistryValueKind type, out byte[] data, int size)
                {
                        byte[] internalData = new byte [size];
                        IntPtr handle = GetHandle (rkey);
@@ -253,13 +329,17 @@ namespace Microsoft.Win32
                
                public int SubKeyCount (RegistryKey rkey)
                {
-                       int index, result;
-                       byte[] stringBuffer = new byte [BufferMaxLength];
+                       int index;
+                       StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
                        IntPtr handle = GetHandle (rkey);
                        
                        for (index = 0; true; index ++) {
-                               result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
-                               
+                               int result = RegEnumKey (handle, index, stringBuffer,
+                                       stringBuffer.Capacity);
+
+                               if (result == Win32ResultCode.MarkedForDeletion)
+                                       throw RegistryKey.CreateMarkedForDeletionException ();
+
                                if (result == Win32ResultCode.Success)
                                        continue;
                                
@@ -275,7 +355,7 @@ namespace Microsoft.Win32
                public int ValueCount (RegistryKey rkey)
                {
                        int index, result, bufferCapacity;
-                       RegistryType type;
+                       RegistryValueKind type;
                        StringBuilder buffer = new StringBuilder (BufferMaxLength);
                        
                        IntPtr handle = GetHandle (rkey);
@@ -286,7 +366,10 @@ namespace Microsoft.Win32
                                                       buffer, ref bufferCapacity,
                                                       IntPtr.Zero, ref type, 
                                                       IntPtr.Zero, IntPtr.Zero);
-                               
+
+                               if (result == Win32ResultCode.MarkedForDeletion)
+                                       throw RegistryKey.CreateMarkedForDeletionException ();
+
                                if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
                                        continue;
                                
@@ -298,51 +381,103 @@ namespace Microsoft.Win32
                        }
                        return index;
                }
-               
-               public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writtable)
+
+               public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
+               {
+                       IntPtr handle = new IntPtr ((int) hKey);
+
+                       IntPtr keyHandle;
+                       int result = RegConnectRegistry (machineName, handle, out keyHandle);
+                       if (result != Win32ResultCode.Success)
+                               GenerateException (result);
+
+                       return new RegistryKey (hKey, keyHandle, true);
+               }
+
+               public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writable)
                {
                        int access = OpenRegKeyRead;
-                       if (writtable) access |= OpenRegKeyWrite;
+                       if (writable) access |= OpenRegKeyWrite;
                        IntPtr handle = GetHandle (rkey);
                        
                        IntPtr subKeyHandle;
                        int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
 
-                       if (result == Win32ResultCode.FileNotFound)
+                       if (result == Win32ResultCode.FileNotFound || result == Win32ResultCode.MarkedForDeletion)
                                return null;
                        
                        if (result != Win32ResultCode.Success)
                                GenerateException (result);
                        
-                       return new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
+                       return new RegistryKey (subKeyHandle, CombineName (rkey, keyName), writable);
                }
 
                public void Flush (RegistryKey rkey)
                {
+                       if (!IsHandleValid (rkey))
+                               return;
                        IntPtr handle = GetHandle (rkey);
                        RegFlushKey (handle);
                }
 
                public void Close (RegistryKey rkey)
                {
+                       if (!IsHandleValid (rkey))
+                               return;
                        IntPtr handle = GetHandle (rkey);
                        RegCloseKey (handle);
                }
 
+#if NET_4_0
+               public RegistryKey FromHandle (SafeRegistryHandle handle)
+               {
+                       // At this point we can't tell whether the key is writable
+                       // or not (nor the name), so we let the error check code handle it later, as
+                       // .Net seems to do.
+                       return new RegistryKey (handle.DangerousGetHandle (), String.Empty, true);
+               }
+#endif
+
                public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
                {
                        IntPtr handle = GetHandle (rkey);
                        IntPtr subKeyHandle;
-                       int result = RegCreateKey (handle , keyName, out subKeyHandle);
+                       int disposition;
+                       int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
+                               RegOptionsNonVolatile,
+                               OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
+
+                       if (result == Win32ResultCode.MarkedForDeletion)
+                               throw RegistryKey.CreateMarkedForDeletionException ();
 
                        if (result != Win32ResultCode.Success) {
                                GenerateException (result);
                        }
                        
-                       RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
+                       return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
+                               true);
+               }
+
+#if NET_4_0
+               public RegistryKey CreateSubKey (RegistryKey rkey, string keyName, RegistryOptions options)
+               {
+                       IntPtr handle = GetHandle (rkey);
+                       IntPtr subKeyHandle;
+                       int disposition;
+                       int result = RegCreateKeyEx (handle , keyName, 0, IntPtr.Zero,
+                               options == RegistryOptions.Volatile ? RegOptionsVolatile : RegOptionsNonVolatile,
+                               OpenRegKeyRead | OpenRegKeyWrite, IntPtr.Zero, out subKeyHandle, out disposition);
+
+                       if (result == Win32ResultCode.MarkedForDeletion)
+                               throw RegistryKey.CreateMarkedForDeletionException ();
 
-                       return subKey;
+                       if (result != Win32ResultCode.Success)
+                               GenerateException (result);
+                       
+                       return new RegistryKey (subKeyHandle, CombineName (rkey, keyName),
+                               true);
                }
+#endif
 
                public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
                {
@@ -363,7 +498,10 @@ namespace Microsoft.Win32
                {
                        IntPtr handle = GetHandle (rkey);
                        int result = RegDeleteValue (handle, value);
-                       
+
+                       if (result == Win32ResultCode.MarkedForDeletion)
+                               return;
+
                        if (result == Win32ResultCode.FileNotFound){
                                if (shouldThrowWhenKeyMissing)
                                        throw new ArgumentException ("value " + value);
@@ -377,15 +515,15 @@ namespace Microsoft.Win32
                public string [] GetSubKeyNames (RegistryKey rkey)
                {
                        IntPtr handle = GetHandle (rkey);
-                       byte[] buffer = new byte [BufferMaxLength];
-                       int bufferCapacity = BufferMaxLength;
+                       StringBuilder buffer = new StringBuilder (BufferMaxLength);
                        ArrayList keys = new ArrayList ();
                                
                        for (int index = 0; true; index ++) {
-                               int result = RegEnumKey (handle, index, buffer, bufferCapacity);
+                               int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
 
                                if (result == Win32ResultCode.Success) {
-                                       keys.Add (RegistryKey.DecodeString (buffer));
+                                       keys.Add (buffer.ToString ());
+                                       buffer.Length = 0;
                                        continue;
                                }
 
@@ -408,7 +546,7 @@ namespace Microsoft.Win32
                        {
                                StringBuilder buffer = new StringBuilder (BufferMaxLength);
                                int bufferCapacity = buffer.Capacity;
-                               RegistryType type = 0;
+                               RegistryValueKind type = 0;
                                
                                int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
                                                        IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
@@ -420,7 +558,10 @@ namespace Microsoft.Win32
                                
                                if (result == Win32ResultCode.NoMoreEntries)
                                        break;
-                                       
+
+                               if (result == Win32ResultCode.MarkedForDeletion)
+                                       throw RegistryKey.CreateMarkedForDeletionException ();
+
                                GenerateException (result);
                        }
 
@@ -436,10 +577,12 @@ namespace Microsoft.Win32
                                case Win32ResultCode.FileNotFound:
                                case Win32ResultCode.InvalidParameter:
                                        throw new ArgumentException ();
-                               
                                case Win32ResultCode.AccessDenied:
                                        throw new SecurityException ();
-
+                               case Win32ResultCode.NetworkPathNotFound:
+                                       throw new IOException ("The network path was not found.");
+                               case Win32ResultCode.InvalidHandle:
+                                       throw new IOException ("Invalid handle.");
                                default:
                                        // unidentified system exception
                                        throw new SystemException ();
@@ -448,9 +591,7 @@ namespace Microsoft.Win32
 
                public string ToString (RegistryKey rkey)
                {
-                       IntPtr handle = GetHandle (rkey);
-                       
-                       return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
+                       return rkey.Name;
                }
 
                /// <summary>
@@ -464,3 +605,5 @@ namespace Microsoft.Win32
        }
 }
 
+#endif // NET_2_1
+