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 | 206 |
1 files changed, 129 insertions, 77 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 6b0cc8962..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; @@ -75,14 +76,14 @@ namespace Microsoft.Win32 // the dispose below and usage elsewhere (other threads). This is By Design. // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary. - Interop.mincore.RegCloseKey(HKEY_PERFORMANCE_DATA); + Interop.Advapi32.RegCloseKey(HKEY_PERFORMANCE_DATA); } private void FlushCore() { 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,17 +447,17 @@ 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 == Int32.MaxValue) + if (size == int.MaxValue) { - // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue + // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond int.MaxValue Win32Error(r, name); } - else if (size > (Int32.MaxValue / 2)) + else if (size > (int.MaxValue / 2)) { // at this point in the loop "size * 2" would cause an overflow - size = Int32.MaxValue; + size = int.MaxValue; } else { @@ -441,47 +493,47 @@ namespace Microsoft.Win32 switch (type) { - case Interop.mincore.RegistryValues.REG_NONE: - case Interop.mincore.RegistryValues.REG_DWORD_BIG_ENDIAN: - case Interop.mincore.RegistryValues.REG_BINARY: + case Interop.Advapi32.RegistryValues.REG_NONE: + case Interop.Advapi32.RegistryValues.REG_DWORD_BIG_ENDIAN: + 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; - case Interop.mincore.RegistryValues.REG_QWORD: + case Interop.Advapi32.RegistryValues.REG_QWORD: { // also REG_QWORD_LITTLE_ENDIAN if (datasize > 8) { // prevent an AV in the edge case that datasize is larger than sizeof(long) - goto case Interop.mincore.RegistryValues.REG_BINARY; + goto case Interop.Advapi32.RegistryValues.REG_BINARY; } 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; } break; - case Interop.mincore.RegistryValues.REG_DWORD: + case Interop.Advapi32.RegistryValues.REG_DWORD: { // also REG_DWORD_LITTLE_ENDIAN if (datasize > 4) { // prevent an AV in the edge case that datasize is larger than sizeof(int) - goto case Interop.mincore.RegistryValues.REG_QWORD; + goto case Interop.Advapi32.RegistryValues.REG_QWORD; } 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; } break; - case Interop.mincore.RegistryValues.REG_SZ: + case Interop.Advapi32.RegistryValues.REG_SZ: { if (datasize % 2 == 1) { @@ -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); @@ -511,7 +563,7 @@ namespace Microsoft.Win32 } break; - case Interop.mincore.RegistryValues.REG_EXPAND_SZ: + case Interop.Advapi32.RegistryValues.REG_EXPAND_SZ: { if (datasize % 2 == 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) { @@ -546,7 +598,7 @@ namespace Microsoft.Win32 } } break; - case Interop.mincore.RegistryValues.REG_MULTI_SZ: + case Interop.Advapi32.RegistryValues.REG_MULTI_SZ: { if (datasize % 2 == 1) { @@ -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) @@ -622,7 +674,7 @@ namespace Microsoft.Win32 data = strings; } break; - case Interop.mincore.RegistryValues.REG_LINK: + case Interop.Advapi32.RegistryValues.REG_LINK: default: break; } @@ -634,14 +686,14 @@ 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); } return - type == Interop.mincore.RegistryValues.REG_NONE ? RegistryValueKind.None : + type == Interop.Advapi32.RegistryValues.REG_NONE ? RegistryValueKind.None : !Enum.IsDefined(typeof(RegistryValueKind), type) ? RegistryValueKind.Unknown : (RegistryValueKind)type; } @@ -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; |