diff options
author | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
---|---|---|
committer | Jiyoung Yun <jy910.yun@samsung.com> | 2017-04-13 14:17:19 +0900 |
commit | a56e30c8d33048216567753d9d3fefc2152af8ac (patch) | |
tree | 7e5d979695fc4a431740982eb1cfecc2898b23a5 /src/mscorlib/src/Microsoft/Win32/RegistryKey.cs | |
parent | 4b11dc566a5bbfa1378d6266525c281b028abcc8 (diff) | |
download | coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.gz coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.tar.bz2 coreclr-a56e30c8d33048216567753d9d3fefc2152af8ac.zip |
Imported Upstream version 2.0.0.11353upstream/2.0.0.11353
Diffstat (limited to 'src/mscorlib/src/Microsoft/Win32/RegistryKey.cs')
-rw-r--r-- | src/mscorlib/src/Microsoft/Win32/RegistryKey.cs | 1290 |
1 files changed, 408 insertions, 882 deletions
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs index f82b276059..e39b95903e 100644 --- a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs +++ b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs @@ -2,28 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -/* - Note on transaction support: - Eventually we will want to add support for NT's transactions to our - RegistryKey API's (possibly Whidbey M3?). When we do this, here's - the list of API's we need to make transaction-aware: - - RegCreateKeyEx - RegDeleteKey - RegDeleteValue - RegEnumKeyEx - RegEnumValue - RegOpenKeyEx - RegQueryInfoKey - RegQueryValueEx - RegSetValueEx - - We can ignore RegConnectRegistry (remote registry access doesn't yet have - transaction support) and RegFlushKey. RegCloseKey doesn't require any - additional work. . - */ - /* Note on ACL support: The key thing to note about ACL's is you set them on a kernel object like a @@ -48,70 +26,41 @@ */ +using Microsoft.Win32.SafeHandles; +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; namespace Microsoft.Win32 { - using System; - using System.Collections; - 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; - /** * Registry encapsulation. To get an instance of a RegistryKey use the * Registry class's static members then call OpenSubKey. - * - * @see Registry - * @security(checkDllCalls=off) - * @security(checkClassLinking=on) */ - internal sealed class RegistryKey : MarshalByRefObject, IDisposable + internal sealed class RegistryKey : MarshalByRefObject, IDisposable { + // Use the public Registry.CurrentUser + internal static readonly RegistryKey CurrentUser = + GetBaseKey(new IntPtr(unchecked((int)0x80000001)), "HKEY_CURRENT_USER"); + + // Use the public Registry.LocalMachine + internal static readonly RegistryKey LocalMachine = + GetBaseKey(new IntPtr(unchecked((int)0x80000002)), "HKEY_LOCAL_MACHINE"); // We could use const here, if C# supported ELEMENT_TYPE_I fully. - internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000)); - internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001)); - internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002)); - internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003)); - internal static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004)); - internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005)); - - // Dirty indicates that we have munged data that should be potentially - // written to disk. - // - private const int STATE_DIRTY = 0x0001; + private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001)); + private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002)); // SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened" // or "closed". // - private const int STATE_SYSTEMKEY = 0x0002; + private const int STATE_SYSTEMKEY = 0x0002; // Access // - private const int STATE_WRITEACCESS = 0x0004; - - // Indicates if this key is for HKEY_PERFORMANCE_DATA - private const int STATE_PERF_DATA = 0x0008; - - // Names of keys. This array must be in the same order as the HKEY values listed above. - // - private static readonly String[] hkeyNames = new String[] { - "HKEY_CLASSES_ROOT", - "HKEY_CURRENT_USER", - "HKEY_LOCAL_MACHINE", - "HKEY_USERS", - "HKEY_PERFORMANCE_DATA", - "HKEY_CURRENT_CONFIG", - }; + private const int STATE_WRITEACCESS = 0x0004; // MSDN defines the following limits for registry key names & values: // Key Name: 255 characters @@ -122,30 +71,7 @@ namespace Microsoft.Win32 private volatile SafeRegistryHandle hkey = null; private volatile int state = 0; - private volatile String keyName; - private volatile bool remoteKey = false; - private volatile RegistryKeyPermissionCheck checkMode; - private volatile RegistryView regView = RegistryView.Default; - - /** - * RegistryInternalCheck values. Useful only for CheckPermission - */ - private enum RegistryInternalCheck { - CheckSubKeyWritePermission = 0, - CheckSubKeyReadPermission = 1, - CheckSubKeyCreatePermission = 2, - CheckSubTreeReadPermission = 3, - CheckSubTreeWritePermission = 4, - CheckSubTreeReadWritePermission = 5, - CheckValueWritePermission = 6, - CheckValueCreatePermission = 7, - CheckValueReadPermission = 8, - CheckKeyReadPermission = 9, - CheckSubTreePermission = 10, - CheckOpenSubKeyWithWritablePermission = 11, - CheckOpenSubKeyPermission = 12 - }; - + private volatile string keyName; /** * Creates a RegistryKey. @@ -156,37 +82,32 @@ namespace Microsoft.Win32 * The remoteKey flag when set to true indicates that we are dealing with registry entries * on a remote machine and requires the program making these calls to have full trust. */ - private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view) { + private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey) + { this.hkey = hkey; - this.keyName = ""; - this.remoteKey = remoteKey; - this.regView = view; - if (systemkey) { - this.state |= STATE_SYSTEMKEY; + keyName = ""; + if (systemkey) + { + state |= STATE_SYSTEMKEY; } - if (writable) { - this.state |= STATE_WRITEACCESS; + if (writable) + { + state |= STATE_WRITEACCESS; } - if (isPerfData) - this.state |= STATE_PERF_DATA; - ValidateKeyView(view); - } - - /** - * Closes this key, flushes it to disk if the contents have been modified. - */ - public void Close() { - Dispose(true); } - private void Dispose(bool disposing) { - if (hkey != null) { - - if (!IsSystemKey()) { - try { + private void Dispose(bool disposing) + { + if (hkey != null) + { + if (!IsSystemKey()) + { + try + { hkey.Dispose(); } - catch (IOException){ + catch (IOException) + { // we don't really care if the handle is invalid at this point } finally @@ -194,20 +115,6 @@ namespace Microsoft.Win32 hkey = null; } } - else if (disposing && IsPerfDataKey()) { - // System keys should never be closed. However, we want to call RegCloseKey - // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources - // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it - // to be refreshed (by re-reading the registry) when accessed subsequently. - // This is the only way we can see the just installed perf counter. - // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent race condition in closing - // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources - // in this situation the down level OSes are not. We have a small window between - // 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. - SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA); - } } } @@ -216,278 +123,199 @@ namespace Microsoft.Win32 Dispose(true); } - public void DeleteValue(String name, bool throwOnMissingValue) { + public void DeleteValue(string name, bool throwOnMissingValue) + { EnsureWriteable(); - CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default); int errorCode = Win32Native.RegDeleteValue(hkey, name); - + // - // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE + // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE // This still means the name doesn't exist. We need to be consistent with previous OS. // - if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE) { - if (throwOnMissingValue) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent); + if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE) + { + if (throwOnMissingValue) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent); } + // Otherwise, just return giving no indication to the user. // (For compatibility) } + // We really should throw an exception here if errorCode was bad, // but we can't for compatibility reasons. - BCLDebug.Correctness(errorCode == 0, "RegDeleteValue failed. Here's your error code: "+errorCode); - } - - /** - * Retrieves a new RegistryKey that represents the requested key. Valid - * values are: - * - * HKEY_CLASSES_ROOT, - * HKEY_CURRENT_USER, - * HKEY_LOCAL_MACHINE, - * HKEY_USERS, - * HKEY_PERFORMANCE_DATA, - * HKEY_CURRENT_CONFIG, - * HKEY_DYN_DATA. - * - * @param hKey HKEY_* to open. - * - * @return the RegistryKey requested. - */ - internal static RegistryKey GetBaseKey(IntPtr hKey) { - return GetBaseKey(hKey, RegistryView.Default); + Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode); } - internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view) { - - int index = ((int)hKey) & 0x0FFFFFFF; - BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!"); - BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!"); - - bool isPerf = hKey == HKEY_PERFORMANCE_DATA; - // only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA. - SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf); + private static RegistryKey GetBaseKey(IntPtr hKey, string keyName) + { + SafeRegistryHandle srh = new SafeRegistryHandle(hKey, ownsHandle: false); - RegistryKey key = new RegistryKey(srh, true, true,false, isPerf, view); - key.checkMode = RegistryKeyPermissionCheck.Default; - key.keyName = hkeyNames[index]; + RegistryKey key = new RegistryKey(srh, true, true); + key.keyName = keyName; return key; } - /** - * Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with - * read-only access. - * - * @param name Name or path of subkey to open. - * @param readonly Set to <b>true</b> if you only need readonly access. - * - * @return the Subkey requested, or <b>null</b> if the operation failed. - */ - public RegistryKey OpenSubKey(string name, bool writable ) { + /// <summary> + /// Retrieves a subkey or null if the operation failed. + /// </summary> + /// <param name="writable">True to open writable, otherwise opens the key read-only.</param> + public RegistryKey OpenSubKey(string name, bool writable) + { ValidateKeyName(name); EnsureNotDisposed(); - 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, 0, - GetRegistryKeyAccess(writable) | (int)regView, + writable ? Win32Native.KEY_READ | Win32Native.KEY_WRITE : Win32Native.KEY_READ, out result); - if (ret == 0 && !result.IsInvalid) { - RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView); - key.checkMode = GetSubKeyPermissonCheck(writable); + if (ret == 0 && !result.IsInvalid) + { + RegistryKey key = new RegistryKey(result, writable, false); key.keyName = keyName + "\\" + name; return key; } // Return null if we didn't find the key. - if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) { + if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) + { // We need to throw SecurityException here for compatibility reasons, // although UnauthorizedAccessException will make more sense. ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission); } - - return null; - } - - // This required no security checks. This is to get around the Deleting SubKeys which only require - // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks - internal RegistryKey InternalOpenSubKey(String name, bool writable) { - ValidateKeyName(name); - EnsureNotDisposed(); - SafeRegistryHandle result = null; - int ret = Win32Native.RegOpenKeyEx(hkey, - name, - 0, - GetRegistryKeyAccess(writable) | (int)regView, - out result); - - if (ret == 0 && !result.IsInvalid) { - RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView); - key.keyName = keyName + "\\" + name; - return key; - } return null; } - /** - * Returns a subkey with read only permissions. - * - * @param name Name or path of subkey to open. - * - * @return the Subkey requested, or <b>null</b> if the operation failed. - */ - public RegistryKey OpenSubKey(String name) { - return OpenSubKey(name, false); - } - - internal int InternalSubKeyCount() { - 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; - } - - /** - * 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() { + /// <summary> + /// Retrieves an array of strings containing all the subkey names. + /// </summary> + public string[] GetSubKeyNames() + { EnsureNotDisposed(); - int subkeys = InternalSubKeyCount(); - String[] names = new String[subkeys]; // Returns 0-length array if empty. - if (subkeys > 0) { - char[] name = new char[MaxKeyLength + 1]; - - int namelen; + var names = new List<string>(); + char[] name = ArrayPool<char>.Shared.Rent(MaxKeyLength + 1); - fixed (char *namePtr = &name[0]) + try + { + 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++) { - 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); + switch (result) + { + 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() { - 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; - } - - /** - * 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); + /// <summary> + /// Retrieves an array of strings containing all the value names. + /// </summary> + public unsafe string[] GetValueNames() + { EnsureNotDisposed(); + var names = new List<string>(); - int values = InternalValueCount(); - String[] names = new 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. - if (values > 0) { - char[] name = new char[MaxValueLength + 1]; - int namelen; + char[] name = ArrayPool<char>.Shared.Rent(100); - fixed (char *namePtr = &name[0]) + try + { + 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++) { - 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); + switch (result) + { + // 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; - } - - /** - * Retrieves the specified value. <b>null</b> is returned if the value - * doesn't exist. - * - * Note that <var>name</var> can be null or "", at which point the - * unnamed or default value of this Registry key is returned, if any. - * - * @param name Name of value to retrieve. - * - * @return the data associated with the value. - */ - public Object GetValue(String name) { - CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); - return InternalGetValue(name, null, false, true); + return names.ToArray(); } /** @@ -505,426 +333,257 @@ namespace Microsoft.Win32 * * @return the data associated with the value. */ - public Object GetValue(String name, Object defaultValue) { - CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); - return InternalGetValue(name, defaultValue, false, true); - } - - public Object GetValue(String name, Object defaultValue, RegistryValueOptions options) { - if( options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames) { - throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options), nameof(options)); - } - bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames); - CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default); - return InternalGetValue(name, defaultValue, doNotExpand, true); - } - - internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity) { - if (checkSecurity) { - // Name can be null! It's the most common use of RegQueryValueEx - EnsureNotDisposed(); - } + public object GetValue(string name, object defaultValue = null, bool doNotExpand = false) + { + EnsureNotDisposed(); - Object data = defaultValue; + object data = defaultValue; int type = 0; int datasize = 0; int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize); - if (ret != 0) { - if (IsPerfDataKey()) { - int size = 65000; - int sizeInput = size; - - int r; - byte[] blob = new byte[size]; - while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput))) { - if (size == Int32.MaxValue) { - // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue - Win32Error(r, name); - } - else if (size > (Int32.MaxValue / 2)) { - // at this point in the loop "size * 2" would cause an overflow - size = Int32.MaxValue; - } - else { - size *= 2; - } - sizeInput = size; - blob = new byte[size]; - } - if (r != 0) - Win32Error(r, name); - return blob; - } - else { - // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data). - // Some OS's returned ERROR_MORE_DATA even in success cases, so we - // want to continue on through the function. - if (ret != Win32Native.ERROR_MORE_DATA) - return data; - } + if (ret != 0) + { + // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data). + // Some OS's returned ERROR_MORE_DATA even in success cases, so we + // want to continue on through the function. + if (ret != Win32Native.ERROR_MORE_DATA) + return data; } - if (datasize < 0) { + if (datasize < 0) + { // unexpected code path - BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize"); + Debug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize"); datasize = 0; } + switch (type) + { + case Win32Native.REG_NONE: + case Win32Native.REG_DWORD_BIG_ENDIAN: + case Win32Native.REG_BINARY: + { + byte[] blob = new byte[datasize]; + ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); + data = blob; + } + break; + case Win32Native.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 Win32Native.REG_BINARY; + } + long blob = 0; + Debug.Assert(datasize == 8, "datasize==8"); + // Here, datasize must be 8 when calling this + ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); - switch (type) { - case Win32Native.REG_NONE: - case Win32Native.REG_DWORD_BIG_ENDIAN: - case Win32Native.REG_BINARY: { - byte[] blob = new byte[datasize]; - ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); - data = blob; - } - break; - case Win32Native.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 Win32Native.REG_BINARY; - } - long blob = 0; - BCLDebug.Assert(datasize==8, "datasize==8"); - // Here, datasize must be 8 when calling this - ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); - - data = blob; - } - break; - case Win32Native.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 Win32Native.REG_QWORD; - } - int blob = 0; - BCLDebug.Assert(datasize==4, "datasize==4"); - // Here, datasize must be four when calling this - ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); - - data = blob; - } - break; - - case Win32Native.REG_SZ: - { - if (datasize % 2 == 1) { - // handle the case where the registry contains an odd-byte length (corrupt data?) - try { - datasize = checked(datasize + 1); - } - catch (OverflowException e) { - throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); - } - } - char[] blob = new char[datasize/2]; - - ret = Win32Native.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); - } - else { - // in the very unlikely case the data is missing null termination, - // pass in the whole char[] to prevent truncating a character - data = new String(blob); - } - } - break; - - case Win32Native.REG_EXPAND_SZ: - { - if (datasize % 2 == 1) { - // handle the case where the registry contains an odd-byte length (corrupt data?) - try { - datasize = checked(datasize + 1); - } - catch (OverflowException e) { - throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); - } - } - char[] blob = new char[datasize/2]; - - ret = Win32Native.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); - } - else { - // in the very unlikely case the data is missing null termination, - // pass in the whole char[] to prevent truncating a character - data = new String(blob); - } - - if (!doNotExpand) - data = Environment.ExpandEnvironmentVariables((String)data); - } - break; - case Win32Native.REG_MULTI_SZ: - { - if (datasize % 2 == 1) { - // handle the case where the registry contains an odd-byte length (corrupt data?) - try { - datasize = checked(datasize + 1); - } - catch (OverflowException e) { - throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); - } - } - char[] blob = new char[datasize/2]; - - ret = Win32Native.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) { - try { - char[] newBlob = new char[checked(blob.Length + 1)]; - for (int i = 0; i < blob.Length; i++) { - newBlob[i] = blob[i]; - } - newBlob[newBlob.Length - 1] = (char)0; - blob = newBlob; - } - catch (OverflowException e) { - throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e); - } - blob[blob.Length - 1] = (char)0; - } - - - IList<String> strings = new List<String>(); - int cur = 0; - int len = blob.Length; - - while (ret == 0 && cur < len) { - int nextNull = cur; - while (nextNull < len && blob[nextNull] != (char)0) { - nextNull++; - } - - if (nextNull < len) { - BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0"); - if (nextNull-cur > 0) { - strings.Add(new String(blob, cur, nextNull-cur)); - } - else { - // we found an empty string. But if we're at the end of the data, - // it's just the extra null terminator. - if (nextNull != len-1) - strings.Add(String.Empty); - } - } - else { - strings.Add(new String(blob, cur, len-cur)); - } - cur = nextNull+1; - } - - data = new String[strings.Count]; - strings.CopyTo((String[])data, 0); - } - break; - case Win32Native.REG_LINK: - default: - break; - } - - return data; - } - - private bool IsSystemKey() { - return (this.state & STATE_SYSTEMKEY) != 0; - } - - private bool IsWritable() { - return (this.state & STATE_WRITEACCESS) != 0; - } - - private bool IsPerfDataKey() { - return (this.state & STATE_PERF_DATA) != 0; - } - - private void SetDirty() { - this.state |= STATE_DIRTY; - } - - /** - * Sets the specified value. - * - * @param name Name of value to store data in. - * @param value Data to store. - */ - public void SetValue(String name, Object value) { - SetValue(name, value, RegistryValueKind.Unknown); - } - - public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind) { - if (value==null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + data = blob; + } + break; + case Win32Native.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 Win32Native.REG_QWORD; + } + int blob = 0; + Debug.Assert(datasize == 4, "datasize==4"); + // Here, datasize must be four when calling this + ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize); - if (name != null && name.Length > MaxValueLength) { - throw new ArgumentException(Environment.GetResourceString("Arg_RegValStrLenBug")); - } + data = blob; + } + break; - if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind)) - throw new ArgumentException(Environment.GetResourceString("Arg_RegBadKeyKind"), nameof(valueKind)); + case Win32Native.REG_SZ: + { + if (datasize % 2 == 1) + { + // handle the case where the registry contains an odd-byte length (corrupt data?) + try + { + datasize = checked(datasize + 1); + } + catch (OverflowException e) + { + throw new IOException(SR.Arg_RegGetOverflowBug, e); + } + } + char[] blob = new char[datasize / 2]; - EnsureWriteable(); + ret = Win32Native.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); + } + else + { + // in the very unlikely case the data is missing null termination, + // pass in the whole char[] to prevent truncating a character + data = new string(blob); + } + } + break; - 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); - } + case Win32Native.REG_EXPAND_SZ: + { + if (datasize % 2 == 1) + { + // handle the case where the registry contains an odd-byte length (corrupt data?) + try + { + datasize = checked(datasize + 1); + } + catch (OverflowException e) + { + throw new IOException(SR.Arg_RegGetOverflowBug, e); + } + } + char[] blob = new char[datasize / 2]; - if (valueKind == RegistryValueKind.Unknown) { - // this is to maintain compatibility with the old way of autodetecting the type. - // SetValue(string, object) will come through this codepath. - valueKind = CalculateValueKind(value); - } + ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize); - int ret = 0; - try { - switch (valueKind) { - case RegistryValueKind.ExpandString: - case RegistryValueKind.String: + if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { - String data = value.ToString(); - ret = Win32Native.RegSetValueEx(hkey, - name, - 0, - valueKind, - data, - checked(data.Length * 2 + 2)); - break; + data = new string(blob, 0, blob.Length - 1); + } + else + { + // in the very unlikely case the data is missing null termination, + // pass in the whole char[] to prevent truncating a character + data = new string(blob); } - case RegistryValueKind.MultiString: + if (!doNotExpand) + data = Environment.ExpandEnvironmentVariables((string)data); + } + break; + case Win32Native.REG_MULTI_SZ: + { + if (datasize % 2 == 1) { - // Other thread might modify the input array after we calculate the buffer length. - // Make a copy of the input array to be safe. - string[] dataStrings = (string[])(((string[])value).Clone()); - int sizeInBytes = 0; - - // First determine the size of the array - // - for (int i=0; i<dataStrings.Length; i++) { - if (dataStrings[i] == null) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetStrArrNull); - } - sizeInBytes = checked(sizeInBytes + (dataStrings[i].Length+1) * 2); - } - sizeInBytes = checked(sizeInBytes + 2); - - byte[] basePtr = new byte[sizeInBytes]; - fixed(byte* b = basePtr) { - IntPtr currentPtr = new IntPtr( (void *) b); - - // Write out the strings... - // - for (int i=0; i<dataStrings.Length; i++) { - // Assumes that the Strings are always null terminated. - String.InternalCopy(dataStrings[i],currentPtr,(checked(dataStrings[i].Length*2))); - currentPtr = new IntPtr((long)currentPtr + (checked(dataStrings[i].Length*2))); - *(char*)(currentPtr.ToPointer()) = '\0'; - currentPtr = new IntPtr((long)currentPtr + 2); - } - - *(char*)(currentPtr.ToPointer()) = '\0'; - currentPtr = new IntPtr((long)currentPtr + 2); - - ret = Win32Native.RegSetValueEx(hkey, - name, - 0, - RegistryValueKind.MultiString, - basePtr, - sizeInBytes); + // handle the case where the registry contains an odd-byte length (corrupt data?) + try + { + datasize = checked(datasize + 1); + } + catch (OverflowException e) + { + throw new IOException(SR.Arg_RegGetOverflowBug, e); } - break; } + char[] blob = new char[datasize / 2]; - case RegistryValueKind.None: - case RegistryValueKind.Binary: - byte[] dataBytes = (byte[]) value; - ret = Win32Native.RegSetValueEx(hkey, - name, - 0, - (valueKind == RegistryValueKind.None ? Win32Native.REG_NONE: RegistryValueKind.Binary), - dataBytes, - dataBytes.Length); - break; - - case RegistryValueKind.DWord: + ret = Win32Native.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) { - // We need to use Convert here because we could have a boxed type cannot be - // unboxed and cast at the same time. I.e. ((int)(object)(short) 5) will fail. - int data = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture); - - ret = Win32Native.RegSetValueEx(hkey, - name, - 0, - RegistryValueKind.DWord, - ref data, - 4); - break; + try + { + char[] newBlob = new char[checked(blob.Length + 1)]; + for (int i = 0; i < blob.Length; i++) + { + newBlob[i] = blob[i]; + } + newBlob[newBlob.Length - 1] = (char)0; + blob = newBlob; + } + catch (OverflowException e) + { + throw new IOException(SR.Arg_RegGetOverflowBug, e); + } + blob[blob.Length - 1] = (char)0; } - case RegistryValueKind.QWord: + IList<string> strings = new List<string>(); + int cur = 0; + int len = blob.Length; + + while (ret == 0 && cur < len) { - long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture); - - ret = Win32Native.RegSetValueEx(hkey, - name, - 0, - RegistryValueKind.QWord, - ref data, - 8); - break; + int nextNull = cur; + while (nextNull < len && blob[nextNull] != (char)0) + { + nextNull++; + } + + if (nextNull < len) + { + Debug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0"); + if (nextNull - cur > 0) + { + strings.Add(new string(blob, cur, nextNull - cur)); + } + else + { + // we found an empty string. But if we're at the end of the data, + // it's just the extra null terminator. + if (nextNull != len - 1) + strings.Add(string.Empty); + } + } + else + { + strings.Add(new string(blob, cur, len - cur)); + } + cur = nextNull + 1; } - } - } - catch (OverflowException) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); - } - catch (InvalidOperationException) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); - } - catch (FormatException) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); - } - catch (InvalidCastException) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind); - } - if (ret == 0) { - SetDirty(); + data = new string[strings.Count]; + strings.CopyTo((string[])data, 0); + } + break; + case Win32Native.REG_LINK: + default: + break; } - else - Win32Error(ret, null); + return data; + } + + private bool IsSystemKey() + { + return (state & STATE_SYSTEMKEY) != 0; } - private RegistryValueKind CalculateValueKind(Object value) { - // This logic matches what used to be in SetValue(string name, object value) in the v1.0 and v1.1 days. - // Even though we could add detection for an int64 in here, we want to maintain compatibility with the - // old behavior. - if (value is Int32) - return RegistryValueKind.DWord; - else if (value is Array) { - if (value is byte[]) - return RegistryValueKind.Binary; - else if (value is String[]) - return RegistryValueKind.MultiString; - else - throw new ArgumentException(Environment.GetResourceString("Arg_RegSetBadArrType", value.GetType().Name)); + private bool IsWritable() + { + return (state & STATE_WRITEACCESS) != 0; + } + + private bool IsPerfDataKey() + { + return false; + } + + public unsafe void SetStringValue(string name, string value) + { + if (value == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); + + if (name != null && name.Length > MaxValueLength) + { + throw new ArgumentException(SR.Arg_RegValStrLenBug); } - else - return RegistryValueKind.String; + + EnsureWriteable(); + + int result = Win32Native.RegSetValueEx(hkey, + name, + 0, + RegistryValueKind.String, + value, + checked(value.Length * 2 + 2)); + + if (result != 0) + Win32Error(result, null); } /** @@ -932,7 +591,8 @@ namespace Microsoft.Win32 * * @return a string representing the key. */ - public override String ToString() { + public override string ToString() + { EnsureNotDisposed(); return keyName; } @@ -944,164 +604,51 @@ namespace Microsoft.Win32 * error, and depending on the error, insert a string into the message * gotten from the ResourceManager. */ - internal void Win32Error(int errorCode, String str) { - switch (errorCode) { + internal void Win32Error(int errorCode, string str) + { + switch (errorCode) + { case Win32Native.ERROR_ACCESS_DENIED: if (str != null) - throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_RegistryKeyGeneric_Key", str)); + throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str)); else throw new UnauthorizedAccessException(); - - case Win32Native.ERROR_INVALID_HANDLE: - /** - * For normal RegistryKey instances we dispose the SafeRegHandle and throw IOException. - * However, for HKEY_PERFORMANCE_DATA (on a local or remote machine) we avoid disposing the - * SafeRegHandle and only throw the IOException. This is to workaround reentrancy issues - * in PerformanceCounter.NextValue() where the API could throw {NullReference, ObjectDisposed, ArgumentNull}Exception - * on reentrant calls because of this error code path in RegistryKey - * - * Normally we'd make our caller synchronize access to a shared RegistryKey instead of doing something like this, - * however we shipped PerformanceCounter.NextValue() un-synchronized in v2.0RTM and customers have taken a dependency on - * this behavior (being able to simultaneously query multiple remote-machine counters on multiple threads, instead of - * having serialized access). - */ - if (!IsPerfDataKey()) { - this.hkey.SetHandleAsInvalid(); - this.hkey = null; - } - goto default; - case Win32Native.ERROR_FILE_NOT_FOUND: - throw new IOException(Environment.GetResourceString("Arg_RegKeyNotFound"), errorCode); + throw new IOException(SR.Arg_RegKeyNotFound, errorCode); default: throw new IOException(Win32Native.GetMessage(errorCode), errorCode); } } - internal static String FixupName(String name) - { - BCLDebug.Assert(name!=null,"[FixupName]name!=null"); - if (name.IndexOf('\\') == -1) - return name; - - StringBuilder sb = new StringBuilder(name); - FixupPath(sb); - int temp = sb.Length - 1; - if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash - sb.Length = temp; - return sb.ToString(); - } - - - private static void FixupPath(StringBuilder path) + private void EnsureNotDisposed() { - Contract.Requires(path != null); - int length = path.Length; - bool fixup = false; - char markerChar = (char)0xFFFF; - - int i = 1; - while (i < length - 1) - { - if (path[i] == '\\') - { - i++; - while (i < length) - { - if (path[i] == '\\') - { - path[i] = markerChar; - i++; - fixup = true; - } - else - break; - } - - } - i++; - } - - if (fixup) + if (hkey == null) { - i = 0; - int j = 0; - while (i < length) - { - if(path[i] == markerChar) - { - i++; - continue; - } - path[j] = path[i]; - i++; - j++; - } - path.Length += j - i; - } - - } - - private void CheckPermission(RegistryInternalCheck check, string item, bool subKeyWritable, RegistryKeyPermissionCheck subKeyCheck) - { - // TODO: Cleanup - } - - private bool ContainsRegistryValue(string name) { - int type = 0; - int datasize = 0; - int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize); - return retval == 0; - } - - private void EnsureNotDisposed(){ - if (hkey == null) { ThrowHelper.ThrowObjectDisposedException(keyName, ExceptionResource.ObjectDisposed_RegKeyClosed); } } - private void EnsureWriteable() { + private void EnsureWriteable() + { EnsureNotDisposed(); - if (!IsWritable()) { + if (!IsWritable()) + { ThrowHelper.ThrowUnauthorizedAccessException(ExceptionResource.UnauthorizedAccess_RegistryNoWrite); } } - static int GetRegistryKeyAccess(bool isWritable) { - int winAccess; - if (!isWritable) { - winAccess = Win32Native.KEY_READ; - } - else { - winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE; - } - - return winAccess; - } - - private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable) { - if( checkMode == RegistryKeyPermissionCheck.Default) { - return checkMode; - } - - if(subkeyWritable) { - return RegistryKeyPermissionCheck.ReadWriteSubTree; - } - else { - return RegistryKeyPermissionCheck.ReadSubTree; - } - } - - static private void ValidateKeyName(string name) { - Contract.Ensures(name != null); - if (name == null) { + static private void ValidateKeyName(string name) + { + if (name == null) + { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name); } int nextSlash = name.IndexOf("\\", StringComparison.OrdinalIgnoreCase); int current = 0; - while (nextSlash != -1) { + while (nextSlash != -1) + { if ((nextSlash - current) > MaxKeyLength) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug); @@ -1111,32 +658,11 @@ namespace Microsoft.Win32 if ((name.Length - current) > MaxKeyLength) ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug); - - } - - static private void ValidateKeyView(RegistryView view) { - if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64) { - ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryViewCheck, ExceptionArgument.view); - } } // Win32 constants for error handling private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; - private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; + private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000; } - - [Flags] - internal enum RegistryValueOptions { - None = 0, - DoNotExpandEnvironmentNames = 1 - } - - // the name for this API is meant to mimic FileMode, which has similar values - - internal enum RegistryKeyPermissionCheck { - Default = 0, - ReadSubTree = 1, - ReadWriteSubTree = 2 - } } |