For the big quest to run Paint.Net:
authorMiguel de Icaza <miguel@gnome.org>
Sat, 12 Nov 2005 21:24:33 +0000 (21:24 -0000)
committerMiguel de Icaza <miguel@gnome.org>
Sat, 12 Nov 2005 21:24:33 +0000 (21:24 -0000)
2005-11-12  Miguel de Icaza  <miguel@novell.com>

* RegistryKey.cs: Add support for a Registry on Unix based on
files under ~/.mono/registry.

Vastly refactored the code, reworked the interface between the
frontend and the backends.

If "RegistryKey" was not sealed, things could have been a lot
cleaner.

svn path=/trunk/mcs/; revision=52953

mcs/class/corlib/Microsoft.Win32/ChangeLog
mcs/class/corlib/Microsoft.Win32/IRegistryApi.cs
mcs/class/corlib/Microsoft.Win32/RegistryKey.cs
mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs [new file with mode: 0644]
mcs/class/corlib/Microsoft.Win32/Win32RegistryApi.cs
mcs/class/corlib/corlib.dll.sources

index 218ef5db1e27116608aae79714a651dbbb9c4031..8c5e9278681f47b326e025e3685600bab157b520 100644 (file)
@@ -1,3 +1,14 @@
+2005-11-12  Miguel de Icaza  <miguel@novell.com>
+
+       * RegistryKey.cs: Add support for a Registry on Unix based on
+       files under ~/.mono/registry.
+
+       Vastly refactored the code, reworked the interface between the
+       frontend and the backends.  
+
+       If "RegistryKey" was not sealed, things could have been a lot
+       cleaner. 
+
 2005-01-31  mei (mei@work.email.ne.jp)
 
        * RegistryKey.cs: Fixes bug 70451: When the key doesn't exist, and
index 070c3a02f649f86e244800fbc58aaa9cdaba6621..9b9aa92b66959579037646da2639f1106c2ee0ab 100644 (file)
@@ -37,70 +37,22 @@ using System.Runtime.InteropServices;
 namespace Microsoft.Win32 {
 
        internal interface IRegistryApi {
-               
-               int OpenRegKeyRead { get; }
-               int OpenRegKeyWrite { get; }
-
-               
-               // type values for registry value data
-               int RegStringType { get; }
-               int RegEnvironmentString { get; }
-               int RegBinaryType { get; }
-               int RegDwordType { get; }
-               int RegStringArrayType { get; } 
-
-               int RegCreateKey (IntPtr keyBase,
-                               string keyName, out IntPtr keyHandle);          
-
-               int RegCloseKey (IntPtr keyHandle);
-
-               int RegFlushKey (IntPtr keyHandle);
-
-               int RegOpenKeyEx (IntPtr keyBase,
-                               string keyName, IntPtr reserved, int access,
-                               out IntPtr keyHandle);
+               RegistryKey CreateSubKey (RegistryKey rkey, string keyname);
+               RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable);
+               void Flush (RegistryKey rkey);
+               void Close (RegistryKey rkey);
 
-               int RegDeleteKey (IntPtr keyHandle, 
-                               string valueName);
+               object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value);
+               void SetValue (RegistryKey rkey, string name, object value);
 
-               int RegDeleteValue (IntPtr keyHandle, 
-                               string valueName);
-
-               int RegEnumKey (IntPtr keyBase, int index,
-                               [Out] byte[] nameBuffer, int bufferLength);
-       
-               int RegEnumValue (IntPtr keyBase, 
-                               int index, StringBuilder nameBuffer, 
-                               ref int nameLength, IntPtr reserved, 
-                               ref int type, IntPtr data, IntPtr dataLength);
-
-               int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               StringBuilder data, int rawDataLength);
-
-               int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               string data, int rawDataLength);
-
-               int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               byte[] rawData, int rawDataLength);
-
-               int RegSetValueEx (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               ref int data, int rawDataLength);
-
-               int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               IntPtr zero, ref int dataSize);
+               int SubKeyCount (RegistryKey rkey);
+               int ValueCount (RegistryKey rkey);
                
-               int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               [Out] byte[] data, ref int dataSize);
-
-               int RegQueryValueEx (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               ref int data, ref int dataSize);
+               void DeleteValue (RegistryKey rkey, string value, bool throw_if_missing);
+               void DeleteKey (RegistryKey rkey, string keyName, bool throw_if_missing);
+               string [] GetSubKeyNames (RegistryKey rkey);
+               string [] GetValueNames (RegistryKey rkey);
+               string ToString (RegistryKey rkey);
        }
 }
 
index dbad875b11f637f8bff4c0dc405c1bbf795e66a6..51e840f899acc2e58d12ae56fc565a6b9d5c34cf 100644 (file)
@@ -44,78 +44,45 @@ namespace Microsoft.Win32
        /// </summary>
        public sealed class RegistryKey : MarshalByRefObject, IDisposable 
        {
-               const char NullChar = '\0';
+               //
+               // This represents the backend data, used when creating the
+               // RegistryKey object
+               //
+               public object Data;
                
-               // Arbitrary max size for key/values names that can be fetched.
-               // .NET framework SDK docs say that the max name length that can 
-               // be used is 255 characters, we'll allow for a bit more.
-               const int BufferMaxLength = 1024;
-
-               // FIXME must be a way to determin this dynamically?
-               const int Int32ByteSize = 4;
-
-               // FIXME this is hard coded on Mono, can it be determined dynamically? 
-               readonly int NativeBytesPerCharacter = Marshal.SystemDefaultCharSize;
-
-               // FIXME this should be determined dynamically.
-               // It will be used to decode some return strings
-               // for which embeded '\0' must be preserved.
-               readonly Encoding Decoder = Encoding.Unicode;
-               
-               
-               IntPtr hkey;    // the reg key handle
                string qname;   // the fully qualified registry key name
                bool isRoot;    // is the an instance of a root key?
                
-               IRegistryApi reg_api;
+               static readonly IRegistryApi RegistryApi;
+
+               static RegistryKey ()
+               {
+                       if (Path.DirectorySeparatorChar == '\\')
+                               RegistryApi = new Win32RegistryApi ();
+                       else
+                               RegistryApi = new UnixRegistryApi ();
+               }
 
                /// <summary>
                ///     Construct an instance of a root registry key entry.
                /// </summary>
                internal RegistryKey (RegistryHive hiveId, string keyName)
                {
-                       hkey = new IntPtr ((int)hiveId);
+                       Data = hiveId;
                        qname = keyName;
                        isRoot = true;
-
-                       InitRegistryApi ();
                }
                
                /// <summary>
                ///     Construct an instance of a registry key entry.
                /// </summary>
-               internal RegistryKey (IntPtr hkey, string keyName)
+               internal RegistryKey (object data, string keyName)
                {
-                       this.hkey = hkey;
+                       Data = data;
                        qname = keyName;
                        isRoot = false;
-
-                       InitRegistryApi ();
-               }
-
-               internal void InitRegistryApi ()
-               {
-                       if (Path.DirectorySeparatorChar == '\\')
-                               reg_api = new Win32RegistryApi ();
-               }
-
-               private IRegistryApi RegistryApi {
-                       get {
-                               if (reg_api == null)
-                                       throw new NotImplementedException ("The registry is" +
-                                                       " only available on Windows.");
-                               return reg_api;
-                       }
                }
 
-               /// <summary>
-               ///     Fetch the inetrnal registry key.
-               /// </summary>
-               private IntPtr Handle {
-                       get { return hkey; }
-               }
-
-               
                #region PublicAPI
 
                /// <summary>
@@ -152,9 +119,7 @@ namespace Microsoft.Win32
                /// </summary>
                public void Flush()
                {
-                       RegTrace (" +Flush");
-                       RegistryApi.RegFlushKey (Handle);
-                       RegTrace (" -Flush");
+                       RegistryApi.Flush (this);
                }
                
                
@@ -167,10 +132,8 @@ namespace Microsoft.Win32
                        if (isRoot)
                                return;
                        
-                       RegTrace (" +Close");
-                       RegistryApi.RegCloseKey (Handle);
-                       hkey = IntPtr.Zero;
-                       RegTrace (" -Close");
+                       RegistryApi.Close (this);
+                       Data = null;
                }
                
                
