*/
-
+using Microsoft.Win32.SafeHandles;
using System;
-using System.Collections;
+using System.Buffers;
using System.Collections.Generic;
-using System.Security;
-using System.Text;
-using System.Threading;
-using System.IO;
-using System.Runtime.Remoting;
-using System.Runtime.InteropServices;
-using Microsoft.Win32.SafeHandles;
-using System.Runtime.Versioning;
-using System.Globalization;
using System.Diagnostics.Contracts;
-using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Text;
namespace Microsoft.Win32
{
public void DeleteValue(String name, bool throwOnMissingValue)
{
EnsureWriteable();
- CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default);
int errorCode = Win32Native.RegDeleteValue(hkey, name);
//
{
ValidateKeyName(name);
EnsureNotDisposed();
- name = FixupName(name); // Fixup multiple slashes to a single slash
+ name = FixupName(name); // Fixup multiple slashes to a single slash
- CheckPermission(RegistryInternalCheck.CheckOpenSubKeyWithWritablePermission, name, writable, RegistryKeyPermissionCheck.Default);
SafeRegistryHandle result = null;
int ret = Win32Native.RegOpenKeyEx(hkey,
name,
return OpenSubKey(name, false);
}
- internal int InternalSubKeyCount()
+ /// <summary>
+ /// Retrieves an array of strings containing all the subkey names.
+ /// </summary>
+ public string[] GetSubKeyNames()
{
EnsureNotDisposed();
- int subkeys = 0;
- int junk = 0;
- int ret = Win32Native.RegQueryInfoKey(hkey,
- null,
- null,
- IntPtr.Zero,
- ref subkeys, // subkeys
- null,
- null,
- ref junk, // values
- null,
- null,
- null,
- null);
-
- if (ret != 0)
- Win32Error(ret, null);
- return subkeys;
- }
+ var names = new List<string>();
+ char[] name = ArrayPool<char>.Shared.Rent(MaxKeyLength + 1);
- /**
- * Retrieves an array of strings containing all the subkey names.
- *
- * @return all subkey names.
- */
- public String[] GetSubKeyNames()
- {
- CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
- return InternalGetSubKeyNames();
- }
-
- internal unsafe String[] InternalGetSubKeyNames()
- {
- EnsureNotDisposed();
- int subkeys = InternalSubKeyCount();
- String[] names = new String[subkeys]; // Returns 0-length array if empty.
-
- if (subkeys > 0)
+ try
{
- char[] name = new char[MaxKeyLength + 1];
-
- int namelen;
-
- fixed (char* namePtr = &name[0])
+ int result;
+ int nameLength = name.Length;
+
+ while ((result = Win32Native.RegEnumKeyEx(
+ hkey,
+ names.Count,
+ name,
+ ref nameLength,
+ null,
+ null,
+ null,
+ null)) != Interop.Errors.ERROR_NO_MORE_ITEMS)
{
- for (int i = 0; i < subkeys; i++)
+ switch (result)
{
- namelen = name.Length; // Don't remove this. The API's doesn't work if this is not properly initialised.
- int ret = Win32Native.RegEnumKeyEx(hkey,
- i,
- namePtr,
- ref namelen,
- null,
- null,
- null,
- null);
- if (ret != 0)
- Win32Error(ret, null);
- names[i] = new String(namePtr);
+ case Interop.Errors.ERROR_SUCCESS:
+ names.Add(new string(name, 0, nameLength));
+ nameLength = name.Length;
+ break;
+ default:
+ // Throw the error
+ Win32Error(result, null);
+ break;
}
}
}
+ finally
+ {
+ ArrayPool<char>.Shared.Return(name);
+ }
- return names;
+ return names.ToArray();
}
- internal int InternalValueCount()
+ /// <summary>
+ /// Retrieves an array of strings containing all the value names.
+ /// </summary>
+ public unsafe string[] GetValueNames()
{
EnsureNotDisposed();
- int values = 0;
- int junk = 0;
- int ret = Win32Native.RegQueryInfoKey(hkey,
- null,
- null,
- IntPtr.Zero,
- ref junk, // subkeys
- null,
- null,
- ref values, // values
- null,
- null,
- null,
- null);
- if (ret != 0)
- Win32Error(ret, null);
- return values;
- }
+ var names = new List<string>();
- /**
- * Retrieves an array of strings containing all the value names.
- *
- * @return all value names.
- */
- public unsafe String[] GetValueNames()
- {
- CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
- EnsureNotDisposed();
+ // 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.
- int values = InternalValueCount();
- String[] names = new String[values];
+ char[] name = ArrayPool<char>.Shared.Rent(100);
- if (values > 0)
+ try
{
- char[] name = new char[MaxValueLength + 1];
- int namelen;
-
- fixed (char* namePtr = &name[0])
+ int result;
+ int nameLength = name.Length;
+
+ while ((result = Win32Native.RegEnumValue(
+ hkey,
+ names.Count,
+ name,
+ ref nameLength,
+ IntPtr.Zero,
+ null,
+ null,
+ null)) != Interop.Errors.ERROR_NO_MORE_ITEMS)
{
- for (int i = 0; i < values; i++)
+ switch (result)
{
- namelen = name.Length;
-
- int ret = Win32Native.RegEnumValue(hkey,
- i,
- namePtr,
- ref namelen,
- IntPtr.Zero,
- null,
- null,
- null);
-
- if (ret != 0)
- {
- // ignore ERROR_MORE_DATA if we're querying HKEY_PERFORMANCE_DATA
- if (!(IsPerfDataKey() && ret == Win32Native.ERROR_MORE_DATA))
- Win32Error(ret, null);
- }
-
- names[i] = new String(namePtr);
+ // 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;
}
+
+ // 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();
}
/**
*/
public Object GetValue(String name)
{
- CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
return InternalGetValue(name, null, false, true);
}
*/
public Object GetValue(String name, Object defaultValue)
{
- CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
return InternalGetValue(name, defaultValue, false, true);
}
throw new ArgumentException(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options));
}
bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames);
- CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
return InternalGetValue(name, defaultValue, doNotExpand, true);
}
EnsureWriteable();
- if (!remoteKey && ContainsRegistryValue(name))
- { // Existing key
- CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default);
- }
- else
- { // Creating a new value
- CheckPermission(RegistryInternalCheck.CheckValueCreatePermission, name, false, RegistryKeyPermissionCheck.Default);
- }
-
if (valueKind == RegistryValueKind.Unknown)
{
// this is to maintain compatibility with the old way of autodetecting the type.
}
}
- private void CheckPermission(RegistryInternalCheck check, string item, bool subKeyWritable, RegistryKeyPermissionCheck subKeyCheck)
- {
- // TODO: Cleanup
- }
-
private bool ContainsRegistryValue(string name)
{
int type = 0;