diff options
Diffstat (limited to 'src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs')
-rw-r--r-- | src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs | 172 |
1 files changed, 112 insertions, 60 deletions
diff --git a/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs b/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs index 492247757..b9221327e 100644 --- a/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs +++ b/src/System.Private.CoreLib/src/Microsoft/Win32/RegistryKey.Windows.cs @@ -4,6 +4,7 @@ using Microsoft.Win32.SafeHandles; using System; +using System.Buffers; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -82,7 +83,7 @@ namespace Microsoft.Win32 { if (_hkey != null && IsDirty()) { - Interop.mincore.RegFlushKey(_hkey); + Interop.Advapi32.RegFlushKey(_hkey); } } @@ -93,7 +94,7 @@ namespace Microsoft.Win32 // By default, the new key will be writable. SafeRegistryHandle result = null; - int ret = Interop.mincore.RegCreateKeyEx(_hkey, + int ret = Interop.Advapi32.RegCreateKeyEx(_hkey, subkey, 0, null, @@ -185,7 +186,7 @@ namespace Microsoft.Win32 private RegistryKey InternalOpenSubKeyCore(string name, RegistryRights rights, bool throwOnPermissionFailure) { SafeRegistryHandle result = null; - int ret = Interop.mincore.RegOpenKeyEx(_hkey, name, 0, ((int)rights | (int)_regView), out result); + int ret = Interop.Advapi32.RegOpenKeyEx(_hkey, name, 0, ((int)rights | (int)_regView), out result); if (ret == 0 && !result.IsInvalid) { RegistryKey key = new RegistryKey(result, IsWritable((int)rights), false, _remoteKey, false, _regView); @@ -242,7 +243,7 @@ namespace Microsoft.Win32 // open the base key so that RegistryKey.Handle will return a valid handle SafeRegistryHandle result; - ret = Interop.mincore.RegOpenKeyEx(baseKey, + ret = Interop.Advapi32.RegOpenKeyEx(baseKey, null, 0, (int)GetRegistryKeyRights(IsWritable()) | (int)_regView, @@ -264,7 +265,7 @@ namespace Microsoft.Win32 { int subkeys = 0; int junk = 0; - int ret = Interop.mincore.RegQueryInfoKey(_hkey, + int ret = Interop.Advapi32.RegQueryInfoKey(_hkey, null, null, IntPtr.Zero, @@ -285,43 +286,52 @@ namespace Microsoft.Win32 return subkeys; } - private unsafe string[] InternalGetSubKeyNamesCore(int subkeys) + private string[] InternalGetSubKeyNamesCore(int subkeys) { - string[] names = new string[subkeys]; - char[] name = new char[MaxKeyLength + 1]; + var names = new List<string>(subkeys); + char[] name = ArrayPool<char>.Shared.Rent(MaxKeyLength + 1); - int namelen; - - fixed (char* namePtr = &name[0]) + try { - for (int i = 0; i < subkeys; i++) + int result; + int nameLength = name.Length; + + while ((result = Interop.Advapi32.RegEnumKeyEx( + _hkey, + names.Count, + name, + ref nameLength, + null, + null, + null, + null)) != Interop.Errors.ERROR_NO_MORE_ITEMS) { - namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialized. - int ret = Interop.mincore.RegEnumKeyEx(_hkey, - i, - namePtr, - ref namelen, - null, - null, - null, - null); - if (ret != 0) + switch (result) { - Win32Error(ret, null); + case Interop.Errors.ERROR_SUCCESS: + names.Add(new string(name, 0, nameLength)); + nameLength = name.Length; + break; + default: + // Throw the error + Win32Error(result, null); + break; } - - names[i] = new string(namePtr); } } + finally + { + ArrayPool<char>.Shared.Return(name); + } - return names; + return names.ToArray(); } private int InternalValueCountCore() { int values = 0; int junk = 0; - int ret = Interop.mincore.RegQueryInfoKey(_hkey, + int ret = Interop.Advapi32.RegQueryInfoKey(_hkey, null, null, IntPtr.Zero, @@ -345,37 +355,79 @@ namespace Microsoft.Win32 /// <returns>All value names.</returns> private unsafe string[] GetValueNamesCore(int values) { - string[] names = new string[values]; - char[] name = new char[MaxValueLength + 1]; - int namelen; + var names = new List<string>(values); + + // Names in the registry aren't usually very long, although they can go to as large + // as 16383 characters (MaxValueLength). + // + // Every call to RegEnumValue will allocate another buffer to get the data from + // NtEnumerateValueKey before copying it back out to our passed in buffer. This can + // add up quickly- we'll try to keep the memory pressure low and grow the buffer + // only if needed. + + char[] name = ArrayPool<char>.Shared.Rent(100); - fixed (char* namePtr = &name[0]) + try { - for (int i = 0; i < values; i++) + int result; + int nameLength = name.Length; + + while ((result = Interop.Advapi32.RegEnumValue( + _hkey, + names.Count, + name, + ref nameLength, + IntPtr.Zero, + null, + null, + null)) != Interop.Errors.ERROR_NO_MORE_ITEMS) { - namelen = name.Length; - - int ret = Interop.mincore.RegEnumValue(_hkey, - i, - namePtr, - ref namelen, - IntPtr.Zero, - null, - null, - null); - - if (ret != 0) + switch (result) { - // ignore ERROR_MORE_DATA if we're querying HKEY_PERFORMANCE_DATA - if (!(IsPerfDataKey() && ret == Interop.Errors.ERROR_MORE_DATA)) - Win32Error(ret, null); + // The size is only ever reported back correctly in the case + // of ERROR_SUCCESS. It will almost always be changed, however. + case Interop.Errors.ERROR_SUCCESS: + names.Add(new string(name, 0, nameLength)); + break; + case Interop.Errors.ERROR_MORE_DATA: + if (IsPerfDataKey()) + { + // Enumerating the values for Perf keys always returns + // ERROR_MORE_DATA, but has a valid name. Buffer does need + // to be big enough however. 8 characters is the largest + // known name. The size isn't returned, but the string is + // null terminated. + fixed (char* c = &name[0]) + { + names.Add(new string(c)); + } + } + else + { + char[] oldName = name; + int oldLength = oldName.Length; + name = null; + ArrayPool<char>.Shared.Return(oldName); + name = ArrayPool<char>.Shared.Rent(checked(oldLength * 2)); + } + break; + default: + // Throw the error + Win32Error(result, null); + break; } - names[i] = new string(namePtr); + // Always set the name length back to the buffer size + nameLength = name.Length; } } + finally + { + if (name != null) + ArrayPool<char>.Shared.Return(name); + } - return names; + return names.ToArray(); } private object InternalGetValueCore(string name, object defaultValue, bool doNotExpand) @@ -384,7 +436,7 @@ namespace Microsoft.Win32 int type = 0; int datasize = 0; - int ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize); + int ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize); if (ret != 0) { @@ -395,7 +447,7 @@ namespace Microsoft.Win32 int r; byte[] blob = new byte[size]; - while (Interop.Errors.ERROR_MORE_DATA == (r = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref sizeInput))) + while (Interop.Errors.ERROR_MORE_DATA == (r = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref sizeInput))) { if (size == int.MaxValue) { @@ -446,7 +498,7 @@ namespace Microsoft.Win32 case Interop.Advapi32.RegistryValues.REG_BINARY: { byte[] blob = new byte[datasize]; - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); data = blob; } break; @@ -460,7 +512,7 @@ namespace Microsoft.Win32 long blob = 0; Debug.Assert(datasize == 8, "datasize==8"); // Here, datasize must be 8 when calling this - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize); data = blob; } @@ -475,7 +527,7 @@ namespace Microsoft.Win32 int blob = 0; Debug.Assert(datasize == 4, "datasize==4"); // Here, datasize must be four when calling this - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize); data = blob; } @@ -497,7 +549,7 @@ namespace Microsoft.Win32 } char[] blob = new char[datasize / 2]; - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { data = new string(blob, 0, blob.Length - 1); @@ -527,7 +579,7 @@ namespace Microsoft.Win32 } char[] blob = new char[datasize / 2]; - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { @@ -562,7 +614,7 @@ namespace Microsoft.Win32 } char[] blob = new char[datasize / 2]; - ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); + ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize); // make sure the string is null terminated before processing the data if (blob.Length > 0 && blob[blob.Length - 1] != (char)0) @@ -634,7 +686,7 @@ namespace Microsoft.Win32 { int type = 0; int datasize = 0; - int ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize); + int ret = Interop.Advapi32.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize); if (ret != 0) { Win32Error(ret, null); @@ -719,8 +771,8 @@ namespace Microsoft.Win32 private static bool IsWritable(int rights) { - return (rights & (Interop.mincore.RegistryOperations.KEY_SET_VALUE | - Interop.mincore.RegistryOperations.KEY_CREATE_SUB_KEY | + return (rights & (Interop.Advapi32.RegistryOperations.KEY_SET_VALUE | + Interop.Advapi32.RegistryOperations.KEY_CREATE_SUB_KEY | (int)RegistryRights.Delete | (int)RegistryRights.TakeOwnership | (int)RegistryRights.ChangePermissions)) != 0; |