@@ -179,31 +142,9 @@ namespace Microsoft.Win32
                /// </summary>
                public int SubKeyCount {
                        get {
-                               RegTrace (" +SubKeyCount");
                                AssertKeyStillValid ();
-                               
-                               int index, result;
-                               byte[] stringBuffer = new byte [BufferMaxLength];
-
-                               for (index = 0; true; index ++)
-                               {
-                                       result = RegistryApi.RegEnumKey (Handle, index, 
-                                                       stringBuffer, BufferMaxLength);
-                                       
-                                       if (result == Win32ResultCode.Success)
-                                               continue;
-                                       
-                                       if (result == Win32ResultCode.NoMoreEntries)
-                                               break;
-                                       
-                                       // something is wrong!!
-                                       RegTrace ("Win32Api::ReEnumKey  result='{0}'  name='{1}'", 
-                                                       result, Name);
-                                       GenerateException (result);
-                               }
-                               
-                               RegTrace (" -SubKeyCount");
-                               return index;
+
+                               return RegistryApi.SubKeyCount (this);
                        }
                }
 
@@ -213,35 +154,9 @@ namespace Microsoft.Win32
                /// </summary>
                public int ValueCount {
                        get {
-                               RegTrace (" +ValueCount");      
                                AssertKeyStillValid ();
-                       
-                               int index, result, type, bufferCapacity;
-                               StringBuilder buffer = new StringBuilder (BufferMaxLength);
-                               
-                               for (index = 0; true; index ++)
-                               {
-                                       type = 0;
-                                       bufferCapacity = buffer.Capacity;
-                                       result = RegistryApi.RegEnumValue (Handle, index, 
-                                                       buffer, ref bufferCapacity,
-                                                       IntPtr.Zero, ref type, 
-                                                       IntPtr.Zero, IntPtr.Zero);
-
-                                       if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
-                                               continue;
-                                       
-                                       if (result == Win32ResultCode.NoMoreEntries)
-                                               break;
-
-                                       // something is wrong
-                                       RegTrace ("Win32Api::RegEnumValue  result='{0}'  name='{1}'", 
-                                                       result, Name);
-                                       GenerateException (result);
-                               }
-
-                               RegTrace (" -ValueCount");
-                               return index;
+
+                               return RegistryApi.ValueCount (this);
                        }
                }
 
@@ -251,66 +166,15 @@ namespace Microsoft.Win32
                /// </summary>
                public void SetValue (string name, object value)
                {
-                       RegTrace (" +SetValue");
                        AssertKeyStillValid ();
                        
                        if (value == null)
                                throw new ArgumentNullException ();
-                       
-                       Type type = value.GetType ();
-                       int result;
-
-                       if (type == typeof (int))
-                       {
-                               int rawValue = (int)value;
-                               result = RegistryApi.RegSetValueEx (Handle, name, 
-                                               IntPtr.Zero, RegistryApi.RegDwordType, 
-                                               ref rawValue, Int32ByteSize); 
-                       }
-                       else if (type == typeof (byte[]))
-                       {
-                               byte[] rawValue = (byte[]) value;
-                               result = RegistryApi.RegSetValueEx (Handle, name,
-                                               IntPtr.Zero, RegistryApi.RegBinaryType,
-                                               rawValue, rawValue.Length);
-                       }
-                       else if (type == typeof (string[]))
-                       {
-                               string[] vals = (string[]) value;
-                               StringBuilder fullStringValue = new StringBuilder ();
-                               foreach (string v in vals)
-                               {
-                                       fullStringValue.Append (v);
-                                       fullStringValue.Append (NullChar);
-                               }
-                               fullStringValue.Append (NullChar);
-
-                               byte[] rawValue = Decoder.GetBytes (fullStringValue.ToString ());
-                       
-                               result = RegistryApi.RegSetValueEx (Handle, name, 
-                                               IntPtr.Zero, RegistryApi.RegStringArrayType, 
-                                               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, NullChar);
-                               result = RegistryApi.RegSetValueEx (Handle, name,
-                                               IntPtr.Zero, RegistryApi.RegStringType,
-                                               rawValue, rawValue.Length * NativeBytesPerCharacter);
-                       }
 
-                       // handle the result codes
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegSetValueEx: result: {0}", result);
-                               GenerateException (result);
-                       }
+                       if (isRoot)
+                               throw new UnauthorizedAccessException ();
                        
-                       RegTrace (" -SetValue");
+                       RegistryApi.SetValue (this, name, value);
                }
 
                
@@ -328,33 +192,10 @@ namespace Microsoft.Win32
                /// </summary>
                public RegistryKey OpenSubKey (string keyName, bool writtable)
                {
-                       RegTrace (" +OpenSubKey");
                        AssertKeyStillValid ();
                        AssertKeyNameNotNull (keyName);
-                       
-                       int access = RegistryApi.OpenRegKeyRead;
-                       if (writtable) access |= RegistryApi.OpenRegKeyWrite;
-                       
-                       IntPtr subKeyHandle;
-                       int result = RegistryApi.RegOpenKeyEx (Handle, keyName, IntPtr.Zero, 
-                                       access, out subKeyHandle);
 
-                       if (result == Win32ResultCode.FileNotFound)
-                       {
-                               RegTrace (" -OpenSubKey");
-                               return null;
-                       }
-                       
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegOpenKeyEx  result='{0}'  key name='{1}'", 
-                                               result, CombineName (keyName));
-                               GenerateException (result);
-                       }
-                       
-                       RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (keyName));
-                       RegTrace (" -OpenSubKey");
-                       return subKey;
+                       return RegistryApi.OpenSubKey (this, keyName, writtable);
                }
                
                
@@ -363,10 +204,7 @@ namespace Microsoft.Win32
                /// </summary>
                public object GetValue (string name)
                {
-                       RegTrace (" +GetValue");
-                       object obj = GetValueImpl (name, false, null);
-                       RegTrace (" -GetValue");
-                       return obj;
+                       return RegistryApi.GetValue (this, name, false, null);
                }
 
                
@@ -375,35 +213,23 @@ namespace Microsoft.Win32
                /// </summary>
                public object GetValue (string name, object defaultValue)
                {
-                       RegTrace (" +GetValue");
-                       object obj = GetValueImpl (name, true, defaultValue);
-                       RegTrace (" -GetValue");
-                       return obj;
+                       AssertKeyStillValid ();
+                       
+                       return RegistryApi.GetValue (this, name, true, defaultValue);
                }
 
                
                /// <summary>
                ///     Create a sub key.
                /// </summary>
