Merge pull request #4051 from Unity-Technologies/registry-bug-fixes
authorNiklas Therning <niklas@therning.org>
Thu, 1 Dec 2016 14:40:40 +0000 (15:40 +0100)
committerGitHub <noreply@github.com>
Thu, 1 Dec 2016 14:40:40 +0000 (15:40 +0100)
Fix a bug in registry API implementation where had wrong assumptions about possible registry size.

mcs/class/corlib/Microsoft.Win32/Win32RegistryApi.cs

index 220967e09e4b157c1bd72f731002968cff817139..033572799dfcbd5ecb6e9fca79f3280d286edfeb 100644 (file)
@@ -92,14 +92,17 @@ namespace Microsoft.Win32
                [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, StringBuilder nameBuffer, int bufferLength);
+               [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegEnumKeyExW")]
+               internal unsafe static extern int RegEnumKeyEx (IntPtr keyHandle, int dwIndex,
+                                       char* lpName, ref int lpcbName, int[] lpReserved,
+                                       [Out]StringBuilder lpClass, int[] lpcbClass,
+                                       long[] lpftLastWriteTime);
 
                [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 RegistryValueKind type, IntPtr data, IntPtr dataLength);
+               internal unsafe static extern int RegEnumValue (IntPtr hKey, int dwIndex,
+                                       char* lpValueName, ref int lpcbValueName,
+                                       IntPtr lpReserved_MustBeZero, int[] lpType, byte[] lpData,
+                                       int[] lpcbData);
 
 //             [DllImport ("advapi32.dll", CharSet=CharSet.Unicode, EntryPoint="RegSetValueEx")]
 //             private static extern int RegSetValueEx (IntPtr keyBase, 
@@ -146,6 +149,14 @@ namespace Microsoft.Win32
                                string valueName, IntPtr reserved, ref RegistryValueKind type,
                                ref long data, ref int dataSize);
 
+               [DllImport ("advapi32.dll", CharSet = CharSet.Unicode, EntryPoint="RegQueryInfoKeyW")]
+               internal static extern int RegQueryInfoKey (IntPtr hKey, [Out]StringBuilder lpClass,
+                       int[] lpcbClass, IntPtr lpReserved_MustBeZero, ref int lpcSubKeys,
+                       int[] lpcbMaxSubKeyLen, int[] lpcbMaxClassLen,
+                       ref int lpcValues, int[] lpcbMaxValueNameLen,
+                       int[] lpcbMaxValueLen, int[] lpcbSecurityDescriptor,
+                       int[] lpftLastWriteTime);
+
                // Returns our handle from the RegistryKey
                public IntPtr GetHandle (RegistryKey key)
                {
@@ -355,59 +366,54 @@ namespace Microsoft.Win32
                        return result;
                }
 
-               
-               // 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;
+               // MSDN defines the following limits for registry key names & values:
+               // Key Name: 255 characters
+               // Value name:  16,383 Unicode characters
+               // Value: either 1 MB or current available memory, depending on registry format.
+               private const int MaxKeyLength = 255;
+               private const int MaxValueLength = 16383;
                
                public int SubKeyCount (RegistryKey rkey)
                {
-                       int index;
-                       StringBuilder stringBuffer = new StringBuilder (BufferMaxLength);
-                       IntPtr handle = GetHandle (rkey);
-                       
-                       for (index = 0; true; index ++) {
-                               int result = RegEnumKey (handle, index, stringBuffer,
-                                       stringBuffer.Capacity);
-
-                               if (result == Win32ResultCode.Success)
-                                       continue;
-                               
-                               if (result == Win32ResultCode.NoMoreEntries)
-                                       break;
-                               
-                               // something is wrong!!
-                               GenerateException (result);
-                       }
-                       return index;
+                       int subkeys = 0;
+                       int junk = 0;
+                       int ret = RegQueryInfoKey (GetHandle (rkey),
+                                                                          null,
+                                                                          null,
+                                                                          IntPtr.Zero,
+                                                                          ref subkeys,  // subkeys
+                                                                          null,
+                                                                          null,
+                                                                          ref junk,     // values
+                                                                          null,
+                                                                          null,
+                                                                          null,
+                                                                          null);
+
+                       if (ret != Win32ResultCode.Success)
+                               GenerateException (ret);
+                       return subkeys;
                }
 
                public int ValueCount (RegistryKey rkey)
                {
-                       int index, result, bufferCapacity;
-                       RegistryValueKind type;
-                       StringBuilder buffer = new StringBuilder (BufferMaxLength);
-                       
-                       IntPtr handle = GetHandle (rkey);
-                       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;
+                       int values = 0;
+                       int junk = 0;
+                       int ret = RegQueryInfoKey (GetHandle (rkey),
+                                                                          null,
+                                                                          null,
+                                                                          IntPtr.Zero,
+                                                                          ref junk,     // subkeys
+                                                                          null,
+                                                                          null,
+                                                                          ref values,   // values
+                                                                          null,
+                                                                          null,
+                                                                          null,
+                                                                          null);
+                       if (ret != Win32ResultCode.Success)
+                               GenerateException (ret);
+                       return values;
                }
 
                public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
