From: danmosemsft Date: Fri, 14 Apr 2017 22:53:33 +0000 (-0700) Subject: Revert "Strip out unused reg code. (dotnet/coreclr#10741)" X-Git-Tag: submit/tizen/20210909.063632~11030^2~7252^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=135ac4d4176a20469fa86df508666fb525ed61ef;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Revert "Strip out unused reg code. (dotnet/coreclr#10741)" This reverts commit dotnet/coreclr@ed4f594abf41a71b126152bb8755051d0831e12d. Commit migrated from https://github.com/dotnet/coreclr/commit/368b35097bb12ef8ec5df64103303c7488d82dd7 --- diff --git a/src/coreclr/src/mscorlib/System.Private.CoreLib.csproj b/src/coreclr/src/mscorlib/System.Private.CoreLib.csproj index 3a0917f..4541ef8 100644 --- a/src/coreclr/src/mscorlib/System.Private.CoreLib.csproj +++ b/src/coreclr/src/mscorlib/System.Private.CoreLib.csproj @@ -86,9 +86,11 @@ portable + PLATFORM_OSX;$(DefineConstants) + System.Private.CoreLib @@ -625,6 +627,7 @@ + @@ -757,4 +760,4 @@ $(IntermediateOutputPath)\System.Private.CoreLib.res - \ No newline at end of file + diff --git a/src/coreclr/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs b/src/coreclr/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs index 57d550d..e18574c 100644 --- a/src/coreclr/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs +++ b/src/coreclr/src/mscorlib/shared/System/Diagnostics/Tracing/EventProvider.cs @@ -555,21 +555,21 @@ namespace System.Diagnostics.Tracing { #if (!ES_BUILD_PCL && !ES_BUILD_PN && !FEATURE_PAL) string regKey = @"\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + m_providerId + "}"; - if (Marshal.SizeOf(typeof(IntPtr)) == 8) - regKey = @"Software" + @"\Wow6432Node" + regKey; + if (System.Runtime.InteropServices.Marshal.SizeOf(typeof(IntPtr)) == 8) + regKey = @"HKEY_LOCAL_MACHINE\Software" + @"\Wow6432Node" + regKey; else - regKey = @"Software" + regKey; + regKey = @"HKEY_LOCAL_MACHINE\Software" + regKey; string valueName = "ControllerData_Session_" + etwSessionId.ToString(CultureInfo.InvariantCulture); - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(regKey, writable: false)) - { - data = key.GetValue(valueName) as byte[]; - } - + // we need to assert this permission for partial trust scenarios +#if !CORECLR + (new RegistryPermission(RegistryPermissionAccess.Read, regKey)).Assert(); +#endif + data = Microsoft.Win32.Registry.GetValue(regKey, valueName, null) as byte[]; if (data != null) { - // We only used the persisted data from the registry for updates. + // We only used the persisted data from the registry for updates. command = ControllerCommand.Update; return true; } diff --git a/src/coreclr/src/mscorlib/src/Microsoft/Win32/Registry.cs b/src/coreclr/src/mscorlib/src/Microsoft/Win32/Registry.cs index aa2dd9b..d0dbb0f 100644 --- a/src/coreclr/src/mscorlib/src/Microsoft/Win32/Registry.cs +++ b/src/coreclr/src/mscorlib/src/Microsoft/Win32/Registry.cs @@ -2,12 +2,142 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + namespace Microsoft.Win32 { + /** + * Registry encapsulation. Contains members representing all top level system + * keys. + * + * @security(checkClassLinking=on) + */ + //This class contains only static members and does not need to be serializable. internal static class Registry { - public static readonly RegistryKey CurrentUser = RegistryKey.CurrentUser; - public static readonly RegistryKey LocalMachine = RegistryKey.LocalMachine; + /** + * Current User Key. + * + * This key should be used as the root for all user specific settings. + */ + public static readonly RegistryKey CurrentUser = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER); + + /** + * Local Machine Key. + * + * This key should be used as the root for all machine specific settings. + */ + public static readonly RegistryKey LocalMachine = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE); + + /** + * Classes Root Key. + * + * This is the root key of class information. + */ + public static readonly RegistryKey ClassesRoot = RegistryKey.GetBaseKey(RegistryKey.HKEY_CLASSES_ROOT); + + /** + * Users Root Key. + * + * This is the root of users. + */ + public static readonly RegistryKey Users = RegistryKey.GetBaseKey(RegistryKey.HKEY_USERS); + + /** + * Performance Root Key. + * + * This is where dynamic performance data is stored on NT. + */ + public static readonly RegistryKey PerformanceData = RegistryKey.GetBaseKey(RegistryKey.HKEY_PERFORMANCE_DATA); + + /** + * Current Config Root Key. + * + * This is where current configuration information is stored. + */ + public static readonly RegistryKey CurrentConfig = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_CONFIG); + + // + // Following function will parse a keyName and returns the basekey for it. + // It will also store the subkey name in the out parameter. + // If the keyName is not valid, we will throw ArgumentException. + // The return value shouldn't be null. + // + private static RegistryKey GetBaseKeyFromKeyName(string keyName, out string subKeyName) + { + if (keyName == null) + { + throw new ArgumentNullException(nameof(keyName)); + } + + string basekeyName; + int i = keyName.IndexOf('\\'); + if (i != -1) + { + basekeyName = keyName.Substring(0, i).ToUpper(System.Globalization.CultureInfo.InvariantCulture); + } + else + { + basekeyName = keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture); + } + RegistryKey basekey = null; + + switch (basekeyName) + { + case "HKEY_CURRENT_USER": + basekey = Registry.CurrentUser; + break; + case "HKEY_LOCAL_MACHINE": + basekey = Registry.LocalMachine; + break; + case "HKEY_CLASSES_ROOT": + basekey = Registry.ClassesRoot; + break; + case "HKEY_USERS": + basekey = Registry.Users; + break; + case "HKEY_PERFORMANCE_DATA": + basekey = Registry.PerformanceData; + break; + case "HKEY_CURRENT_CONFIG": + basekey = Registry.CurrentConfig; + break; + default: + throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName))); + } + if (i == -1 || i == keyName.Length) + { + subKeyName = string.Empty; + } + else + { + subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1); + } + return basekey; + } + + public static object GetValue(string keyName, string valueName, object defaultValue) + { + string subKeyName; + RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName); + BCLDebug.Assert(basekey != null, "basekey can't be null."); + RegistryKey key = basekey.OpenSubKey(subKeyName); + if (key == null) + { // if the key doesn't exist, do nothing + return null; + } + try + { + return key.GetValue(valueName, defaultValue); + } + finally + { + key.Close(); + } + } } } diff --git a/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs index e39b959..521ea65 100644 --- a/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs +++ b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs @@ -2,6 +2,28 @@ // 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 @@ -30,28 +52,34 @@ using Microsoft.Win32.SafeHandles; using System; using System.Buffers; using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics.Contracts; using System.IO; +using System.Text; namespace Microsoft.Win32 { /** * 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 { - // 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. - private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001)); - private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002)); + 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; // SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened" // or "closed". @@ -62,6 +90,20 @@ namespace Microsoft.Win32 // 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", + }; + // MSDN defines the following limits for registry key names & values: // Key Name: 255 characters // Value name: 16,383 Unicode characters @@ -71,7 +113,31 @@ namespace Microsoft.Win32 private volatile SafeRegistryHandle hkey = null; private volatile int state = 0; - private volatile string keyName; + 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 + }; + /** * Creates a RegistryKey. @@ -82,10 +148,12 @@ 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) + private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view) { this.hkey = hkey; keyName = ""; + this.remoteKey = remoteKey; + regView = view; if (systemkey) { state |= STATE_SYSTEMKEY; @@ -94,6 +162,17 @@ namespace Microsoft.Win32 { state |= STATE_WRITEACCESS; } + if (isPerfData) + 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) @@ -115,6 +194,21 @@ 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); + } } } @@ -123,13 +217,13 @@ namespace Microsoft.Win32 Dispose(true); } - public void DeleteValue(string name, bool throwOnMissingValue) + public void DeleteValue(String name, bool throwOnMissingValue) { EnsureWriteable(); 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) @@ -138,44 +232,77 @@ namespace Microsoft.Win32 { 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. - Debug.Assert(errorCode == 0, "RegDeleteValue failed. Here's your error code: " + errorCode); + 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); } - private static RegistryKey GetBaseKey(IntPtr hKey, string keyName) + internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view) { - SafeRegistryHandle srh = new SafeRegistryHandle(hKey, ownsHandle: false); + 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); - RegistryKey key = new RegistryKey(srh, true, true); - key.keyName = keyName; + RegistryKey key = new RegistryKey(srh, true, true, false, isPerf, view); + key.checkMode = RegistryKeyPermissionCheck.Default; + key.keyName = hkeyNames[index]; return key; } - /// - /// Retrieves a subkey or null if the operation failed. - /// - /// True to open writable, otherwise opens the key read-only. + /** + * Retrieves a subkey. If readonly is true, then the subkey is opened with + * read-only access. + * + * @param name Name or path of subkey to open. + * @param readonly Set to true if you only need readonly access. + * + * @return the Subkey requested, or null if the operation failed. + */ public RegistryKey OpenSubKey(string name, bool writable) { ValidateKeyName(name); EnsureNotDisposed(); + name = FixupName(name); // Fixup multiple slashes to a single slash SafeRegistryHandle result = null; int ret = Win32Native.RegOpenKeyEx(hkey, name, 0, - writable ? Win32Native.KEY_READ | Win32Native.KEY_WRITE : Win32Native.KEY_READ, + GetRegistryKeyAccess(writable) | (int)regView, out result); if (ret == 0 && !result.IsInvalid) { - RegistryKey key = new RegistryKey(result, writable, false); + RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView); + key.checkMode = GetSubKeyPermissonCheck(writable); key.keyName = keyName + "\\" + name; return key; } @@ -191,6 +318,41 @@ namespace Microsoft.Win32 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 null if the operation failed. + */ + public RegistryKey OpenSubKey(String name) + { + return OpenSubKey(name, false); + } + /// /// Retrieves an array of strings containing all the subkey names. /// @@ -319,6 +481,22 @@ namespace Microsoft.Win32 } /** + * Retrieves the specified value. null is returned if the value + * doesn't exist. + * + * Note that name 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) + { + return InternalGetValue(name, null, false, true); + } + + /** * Retrieves the specified value. defaultValue is returned if the value doesn't exist. * * Note that name can be null or "", at which point the @@ -333,11 +511,30 @@ namespace Microsoft.Win32 * * @return the data associated with the value. */ - public object GetValue(string name, object defaultValue = null, bool doNotExpand = false) + public Object GetValue(String name, Object defaultValue) { - EnsureNotDisposed(); + 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(SR.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); + } + bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames); + 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(); + } - object data = defaultValue; + Object data = defaultValue; int type = 0; int datasize = 0; @@ -345,20 +542,54 @@ namespace Microsoft.Win32 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 (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 (datasize < 0) { // unexpected code path - Debug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize"); + BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize"); datasize = 0; } + switch (type) { case Win32Native.REG_NONE: @@ -378,7 +609,7 @@ namespace Microsoft.Win32 goto case Win32Native.REG_BINARY; } long blob = 0; - Debug.Assert(datasize == 8, "datasize==8"); + 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); @@ -393,7 +624,7 @@ namespace Microsoft.Win32 goto case Win32Native.REG_QWORD; } int blob = 0; - Debug.Assert(datasize == 4, "datasize==4"); + 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); @@ -420,13 +651,13 @@ namespace Microsoft.Win32 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); + 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); + data = new String(blob); } } break; @@ -451,17 +682,17 @@ namespace Microsoft.Win32 if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) { - data = new string(blob, 0, blob.Length - 1); + 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); + data = new String(blob); } if (!doNotExpand) - data = Environment.ExpandEnvironmentVariables((string)data); + data = Environment.ExpandEnvironmentVariables((String)data); } break; case Win32Native.REG_MULTI_SZ: @@ -502,7 +733,8 @@ namespace Microsoft.Win32 blob[blob.Length - 1] = (char)0; } - IList strings = new List(); + + IList strings = new List(); int cur = 0; int len = blob.Length; @@ -516,28 +748,28 @@ namespace Microsoft.Win32 if (nextNull < len) { - Debug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0"); + BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0"); if (nextNull - cur > 0) { - strings.Add(new string(blob, cur, nextNull - cur)); + 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); + strings.Add(String.Empty); } } else { - strings.Add(new string(blob, cur, len - cur)); + strings.Add(new String(blob, cur, len - cur)); } cur = nextNull + 1; } - data = new string[strings.Count]; - strings.CopyTo((string[])data, 0); + data = new String[strings.Count]; + strings.CopyTo((String[])data, 0); } break; case Win32Native.REG_LINK: @@ -560,10 +792,26 @@ namespace Microsoft.Win32 private bool IsPerfDataKey() { - return false; + return (state & STATE_PERF_DATA) != 0; + } + + private void SetDirty() + { + 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 SetStringValue(string name, string value) + public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind) { if (value == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value); @@ -573,17 +821,167 @@ namespace Microsoft.Win32 throw new ArgumentException(SR.Arg_RegValStrLenBug); } + if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind)) + throw new ArgumentException(SR.Arg_RegBadKeyKind, nameof(valueKind)); + EnsureWriteable(); - int result = Win32Native.RegSetValueEx(hkey, - name, - 0, - RegistryValueKind.String, - value, - checked(value.Length * 2 + 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); + } + + int ret = 0; + try + { + switch (valueKind) + { + case RegistryValueKind.ExpandString: + case RegistryValueKind.String: + { + String data = value.ToString(); + ret = Win32Native.RegSetValueEx(hkey, + name, + 0, + valueKind, + data, + checked(data.Length * 2 + 2)); + break; + } + + case RegistryValueKind.MultiString: + { + // 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); + } + break; + } + + 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: + { + // 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; + } - if (result != 0) - Win32Error(result, null); + case RegistryValueKind.QWord: + { + long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture); + + ret = Win32Native.RegSetValueEx(hkey, + name, + 0, + RegistryValueKind.QWord, + ref data, + 8); + break; + } + } + } + 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(); + } + else + Win32Error(ret, null); + } + + 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(SR.Format(SR.Arg_RegSetBadArrType, value.GetType().Name)); + } + else + return RegistryValueKind.String; } /** @@ -591,7 +989,7 @@ namespace Microsoft.Win32 * * @return a string representing the key. */ - public override string ToString() + public override String ToString() { EnsureNotDisposed(); return keyName; @@ -604,7 +1002,7 @@ 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) + internal void Win32Error(int errorCode, String str) { switch (errorCode) { @@ -613,6 +1011,27 @@ namespace Microsoft.Win32 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()) + { + hkey.SetHandleAsInvalid(); + hkey = null; + } + goto default; + case Win32Native.ERROR_FILE_NOT_FOUND: throw new IOException(SR.Arg_RegKeyNotFound, errorCode); @@ -621,6 +1040,76 @@ namespace Microsoft.Win32 } } + 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) + { + 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) + { + 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 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) @@ -638,8 +1127,41 @@ namespace Microsoft.Win32 } } + private 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) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name); @@ -660,9 +1182,33 @@ namespace Microsoft.Win32 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_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 + } } diff --git a/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs index 90c880b..6e2a4f3 100644 --- a/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs +++ b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryValueKind.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. + namespace Microsoft.Win32 { internal enum RegistryValueKind @@ -12,11 +13,8 @@ namespace Microsoft.Win32 DWord = Win32Native.REG_DWORD, MultiString = Win32Native.REG_MULTI_SZ, QWord = Win32Native.REG_QWORD, - Unknown = 0, - - // REG_NONE is defined as zero but BCL, mistakingly overrode this value. - // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally. - None = unchecked((int)0xFFFFFFFF), - } + Unknown = 0, // REG_NONE is defined as zero but BCL + None = unchecked((int)0xFFFFFFFF), // mistakingly overrode this value. + } // Now instead of using Win32Native.REG_NONE we use "-1" and play games internally. } diff --git a/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryView.cs b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryView.cs new file mode 100644 index 0000000..e415865 --- /dev/null +++ b/src/coreclr/src/mscorlib/src/Microsoft/Win32/RegistryView.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// +// +// Implements Microsoft.Win32.RegistryView +// +// ====================================================================================== + +using System; + +namespace Microsoft.Win32 +{ + internal enum RegistryView + { + Default = 0, // 0x0000 operate on the default registry view + Registry64 = Win32Native.KEY_WOW64_64KEY, // 0x0100 operate on the 64-bit registry view + Registry32 = Win32Native.KEY_WOW64_32KEY, // 0x0200 operate on the 32-bit registry view + }; +} diff --git a/src/coreclr/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs b/src/coreclr/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs index 1215000..64dbb2c 100644 --- a/src/coreclr/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs +++ b/src/coreclr/src/mscorlib/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs @@ -2,9 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +// +// +// +// Implements Microsoft.Win32.SafeHandles.SafeRegistryHandle +// +// ====================================================================================== + using System; using System.Security; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Runtime.ConstrainedExecution; +using System.Runtime.Versioning; namespace Microsoft.Win32.SafeHandles { diff --git a/src/coreclr/src/mscorlib/src/System/Environment.cs b/src/coreclr/src/mscorlib/src/System/Environment.cs index b9070ae..dddbdc2 100644 --- a/src/coreclr/src/mscorlib/src/System/Environment.cs +++ b/src/coreclr/src/mscorlib/src/System/Environment.cs @@ -820,7 +820,7 @@ namespace System } else { - environmentKey.SetStringValue(variable, value); + environmentKey.SetValue(variable, value); } } } diff --git a/src/coreclr/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs b/src/coreclr/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs index 4ba95c8..869b809 100644 --- a/src/coreclr/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs +++ b/src/coreclr/src/mscorlib/src/System/Globalization/HijriCalendar.Win32.cs @@ -42,41 +42,54 @@ namespace System.Globalization int hijriAdvance = 0; Microsoft.Win32.RegistryKey key = null; - using (key = Registry.CurrentUser.OpenSubKey(InternationalRegKey, writable: false)) + try { - if (key == null) - return 0; + // Open in read-only mode. + // Use InternalOpenSubKey so that we avoid the security check. + key = RegistryKey.GetBaseKey(RegistryKey.HKEY_CURRENT_USER).OpenSubKey(InternationalRegKey, false); + } + //If this fails for any reason, we'll just return 0. + catch (ObjectDisposedException) { return 0; } + catch (ArgumentException) { return 0; } - Object value = key.GetValue(HijriAdvanceRegKeyEntry); - if (value == null) - { - return (0); - } - String str = value.ToString(); - if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0) + if (key != null) + { + try { - if (str.Length == HijriAdvanceRegKeyEntry.Length) - hijriAdvance = -1; - else + Object value = key.InternalGetValue(HijriAdvanceRegKeyEntry, null, false, false); + if (value == null) { - str = str.Substring(HijriAdvanceRegKeyEntry.Length); - try + return (0); + } + String str = value.ToString(); + if (String.Compare(str, 0, HijriAdvanceRegKeyEntry, 0, HijriAdvanceRegKeyEntry.Length, StringComparison.OrdinalIgnoreCase) == 0) + { + if (str.Length == HijriAdvanceRegKeyEntry.Length) + hijriAdvance = -1; + else { - int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture); - if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri)) + str = str.Substring(HijriAdvanceRegKeyEntry.Length); + try { - hijriAdvance = advance; + int advance = Int32.Parse(str.ToString(), CultureInfo.InvariantCulture); + if ((advance >= MinAdvancedHijri) && (advance <= MaxAdvancedHijri)) + { + hijriAdvance = advance; + } } + // If we got garbage from registry just ignore it. + // hijriAdvance = 0 because of declaraction assignment up above. + catch (ArgumentException) { } + catch (FormatException) { } + catch (OverflowException) { } } - // If we got garbage from registry just ignore it. - // hijriAdvance = 0 because of declaraction assignment up above. - catch (ArgumentException) { } - catch (FormatException) { } - catch (OverflowException) { } } } + finally + { + key.Close(); + } } - return (hijriAdvance); } } diff --git a/src/coreclr/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs b/src/coreclr/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs index fe8b1b5..a83c4fa 100644 --- a/src/coreclr/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs +++ b/src/coreclr/src/mscorlib/src/System/Globalization/JapaneseCalendar.Win32.cs @@ -36,30 +36,30 @@ namespace System.Globalization try { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(c_japaneseErasHive, writable: false)) + // Need to access registry + RegistryKey key = RegistryKey.GetBaseKey(RegistryKey.HKEY_LOCAL_MACHINE).OpenSubKey(c_japaneseErasHive, false); + + // Abort if we didn't find anything + if (key == null) return null; + + // Look up the values in our reg key + String[] valueNames = key.GetValueNames(); + if (valueNames != null && valueNames.Length > 0) { - // Abort if we didn't find anything - if (key == null) return null; + registryEraRanges = new EraInfo[valueNames.Length]; - // Look up the values in our reg key - String[] valueNames = key.GetValueNames(); - if (valueNames != null && valueNames.Length > 0) + // Loop through the registry and read in all the values + for (int i = 0; i < valueNames.Length; i++) { - registryEraRanges = new EraInfo[valueNames.Length]; - - // Loop through the registry and read in all the values - for (int i = 0; i < valueNames.Length; i++) - { - // See if the era is a valid date - EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString()); + // See if the era is a valid date + EraInfo era = GetEraFromValue(valueNames[i], key.GetValue(valueNames[i]).ToString()); - // continue if not valid - if (era == null) continue; + // continue if not valid + if (era == null) continue; - // Remember we found one. - registryEraRanges[iFoundEras] = era; - iFoundEras++; - } + // Remember we found one. + registryEraRanges[iFoundEras] = era; + iFoundEras++; } } } diff --git a/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.Win32.cs b/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.Win32.cs index b6585bd..f359a2a 100644 --- a/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.Win32.cs +++ b/src/coreclr/src/mscorlib/src/System/TimeZoneInfo.Win32.cs @@ -576,8 +576,8 @@ namespace System // read LastEntry {(yearN, 1, 1) - MaxValue } // read the FirstEntry and LastEntry key values (ex: "1980", "2038") - int first = (int)dynamicKey.GetValue(FirstEntryValue, -1); - int last = (int)dynamicKey.GetValue(LastEntryValue, -1); + int first = (int)dynamicKey.GetValue(FirstEntryValue, -1, RegistryValueOptions.None); + int last = (int)dynamicKey.GetValue(LastEntryValue, -1, RegistryValueOptions.None); if (first == -1 || last == -1 || first > last) { @@ -587,7 +587,7 @@ namespace System // read the first year entry Win32Native.RegistryTimeZoneInformation dtzi; - byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture)) as byte[]; + byte[] regValue = dynamicKey.GetValue(first.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[]; if (regValue == null || regValue.Length != RegByteLength) { rules = null; @@ -620,7 +620,7 @@ namespace System // read the middle year entries for (int i = first + 1; i < last; i++) { - regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture)) as byte[]; + regValue = dynamicKey.GetValue(i.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[]; if (regValue == null || regValue.Length != RegByteLength) { rules = null; @@ -640,7 +640,7 @@ namespace System } // read the last year entry - regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture)) as byte[]; + regValue = dynamicKey.GetValue(last.ToString(CultureInfo.InvariantCulture), null, RegistryValueOptions.None) as byte[]; dtzi = new Win32Native.RegistryTimeZoneInformation(regValue); if (regValue == null || regValue.Length != RegByteLength) { @@ -719,7 +719,7 @@ namespace System } Win32Native.RegistryTimeZoneInformation registryTimeZoneInfo; - byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[]; + byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[]; if (regValue == null || regValue.Length != RegByteLength) return false; registryTimeZoneInfo = new Win32Native.RegistryTimeZoneInformation(regValue); @@ -756,7 +756,7 @@ namespace System // if (result) { - string registryStandardName = key.GetValue(StandardValue, string.Empty) as string; + string registryStandardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string; result = string.Equals(registryStandardName, timeZone.StandardName, StringComparison.Ordinal); } return result; @@ -884,9 +884,9 @@ namespace System daylightName = string.Empty; // read the MUI_ registry keys - string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty) as string; - string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty) as string; - string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty) as string; + string displayNameMuiResource = key.GetValue(MuiDisplayValue, string.Empty, RegistryValueOptions.None) as string; + string standardNameMuiResource = key.GetValue(MuiStandardValue, string.Empty, RegistryValueOptions.None) as string; + string daylightNameMuiResource = key.GetValue(MuiDaylightValue, string.Empty, RegistryValueOptions.None) as string; // try to load the strings from the native resource DLL(s) if (!string.IsNullOrEmpty(displayNameMuiResource)) @@ -907,15 +907,15 @@ namespace System // fallback to using the standard registry keys if (string.IsNullOrEmpty(displayName)) { - displayName = key.GetValue(DisplayValue, string.Empty) as string; + displayName = key.GetValue(DisplayValue, string.Empty, RegistryValueOptions.None) as string; } if (string.IsNullOrEmpty(standardName)) { - standardName = key.GetValue(StandardValue, string.Empty) as string; + standardName = key.GetValue(StandardValue, string.Empty, RegistryValueOptions.None) as string; } if (string.IsNullOrEmpty(daylightName)) { - daylightName = key.GetValue(DaylightValue, string.Empty) as string; + daylightName = key.GetValue(DaylightValue, string.Empty, RegistryValueOptions.None) as string; } return true; @@ -964,7 +964,7 @@ namespace System } Win32Native.RegistryTimeZoneInformation defaultTimeZoneInformation; - byte[] regValue = key.GetValue(TimeZoneInfoValue) as byte[]; + byte[] regValue = key.GetValue(TimeZoneInfoValue, null, RegistryValueOptions.None) as byte[]; if (regValue == null || regValue.Length != RegByteLength) { // the registry value could not be cast to a byte array