-               public RegistryKey CreateSubKey (string keyName)
+               [MonoTODO("RegistryPermission")]
+               public RegistryKey CreateSubKey (string subkey)
                {
-                       RegTrace (" +CreateSubKey");
                        AssertKeyStillValid ();
-                       AssertKeyNameNotNull (keyName);
-                       
-                       IntPtr subKeyHandle;
-                       int result = RegistryApi.RegCreateKey (Handle , keyName, out subKeyHandle);
-
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegCreateKey: result='{0}' key name='{1}'", 
-                                               result, CombineName (keyName));
-                               GenerateException (result);
-                       }
-                       
-                       RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (keyName));
-                       RegTrace (" -CreateSubKey");
-                       return subKey;
+                       AssertKeyNameNotNull (subkey);
+                       if (subkey.Length > 255)
+                               throw new ArgumentException ("keyName length is larger than 255 characters", subkey);
+                       return RegistryApi.CreateSubKey (this, subkey);
                }
                
                
@@ -419,44 +245,26 @@ namespace Microsoft.Win32
                /// <summary>
                ///     Delete the specified subkey.
                /// </summary>
-               public void DeleteSubKey(string keyName, bool shouldThrowWhenKeyMissing)
+               public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
                {
-                       RegTrace (" +DeleteSubKey");
                        AssertKeyStillValid ();
-                       AssertKeyNameNotNull (keyName);
-                       
-                       RegistryKey child = OpenSubKey (keyName);
+                       AssertKeyNameNotNull (subkey);
+
+                       RegistryKey child = OpenSubKey (subkey);
                        
-                       if (child == null)
-                       {
-                               if (shouldThrowWhenKeyMissing)
-                                       throw new ArgumentException ("key " + keyName);
-                               RegTrace (" -DeleteSubKey");
+                       if (child == null) {
+                               if (throwOnMissingSubKey)
+                                       throw new ArgumentException ("key missing: " + subkey, "subkey");
                                return;
                        }
 
-                       if (child.SubKeyCount > 0)
-                               throw new InvalidOperationException ("key " + keyName + " has sub keys");
-                       
-                       child.Close ();
-
-                       int result = RegistryApi.RegDeleteKey (Handle, keyName);
-                       if (result == Win32ResultCode.FileNotFound)
-                       {
-                               if (shouldThrowWhenKeyMissing)
-                                       throw new ArgumentException ("key " + keyName);
-                               RegTrace (" -DeleteSubKey");
-                               return;
+                       if (child.SubKeyCount > 0){
+                               throw new InvalidOperationException ("key " + subkey + " has sub keys");
                        }
                        
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegDeleteKey: result='{0}' key name='{1}'", 
-                                               result, CombineName (keyName));
-                               GenerateException (result);
-                       }
+                       child.Close ();
 
-                       RegTrace (" -DeleteSubKey");
+                       RegistryApi.DeleteKey (this, subkey, throwOnMissingSubKey);
                }
                
                
@@ -468,18 +276,17 @@ namespace Microsoft.Win32
                        // Note: this is done by deleting sub-nodes recursively.
                        // The preformance is not very good. There may be a 
                        // better way to implement this.
-                       RegTrace (" +DeleteSubKeyTree");
+                       
                        AssertKeyStillValid ();
                        AssertKeyNameNotNull (keyName);
                        
                        RegistryKey child = OpenSubKey (keyName, true);
                        if (child == null)
-                               throw new ArgumentException ("key " + keyName);
+                               throw new ArgumentException ("key " + keyName + " at " + Name);
 
                        child.DeleteChildKeysAndValues ();
                        child.Close ();
                        DeleteSubKey (keyName, false);
-                       RegTrace (" -DeleteSubKeyTree");
                }
                
 
@@ -497,28 +304,10 @@ namespace Microsoft.Win32
                /// </summary>
                public void DeleteValue(string value, bool shouldThrowWhenKeyMissing)
                {
-                       RegTrace (" +DeleteValue");
                        AssertKeyStillValid ();
                        AssertKeyNameNotNull (value);
-                       
-                       int result = RegistryApi.RegDeleteValue (Handle, value);
-                       
-                       if (result == Win32ResultCode.FileNotFound)
-                       {
-                               if (shouldThrowWhenKeyMissing)
-                                       throw new ArgumentException ("value " + value);
-                               RegTrace (" -DeleteValue");
-                               return;
-                       }
-                       
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegDeleteValue: result='{0}' value name='{1}'", 
-                                               result, CombineName (value));
-                               GenerateException (result);
-                       }
-                       
-                       RegTrace (" -DeleteValue");
+
+                       RegistryApi.DeleteValue (this, value, shouldThrowWhenKeyMissing);
                }
                
                
@@ -527,34 +316,9 @@ namespace Microsoft.Win32
                /// </summary>
                public string[] GetSubKeyNames()
                {
-                       RegTrace (" +GetSubKeyNames");
                        AssertKeyStillValid ();
-                       
-                       byte[] buffer = new byte [BufferMaxLength];
-                       int bufferCapacity = BufferMaxLength;
-                       ArrayList keys = new ArrayList ();
-                               
-                       for (int index = 0; true; index ++)
-                       {
-                               int result = RegistryApi.RegEnumKey (Handle, index, buffer, bufferCapacity);
-
-                               if (result == Win32ResultCode.Success)
-                               {
-                                       keys.Add (DecodeString (buffer));
-                                       continue;
-                               }
-
-                               if (result == Win32ResultCode.NoMoreEntries)
-                                       break;
-
-                               // should not be here!
-                               RegTrace ("Win32Api::RegEnumKey: result='{0}' value name='{1}'", 
-                                               result, CombineName (Name));
-                               GenerateException (result);
-                       }
 
-                       RegTrace (" -GetSubKeyNames");
-                       return (string []) keys.ToArray (typeof(String));
+                       return RegistryApi.GetSubKeyNames (this);
                }
                
                