@@ -536,57 +542,70 @@ namespace Microsoft.Win32
                                GenerateException (result);
                }
 
-               public string [] GetSubKeyNames (RegistryKey rkey)
+               public unsafe string [] GetSubKeyNames (RegistryKey rkey)
                {
-                       IntPtr handle = GetHandle (rkey);
-                       StringBuilder buffer = new StringBuilder (BufferMaxLength);
-                       var keys = new List<string> ();
-                               
-                       for (int index = 0; true; index ++) {
-                               int result = RegEnumKey (handle, index, buffer, buffer.Capacity);
-
-                               if (result == Win32ResultCode.Success) {
-                                       keys.Add (buffer.ToString ());
-                                       buffer.Length = 0;
-                                       continue;
+                       int subkeys = SubKeyCount (rkey);
+                       var names = new string [subkeys];  // Returns 0-length array if empty.
+
+                       if (subkeys > 0) {
+                               var hkey = GetHandle (rkey);
+                               char[] name = new char [MaxKeyLength + 1];
+                               int namelen;
+
+                               fixed (char* namePtr = &name [0]) {
+                                       for (int i = 0; i < subkeys; i++) {
+                                               namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialised.
+                                               int ret = RegEnumKeyEx (hkey,
+                                                       i,
+                                                       namePtr,
+                                                       ref namelen,
+                                                       null,
+                                                       null,
+                                                       null,
+                                                       null);
+
+                                               if (ret != 0)
+                                                       GenerateException (ret);
+                                               names [i] = new String (namePtr);
+                                       }
                                }
-
-                               if (result == Win32ResultCode.NoMoreEntries)
-                                       break;
-
-                               // should not be here!
-                               GenerateException (result);
                        }
-                       return keys.ToArray ();
-               }
 
+                       return names;
+               }
 
-               public string [] GetValueNames (RegistryKey rkey)
+               public unsafe string [] GetValueNames (RegistryKey rkey)
                {
-                       IntPtr handle = GetHandle (rkey);
-                       var values = new List<string> ();
-                       
-                       for (int index = 0; true; index ++)
-                       {
-                               StringBuilder buffer = new StringBuilder (BufferMaxLength);
-                               int bufferCapacity = buffer.Capacity;
-                               RegistryValueKind 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;
+                       int values = ValueCount (rkey);
+                       String[] names = new String [values];
+
+                       if (values > 0) {
+                               IntPtr hkey = GetHandle (rkey);
+                               char[] name = new char [MaxValueLength + 1];
+                               int namelen;
+
+                               fixed (char* namePtr = &name [0]) {
+                                       for (int i = 0; i < values; i++) {
+                                               namelen = name.Length;
+
+                                               int ret = RegEnumValue (hkey,
+                                                       i,
+                                                       namePtr,
+                                                       ref namelen,
+                                                       IntPtr.Zero,
+                                                       null,
+                                                       null,
+                                                       null);
+
+                                               if (ret != Win32ResultCode.Success && ret != Win32Native.ERROR_MORE_DATA)
+                                                       GenerateException (ret);
+
+                                               names [i] = new String (namePtr);
+                                       }
                                }
-                               
-                               if (result == Win32ResultCode.NoMoreEntries)
-                                       break;
-
-                               GenerateException (result);
                        }
 
-                       return values.ToArray ();
+                       return names;
                }
 
                private void CheckResult (int result)