@@ -563,37 +327,8 @@ namespace Microsoft.Win32
                /// </summary>
                public string[] GetValueNames()
                {
-                       RegTrace (" +GetValueNames");
                        AssertKeyStillValid ();
-                       
-                       ArrayList values = new ArrayList ();
-                       
-                       for (int index = 0; true; index ++)
-                       {
-                               StringBuilder buffer = new StringBuilder (BufferMaxLength);
-                               int bufferCapacity = buffer.Capacity;
-                               int type = 0;
-                               
-                               int result = RegistryApi.RegEnumValue (Handle, index, buffer, ref bufferCapacity,
-                                                       IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
-
-                               if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
-                               {
-                                       values.Add (buffer.ToString ());
-                                       continue;
-                               }
-                               
-                               if (result == Win32ResultCode.NoMoreEntries)
-                                       break;
-                                       
-                               // should not be here!
-                               RegTrace ("RegistryApi.RegEnumValue: result code='{0}' name='{1}'", 
-                                               result, CombineName (Name));
-                               GenerateException (result);
-                       }
-
-                       RegTrace (" -GetValueNames");
-                       return (string []) values.ToArray (typeof(String));
+                       return RegistryApi.GetValueNames (this);
                }
                
                
@@ -611,7 +346,7 @@ namespace Microsoft.Win32
                /// </summary>
                public override string ToString()
                {
-                       return String.Format ("{0} [0x{1:X}]", Name, Handle.ToInt32 ());
+                       return RegistryApi.ToString (this);
                }
 
                #endregion // PublicAPI
@@ -622,7 +357,7 @@ namespace Microsoft.Win32
                /// </summary>
                private void AssertKeyStillValid ()
                {
-                       if (Handle == IntPtr.Zero)
+                       if (Data == null)
                                throw new ObjectDisposedException ("Microsoft.Win32.RegistryKey");
                }
 
@@ -645,12 +380,8 @@ namespace Microsoft.Win32
                /// </summary>
                private void DeleteChildKeysAndValues ()
                {
-                       RegTrace (" +DeleteChildKeysAndValues");
                        if (isRoot)
-                       {
-                               RegTrace (" -DeleteChildKeysAndValues");
                                return;
-                       }
                        
                        string[] subKeys = GetSubKeyNames ();
                        foreach (string subKey in subKeys)
@@ -662,167 +393,22 @@ namespace Microsoft.Win32
                        }
 
                        string[] values = GetValueNames ();
-                       foreach (string value in values)
-                       {
+                       foreach (string value in values) {
                                DeleteValue (value, false);
                        }
-                       
-                       RegTrace (" -DeleteChildKeysAndValues");
                }
 
-
-               /// <summary>
-               ///     Acctually read a registry value. Requires knoledge of the
-               ///     value's type and size.
-               /// </summary>
-               private object GetValueImpl (string name, bool returnDefaultValue, object defaultValue)
-               {
-                       RegTrace (" +GetValueImpl");
-                       AssertKeyStillValid ();
-                       
-                       int type = 0;
-                       int size = 0;
-                       object obj = null;
-                       
-                       int result = RegistryApi.RegQueryValueEx (Handle, name, IntPtr.Zero,
-                                       ref type, IntPtr.Zero, ref size);
-
-                       if (result == Win32ResultCode.FileNotFound)
-                       {
-                               if (returnDefaultValue) {
-                                       RegTrace (" -GetValueImpl");
-                                       return defaultValue;
-                               }
-                               return null;
-                       }
-                       
-                       if (result != Win32ResultCode.MoreData && result != Win32ResultCode.Success )
-                       {
-                               RegTrace ("Win32Api::RegQueryValueEx: result='{0}'  name='{1}'  type='{2}'  size='{3}'",        
-                                               result, name, type, size);
-                               GenerateException (result);
-                       }
-                       
-                       if (type == RegistryApi.RegStringType || type == RegistryApi.RegEnvironmentString)
-                       {
-                               byte[] data;
-                               result = GetBinaryValue (name, type, out data, size);
-                               obj = DecodeString (data);
-                       }
-                       else if (type == RegistryApi.RegDwordType)
-                       {
-                               int data = 0;
-                               result = RegistryApi.RegQueryValueEx (Handle, name, IntPtr.Zero,
-                                               ref type, ref data, ref size);
-                               obj = data;
-                       }
-                       else if (type == RegistryApi.RegBinaryType)
-                       {
-                               byte[] data;
-                               result = GetBinaryValue (name, type, out data, size);
-                               obj = data;
-                       }
-                       else if (type == RegistryApi.RegStringArrayType)
-                       {
-                               obj = null;
-                               byte[] data;
-                               result = GetBinaryValue (name, type, out data, size);
-                               
-                               if (result == Win32ResultCode.Success)
-                                       obj = DecodeString (data).Split (NullChar);
-                       }
-                       else
-                       {
-                               // should never get here
-                               throw new SystemException ();
-                       }
-
-                       // check result codes again:
-                       if (result != Win32ResultCode.Success)
-                       {
-                               RegTrace ("Win32Api::RegQueryValueEx: result='{0}' name='{1}'", 
-                                               result, name);
-                               GenerateException (result);
-                       }
-                       
-                       RegTrace (" -ReadValueImpl");
-                       return obj;
-               }
-
-               
-               /// <summary>
-               ///     Get a binary value.
-               /// </summary>
-               private int GetBinaryValue (string name, int type, out byte[] data, int size)
-               {
-                       byte[] internalData = new byte [size];
-                       int result = RegistryApi.RegQueryValueEx (Handle, name, 
-                                       IntPtr.Zero, ref type, internalData, ref size);
-                       data = internalData;
-                       return result;
-               }
-
-               
                /// <summary>
                ///     decode a byte array as a string, and strip trailing nulls
                /// </summary>
-               private string DecodeString (byte[] data)
+               static internal string DecodeString (byte[] data)
                {
-                       string stringRep = Decoder.GetString (data);
-                       int idx = stringRep.IndexOf (NullChar);
+                       string stringRep = Encoding.Unicode.GetString (data);
+                       int idx = stringRep.IndexOf ('\0');
                        if (idx >= 0)
-                                       stringRep = stringRep.Substring (0, idx);
+                               stringRep = stringRep.Substring (0, idx);
                        return stringRep;
                }
-               
-               
-               /// <summary>
-               ///     utility: Combine the sub key name to the current name to produce a 
-               ///     fully qualified sub key name.
-               /// </summary>
-               private string CombineName (string localName)
-               {
-                       return String.Format ("{0}\\{1}", Name, localName);
-               }
-               
-               
-               /// <summary>
-               /// convert a win32 error code into an appropriate exception.
-               /// </summary>
-               private void GenerateException (int errorCode)
-               {
-                       switch (errorCode) {
-                               case Win32ResultCode.FileNotFound:
-                               case Win32ResultCode.InvalidParameter:
-                                       throw new ArgumentException ();
-                               
-                               case Win32ResultCode.AccessDenied:
-                                       throw new SecurityException ();
-
-                               default:
-                                       // unidentified system exception
-                                       throw new SystemException ();
-                       }
-               }
-               
-#if (false)
-               /// <summary>
-               ///     dump trace messages if this code was compiled with tracing enabled.
-               /// </summary>
-               [Conditional("TRACE")]
-               private static void RegTrace (string message, params object[] args)
-               {
-                       message = "REG " + message;
-                       if (args.Length > 0)
-                               message = String.Format (message, args);
-
-                       Trace.WriteLine (message);
-                       //Console.WriteLine (message);
-               }
-#endif         
-               private static void RegTrace (string message, params object[] args)
-               {
-               }
        }
 }
 
diff --git a/mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs b/mcs/class/corlib/Microsoft.Win32/UnixRegistryApi.cs
new file mode 100644 (file)
index 0000000..2973eab
--- /dev/null
@@ -0,0 +1,417 @@
+//
+// Microsoft.Win32/IRegistryApi.cs
+//
+// Authors:
+//     Miguel de Icaza (miguel@gnome.org)
+//
+// (C) 2005 Novell, Inc (http://www.novell.com)
+// 
+// MISSING:
+//   Someone could the same subkey twice: once read/write once readonly,
+//   currently since we use a unique hash based on the file name, we are unable
+//   to have two versions of the same key and hence unable to throw an exception
+//   if the user tries to write to a read-only key.
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+using System.Reflection;
+using System.Security;
+using System.Threading;
+
+namespace Microsoft.Win32 {
+
+       class KeyHandler {
+               static Hashtable key_to_handler = new Hashtable ();
+               static Hashtable dir_to_key = new Hashtable ();
+               public string Dir;
+               public IntPtr Handle;
+
+               public Hashtable values;
+               string file;
+               bool dirty;
+               
+               KeyHandler (RegistryKey rkey, string basedir)
+               {
+                       if (!Directory.Exists (basedir)){
+                               try {
+                                       Directory.CreateDirectory (basedir);
+                               } catch (Exception e){
+                                       Console.Error.WriteLine ("KeyHandler error while creating directory {0}:\n{1}", basedir, e);
+                               }
+                       }
+                       Dir = basedir;
+                       file = Path.Combine (Dir, "values.xml");
+                       Load ();
+               }
+
+               public void Load ()
+               {
+                       values = new Hashtable ();
+                       if (!File.Exists (file))
+                               return;
+                       
+                       try {
+                               using (FileStream fs = File.OpenRead (file)){
+                                       StreamReader r = new StreamReader (fs);
+                                       string xml = r.ReadToEnd ();
+                                       if (xml.Length == 0)
+                                               return;
+                                       
+                                       SecurityElement tree = SecurityElement.FromString (xml);
+                                       if (tree.Tag == "values"){
+                                               foreach (SecurityElement value in tree.Children){
+                                                       if (value.Tag == "value"){
+                                                               LoadKey (value);
+                                                       }
+                                               }
+                                       }
+                               }
+                       } catch (Exception e){
+                               Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
+                               values.Clear ();
+                       }
+               }
+
+               void LoadKey (SecurityElement se)
+               {
+                       Hashtable h = se.Attributes;
+                       try {
+                               string name = (string) h ["name"];
+                               if (name == null)
+                                       return;
+                               string type = (string) h ["type"];
+                               if (type == null)
+                                       return;
+                               
+                               switch (type){
+                               case "int":
+                                       values [name] = Int32.Parse (se.Text);
+                                       break;
+                               case "bytearray":
+                                       Convert.FromBase64String (se.Text);
+                                       break;
+                               case "string":
+                                       values [name] = se.Text;
+                                       break;
+                               case "string-array":
+                                       ArrayList sa = new ArrayList ();
+                                       if (se.Children != null){
+                                               foreach (SecurityElement stre in se.Children){
+                                                       sa.Add (stre.Text);
+                                               }
+                                       }
+                                       values [name] = sa.ToArray (typeof (string));
+                                       break;
+                               }
+                       } catch {
+                               // We ignore individual errors in the file.
+                       }
+               }
+               
+               public RegistryKey Ensure (RegistryKey rkey, string extra)
+               {
+                       lock (typeof (KeyHandler)){
+                               string f = Path.Combine (Dir, extra);
+                               if (dir_to_key.Contains (f))
+                                       return (RegistryKey) dir_to_key [f];
+
+                               KeyHandler kh = new KeyHandler (rkey, f);
+                               RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
+                               key_to_handler [rk] = kh;
+                               dir_to_key [f] = rk;
+                               return rk;
+                       }
+               }
+
+               public RegistryKey Probe (RegistryKey rkey, string extra, bool write)
+               {
+                       lock (typeof (KeyHandler)){
+                               string f = Path.Combine (Dir, extra);
+                               if (dir_to_key.Contains (f))
+                                       return (RegistryKey) dir_to_key [f];
+                               Console.WriteLine ("Trying: " + f);
+                               if (Directory.Exists (f)){
+                                       KeyHandler kh = new KeyHandler (rkey, f);
+                                       RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
+                                       dir_to_key [f] = rk;
+                                       key_to_handler [rk] = kh;
+                                       return rk;
+                               }
+                               return null;
+                       }
+               }
+
+               static string CombineName (RegistryKey rkey, string extra)
+               {
+                       if (extra.IndexOf ('/') != -1)
+                               extra = extra.Replace ('/', '\\');
+                       
+                       return String.Concat (rkey.Name, "\\", extra);
+               }
+               
+               public static KeyHandler Lookup (RegistryKey rkey)
+               {
+                       lock (typeof (KeyHandler)){
+                               KeyHandler k = (KeyHandler) key_to_handler [rkey];
+                               if (k != null)
+                                       return k;
+
+                               RegistryHive x = (RegistryHive) rkey.Data;
+                               switch (x){
+                               case RegistryHive.ClassesRoot:
+                               case RegistryHive.CurrentConfig:
+                               case RegistryHive.CurrentUser:
+                               case RegistryHive.DynData:
+                               case RegistryHive.LocalMachine:
+                               case RegistryHive.PerformanceData:
+                               case RegistryHive.Users:
+                                       string d = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono/registry");
+                                       d = Path.Combine (d, x.ToString ());
+                                       
+                                       k = new KeyHandler (rkey, d);
+                                       key_to_handler [rkey] = k;
+                                       break;
+                               default:
+                                       throw new Exception ("Unknown RegistryHive");
+                               }
+                               key_to_handler [rkey] = k;
+                               return k;
+                       }
+               }
+
+               public static void Drop (RegistryKey rkey)
+               {
+                       KeyHandler k = (KeyHandler) key_to_handler [rkey];
+                       if (k == null)
+                               return;
+                       dir_to_key.Remove (k.Dir);
+                       key_to_handler.Remove (rkey);
+               }
+
+               public static void Drop (string dir)
+               {
+                       if (dir_to_key.Contains (dir)){
+                               key_to_handler.Remove (dir_to_key [dir]); 
+                               dir_to_key.Remove (dir);
+                       }
+               }
+
+               public void SetValue (string name, object value)
+               {
+                       values [name] = value;
+                       SetDirty ();
+               }
+
+               void SetDirty ()
+               {
+                       lock (typeof (KeyHandler)){
+                               if (dirty)
+                                       return;
+                               dirty = true;
+                               new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
+                       }
+               }
+
+               public void DirtyTimeout (object state)
+               {
+                       Flush ();
+               }
+               
+               public void Flush ()
+               {
+                       lock (typeof (KeyHandler)){
+                               dirty = false;
+                               Save ();
+                       }
+               }
+
+               ~KeyHandler ()
+               {
+                       Flush ();
+               }
+               
+               void Save ()
+               {
+                       if (!File.Exists (file) && values.Count == 0)
+                               return;
+                       
+                       SecurityElement se = new SecurityElement ("values");
+                       
+                       foreach (DictionaryEntry de in values){
+                               object val = de.Value;
+                               SecurityElement value = new SecurityElement ("value");
+                               value.AddAttribute ("name", (string) de.Key);
+                               
+                               if (val is string){
+                                       value.AddAttribute ("type", "string");
+                                       value.Text = (string) val;
+                               } else if (val is int){
+                                       value.AddAttribute ("type", "int");
+                                       value.Text = val.ToString ();
+                               } else if (val is byte []){
+                                       value.AddAttribute ("type", "bytearray");
+                                       value.Text = Convert.ToBase64String ((byte[]) val);
+                               } else if (val is string []){
+                                       value.AddAttribute ("type", "string-array");
+
+                                       foreach (string ss in (string[]) val){
+                                               SecurityElement str = new SecurityElement ("string");
+                                               str.Text = ss; 
+                                               value.AddChild (str);
+                                       }
+                               }
+                               se.AddChild (value);
+                       }
+
+                       try {
+                               using (FileStream fs = File.Create (file)){
+                                       StreamWriter sw = new StreamWriter (fs);
+
+                                       sw.Write (se.ToString ());
+                                       sw.Flush ();
+                               }
+                       } catch (Exception e){
+                               Console.Error.WriteLine ("When saving {0} got {1}", file, e);
+                       }
+               }
+       }
+       
+       internal class UnixRegistryApi : IRegistryApi {
+
+               static string ToUnix (string keyname)
+               {
+                       if (keyname.IndexOf ('\\') != -1)
+                               keyname = keyname.Replace ('\\', '/');
+                       return keyname.ToLower ();
+               }
+               
+               public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       return self.Ensure (rkey, ToUnix (keyname));
+               }
+
+               public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       return self.Probe (rkey, ToUnix (keyname), writtable);
+               }
+               
+               public void Flush (RegistryKey rkey)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       self.Flush ();
+               }
+               
+               public void Close (RegistryKey rkey)
+               {
+                       KeyHandler.Drop (rkey);
+               }
+               
+               public object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+
+                       if (self.values.Contains (name))
+                               return self.values [name];
+                       if (return_default_value)
+                               return default_value;
+                       return null;
+               }
+               
+               public void SetValue (RegistryKey rkey, string name, object value)
+               {
+                       if (!((value is int) || (value is string) || (value is string []) || (value is byte [])))
+                               throw new ArgumentException ("The value is not int, string, string[] or byte[]", "value");
+                       
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       self.SetValue (name, value);
+               }
+
+               public int SubKeyCount (RegistryKey rkey)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+
+                       return Directory.GetDirectories (self.Dir).Length;
+               }
+               
+               public int ValueCount (RegistryKey rkey)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+
+                       return self.values.Keys.Count;
+               }
+               
+               public void DeleteValue (RegistryKey rkey, string value, bool throw_if_missing)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+
+                       foreach (DictionaryEntry de in self.values){
+                               if ((string)de.Value == value){
+                                       self.values.Remove (de.Key);
+                                       return;
+                               }
+                       }
+                       if (throw_if_missing)
+                               throw new ArgumentException ("the given value does not exist", "value");
+               }
+               
+               public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       string dir = Path.Combine (self.Dir, keyname);
+                       
+                       if (Directory.Exists (dir)){
+                               Directory.Delete (dir, true);
+                               KeyHandler.Drop (dir);
+                       } else if (throw_if_missing)
+                               throw new ArgumentException ("the given value does not exist", "value");
+               }
+               
+               public string [] GetSubKeyNames (RegistryKey rkey)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       return Directory.GetDirectories (self.Dir);
+               }
+               
+               public string [] GetValueNames (RegistryKey rkey)
+               {
+                       KeyHandler self = KeyHandler.Lookup (rkey);
+                       ICollection keys = self.values.Keys;
+
+                       string [] vals = new string [keys.Count];
+                       keys.CopyTo (vals, 0);
+                       return vals;
+               }
+
+               public string ToString (RegistryKey rkey)
+               {
+                       return rkey.Name;
+               }
+       }
+}
index 789baf8f1ba876347ee36a98ec3307cc1f4301aa..a32e49e642de70db929f795438db248ace763c8d 100644 (file)
@@ -4,13 +4,14 @@
 // Authos:
 //     Erik LeBel (eriklebel@yahoo.ca)
 //      Jackson Harper (jackson@ximian.com)
+//      Miguel de Icaza (miguel@gnome.org)
 //
 // Copyright (C) Erik LeBel 2004
-// (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2004, 2005 Novell, Inc (http://www.novell.com)
 // 
 
 //
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004, 2005 Novell, Inc (http://www.novell.com)
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -33,7 +34,9 @@
 //
 
 using System;
+using System.Collections;
 using System.Runtime.InteropServices;
+using System.Security;
 using System.Text;
 
 namespace Microsoft.Win32
@@ -45,244 +48,411 @@ namespace Microsoft.Win32
        internal class Win32RegistryApi : IRegistryApi
        {
                // bit masks for registry key open access permissions
-               public int OpenRegKeyRead {
-                       get { return 0x00020019; }
-               }
+               const int OpenRegKeyRead = 0x00020019; 
+               const int OpenRegKeyWrite = 0x00020006; 
+
+               // FIXME must be a way to determin this dynamically?
+               const int Int32ByteSize = 4;
 
-               public int OpenRegKeyWrite {
-                       get { return 0x00020006; }
+               // 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,
                }
                
-               // type values for registry value data
-               public int RegStringType {
-                       get { return 1; }
-               }
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
+               static extern int RegCreateKey (IntPtr keyBase, string keyName, out IntPtr keyHandle);
+              
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
+               static extern int RegCloseKey (IntPtr keyHandle);
 
-               public int RegEnvironmentString {
-                       get { return 2; }
-               }
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
+               private static extern int RegFlushKey (IntPtr keyHandle);
 
-               public int RegBinaryType {
-                       get { return 3; }
-               }
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
+               private static extern int RegOpenKeyEx (IntPtr keyBase,
+                               string keyName, IntPtr reserved, int access,
+                               out IntPtr keyHandle);
 
-               public int RegDwordType {
-                       get { return 4; }
-               }
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
+               private static extern int RegDeleteKey (IntPtr keyHandle, string valueName);
 
-               public int RegStringArrayType {
-                       get { return 7; }
-               }
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
+               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);
+
+               [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);
+
+               [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, RegistryType 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,
+                               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,
+                               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,
+                               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,
+                               [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,
+                               ref int data, ref int dataSize);
 
                /// <summary>
-               ///     Create a registry key.
+               ///     Acctually read a registry value. Requires knoledge of the
+               ///     value's type and size.
                /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCreateKey")]
-               private static extern int RegCreateKey_Internal (IntPtr keyBase, 
-                               string keyName, out IntPtr keyHandle);
-
-               public int RegCreateKey (IntPtr keybase, string keyname, out IntPtr handle)
+               public object GetValue (RegistryKey rkey, string name, bool returnDefaultValue, object defaultValue)
                {
-                       return RegCreateKey_Internal (keybase, keyname, out handle);
+                       RegistryType type = 0;
+                       int size = 0;
+                       object obj = null;
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       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.MoreData && result != Win32ResultCode.Success ) {
+                               GenerateException (result);
+                       }
+                       
+                       if (type == RegistryType.String || type == RegistryType.EnvironmentString) {
+                               byte[] data;
+                               result = GetBinaryValue (rkey, name, type, out data, size);
+                               obj = RegistryKey.DecodeString (data);
+                       } else if (type == RegistryType.Dword) {
+                               int data = 0;
+                               result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, ref data, ref size);
+                               obj = data;
+                       } else if (type == RegistryType.Binary) {
+                               byte[] data;
+                               result = GetBinaryValue (rkey, name, type, out data, size);
+                               obj = data;
+                       } else if (type == RegistryType.StringArray) {
+                               obj = null;
+                               byte[] data;
+                               result = GetBinaryValue (rkey, name, type, out data, size);
+                               
+                               if (result == Win32ResultCode.Success)
+                                       obj = RegistryKey.DecodeString (data).Split ('\0');
+                       } else {
+                               // should never get here
+                               throw new SystemException ();
+                       }
+
+                       // check result codes again:
+                       if (result != Win32ResultCode.Success)
+                       {
+                               GenerateException (result);
+                       }
+                       
+
+                       return obj;
                }
-              
-               /// <summary>
-               ///     Close a registry key.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegCloseKey")]
-               private static extern int RegCloseKey_Internal (IntPtr keyHandle);
 
-               public int RegCloseKey (IntPtr handle)
+               public void SetValue (RegistryKey rkey, string name, object value)
                {
-                       return RegCloseKey_Internal (handle);
+                       Type type = value.GetType ();
+                       int result;
+                       IntPtr handle = (IntPtr)rkey.Data;
+
+                       if (type == typeof (int)) {
+                               int rawValue = (int)value;
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Dword, ref rawValue, Int32ByteSize); 
+                       } else if (type == typeof (byte[])) {
+                               byte[] rawValue = (byte[]) value;
+                               result = RegSetValueEx (handle, name, IntPtr.Zero, RegistryType.Binary, rawValue, rawValue.Length);
+                       } else if (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, RegistryType.StringArray, 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,
+                                                       rawValue.Length * NativeBytesPerCharacter);
+                       }
+
+                       // handle the result codes
+                       if (result != Win32ResultCode.Success)
+                       {
+                               GenerateException (result);
+                       }
                }
 
                /// <summary>
-               ///     Flush a registry key's current state to disk.
+               ///     Get a binary value.
                /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegFlushKey")]
-               private static extern int RegFlushKey_Internal (IntPtr keyHandle);
-
-               public int RegFlushKey (IntPtr handle)
+               private int GetBinaryValue (RegistryKey rkey, string name, RegistryType type, out byte[] data, int size)
                {
-                       return RegFlushKey_Internal (handle);
+                       byte[] internalData = new byte [size];
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       int result = RegQueryValueEx (handle, name, IntPtr.Zero, ref type, internalData, ref size);
+                       data = internalData;
+                       return result;
                }
 
-               /// <summary>
-               ///     Open a registry key.
-               ///     'unknown' must be zero.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegOpenKeyEx")]
-               private static extern int RegOpenKeyEx_Internal (IntPtr keyBase,
-                               string keyName, IntPtr reserved, int access,
-                               out IntPtr keyHandle);
-
-               public int RegOpenKeyEx (IntPtr keybase, string keyname, IntPtr reserved,
-                               int access, out IntPtr handle)
+               
+               // Arbitrary max size for key/values names that can be fetched.
+               // .NET framework SDK docs say that the max name length that can 
+               // be used is 255 characters, we'll allow for a bit more.
+               const int BufferMaxLength = 1024;
+               
+               public int SubKeyCount (RegistryKey rkey)
                {
-                       return RegOpenKeyEx_Internal (keybase, keyname, reserved, access, out handle);
+                       int index, result;
+                       byte[] stringBuffer = new byte [BufferMaxLength];
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       
+                       for (index = 0; true; index ++) {
+                               result = RegEnumKey (handle, index, stringBuffer, BufferMaxLength);
+                               
+                               if (result == Win32ResultCode.Success)
+                                       continue;
+                               
+                               if (result == Win32ResultCode.NoMoreEntries)
+                                       break;
+                               
+                               // something is wrong!!
+                               GenerateException (result);
+                       }
+                       return index;
                }
 
-               /// <summary>
-               ///     Delete a registry key.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteKey")]
-               private static extern int RegDeleteKey_Internal (IntPtr keyHandle, 
-                               string valueName);
-
-               public int RegDeleteKey (IntPtr handle, string valuename)
+               public int ValueCount (RegistryKey rkey)
                {
-                       return RegDeleteKey_Internal (handle, valuename);
+                       int index, result, bufferCapacity;
+                       RegistryType type;
+                       StringBuilder buffer = new StringBuilder (BufferMaxLength);
+                       
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       for (index = 0; true; index ++) {
+                               type = 0;
+                               bufferCapacity = buffer.Capacity;
+                               result = RegEnumValue (handle, index, 
+                                                      buffer, ref bufferCapacity,
+                                                      IntPtr.Zero, ref type, 
+                                                      IntPtr.Zero, IntPtr.Zero);
+                               
+                               if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData)
+                                       continue;
+                               
+                               if (result == Win32ResultCode.NoMoreEntries)
+                                       break;
+                               
+                               // something is wrong
+                               GenerateException (result);
+                       }
+                       return index;
                }
-
-               /// <summary>
-               ///     Delete a registry value.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegDeleteValue")]
-               private static extern int RegDeleteValue_Internal (IntPtr keyHandle, 
-                               string valueName);
-
-               public int RegDeleteValue (IntPtr handle, string valuename)
+               
+               public RegistryKey OpenSubKey (RegistryKey rkey, string keyName, bool writtable)
                {
-                       return RegDeleteValue_Internal (handle, valuename);
+                       int access = OpenRegKeyRead;
+                       if (writtable) access |= OpenRegKeyWrite;
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       
+                       IntPtr subKeyHandle;
+                       int result = RegOpenKeyEx (handle, keyName, IntPtr.Zero, access, out subKeyHandle);
+
+                       if (result == Win32ResultCode.FileNotFound)
+                               return null;
+                       
+                       if (result != Win32ResultCode.Success)
+                               GenerateException (result);
+                       
+                       return new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
                }
 
-               /// <summary>
-               ///     Fetch registry key subkeys itteratively.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKey")]
-               private static extern int RegEnumKey_Internal (IntPtr keyBase, int index,
-                               [Out] byte[] nameBuffer, int bufferLength);
-
-               public int RegEnumKey (IntPtr keybase, int index,
-                               [Out] byte [] namebuffer, int buffer_length)
+               public void Flush (RegistryKey rkey)
                {
-                       return RegEnumKey_Internal (keybase, index, namebuffer, buffer_length);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       RegFlushKey (handle);
                }
 
-               /// <summary>
-               ///     Fetch registry key value names itteratively.
-               ///
-               ///     Arguments 'reserved', 'data', 'dataLength' 
-               ///     should be set to IntPtr.Zero.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumValue")]
-               private static extern int RegEnumValue_Internal (IntPtr keyBase, 
-                               int index, StringBuilder nameBuffer, 
-                               ref int nameLength, IntPtr reserved, 
-                               ref int type, IntPtr data, IntPtr dataLength);
-
-               public int RegEnumValue (IntPtr keybase, int index, StringBuilder namebuffer,
-                               ref int namelength, IntPtr reserved, ref int type, IntPtr data,
-                               IntPtr datalength)
+               public void Close (RegistryKey rkey)
                {
-                       return RegEnumValue_Internal (keybase, index, namebuffer, ref namelength,
-                                       reserved, ref type, data, datalength);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       RegCloseKey (handle);
                }
 
-               /// <summary>
-               ///     Set a registry value with string builder data.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
-               private static extern int RegSetValueEx_Internal (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               StringBuilder data, int rawDataLength);
-
-               public int RegSetValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               int type, StringBuilder data, int datalength)
+               public RegistryKey CreateSubKey (RegistryKey rkey, string keyName)
                {
-                       return RegSetValueEx_Internal (keybase, valuename, reserved,
-                                       type, data, datalength);
-               }
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       IntPtr subKeyHandle;
+                       int result = RegCreateKey (handle , keyName, out subKeyHandle);
 
-               /// <summary>
-               ///     Set a registry value with string data.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
-               private static extern int RegSetValueEx_Internal (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               string data, int rawDataLength);
+                       if (result != Win32ResultCode.Success) {
+                               GenerateException (result);
+                       }
+                       
+                       RegistryKey subKey = new RegistryKey (subKeyHandle, CombineName (rkey, keyName));
+
+                       return subKey;
+               }
 
-               public int RegSetValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               int type, string data, int datalength)
+               public void DeleteKey (RegistryKey rkey, string keyName, bool shouldThrowWhenKeyMissing)
                {
-                       return RegSetValueEx_Internal (keybase, valuename, reserved,
-                                       type, data, datalength);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       int result = RegDeleteKey (handle, keyName);
+
+                       if (result == Win32ResultCode.FileNotFound) {
+                               if (shouldThrowWhenKeyMissing)
+                                       throw new ArgumentException ("key " + keyName);
+                               return;
+                       }
+                       
+                       if (result != Win32ResultCode.Success)
+                               GenerateException (result);
                }
-               
-               /// <summary>
-               ///     Set a registry value with binary data (a byte array).
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
-               private static extern int RegSetValueEx_Internal (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               byte[] rawData, int rawDataLength);
 
-               public int RegSetValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               int type, byte [] data, int datalength)
+               public void DeleteValue (RegistryKey rkey, string value, bool shouldThrowWhenKeyMissing)
                {
-                       return RegSetValueEx_Internal (keybase, valuename, reserved,
-                                       type, data, datalength);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       int result = RegDeleteValue (handle, value);
+                       
+                       if (result == Win32ResultCode.FileNotFound){
+                               if (shouldThrowWhenKeyMissing)
+                                       throw new ArgumentException ("value " + value);
+                               return;
+                       }
+                       
+                       if (result != Win32ResultCode.Success)
+                               GenerateException (result);
                }
-               
-               /// <summary>
-               ///     Set a registry value to a DWORD value.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
-               private static extern int RegSetValueEx_Internal (IntPtr keyBase, 
-                               string valueName, IntPtr reserved, int type,
-                               ref int data, int rawDataLength);
 
-               public int RegSetValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               int type, ref int data, int datalength)
+               public string [] GetSubKeyNames (RegistryKey rkey)
                {
-                       return RegSetValueEx_Internal (keybase, valuename, reserved, type,
-                                       ref data, datalength);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       byte[] buffer = new byte [BufferMaxLength];
+                       int bufferCapacity = BufferMaxLength;
+                       ArrayList keys = new ArrayList ();
+                               
+                       for (int index = 0; true; index ++) {
+                               int result = RegEnumKey (handle, index, buffer, bufferCapacity);
+
+                               if (result == Win32ResultCode.Success) {
+                                       keys.Add (RegistryKey.DecodeString (buffer));
+                                       continue;
+                               }
+
+                               if (result == Win32ResultCode.NoMoreEntries)
+                                       break;
+
+                               // should not be here!
+                               GenerateException (result);
+                       }
+                       return (string []) keys.ToArray (typeof(String));
                }
 
-               /// <summary>
-               ///     Get a registry value's info. No data.
-               /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
-               private static extern int RegQueryValueEx_Internal (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               IntPtr zero, ref int dataSize);
 
-               public int RegQueryValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               ref int type, IntPtr zero, ref int datasize)
+               public string [] GetValueNames (RegistryKey rkey)
                {
-                       return RegQueryValueEx_Internal (keybase, valuename, reserved,
-                                       ref type, zero, ref datasize);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       ArrayList values = new ArrayList ();
+                       
+                       for (int index = 0; true; index ++)
+                       {
+                               StringBuilder buffer = new StringBuilder (BufferMaxLength);
+                               int bufferCapacity = buffer.Capacity;
+                               RegistryType type = 0;
+                               
+                               int result = RegEnumValue (handle, index, buffer, ref bufferCapacity,
+                                                       IntPtr.Zero, ref type, IntPtr.Zero, IntPtr.Zero);
+
+                               if (result == Win32ResultCode.Success || result == Win32ResultCode.MoreData) {
+                                       values.Add (buffer.ToString ());
+                                       continue;
+                               }
+                               
+                               if (result == Win32ResultCode.NoMoreEntries)
+                                       break;
+                                       
+                               GenerateException (result);
+                       }
+
+                       return (string []) values.ToArray (typeof(String));
                }
 
                /// <summary>
-               ///     Get a registry value. Binary data.
+               /// convert a win32 error code into an appropriate exception.
                /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
-               private static extern int RegQueryValueEx_Internal (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               [Out] byte[] data, ref int dataSize);
+               private void GenerateException (int errorCode)
+               {
+                       switch (errorCode) {
+                               case Win32ResultCode.FileNotFound:
+                               case Win32ResultCode.InvalidParameter:
+                                       throw new ArgumentException ();
+                               
+                               case Win32ResultCode.AccessDenied:
+                                       throw new SecurityException ();
+
+                               default:
+                                       // unidentified system exception
+                                       throw new SystemException ();
+                       }
+               }
 
-               public int RegQueryValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               ref int type, [Out] byte [] data, ref int datasize)
+               public string ToString (RegistryKey rkey)
                {
-                       return RegQueryValueEx_Internal (keybase, valuename, reserved,
-                                       ref type, data, ref datasize);
+                       IntPtr handle = (IntPtr)rkey.Data;
+                       
+                       return String.Format ("{0} [0x{1:X}]", rkey.Name, handle.ToInt32 ());
                }
 
                /// <summary>
-               ///     Get a registry value. DWORD data.
+               ///     utility: Combine the sub key name to the current name to produce a 
+               ///     fully qualified sub key name.
                /// </summary>
-               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegQueryValueEx")]
-               private static extern int RegQueryValueEx_Internal (IntPtr keyBase,
-                               string valueName, IntPtr reserved, ref int type,
-                               ref int data, ref int dataSize);
-
-               public int RegQueryValueEx (IntPtr keybase, string valuename, IntPtr reserved,
-                               ref int type, ref int data, ref int datasize)
+               internal static string CombineName (RegistryKey rkey, string localName)
                {
-                       return RegQueryValueEx_Internal (keybase, valuename, reserved,
-                                       ref type, ref data, ref datasize);
+                       return String.Concat (rkey.Name, "\\", localName);
                }
        }
 }
index 56041f432c8e851c1fa0dc7099d608f7e9788696..6d9c98b5c7337e2cfc38fd16a24230669a8ac299 100644 (file)
@@ -5,6 +5,7 @@ Microsoft.Win32/IRegistryApi.cs
 Microsoft.Win32/RegistryKey.cs
 Microsoft.Win32/Registry.cs
 Microsoft.Win32/RegistryHive.cs
+Microsoft.Win32/UnixRegistryApi.cs
 Microsoft.Win32/Win32RegistryApi.cs
 Microsoft.Win32/Win32ResultCode.cs
 Microsoft.Win32.SafeHandles/SafeHandleZeroOrMinusOneIsInvalid.cs