Introduct a mini-PAL into Microsoft.Win32.Registry
authorStephen Toub <stoub@microsoft.com>
Thu, 16 Jun 2016 02:25:25 +0000 (22:25 -0400)
committerStephen Toub <stoub@microsoft.com>
Thu, 23 Jun 2016 17:58:39 +0000 (13:58 -0400)
With the expectation that we'll at some point want to make Microsoft.Win32.Registry work on other systems, with a file-system-based simulation of the registry, this commit separates out the shared functionality from the platform-specific functionality, providing placeholders where a file-system-based implementation could be plugged in.

Commit migrated from https://github.com/dotnet/corefx/commit/e36e8cab303fa8aafa5af735bb1b3d58af0e5a52

14 files changed:
src/libraries/Microsoft.Win32.Registry/Microsoft.Win32.Registry.sln
src/libraries/Microsoft.Win32.Registry/src/Microsoft.Win32.Registry.csproj
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/Registry.cs
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryHive.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.FileSystem.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryValueKind.cs
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryValueOptions.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryView.cs
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.FileSystem.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.Windows.cs [new file with mode: 0644]
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.cs
src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/ThrowHelper.cs

index 4a739a2..609713d 100644 (file)
@@ -1,7 +1,7 @@
 
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30723.0
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
 MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Win32.Registry", "src\Microsoft.Win32.Registry.csproj", "{D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}"
 EndProject
@@ -14,14 +14,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Win32.Registry.Te
 EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Unix_Debug|Any CPU = Unix_Debug|Any CPU
+               Unix_Release|Any CPU = Unix_Release|Any CPU
                Windows_Debug|Any CPU = Windows_Debug|Any CPU
                Windows_Release|Any CPU = Windows_Release|Any CPU
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Unix_Debug|Any CPU.ActiveCfg = Unix_Debug|Any CPU
+               {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Unix_Debug|Any CPU.Build.0 = Unix_Debug|Any CPU
+               {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Unix_Release|Any CPU.ActiveCfg = Unix_Release|Any CPU
+               {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Unix_Release|Any CPU.Build.0 = Unix_Release|Any CPU
                {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Windows_Debug|Any CPU.ActiveCfg = Windows_Debug|Any CPU
                {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Windows_Debug|Any CPU.Build.0 = Windows_Debug|Any CPU
                {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Windows_Release|Any CPU.ActiveCfg = Windows_Release|Any CPU
                {D3F18ACC-D327-4ABB-BA6C-E9C34A041B2F}.Windows_Release|Any CPU.Build.0 = Windows_Release|Any CPU
+               {20A2BA2C-5517-483F-8FFE-643441A59852}.Unix_Debug|Any CPU.ActiveCfg = Debug|Any CPU
+               {20A2BA2C-5517-483F-8FFE-643441A59852}.Unix_Debug|Any CPU.Build.0 = Debug|Any CPU
+               {20A2BA2C-5517-483F-8FFE-643441A59852}.Unix_Release|Any CPU.ActiveCfg = Release|Any CPU
+               {20A2BA2C-5517-483F-8FFE-643441A59852}.Unix_Release|Any CPU.Build.0 = Release|Any CPU
                {20A2BA2C-5517-483F-8FFE-643441A59852}.Windows_Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {20A2BA2C-5517-483F-8FFE-643441A59852}.Windows_Debug|Any CPU.Build.0 = Debug|Any CPU
                {20A2BA2C-5517-483F-8FFE-643441A59852}.Windows_Release|Any CPU.ActiveCfg = Release|Any CPU
index 3a908da..eb3f469 100644 (file)
@@ -11,7 +11,6 @@
     <AssemblyVersion>4.0.1.0</AssemblyVersion>
     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
     <PackageTargetFramework Condition="'$(PackageTargetFramework)' == ''">netstandard1.3</PackageTargetFramework>
-    <GeneratePlatformNotSupportedAssembly Condition="'$(TargetsUnix)' == 'true'">true</GeneratePlatformNotSupportedAssembly>
     <IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'net46'">true</IsPartialFacadeAssembly>
     <NuGetTargetMoniker Condition="'$(TargetGroup)' == ''">.NETStandard,Version=v1.3</NuGetTargetMoniker>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Unix_Release|AnyCPU'" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46_Debug|AnyCPU'" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46_Release|AnyCPU'" />
+  <ItemGroup Condition="'$(TargetGroup)' != 'net46'">
+    <Compile Include="$(CommonPath)\Interop\Windows\mincore\Interop.RegistryOptions.cs">
+      <Link>Common\Interop\Windows\Interop.RegistryOptions.cs</Link>
+    </Compile>
+    <Compile Include="Microsoft\Win32\Registry.cs" />
+    <Compile Include="Microsoft\Win32\RegistryKey.cs" />
+    <Compile Include="Microsoft\Win32\RegistryHive.cs" />
+    <Compile Include="Microsoft\Win32\RegistryOptions.cs" />
+    <Compile Include="Microsoft\Win32\RegistryValueKind.cs" />
+    <Compile Include="Microsoft\Win32\RegistryValueOptions.cs" />
+    <Compile Include="Microsoft\Win32\RegistryView.cs" />
+    <Compile Include="Microsoft\Win32\ThrowHelper.cs" />
+    <Compile Include="Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />
+    <Compile Include="System\Security\AccessControl\RegistryRights.cs" />
+  </ItemGroup>
   <ItemGroup Condition="'$(TargetGroup)' != 'net46' AND '$(TargetsWindows)' == 'true'">
     <Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs">
       <Link>Common\Interop\Windows\Interop.Libraries.cs</Link>
@@ -56,9 +70,6 @@
     <Compile Include="$(CommonPath)\Interop\Windows\mincore\Interop.RegFlushKey.cs">
       <Link>Common\Interop\Windows\Interop.RegFlushKey.cs</Link>
     </Compile>
-    <Compile Include="$(CommonPath)\Interop\Windows\mincore\Interop.RegistryOptions.cs">
-      <Link>Common\Interop\Windows\Interop.RegistryOptions.cs</Link>
-    </Compile>
     <Compile Include="$(CommonPath)\Interop\Windows\mincore\Interop.RegOpenKeyEx.cs">
       <Link>Common\Interop\Windows\Interop.RegOpenKeyEx.cs</Link>
     </Compile>
     <Compile Include="$(CommonPath)\Interop\Windows\mincore\Interop.SECURITY_ATTRIBUTES.cs">
       <Link>Common\Interop\Windows\Interop.SECURITY_ATTRIBUTES.cs</Link>
     </Compile>
-    <Compile Include="Microsoft\Win32\Registry.cs" />
-    <Compile Include="Microsoft\Win32\RegistryKey.cs" />
-    <Compile Include="Microsoft\Win32\RegistryOptions.cs" />
-    <Compile Include="Microsoft\Win32\RegistryValueKind.cs" />
-    <Compile Include="Microsoft\Win32\RegistryView.cs" />
-    <Compile Include="Microsoft\Win32\SafeHandles\SafeRegistryHandle.cs" />
-    <Compile Include="Microsoft\Win32\ThrowHelper.cs" />
-    <Compile Include="System\Security\AccessControl\RegistryRights.cs" />
+    <Compile Include="Microsoft\Win32\RegistryKey.Windows.cs" />
+    <Compile Include="Microsoft\Win32\SafeHandles\SafeRegistryHandle.Windows.cs" />
+  </ItemGroup>
+  <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
+    <Compile Include="Microsoft\Win32\RegistryKey.FileSystem.cs" />
+    <Compile Include="Microsoft\Win32\SafeHandles\SafeRegistryHandle.FileSystem.cs" />
   </ItemGroup>
   <ItemGroup Condition="'$(TargetGroup)' == 'net46'">
     <TargetingPackReference Include="mscorlib" />
index 1add75b..b656c66 100644 (file)
 
 using System;
 using System.Diagnostics;
-using System.Runtime.InteropServices;
+using System.Security;
 
 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.
+    /// <summary>Registry encapsulation. Contains members representing all top level system keys.</summary>
     public static class Registry
     {
-        /**
-         * 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. 
-        //
-        [System.Security.SecurityCritical]  
-        private static RegistryKey GetBaseKeyFromKeyName(string keyName, out string subKeyName)
+        /// <summary>Current User Key. This key should be used as the root for all user specific settings.</summary>
+        public static readonly RegistryKey CurrentUser = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default);
+
+        /// <summary>Local Machine key. This key should be used as the root for all machine specific settings.</summary>
+        public static readonly RegistryKey LocalMachine = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Default);
+        
+        /// <summary>Classes Root Key. This is the root key of class information.</summary>
+        public static readonly RegistryKey ClassesRoot = RegistryKey.OpenBaseKey(RegistryHive.ClassesRoot, RegistryView.Default);
+
+        /// <summary>Users Root Key. This is the root of users.</summary>
+        public static readonly RegistryKey Users = RegistryKey.OpenBaseKey(RegistryHive.Users, RegistryView.Default);
+
+        /// <summary>Performance Root Key. This is where dynamic performance data is stored on NT.</summary>
+        public static readonly RegistryKey PerformanceData = RegistryKey.OpenBaseKey(RegistryHive.PerformanceData, RegistryView.Default);
+
+        /// <summary>Current Config Root Key. This is where current configuration information is stored.</summary>
+        public static readonly RegistryKey CurrentConfig = RegistryKey.OpenBaseKey(RegistryHive.CurrentConfig, RegistryView.Default);
+
+        [SecuritySafeCritical]
+        public static object GetValue(string keyName, string valueName, object defaultValue)
         {
-            if (keyName == null)
+            string subKeyName;
+            RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
+
+            using (RegistryKey key = basekey.OpenSubKey(subKeyName))
             {
-                throw new ArgumentNullException(nameof(keyName));
+                return key != null ?
+                    key.GetValue(valueName, defaultValue) :
+                    null;
             }
+        }
 
-            string basekeyName;
-            int i = keyName.IndexOf('\\');
-            if (i != -1)
+        public static void SetValue(string keyName, string valueName, object value)
+        {
+            SetValue(keyName, valueName, value, RegistryValueKind.Unknown);
+        }
+
+        [SecuritySafeCritical]
+        public static void SetValue(string keyName, string valueName, object value, RegistryValueKind valueKind)
+        {
+            string subKeyName;
+            RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
+
+            using (RegistryKey key = basekey.CreateSubKey(subKeyName))
             {
-                basekeyName = keyName.Substring(0, i).ToUpperInvariant();
+                Debug.Assert(key != null, "An exception should be thrown if failed!");
+                key.SetValue(valueName, value, valueKind);
             }
-            else
+        }
+
+        /// <summary>
+        /// 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. 
+        /// </summary>
+        [System.Security.SecurityCritical]
+        private static RegistryKey GetBaseKeyFromKeyName(string keyName, out string subKeyName)
+        {
+            if (keyName == null)
             {
-                basekeyName = keyName.ToUpperInvariant();
+                throw new ArgumentNullException(nameof(keyName));
             }
-            RegistryKey basekey = null;
 
+            int i = keyName.IndexOf('\\');
+            string basekeyName = i != -1 ?
+                keyName.Substring(0, i).ToUpperInvariant() :
+                keyName.ToUpperInvariant();
+
+            RegistryKey basekey = null;
             switch (basekeyName)
             {
                 case "HKEY_CURRENT_USER":
-                    basekey = Registry.CurrentUser;
+                    basekey = CurrentUser;
                     break;
                 case "HKEY_LOCAL_MACHINE":
-                    basekey = Registry.LocalMachine;
+                    basekey = LocalMachine;
                     break;
                 case "HKEY_CLASSES_ROOT":
-                    basekey = Registry.ClassesRoot;
+                    basekey = ClassesRoot;
                     break;
                 case "HKEY_USERS":
-                    basekey = Registry.Users;
+                    basekey = Users;
                     break;
                 case "HKEY_PERFORMANCE_DATA":
-                    basekey = Registry.PerformanceData;
+                    basekey = PerformanceData;
                     break;
                 case "HKEY_CURRENT_CONFIG":
-                    basekey = Registry.CurrentConfig;
+                    basekey = CurrentConfig;
                     break;
                 default:
-                    throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)));
+                    throw new ArgumentException(SR.Format(SR.Arg_RegInvalidKeyName, nameof(keyName)), nameof(keyName));
             }
-            if (i == -1 || i == keyName.Length)
-            {
-                subKeyName = string.Empty;
-            }
-            else
-            {
-                subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1);
-            }
-            return basekey;
-        }
 
-        [System.Security.SecuritySafeCritical]  
-        public static object GetValue(string keyName, string valueName, object defaultValue)
-        {
-            string subKeyName;
-            RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
-            Debug.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();
-            }
-        }
-
-        public static void SetValue(string keyName, string valueName, object value)
-        {
-            SetValue(keyName, valueName, value, RegistryValueKind.Unknown);
-        }
+            subKeyName = (i == -1 || i == keyName.Length) ?
+                string.Empty :
+                keyName.Substring(i + 1, keyName.Length - i - 1);
 
-        [System.Security.SecuritySafeCritical]  
-        public static void SetValue(string keyName, string valueName, object value, RegistryValueKind valueKind)
-        {
-            string subKeyName;
-            RegistryKey basekey = GetBaseKeyFromKeyName(keyName, out subKeyName);
-            Debug.Assert(basekey != null, "basekey can't be null!");
-            RegistryKey key = basekey.CreateSubKey(subKeyName);
-            Debug.Assert(key != null, "An exception should be thrown if failed!");
-            try
-            {
-                key.SetValue(valueName, value, valueKind);
-            }
-            finally
-            {
-                key.Close();
-            }
+            return basekey;
         }
     }
 }
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryHive.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryHive.cs
new file mode 100644 (file)
index 0000000..059e484
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+namespace Microsoft.Win32
+{
+    /**
+     * Registry hive values.  Useful only for GetRemoteBaseKey
+     */
+    public enum RegistryHive
+    {
+        ClassesRoot = unchecked((int)0x80000000),
+        CurrentUser = unchecked((int)0x80000001),
+        LocalMachine = unchecked((int)0x80000002),
+        Users = unchecked((int)0x80000003),
+        PerformanceData = unchecked((int)0x80000004),
+        CurrentConfig = unchecked((int)0x80000005),
+    }
+}
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.FileSystem.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.FileSystem.cs
new file mode 100644 (file)
index 0000000..db88827
--- /dev/null
@@ -0,0 +1,141 @@
+// 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.
+
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Security;
+using System.Security.AccessControl;
+
+namespace Microsoft.Win32
+{
+    public sealed partial class RegistryKey : IDisposable
+    {
+        [SecuritySafeCritical]
+        private void ClosePerfDataKey()
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private void FlushCore()
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private RegistryKey CreateSubKeyInternalCore(string subkey, bool writable, RegistryOptions registryOptions)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteSubKeyCore(string subkey, bool throwOnMissingSubKey)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteSubKeyTreeCore(string subkey)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteValueCore(string name, bool throwOnMissingValue)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        private static RegistryKey OpenBaseKeyCore(RegistryHive hKey, RegistryView view)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private static RegistryKey OpenRemoteBaseKeyCore(RegistryHive hKey, string machineName, RegistryView view)
+        {
+            throw new PlatformNotSupportedException(SR.Security_RegistryPermission); // remote stores not supported on Unix
+        }
+
+        [SecurityCritical]
+        private RegistryKey InternalOpenSubKeyCore(string name, RegistryRights rights, bool throwOnPermissionFailure)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        private SafeRegistryHandle SystemKeyHandle
+        {
+            [SecurityCritical]
+            get
+            {
+                // TODO: Implement this
+                throw new PlatformNotSupportedException();
+            }
+        }
+
+        [SecurityCritical]
+        private int InternalSubKeyCountCore()
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecurityCritical]
+        private string[] InternalGetSubKeyNamesCore(int subkeys)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecurityCritical]
+        private int InternalValueCountCore()
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private string[] GetValueNamesCore(int values)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecurityCritical]
+        private Object InternalGetValueCore(string name, Object defaultValue, bool doNotExpand)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private RegistryValueKind GetValueKindCore(string name)
+        {
+            // TODO: Implement this
+            throw new PlatformNotSupportedException();
+        }
+
+        [SecuritySafeCritical]
+        private void SetValueCore(string name, Object value, RegistryValueKind valueKind)
+        {
+            try
+            { 
+                // TODO: Implement this
+                throw new PlatformNotSupportedException();
+            }
+            catch (Exception exc) when (exc is OverflowException || exc is InvalidOperationException || exc is FormatException || exc is InvalidCastException)
+            {
+                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
+            }
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.Windows.cs
new file mode 100644 (file)
index 0000000..1de5045
--- /dev/null
@@ -0,0 +1,895 @@
+// 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.
+
+using Microsoft.Win32.SafeHandles;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Security;
+using System.Security.AccessControl;
+
+/*
+  Note on transaction support:
+  Eventually we will want to add support for NT's transactions to our
+  RegistryKey API's.  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
+  registry key, then the ACL only gets checked when you construct handles to 
+  them.  So if you set an ACL to deny read access to yourself, you'll still be
+  able to read with that handle, but not with new handles.
+
+  Another peculiarity is a Terminal Server app compatibility hack.  The OS
+  will second guess your attempt to open a handle sometimes.  If a certain
+  combination of Terminal Server app compat registry keys are set, then the
+  OS will try to reopen your handle with lesser permissions if you couldn't
+  open it in the specified mode.  So on some machines, we will see handles that
+  may not be able to read or write to a registry key.  It's very strange.  But
+  the real test of these handles is attempting to read or set a value in an
+  affected registry key.
+  
+  For reference, at least two registry keys must be set to particular values 
+  for this behavior:
+  HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\RegistryExtensionFlags, the least significant bit must be 1.
+  HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\TSAppCompat must be 1
+  There might possibly be an interaction with yet a third registry key as well.
+*/
+
+namespace Microsoft.Win32
+{
+    public sealed partial class RegistryKey : IDisposable
+    {
+        [SecuritySafeCritical]
+        private void ClosePerfDataKey()
+        {
+            // 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 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 of race 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. 
+            Interop.mincore.RegCloseKey(HKEY_PERFORMANCE_DATA);
+        }
+
+        [SecuritySafeCritical]
+        private void FlushCore()
+        {
+            if (_hkey != null && IsDirty())
+            {
+                Interop.mincore.RegFlushKey(_hkey);
+            }
+        }
+
+        [SecuritySafeCritical]
+        private unsafe RegistryKey CreateSubKeyInternalCore(string subkey, bool writable, RegistryOptions registryOptions)
+        {
+            Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
+            int disposition = 0;
+
+            // By default, the new key will be writable.
+            SafeRegistryHandle result = null;
+            int ret = Interop.mincore.RegCreateKeyEx(_hkey,
+                subkey,
+                0,
+                null,
+                (int)registryOptions /* specifies if the key is volatile */,
+                (int)GetRegistryKeyRights(writable) | (int)_regView,
+                ref secAttrs,
+                out result,
+                out disposition);
+
+            if (ret == 0 && !result.IsInvalid)
+            {
+                RegistryKey key = new RegistryKey(result, writable, false, _remoteKey, false, _regView);
+                if (subkey.Length == 0)
+                {
+                    key._keyName = _keyName;
+                }
+                else
+                {
+                    key._keyName = _keyName + "\\" + subkey;
+                }
+                return key;
+            }
+            else if (ret != 0) // syscall failed, ret is an error code.
+            {
+                Win32Error(ret, _keyName + "\\" + subkey);  // Access denied?
+            }
+
+            Debug.Fail("Unexpected code path in RegistryKey::CreateSubKey");
+            return null;
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteSubKeyCore(string subkey, bool throwOnMissingSubKey)
+        {
+            int ret = Interop.mincore.RegDeleteKeyEx(_hkey, subkey, (int)_regView, 0);
+
+            if (ret != 0)
+            {
+                if (ret == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
+                {
+                    if (throwOnMissingSubKey)
+                    {
+                        ThrowHelper.ThrowArgumentException(SR.Arg_RegSubKeyAbsent);
+                    }
+                }
+                else
+                {
+                    Win32Error(ret, null);
+                }
+            }
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteSubKeyTreeCore(string subkey)
+        {
+            int ret = Interop.mincore.RegDeleteKeyEx(_hkey, subkey, (int)_regView, 0);
+            if (ret != 0)
+            {
+                Win32Error(ret, null);
+            }
+        }
+
+        [SecuritySafeCritical]
+        private void DeleteValueCore(string name, bool throwOnMissingValue)
+        {
+            int errorCode = Interop.mincore.RegDeleteValue(_hkey, name);
+
+            //
+            // 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 == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND ||
+                errorCode == Interop.mincore.Errors.ERROR_FILENAME_EXCED_RANGE)
+            {
+                if (throwOnMissingValue)
+                {
+                    ThrowHelper.ThrowArgumentException(SR.Arg_RegSubKeyValueAbsent);
+                }
+                else
+                {
+                    // Otherwise, reset and just return giving no indication to the user.
+                    // (For compatibility)
+                    errorCode = 0;
+                }
+            }
+            // 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);
+        }
+
+        /// <summary>
+        /// 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.
+        /// </summary>
+        /// <param name="hKeyHive">HKEY_* to open.</param>
+        /// <returns>The RegistryKey requested.</returns>
+        [SecuritySafeCritical]
+        private static RegistryKey OpenBaseKeyCore(RegistryHive hKeyHive, RegistryView view)
+        {
+            IntPtr hKey = (IntPtr)((int)hKeyHive);
+
+            int index = ((int)hKey) & 0x0FFFFFFF;
+            Debug.Assert(index >= 0 && index < s_hkeyNames.Length, "index is out of range!");
+            Debug.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, false, isPerf, view);
+            key._keyName = s_hkeyNames[index];
+            return key;
+        }
+
+        [SecuritySafeCritical]
+        private static RegistryKey OpenRemoteBaseKeyCore(RegistryHive hKey, string machineName, RegistryView view)
+        {
+            int index = (int)hKey & 0x0FFFFFFF;
+            if (index < 0 || index >= s_hkeyNames.Length || ((int)hKey & 0xFFFFFFF0) != 0x80000000)
+            {
+                throw new ArgumentException(SR.Arg_RegKeyOutOfRange);
+            }
+
+            // connect to the specified remote registry
+            SafeRegistryHandle foreignHKey = null;
+            int ret = Interop.mincore.RegConnectRegistry(machineName, new SafeRegistryHandle(new IntPtr((int)hKey), false), out foreignHKey);
+
+            if (ret == Interop.mincore.Errors.ERROR_DLL_INIT_FAILED)
+            {
+                // return value indicates an error occurred
+                throw new ArgumentException(SR.Arg_DllInitFailure);
+            }
+
+            if (ret != 0)
+            {
+                Win32ErrorStatic(ret, null);
+            }
+
+            if (foreignHKey.IsInvalid)
+            {
+                // return value indicates an error occurred
+                throw new ArgumentException(SR.Format(SR.Arg_RegKeyNoRemoteConnect, machineName));
+            }
+
+            RegistryKey key = new RegistryKey(foreignHKey, true, false, true, ((IntPtr)hKey) == HKEY_PERFORMANCE_DATA, view);
+            key._keyName = s_hkeyNames[index];
+            return key;
+        }
+
+        [SecurityCritical]
+        private RegistryKey InternalOpenSubKeyCore(string name, RegistryRights rights, bool throwOnPermissionFailure)
+        {
+            SafeRegistryHandle result = null;
+            int ret = Interop.mincore.RegOpenKeyEx(_hkey, name, 0, ((int)rights | (int)_regView), out result);
+            if (ret == 0 && !result.IsInvalid)
+            {
+                RegistryKey key = new RegistryKey(result, IsWritable((int)rights), false, _remoteKey, false, _regView);
+                key._keyName = _keyName + "\\" + name;
+                return key;
+            }
+
+            if (throwOnPermissionFailure)
+            {
+                if (ret == Interop.mincore.Errors.ERROR_ACCESS_DENIED || ret == Interop.mincore.Errors.ERROR_BAD_IMPERSONATION_LEVEL)
+                {
+                    // We need to throw SecurityException here for compatibility reason,
+                    // although UnauthorizedAccessException will make more sense.
+                    ThrowHelper.ThrowSecurityException(SR.Security_RegistryPermission);
+                }
+            }
+
+            // Return null if we didn't find the key.
+            return null;
+        }
+
+        private SafeRegistryHandle SystemKeyHandle
+        {
+            [SecurityCritical]
+            get
+            {
+                Debug.Assert(IsSystemKey());
+
+                int ret = Interop.mincore.Errors.ERROR_INVALID_HANDLE;
+                IntPtr baseKey = (IntPtr)0;
+                switch (_keyName)
+                {
+                    case "HKEY_CLASSES_ROOT":
+                        baseKey = HKEY_CLASSES_ROOT;
+                        break;
+                    case "HKEY_CURRENT_USER":
+                        baseKey = HKEY_CURRENT_USER;
+                        break;
+                    case "HKEY_LOCAL_MACHINE":
+                        baseKey = HKEY_LOCAL_MACHINE;
+                        break;
+                    case "HKEY_USERS":
+                        baseKey = HKEY_USERS;
+                        break;
+                    case "HKEY_PERFORMANCE_DATA":
+                        baseKey = HKEY_PERFORMANCE_DATA;
+                        break;
+                    case "HKEY_CURRENT_CONFIG":
+                        baseKey = HKEY_CURRENT_CONFIG;
+                        break;
+                    default:
+                        Win32Error(ret, null);
+                        break;
+                }
+
+                // open the base key so that RegistryKey.Handle will return a valid handle
+                SafeRegistryHandle result;
+                ret = Interop.mincore.RegOpenKeyEx(baseKey,
+                    null,
+                    0,
+                    (int)GetRegistryKeyRights(IsWritable()) | (int)_regView,
+                    out result);
+
+                if (ret == 0 && !result.IsInvalid)
+                {
+                    return result;
+                }
+                else
+                {
+                    Win32Error(ret, null);
+                    throw new IOException(Interop.mincore.GetMessage(ret), ret);
+                }
+            }
+        }
+
+        [SecurityCritical]
+        private int InternalSubKeyCountCore()
+        {
+            int subkeys = 0;
+            int junk = 0;
+            int ret = Interop.mincore.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;
+        }
+
+        [SecurityCritical]
+        private unsafe string[] InternalGetSubKeyNamesCore(int subkeys)
+        {
+            string[] names = new string[subkeys];
+            char[] name = new char[MaxKeyLength + 1];
+
+            int namelen;
+
+            fixed (char* namePtr = &name[0])
+            {
+                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 initialized.
+                    int ret = Interop.mincore.RegEnumKeyEx(_hkey,
+                        i,
+                        namePtr,
+                        ref namelen,
+                        null,
+                        null,
+                        null,
+                        null);
+                    if (ret != 0)
+                    {
+                        Win32Error(ret, null);
+                    }
+
+                    names[i] = new string(namePtr);
+                }
+            }
+
+            return names;
+        }
+
+        [SecurityCritical]
+        private int InternalValueCountCore()
+        {
+            int values = 0;
+            int junk = 0;
+            int ret = Interop.mincore.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;
+        }
+
+        /// <summary>Retrieves an array of strings containing all the value names.</summary>
+        /// <returns>All value names.</returns>
+        [SecuritySafeCritical]
+        private unsafe string[] GetValueNamesCore(int values)
+        {
+            string[] names = new string[values];
+            char[] name = new char[MaxValueLength + 1];
+            int namelen;
+
+            fixed (char* namePtr = &name[0])
+            {
+                for (int i = 0; i < values; i++)
+                {
+                    namelen = name.Length;
+
+                    int ret = Interop.mincore.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 == Interop.mincore.Errors.ERROR_MORE_DATA))
+                            Win32Error(ret, null);
+                    }
+
+                    names[i] = new string(namePtr);
+                }
+            }
+
+            return names;
+        }
+
+        [SecurityCritical]
+        private object InternalGetValueCore(string name, object defaultValue, bool doNotExpand)
+        {
+            object data = defaultValue;
+            int type = 0;
+            int datasize = 0;
+
+            int ret = Interop.mincore.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 (Interop.mincore.Errors.ERROR_MORE_DATA == (r = Interop.mincore.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 != Interop.mincore.Errors.ERROR_MORE_DATA)
+                    {
+                        return data;
+                    }
+                }
+            }
+
+            if (datasize < 0)
+            {
+                // unexpected code path
+                Debug.Fail("[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
+                datasize = 0;
+            }
+
+            switch (type)
+            {
+                case Interop.mincore.RegistryValues.REG_NONE:
+                case Interop.mincore.RegistryValues.REG_DWORD_BIG_ENDIAN:
+                case Interop.mincore.RegistryValues.REG_BINARY:
+                    {
+                        byte[] blob = new byte[datasize];
+                        ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);
+                        data = blob;
+                    }
+                    break;
+                case Interop.mincore.RegistryValues.REG_QWORD:
+                    {    // also REG_QWORD_LITTLE_ENDIAN
+                        if (datasize > 8)
+                        {
+                            // prevent an AV in the edge case that datasize is larger than sizeof(long)
+                            goto case Interop.mincore.RegistryValues.REG_BINARY;
+                        }
+                        long blob = 0;
+                        Debug.Assert(datasize == 8, "datasize==8");
+                        // Here, datasize must be 8 when calling this
+                        ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);
+
+                        data = blob;
+                    }
+                    break;
+                case Interop.mincore.RegistryValues.REG_DWORD:
+                    {    // also REG_DWORD_LITTLE_ENDIAN
+                        if (datasize > 4)
+                        {
+                            // prevent an AV in the edge case that datasize is larger than sizeof(int)
+                            goto case Interop.mincore.RegistryValues.REG_QWORD;
+                        }
+                        int blob = 0;
+                        Debug.Assert(datasize == 4, "datasize==4");
+                        // Here, datasize must be four when calling this
+                        ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);
+
+                        data = blob;
+                    }
+                    break;
+
+                case Interop.mincore.RegistryValues.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];
+
+                        ret = Interop.mincore.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 Interop.mincore.RegistryValues.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];
+
+                        ret = Interop.mincore.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 Interop.mincore.RegistryValues.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(SR.Arg_RegGetOverflowBug, e);
+                            }
+                        }
+                        char[] blob = new char[datasize / 2];
+
+                        ret = Interop.mincore.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)
+                        {
+                            Array.Resize(ref blob, blob.Length + 1);
+                        }
+
+                        var 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)
+                            {
+                                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;
+                        }
+
+                        data = strings.ToArray();
+                    }
+                    break;
+                case Interop.mincore.RegistryValues.REG_LINK:
+                default:
+                    break;
+            }
+
+            return data;
+        }
+
+        [SecuritySafeCritical]
+        private RegistryValueKind GetValueKindCore(string name)
+        {
+            int type = 0;
+            int datasize = 0;
+            int ret = Interop.mincore.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize);
+            if (ret != 0)
+            {
+                Win32Error(ret, null);
+            }
+
+            return
+                type == Interop.mincore.RegistryValues.REG_NONE ? RegistryValueKind.None :
+                !Enum.IsDefined(typeof(RegistryValueKind), type) ? RegistryValueKind.Unknown :
+                (RegistryValueKind)type;
+        }
+
+        [SecuritySafeCritical]
+        private unsafe void SetValueCore(string name, object value, RegistryValueKind valueKind)
+        {
+            int ret = 0;
+            try
+            {
+                switch (valueKind)
+                {
+                    case RegistryValueKind.ExpandString:
+                    case RegistryValueKind.String:
+                        {
+                            string data = value.ToString();
+                            ret = Interop.mincore.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());
+
+                            // First determine the size of the array
+                            //
+                            // Format is null terminator between strings and final null terminator at the end.
+                            //    e.g. str1\0str2\0str3\0\0 
+                            //
+                            int sizeInChars = 1; // no matter what, we have the final null terminator.
+                            for (int i = 0; i < dataStrings.Length; i++)
+                            {
+                                if (dataStrings[i] == null)
+                                {
+                                    ThrowHelper.ThrowArgumentException(SR.Arg_RegSetStrArrNull);
+                                }
+                                sizeInChars = checked(sizeInChars + (dataStrings[i].Length + 1));
+                            }
+                            int sizeInBytes = checked(sizeInChars * sizeof(char));
+
+                            // Write out the strings...
+                            //
+                            char[] dataChars = new char[sizeInChars];
+                            int destinationIndex = 0;
+                            for (int i = 0; i < dataStrings.Length; i++)
+                            {
+                                int length = dataStrings[i].Length;
+                                dataStrings[i].CopyTo(0, dataChars, destinationIndex, length);
+                                destinationIndex += (length + 1); // +1 for null terminator, which is already zero-initialized in new array.
+                            }
+
+                            ret = Interop.mincore.RegSetValueEx(_hkey,
+                                name,
+                                0,
+                                RegistryValueKind.MultiString,
+                                dataChars,
+                                sizeInBytes);
+
+                            break;
+                        }
+
+                    case RegistryValueKind.None:
+                    case RegistryValueKind.Binary:
+                        byte[] dataBytes = (byte[])value;
+                        ret = Interop.mincore.RegSetValueEx(_hkey,
+                            name,
+                            0,
+                            (valueKind == RegistryValueKind.None ? Interop.mincore.RegistryValues.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 = Interop.mincore.RegSetValueEx(_hkey,
+                                name,
+                                0,
+                                RegistryValueKind.DWord,
+                                ref data,
+                                4);
+                            break;
+                        }
+
+                    case RegistryValueKind.QWord:
+                        {
+                            long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture);
+
+                            ret = Interop.mincore.RegSetValueEx(_hkey,
+                                name,
+                                0,
+                                RegistryValueKind.QWord,
+                                ref data,
+                                8);
+                            break;
+                        }
+                }
+            }
+            catch (Exception exc) when (exc is OverflowException || exc is InvalidOperationException || exc is FormatException || exc is InvalidCastException)
+            {
+                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
+            }
+
+            if (ret == 0)
+            {
+                SetDirty();
+            }
+            else
+            {
+                Win32Error(ret, null);
+            }
+        }
+
+        /// <summary>
+        /// After calling GetLastWin32Error(), it clears the last error field,
+        /// so you must save the HResult and pass it to this method.  This method
+        /// will determine the appropriate exception to throw dependent on your
+        /// error, and depending on the error, insert a string into the message
+        /// gotten from the ResourceManager.
+        /// </summary>
+        [SecuritySafeCritical]
+        private void Win32Error(int errorCode, string str)
+        {
+            switch (errorCode)
+            {
+                case Interop.mincore.Errors.ERROR_ACCESS_DENIED:
+                    throw str != null ?
+                        new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str)) :
+                        new UnauthorizedAccessException();
+
+                case Interop.mincore.Errors.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 Interop.mincore.Errors.ERROR_FILE_NOT_FOUND:
+                    throw new IOException(SR.Arg_RegKeyNotFound, errorCode);
+
+                default:
+                    throw new IOException(Interop.mincore.GetMessage(errorCode), errorCode);
+            }
+        }
+
+        [SecuritySafeCritical]
+        private static void Win32ErrorStatic(int errorCode, string str)
+        {
+            switch (errorCode)
+            {
+                case Interop.mincore.Errors.ERROR_ACCESS_DENIED:
+                    throw str != null ?
+                        new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str)) :
+                        new UnauthorizedAccessException();
+
+                default:
+                    throw new IOException(Interop.mincore.GetMessage(errorCode), errorCode);
+            }
+        }
+
+        private static bool IsWritable(int rights)
+        {
+            return (rights & (Interop.mincore.RegistryOperations.KEY_SET_VALUE |
+                              Interop.mincore.RegistryOperations.KEY_CREATE_SUB_KEY |
+                              (int)RegistryRights.Delete |
+                              (int)RegistryRights.TakeOwnership |
+                              (int)RegistryRights.ChangePermissions)) != 0;
+        }
+    }
+}
index cc0c31d..ac18416 100644 (file)
 // 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.  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
-  registry key, then the ACL only gets checked when you construct handles to 
-  them.  So if you set an ACL to deny read access to yourself, you'll still be
-  able to read with that handle, but not with new handles.
-
-  Another peculiarity is a Terminal Server app compatibility hack.  The OS
-  will second guess your attempt to open a handle sometimes.  If a certain
-  combination of Terminal Server app compat registry keys are set, then the
-  OS will try to reopen your handle with lesser permissions if you couldn't
-  open it in the specified mode.  So on some machines, we will see handles that
-  may not be able to read or write to a registry key.  It's very strange.  But
-  the real test of these handles is attempting to read or set a value in an
-  affected registry key.
-  
-  For reference, at least two registry keys must be set to particular values 
-  for this behavior:
-  HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\RegistryExtensionFlags, the least significant bit must be 1.
-  HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\TSAppCompat must be 1
-  There might possibly be an interaction with yet a third registry key as well.
-
-*/
-
 using Microsoft.Win32.SafeHandles;
 using System;
-using System.Collections;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.Contracts;
 using System.IO;
-using System.Runtime.InteropServices;
-using System.Text;
+using System.Security;
 using System.Security.AccessControl;
+using System.Text;
 
 namespace Microsoft.Win32
 {
-    /**
-     * Registry hive values.  Useful only for GetRemoteBaseKey
-     */
-    public enum RegistryHive
-    {
-        ClassesRoot = unchecked((int)0x80000000),
-        CurrentUser = unchecked((int)0x80000001),
-        LocalMachine = unchecked((int)0x80000002),
-        Users = unchecked((int)0x80000003),
-        PerformanceData = unchecked((int)0x80000004),
-        CurrentConfig = unchecked((int)0x80000005),
-    }
-
-    /**
-     * 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)
-     */
-    public sealed class RegistryKey : IDisposable
+    /// <summary>Registry encapsulation. To get an instance of a RegistryKey use the Registry class's static members then call OpenSubKey.</summary>
+    public sealed partial class RegistryKey : IDisposable
     {
-        // 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;
-
-        // SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened"
-        // or "closed".
-        //
-        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 static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));
+        private static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));
+        private static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));
+        private static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));
+        private static readonly IntPtr HKEY_PERFORMANCE_DATA = new IntPtr(unchecked((int)0x80000004));
+        private static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));
+
+        /// <summary>Names of keys.  This array must be in the same order as the HKEY values listed above.</summary>
+        private static readonly string[] s_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
@@ -127,94 +42,70 @@ namespace Microsoft.Win32
         private const int MaxKeyLength = 255;
         private const int MaxValueLength = 16383;
 
-        [System.Security.SecurityCritical] 
-        private volatile SafeRegistryHandle hkey = null;
-        private volatile int state = 0;
-        private volatile String keyName;
-        private volatile bool remoteKey = false;
-        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.
-         *
-         * This key is bound to hkey, if writable is <b>false</b> then no write operations
-         * will be allowed.
-         */
-        [System.Security.SecurityCritical]  // auto-generated
-        private RegistryKey(SafeRegistryHandle hkey, bool writable, RegistryView view)
-            : this(hkey, writable, false, false, false, view)
+        [SecurityCritical]
+        private volatile SafeRegistryHandle _hkey;
+        private volatile string _keyName;
+        private volatile bool _remoteKey;
+        private volatile StateFlags _state;
+        private volatile RegistryView _regView = RegistryView.Default;
+
+        /// <summary>
+        /// Creates a RegistryKey. This key is bound to hkey, if writable is <b>false</b> then no write operations will be allowed.
+        /// </summary>
+        [SecurityCritical]
+        private RegistryKey(SafeRegistryHandle hkey, bool writable, RegistryView view) :
+            this(hkey, writable, false, false, false, view)
+        {
+        }
+
+        /// <summary>
+        /// Creates a RegistryKey.
+        /// This key is bound to hkey, if writable is <b>false</b> then no write operations
+        /// will be allowed. If systemkey is set then the hkey won't be released
+        /// when the object is GC'ed.
+        /// 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.
+        /// </summary>
+        [SecurityCritical]
+        private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)
         {
-        }
+            ValidateKeyView(view);
 
+            _hkey = hkey;
+            _keyName = "";
+            _remoteKey = remoteKey;
+            _regView = view;
 
-        /**
-         * Creates a RegistryKey.
-         *
-         * This key is bound to hkey, if writable is <b>false</b> then no write operations
-         * will be allowed. If systemkey is set then the hkey won't be released
-         * when the object is GC'ed.
-         * 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.
-         */
-        [System.Security.SecurityCritical]  
-        private RegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey, bool remoteKey, bool isPerfData, RegistryView view)
-        {
-            this.hkey = hkey;
-            this.keyName = "";
-            this.remoteKey = remoteKey;
-            this.regView = view;
             if (systemkey)
             {
-                this.state |= STATE_SYSTEMKEY;
+                _state |= StateFlags.SystemKey;
             }
             if (writable)
             {
-                this.state |= STATE_WRITEACCESS;
+                _state |= StateFlags.WriteAccess;
             }
             if (isPerfData)
-                this.state |= STATE_PERF_DATA;
-            ValidateKeyView(view);
+            {
+                _state |= StateFlags.PerfData;
+            }
         }
 
-        /**
-         * Closes this key, flushes it to disk if the contents have been modified.
-         */
-        internal void Close()
+        [SecuritySafeCritical]
+        public void Flush()
         {
-            Dispose(true);
+            FlushCore();
         }
 
-        [System.Security.SecuritySafeCritical]  
-        private void Dispose(bool disposing)
+        [SecuritySafeCritical]
+        public void Dispose()
         {
-            if (hkey != null)
+            if (_hkey != null)
             {
                 if (!IsSystemKey())
                 {
                     try
                     {
-                        hkey.Dispose();
+                        _hkey.Dispose();
                     }
                     catch (IOException)
                     {
@@ -222,69 +113,37 @@ namespace Microsoft.Win32
                     }
                     finally
                     {
-                        hkey = null;
+                        _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 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 of race 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. 
-                    Interop.mincore.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
-                }
-            }
-        }
-
-        [System.Security.SecuritySafeCritical]  
-        public void Flush()
-        {
-            if (hkey != null)
-            {
-                if (IsDirty())
+                else if (IsPerfDataKey())
                 {
-                    Interop.mincore.RegFlushKey(hkey);
+                    ClosePerfDataKey();
                 }
             }
         }
 
-        public void Dispose()
-        {
-            Dispose(true);
-        }
-
-        /**
-         * Creates a new subkey, or opens an existing one.
-         *
-         * @param subkey Name or path to subkey to create or open.
-         *
-         * @return the subkey, or <b>null</b> if the operation failed.
-         */
+        /// <summary>Creates a new subkey, or opens an existing one.</summary>
+        /// <param name="subkey">Name or path to subkey to create or open.</param>
+        /// <returns>The subkey, or <b>null</b> if the operation failed.</returns>
         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
-        public RegistryKey CreateSubKey(String subkey)
+        public RegistryKey CreateSubKey(string subkey)
         {
             return CreateSubKey(subkey, IsWritable());
         }
 
-        public RegistryKey CreateSubKey(String subkey, bool writable)
+        public RegistryKey CreateSubKey(string subkey, bool writable)
         {
             return CreateSubKeyInternal(subkey, writable, RegistryOptions.None);
         }
 
-        public RegistryKey CreateSubKey(String subkey, bool writable, RegistryOptions options)
+        public RegistryKey CreateSubKey(string subkey, bool writable, RegistryOptions options)
         {
             return CreateSubKeyInternal(subkey, writable, options);
         }
 
-        [System.Security.SecuritySafeCritical]  
-        private unsafe RegistryKey CreateSubKeyInternal(String subkey, bool writable, RegistryOptions registryOptions)
+        [SecuritySafeCritical]  
+        private RegistryKey CreateSubKeyInternal(string subkey, bool writable, RegistryOptions registryOptions)
         {
             ValidateKeyOptions(registryOptions);
             ValidateKeyName(subkey);
@@ -292,62 +151,32 @@ namespace Microsoft.Win32
             subkey = FixupName(subkey); // Fixup multiple slashes to a single slash
 
             // only keys opened under read mode is not writable
-            if (!remoteKey)
+            if (!_remoteKey)
             {
                 RegistryKey key = InternalOpenSubKey(subkey, writable);
                 if (key != null)
-                { // Key already exits
+                { 
+                    // Key already exits
                     return key;
                 }
             }
 
-            Interop.mincore.SECURITY_ATTRIBUTES secAttrs = default(Interop.mincore.SECURITY_ATTRIBUTES);
-            int disposition = 0;
-
-            // By default, the new key will be writable.
-            SafeRegistryHandle result = null;
-            int ret = Interop.mincore.RegCreateKeyEx(hkey,
-                subkey,
-                0,
-                null,
-                (int)registryOptions /* specifies if the key is volatile */,
-                GetRegistryKeyAccess(writable) | (int)regView,
-                ref secAttrs,
-                out result,
-                out disposition);
-
-            if (ret == 0 && !result.IsInvalid)
-            {
-                RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
-
-                if (subkey.Length == 0)
-                    key.keyName = keyName;
-                else
-                    key.keyName = keyName + "\\" + subkey;
-                return key;
-            }
-            else if (ret != 0) // syscall failed, ret is an error code.
-                Win32Error(ret, keyName + "\\" + subkey);  // Access denied?
-
-            Debug.Fail("Unexpected code path in RegistryKey::CreateSubKey");
-            return null;
+            return CreateSubKeyInternalCore(subkey, writable, registryOptions);
         }
 
-        /**
-         * Deletes the specified subkey. Will throw an exception if the subkey has
-         * subkeys. To delete a tree of subkeys use, DeleteSubKeyTree.
-         *
-         * @param subkey SubKey to delete.
-         *
-         * @exception InvalidOperationException thrown if the subkey has child subkeys.
-         */
-        public void DeleteSubKey(String subkey)
+        /// <summary>
+        /// Deletes the specified subkey. Will throw an exception if the subkey has
+        /// subkeys. To delete a tree of subkeys use, DeleteSubKeyTree.
+        /// </summary>
+        /// <param name="subkey">SubKey to delete.</param>
+        /// <exception cref="InvalidOperationException">Thrown if the subkey has child subkeys.</exception>
+        public void DeleteSubKey(string subkey)
         {
             DeleteSubKey(subkey, true);
         }
 
-        [System.Security.SecuritySafeCritical]  
-        public void DeleteSubKey(String subkey, bool throwOnMissingSubKey)
+        [SecuritySafeCritical]  
+        public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
         {
             ValidateKeyName(subkey);
             EnsureWriteable();
@@ -367,38 +196,26 @@ namespace Microsoft.Win32
                     }
                 }
 
-                int ret = Interop.mincore.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
-
-                if (ret != 0)
-                {
-                    if (ret == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND)
-                    {
-                        if (throwOnMissingSubKey)
-                            ThrowHelper.ThrowArgumentException(SR.Arg_RegSubKeyAbsent);
-                    }
-                    else
-                        Win32Error(ret, null);
-                }
+                DeleteSubKeyCore(subkey, throwOnMissingSubKey);
             }
-            else
-            { // there is no key which also means there is no subkey
+            else // there is no key which also means there is no subkey
+            {
                 if (throwOnMissingSubKey)
+                {
                     ThrowHelper.ThrowArgumentException(SR.Arg_RegSubKeyAbsent);
+                }
             }
         }
 
-        /**
-         * Recursively deletes a subkey and any child subkeys.
-         *
-         * @param subkey SubKey to delete.
-         */
-        public void DeleteSubKeyTree(String subkey)
+        /// <summary>Recursively deletes a subkey and any child subkeys.</summary>
+        /// <param name="subkey">SubKey to delete.</param>
+        public void DeleteSubKeyTree(string subkey)
         {
-            DeleteSubKeyTree(subkey, true /*throwOnMissingSubKey*/);
+            DeleteSubKeyTree(subkey, throwOnMissingSubKey: true);
         }
 
-        [System.Security.SecuritySafeCritical]  
-        public void DeleteSubKeyTree(String subkey, Boolean throwOnMissingSubKey)
+        [SecuritySafeCritical]  
+        public void DeleteSubKeyTree(string subkey, bool throwOnMissingSubKey)
         {
             ValidateKeyName(subkey);
 
@@ -420,7 +237,7 @@ namespace Microsoft.Win32
                 {
                     if (key.InternalSubKeyCount() > 0)
                     {
-                        String[] keys = key.InternalGetSubKeyNames();
+                        string[] keys = key.InternalGetSubKeyNames();
 
                         for (int i = 0; i < keys.Length; i++)
                         {
@@ -429,9 +246,7 @@ namespace Microsoft.Win32
                     }
                 }
 
-                int ret = Interop.mincore.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
-
-                if (ret != 0) Win32Error(ret, null);
+                DeleteSubKeyTreeCore(subkey);
             }
             else if (throwOnMissingSubKey)
             {
@@ -439,9 +254,11 @@ namespace Microsoft.Win32
             }
         }
 
-        // An internal version which does no security checks or argument checking.  Skipping the 
-        // security checks should give us a slight perf gain on large trees. 
-        [System.Security.SecurityCritical]  
+        /// <summary>
+        /// An internal version which does no security checks or argument checking.  Skipping the 
+        /// security checks should give us a slight perf gain on large trees. 
+        /// </summary>
+        [SecurityCritical]  
         private void DeleteSubKeyTreeInternal(string subkey)
         {
             RegistryKey key = InternalOpenSubKey(subkey, true);
@@ -451,8 +268,7 @@ namespace Microsoft.Win32
                 {
                     if (key.InternalSubKeyCount() > 0)
                     {
-                        String[] keys = key.InternalGetSubKeyNames();
-
+                        string[] keys = key.InternalGetSubKeyNames();
                         for (int i = 0; i < keys.Length; i++)
                         {
                             key.DeleteSubKeyTreeInternal(keys[i]);
@@ -460,9 +276,7 @@ namespace Microsoft.Win32
                     }
                 }
 
-                int ret = Interop.mincore.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
-
-                if (ret != 0) Win32Error(ret, null);
+                DeleteSubKeyTreeCore(subkey);
             }
             else
             {
@@ -470,860 +284,280 @@ namespace Microsoft.Win32
             }
         }
 
-        /**
-         * Deletes the specified value from this key.
-         *
-         * @param name Name of value to delete.
-         */
-        public void DeleteValue(String name)
+        /// <summary>Deletes the specified value from this key.</summary>
+        /// <param name="name">Name of value to delete.</param>
+        public void DeleteValue(string name)
         {
             DeleteValue(name, true);
         }
 
-        [System.Security.SecuritySafeCritical]  
-        public void DeleteValue(String name, bool throwOnMissingValue)
+        [SecuritySafeCritical]  
+        public void DeleteValue(string name, bool throwOnMissingValue)
         {
             EnsureWriteable();
-            int errorCode = Interop.mincore.RegDeleteValue(hkey, name);
-
-            //
-            // 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 == Interop.mincore.Errors.ERROR_FILE_NOT_FOUND || errorCode == Interop.mincore.Errors.ERROR_FILENAME_EXCED_RANGE)
-            {
-                if (throwOnMissingValue)
-                {
-                    ThrowHelper.ThrowArgumentException(SR.Arg_RegSubKeyValueAbsent);
-                }
-                else
-                {
-                    // Otherwise, reset and just return giving no indication to the user.
-                    // (For compatibility)
-                    errorCode = 0;
-                }
-            }
-            // 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);
+            DeleteValueCore(name, throwOnMissingValue);
         }
 
-        /**
-         * 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.
-         *
-         * @param hKey HKEY_* to open.
-         *
-         * @return the RegistryKey requested.
-         */
-        [System.Security.SecurityCritical]  
-        internal static RegistryKey GetBaseKey(IntPtr hKey)
-        {
-            return GetBaseKey(hKey, RegistryView.Default);
-        }
-
-        [System.Security.SecurityCritical]  
-        internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view)
-        {
-            int index = ((int)hKey) & 0x0FFFFFFF;
-            Debug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!");
-            Debug.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, false, isPerf, view);
-            key.keyName = hkeyNames[index];
-            return key;
-        }
-
-
-        [System.Security.SecuritySafeCritical]  
+        [SecuritySafeCritical]  
         public static RegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view)
         {
             ValidateKeyView(view);
-            return GetBaseKey((IntPtr)((int)hKey), view);
+            return OpenBaseKeyCore(hKey, view);
         }
 
-        /**
-         * Retrieves a new RegistryKey that represents the requested key on a foreign
-         * machine.  Valid values for hKey are members of the RegistryHive enum, or
-         * Win32 integers such as:
-         *
-         * HKEY_CLASSES_ROOT,
-         * HKEY_CURRENT_USER,
-         * HKEY_LOCAL_MACHINE,
-         * HKEY_USERS,
-         * HKEY_PERFORMANCE_DATA,
-         * HKEY_CURRENT_CONFIG.
-         *
-         * @param hKey HKEY_* to open.
-         * @param machineName the machine to connect to
-         *
-         * @return the RegistryKey requested.
-         */
-        public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName)
+        /// <summary>Retrieves a new RegistryKey that represents the requested key on a foreign machine.</summary>
+        /// <param name="hKey">hKey HKEY_* to open.</param>
+        /// <param name="machineName">Name the machine to connect to.</param>
+        /// <returns>The RegistryKey requested.</returns>
+        public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, string machineName)
         {
             return OpenRemoteBaseKey(hKey, machineName, RegistryView.Default);
         }
 
-        [System.Security.SecuritySafeCritical]  
-        public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName, RegistryView view)
+        [SecuritySafeCritical]  
+        public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, string machineName, RegistryView view)
         {
             if (machineName == null)
-                throw new ArgumentNullException(nameof(machineName));
-            int index = (int)hKey & 0x0FFFFFFF;
-            if (index < 0 || index >= hkeyNames.Length || ((int)hKey & 0xFFFFFFF0) != 0x80000000)
             {
-                throw new ArgumentException(SR.Arg_RegKeyOutOfRange);
+                throw new ArgumentNullException(nameof(machineName));
             }
             ValidateKeyView(view);
 
-            // connect to the specified remote registry
-            SafeRegistryHandle foreignHKey = null;
-            int ret = Interop.mincore.RegConnectRegistry(machineName, new SafeRegistryHandle(new IntPtr((int)hKey), false), out foreignHKey);
-
-            if (ret == Interop.mincore.Errors.ERROR_DLL_INIT_FAILED)
-                // return value indicates an error occurred
-                throw new ArgumentException(SR.Arg_DllInitFailure);
-
-            if (ret != 0)
-                Win32ErrorStatic(ret, null);
-
-            if (foreignHKey.IsInvalid)
-                // return value indicates an error occurred
-                throw new ArgumentException(SR.Format(SR.Arg_RegKeyNoRemoteConnect, machineName));
-
-            RegistryKey key = new RegistryKey(foreignHKey, true, false, true, ((IntPtr)hKey) == HKEY_PERFORMANCE_DATA, view);
-            key.keyName = hkeyNames[index];
-            return key;
+            return OpenRemoteBaseKeyCore(hKey, machineName, view);
         }
 
-        /**
-         * 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.
-         */
-        [System.Security.SecuritySafeCritical]
+        /// <summary>
+        /// Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with
+        /// read-only access.
+        /// </summary>
+        /// <returns>the Subkey requested, or <b>null</b> if the operation failed.</returns>
+        [SecuritySafeCritical]
         public RegistryKey OpenSubKey(string name, bool writable)
         {
-            return InternalOpenSubKey(name, GetRegistryKeyAccess(writable));
+            return InternalOpenSubKey(name, writable);
         }
 
-        public RegistryKey OpenSubKey(String name, RegistryRights rights)
-        {
-            return InternalOpenSubKey(name, (int)rights);
-        }
-
-        [System.Security.SecurityCritical]  
-        private RegistryKey InternalOpenSubKey(String name, int rights)
+        public RegistryKey OpenSubKey(string name, RegistryRights rights)
         {
             ValidateKeyName(name);
-
             EnsureNotDisposed();
             name = FixupName(name); // Fixup multiple slashes to a single slash
-
-            SafeRegistryHandle result = null;
-            int ret = Interop.mincore.RegOpenKeyEx(hkey, name, 0, (rights | (int)regView), out result);
-            if (ret == 0 && !result.IsInvalid)
-            {
-                RegistryKey key = new RegistryKey(result, IsWritable(rights), false, remoteKey, false, regView);
-                key.keyName = keyName + "\\" + name;
-                return key;
-            }
-
-            // Return null if we didn't find the key.
-            if (ret == Interop.mincore.Errors.ERROR_ACCESS_DENIED || ret == Interop.mincore.Errors.ERROR_BAD_IMPERSONATION_LEVEL)
-            {
-                // We need to throw SecurityException here for compatibility reason,
-                // although UnauthorizedAccessException will make more sense.
-                ThrowHelper.ThrowSecurityException(SR.Security_RegistryPermission);
-            }
-
-            return null;
+            return InternalOpenSubKeyCore(name, rights, throwOnPermissionFailure: true);
         }
 
-        // 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
-        [System.Security.SecurityCritical]  
-        internal RegistryKey InternalOpenSubKey(String name, bool writable)
+        /// <summary>
+        /// 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
+        /// </summary>
+        [SecurityCritical]  
+        private RegistryKey InternalOpenSubKey(string name, bool writable)
         {
             ValidateKeyName(name);
             EnsureNotDisposed();
-
-            SafeRegistryHandle result = null;
-            int ret = Interop.mincore.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;
+            return InternalOpenSubKeyCore(name, GetRegistryKeyRights(writable), throwOnPermissionFailure: false);
         }
 
-        /**
-         * 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.
-         */
-        [System.Security.SecurityCritical]
-        public RegistryKey OpenSubKey(String name)
+        /// <summary>Returns a subkey with read only permissions.</summary>
+        /// <param name="name">Name or path of subkey to open.</param>
+        /// <returns>The Subkey requested, or <b>null</b> if the operation failed.</returns>
+        [SecurityCritical]
+        public RegistryKey OpenSubKey(string name)
         {
             return OpenSubKey(name, false);
         }
 
-        /**
-         * Retrieves the count of subkeys.
-         *
-         * @return a count of subkeys.
-         */
+        /// <summary>Retrieves the count of subkeys.</summary>
+        /// <returns>A count of subkeys.</returns>
         public int SubKeyCount
         {
-            [System.Security.SecuritySafeCritical]  
-            get
-            {
-                return InternalSubKeyCount();
-            }
+            [SecuritySafeCritical]  
+            get { return InternalSubKeyCount(); }
         }
 
         public RegistryView View
         {
-            [System.Security.SecuritySafeCritical]
+            [SecuritySafeCritical]
             get
             {
                 EnsureNotDisposed();
-                return regView;
+                return _regView;
             }
         }
 
         public SafeRegistryHandle Handle
         {
-            [System.Security.SecurityCritical]
+            [SecurityCritical]
             get
             {
                 EnsureNotDisposed();
-                int ret = Interop.mincore.Errors.ERROR_INVALID_HANDLE;
-                if (IsSystemKey())
-                {
-                    IntPtr baseKey = (IntPtr)0;
-                    switch (keyName)
-                    {
-                        case "HKEY_CLASSES_ROOT":
-                            baseKey = HKEY_CLASSES_ROOT;
-                            break;
-                        case "HKEY_CURRENT_USER":
-                            baseKey = HKEY_CURRENT_USER;
-                            break;
-                        case "HKEY_LOCAL_MACHINE":
-                            baseKey = HKEY_LOCAL_MACHINE;
-                            break;
-                        case "HKEY_USERS":
-                            baseKey = HKEY_USERS;
-                            break;
-                        case "HKEY_PERFORMANCE_DATA":
-                            baseKey = HKEY_PERFORMANCE_DATA;
-                            break;
-                        case "HKEY_CURRENT_CONFIG":
-                            baseKey = HKEY_CURRENT_CONFIG;
-                            break;
-                        default:
-                            Win32Error(ret, null);
-                            break;
-                    }
-                    // open the base key so that RegistryKey.Handle will return a valid handle
-                    SafeRegistryHandle result;
-                    ret = Interop.mincore.RegOpenKeyEx(baseKey,
-                        null,
-                        0,
-                        GetRegistryKeyAccess(IsWritable()) | (int)regView,
-                        out result);
-
-                    if (ret == 0 && !result.IsInvalid)
-                    {
-                        return result;
-                    }
-                    else
-                    {
-                        Win32Error(ret, null);
-                    }
-                }
-                else
-                {
-                    return hkey;
-                }
-                throw new IOException(Interop.mincore.GetMessage(ret), ret);
+                return IsSystemKey() ? SystemKeyHandle : _hkey;
             }
         }
 
-        [System.Security.SecurityCritical]
+        [SecurityCritical]
         public static RegistryKey FromHandle(SafeRegistryHandle handle)
         {
             return FromHandle(handle, RegistryView.Default);
         }
 
-        [System.Security.SecurityCritical]
+        [SecurityCritical]
         public static RegistryKey FromHandle(SafeRegistryHandle handle, RegistryView view)
         {
             if (handle == null) throw new ArgumentNullException(nameof(handle));
             ValidateKeyView(view);
 
-            return new RegistryKey(handle, true /* isWritable */, view);
+            return new RegistryKey(handle, writable: true, view: view);
         }
 
-        [System.Security.SecurityCritical]  
-        internal int InternalSubKeyCount()
+        [SecurityCritical]  
+        private int InternalSubKeyCount()
         {
             EnsureNotDisposed();
-
-            int subkeys = 0;
-            int junk = 0;
-            int ret = Interop.mincore.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;
+            return InternalSubKeyCountCore();
         }
 
-        /**
-         * Retrieves an array of strings containing all the subkey names.
-         *
-         * @return all subkey names.
-         */
-        [System.Security.SecurityCritical] 
-        public String[] GetSubKeyNames()
+        /// <summary>Retrieves an array of strings containing all the subkey names.</summary>
+        /// <returns>All subkey names.</returns>
+        [SecurityCritical] 
+        public string[] GetSubKeyNames()
         {
             return InternalGetSubKeyNames();
         }
 
-        [System.Security.SecurityCritical]  
-        internal unsafe String[] InternalGetSubKeyNames()
+        [SecurityCritical]  
+        private string[] InternalGetSubKeyNames()
         {
             EnsureNotDisposed();
             int subkeys = InternalSubKeyCount();
-
-            if (subkeys > 0)
-            {
-                String[] names = new String[subkeys];
-                char[] name = new char[MaxKeyLength + 1];
-
-                int namelen;
-
-                fixed (char* namePtr = &name[0])
-                {
-                    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 initialized.
-                        int ret = Interop.mincore.RegEnumKeyEx(hkey,
-                            i,
-                            namePtr,
-                            ref namelen,
-                            null,
-                            null,
-                            null,
-                            null);
-                        if (ret != 0)
-                            Win32Error(ret, null);
-                        names[i] = new String(namePtr);
-                    }
-                }
-
-                return names;
-            }
-
-            return Array.Empty<String>();
+            return subkeys > 0 ?
+                InternalGetSubKeyNamesCore(subkeys) :
+                Array.Empty<string>();
         }
 
-        /**
-         * Retrieves the count of values.
-         *
-         * @return a count of values.
-         */
+        /// <summary>Retrieves the count of values.</summary>
+        /// <returns>A count of values.</returns>
         public int ValueCount
         {
-            [System.Security.SecuritySafeCritical]  
-            get
-            {
-                return InternalValueCount();
-            }
+            [SecuritySafeCritical]  
+            get { return InternalValueCount(); }
         }
 
-        [System.Security.SecurityCritical]  
-        internal int InternalValueCount()
+        [SecurityCritical]  
+        private int InternalValueCount()
         {
             EnsureNotDisposed();
-            int values = 0;
-            int junk = 0;
-            int ret = Interop.mincore.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;
+            return InternalValueCountCore();
         }
 
-        /**
-         * Retrieves an array of strings containing all the value names.
-         *
-         * @return all value names.
-         */
-        [System.Security.SecuritySafeCritical]  
-        public unsafe String[] GetValueNames()
+        /// <summary>Retrieves an array of strings containing all the value names.</summary>
+        /// <returns>All value names.</returns>
+        [SecuritySafeCritical]  
+        public string[] GetValueNames()
         {
             EnsureNotDisposed();
 
             int values = InternalValueCount();
-
-            if (values > 0)
-            {
-                String[] names = new String[values];
-                char[] name = new char[MaxValueLength + 1];
-                int namelen;
-
-                fixed (char* namePtr = &name[0])
-                {
-                    for (int i = 0; i < values; i++)
-                    {
-                        namelen = name.Length;
-
-                        int ret = Interop.mincore.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 == Interop.mincore.Errors.ERROR_MORE_DATA))
-                                Win32Error(ret, null);
-                        }
-
-                        names[i] = new String(namePtr);
-                    }
-                }
-
-                return names;
-            }
-
-            return Array.Empty<String>();
+            return values > 0 ?
+                GetValueNamesCore(values) :
+                Array.Empty<string>();
         }
 
-        /**
-         * 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.
-         */
-        [System.Security.SecuritySafeCritical]  
-        public Object GetValue(String name)
+        /// <summary>Retrieves the specified value. <b>null</b> is returned if the value doesn't exist</summary>
+        /// <remarks>
+        /// 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.
+        /// </remarks>
+        /// <param name="name">Name of value to retrieve.</param>
+        /// <returns>The data associated with the value.</returns>
+        [SecuritySafeCritical]  
+        public object GetValue(string name)
         {
             return InternalGetValue(name, null, false, true);
         }
 
-        /**
-         * Retrieves the specified value. <i>defaultValue</i> 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.
-         * The default values for RegistryKeys are OS-dependent.  NT doesn't
-         * have them by default, but they can exist and be of any type.  On
-         * Win95, the default value is always an empty key of type REG_SZ.
-         * Win98 supports default values of any type, but defaults to REG_SZ.
-         *
-         * @param name Name of value to retrieve.
-         * @param defaultValue Value to return if <i>name</i> doesn't exist.
-         *
-         * @return the data associated with the value.
-         */
-        [System.Security.SecuritySafeCritical]
-        public Object GetValue(String name, Object defaultValue)
+        /// <summary>Retrieves the specified value. <i>defaultValue</i> is returned if the value doesn't exist.</summary>
+        /// <remarks>
+        /// 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.
+        /// The default values for RegistryKeys are OS-dependent.  NT doesn't
+        /// have them by default, but they can exist and be of any type.  On
+        /// Win95, the default value is always an empty key of type REG_SZ.
+        /// Win98 supports default values of any type, but defaults to REG_SZ.
+        /// </remarks>
+        /// <param name="name">Name of value to retrieve.</param>
+        /// <param name="defaultValue">Value to return if <i>name</i> doesn't exist.</param>
+        /// <returns>The data associated with the value.</returns>
+        [SecuritySafeCritical]
+        public object GetValue(string name, object defaultValue)
         {
             return InternalGetValue(name, defaultValue, false, true);
         }
 
-        [System.Security.SecuritySafeCritical]
-        public Object GetValue(String name, Object defaultValue, RegistryValueOptions options)
+        [SecuritySafeCritical]
+        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);
+            return InternalGetValue(name, defaultValue, doNotExpand, checkSecurity: true);
         }
 
-        [System.Security.SecurityCritical]  
-        internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity)
+        [SecurityCritical]  
+        private 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;
-            int type = 0;
-            int datasize = 0;
-
-            int ret = Interop.mincore.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 (Interop.mincore.Errors.ERROR_MORE_DATA == (r = Interop.mincore.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 != Interop.mincore.Errors.ERROR_MORE_DATA)
-                        return data;
-                }
-            }
-
-            if (datasize < 0)
-            {
-                // unexpected code path
-                Debug.Fail("[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
-                datasize = 0;
-            }
-
-
-            switch (type)
-            {
-                case Interop.mincore.RegistryValues.REG_NONE:
-                case Interop.mincore.RegistryValues.REG_DWORD_BIG_ENDIAN:
-                case Interop.mincore.RegistryValues.REG_BINARY:
-                    {
-                        byte[] blob = new byte[datasize];
-                        ret = Interop.mincore.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
-                        data = blob;
-                    }
-                    break;
-                case Interop.mincore.RegistryValues.REG_QWORD:
-                    {    // also REG_QWORD_LITTLE_ENDIAN
-                        if (datasize > 8)
-                        {
-                            // prevent an AV in the edge case that datasize is larger than sizeof(long)
-                            goto case Interop.mincore.RegistryValues.REG_BINARY;
-                        }
-                        long blob = 0;
-                        Debug.Assert(datasize == 8, "datasize==8");
-                        // Here, datasize must be 8 when calling this
-                        ret = Interop.mincore.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
-
-                        data = blob;
-                    }
-                    break;
-                case Interop.mincore.RegistryValues.REG_DWORD:
-                    {    // also REG_DWORD_LITTLE_ENDIAN
-                        if (datasize > 4)
-                        {
-                            // prevent an AV in the edge case that datasize is larger than sizeof(int)
-                            goto case Interop.mincore.RegistryValues.REG_QWORD;
-                        }
-                        int blob = 0;
-                        Debug.Assert(datasize == 4, "datasize==4");
-                        // Here, datasize must be four when calling this
-                        ret = Interop.mincore.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
-
-                        data = blob;
-                    }
-                    break;
-
-                case Interop.mincore.RegistryValues.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];
-
-                        ret = Interop.mincore.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 Interop.mincore.RegistryValues.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];
-
-                        ret = Interop.mincore.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 Interop.mincore.RegistryValues.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(SR.Arg_RegGetOverflowBug, e);
-                            }
-                        }
-                        char[] blob = new char[datasize / 2];
-
-                        ret = Interop.mincore.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)
-                        {
-                            Array.Resize(ref blob, blob.Length + 1);
-                        }
-
-                        var 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)
-                            {
-                                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;
-                        }
-
-                        data = strings.ToArray();
-                    }
-                    break;
-                case Interop.mincore.RegistryValues.REG_LINK:
-                default:
-                    break;
-            }
-
-            return data;
+            
+            // Name can be null!  It's the most common use of RegQueryValueEx
+            return InternalGetValueCore(name, defaultValue, doNotExpand);
         }
 
-        [System.Security.SecuritySafeCritical]  
+        [SecuritySafeCritical]  
         public RegistryValueKind GetValueKind(string name)
         {
             EnsureNotDisposed();
-
-            int type = 0;
-            int datasize = 0;
-            int ret = Interop.mincore.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
-            if (ret != 0)
-                Win32Error(ret, null);
-            if (type == Interop.mincore.RegistryValues.REG_NONE)
-                return RegistryValueKind.None;
-            else if (!Enum.IsDefined(typeof(RegistryValueKind), type))
-                return RegistryValueKind.Unknown;
-            else
-                return (RegistryValueKind)type;
-        }
-
-        /**
-         * Retrieves the current state of the dirty property.
-         *
-         * A key is marked as dirty if any operation has occurred that modifies the
-         * contents of the key.
-         *
-         * @return <b>true</b> if the key has been modified.
-         */
-        private bool IsDirty()
-        {
-            return (this.state & STATE_DIRTY) != 0;
+            return GetValueKindCore(name);
         }
 
-        private bool IsSystemKey()
+        public string Name
         {
-            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;
-        }
-
-        public String Name
-        {
-            [System.Security.SecuritySafeCritical]  
+            [SecuritySafeCritical]  
             get
             {
                 EnsureNotDisposed();
-                return keyName;
+                return _keyName;
             }
         }
 
-        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)
+        /// <summary>Sets the specified value.</summary>
+        /// <param name="name">Name of value to store data in.</param>
+        /// <param name="value">Data to store.</param>
+        public void SetValue(string name, object value)
         {
             SetValue(name, value, RegistryValueKind.Unknown);
         }
 
-        [System.Security.SecuritySafeCritical] 
-        public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind)
+        [SecuritySafeCritical]
+        public void SetValue(string name, object value, RegistryValueKind valueKind)
         {
             if (value == null)
-                ThrowHelper.ThrowArgumentNullException("value");
+            {
+                ThrowHelper.ThrowArgumentNullException(nameof(value));
+            }
 
             if (name != null && name.Length > MaxValueLength)
             {
-                throw new ArgumentException(SR.Arg_RegValStrLenBug);
+                throw new ArgumentException(SR.Arg_RegValStrLenBug, nameof(name));
             }
 
             if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind))
+            {
                 throw new ArgumentException(SR.Arg_RegBadKeyKind, nameof(valueKind));
+            }
 
             EnsureWriteable();
 
@@ -1334,242 +568,67 @@ namespace Microsoft.Win32
                 valueKind = CalculateValueKind(value);
             }
 
-            int ret = 0;
-            try
-            {
-                switch (valueKind)
-                {
-                    case RegistryValueKind.ExpandString:
-                    case RegistryValueKind.String:
-                        {
-                            String data = value.ToString();
-                            ret = Interop.mincore.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());
-
-                            // First determine the size of the array
-                            //
-                            // Format is null terminator between strings and final null terminator at the end.
-                            //    e.g. str1\0str2\0str3\0\0 
-                            //
-                            int sizeInChars = 1; // no matter what, we have the final null terminator.
-                            for (int i = 0; i < dataStrings.Length; i++)
-                            {
-                                if (dataStrings[i] == null)
-                                {
-                                    ThrowHelper.ThrowArgumentException(SR.Arg_RegSetStrArrNull);
-                                }
-                                sizeInChars = checked(sizeInChars + (dataStrings[i].Length + 1));
-                            }
-                            int sizeInBytes = checked(sizeInChars * sizeof(char));
-
-                            // Write out the strings...
-                            //
-                            char[] dataChars = new char[sizeInChars];
-                            int destinationIndex = 0;
-                            for (int i = 0; i < dataStrings.Length; i++)
-                            {
-                                int length = dataStrings[i].Length;
-                                dataStrings[i].CopyTo(0, dataChars, destinationIndex, length);
-                                destinationIndex += (length + 1); // +1 for null terminator, which is already zero-initialized in new array.
-                            }
-
-                            ret = Interop.mincore.RegSetValueEx(hkey,
-                                name,
-                                0,
-                                RegistryValueKind.MultiString,
-                                dataChars,
-                                sizeInBytes);
-
-                            break;
-                        }
-
-                    case RegistryValueKind.None:
-                    case RegistryValueKind.Binary:
-                        byte[] dataBytes = (byte[])value;
-                        ret = Interop.mincore.RegSetValueEx(hkey,
-                            name,
-                            0,
-                            (valueKind == RegistryValueKind.None ? Interop.mincore.RegistryValues.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 = Interop.mincore.RegSetValueEx(hkey,
-                                name,
-                                0,
-                                RegistryValueKind.DWord,
-                                ref data,
-                                4);
-                            break;
-                        }
-
-                    case RegistryValueKind.QWord:
-                        {
-                            long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture);
-
-                            ret = Interop.mincore.RegSetValueEx(hkey,
-                                name,
-                                0,
-                                RegistryValueKind.QWord,
-                                ref data,
-                                8);
-                            break;
-                        }
-                }
-            }
-            catch (OverflowException)
-            {
-                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
-            }
-            catch (InvalidOperationException)
-            {
-                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
-            }
-            catch (FormatException)
-            {
-                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
-            }
-            catch (InvalidCastException)
-            {
-                ThrowHelper.ThrowArgumentException(SR.Arg_RegSetMismatchedKind);
-            }
-
-            if (ret == 0)
-            {
-                SetDirty();
-            }
-            else
-                Win32Error(ret, null);
+            SetValueCore(name, value, valueKind);
         }
 
-        private RegistryValueKind CalculateValueKind(Object value)
+        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)
+            if (value is int)
+            {
                 return RegistryValueKind.DWord;
+            }
             else if (value is Array)
             {
                 if (value is byte[])
+                {
                     return RegistryValueKind.Binary;
-                else if (value is String[])
+                }
+                else if (value is string[])
+                {
                     return RegistryValueKind.MultiString;
+                }
                 else
+                {
                     throw new ArgumentException(SR.Format(SR.Arg_RegSetBadArrType, value.GetType().Name));
+                }
             }
             else
-                return RegistryValueKind.String;
-        }
-
-        /**
-         * Retrieves a string representation of this key.
-         *
-         * @return a string representing the key.
-         */
-        [System.Security.SecuritySafeCritical]  
-        public override String ToString()
-        {
-            EnsureNotDisposed();
-            return keyName;
-        }
-
-        /**
-         * After calling GetLastWin32Error(), it clears the last error field,
-         * so you must save the HResult and pass it to this method.  This method
-         * will determine the appropriate exception to throw dependent on your
-         * error, and depending on the error, insert a string into the message
-         * gotten from the ResourceManager.
-         */
-        [System.Security.SecuritySafeCritical]  
-        internal void Win32Error(int errorCode, String str)
-        {
-            switch (errorCode)
             {
-                case Interop.mincore.Errors.ERROR_ACCESS_DENIED:
-                    if (str != null)
-                        throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str));
-                    else
-                        throw new UnauthorizedAccessException();
-
-                case Interop.mincore.Errors.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 Interop.mincore.Errors.ERROR_FILE_NOT_FOUND:
-                    throw new IOException(SR.Arg_RegKeyNotFound, errorCode);
-
-                default:
-                    throw new IOException(Interop.mincore.GetMessage(errorCode), errorCode);
+                return RegistryValueKind.String;
             }
         }
 
-        [System.Security.SecuritySafeCritical]
-        internal static void Win32ErrorStatic(int errorCode, String str)
+        /// <summary>Retrieves a string representation of this key.</summary>
+        /// <returns>A string representing the key.</returns>
+        [SecuritySafeCritical]  
+        public override string ToString()
         {
-            switch (errorCode)
-            {
-                case Interop.mincore.Errors.ERROR_ACCESS_DENIED:
-                    if (str != null)
-                        throw new UnauthorizedAccessException(SR.Format(SR.UnauthorizedAccess_RegistryKeyGeneric_Key, str));
-                    else
-                        throw new UnauthorizedAccessException();
-
-                default:
-                    throw new IOException(Interop.mincore.GetMessage(errorCode), errorCode);
-            }
+            EnsureNotDisposed();
+            return _keyName;
         }
 
-        internal static String FixupName(String name)
+        private static string FixupName(string name)
         {
             Debug.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);
@@ -1583,16 +642,11 @@ namespace Microsoft.Win32
                 if (path[i] == '\\')
                 {
                     i++;
-                    while (i < length)
+                    while (i < length && path[i] == '\\')
                     {
-                        if (path[i] == '\\')
-                        {
-                            path[i] = markerChar;
-                            i++;
-                            fixup = true;
-                        }
-                        else
-                            break;
+                        path[i] = markerChar;
+                        i++;
+                        fixup = true;
                     }
                 }
                 i++;
@@ -1617,25 +671,16 @@ namespace Microsoft.Win32
             }
         }
 
-        [System.Security.SecurityCritical]  
-        private bool ContainsRegistryValue(string name)
-        {
-            int type = 0;
-            int datasize = 0;
-            int retval = Interop.mincore.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
-            return retval == 0;
-        }
-
-        [System.Security.SecurityCritical]  
+        [SecurityCritical]  
         private void EnsureNotDisposed()
         {
-            if (hkey == null)
+            if (_hkey == null)
             {
-                ThrowHelper.ThrowObjectDisposedException(keyName, SR.ObjectDisposed_RegKeyClosed);
+                ThrowHelper.ThrowObjectDisposedException(_keyName, SR.ObjectDisposed_RegKeyClosed);
             }
         }
 
-        [System.Security.SecurityCritical]  
+        [SecurityCritical]  
         private void EnsureWriteable()
         {
             EnsureNotDisposed();
@@ -1645,36 +690,12 @@ namespace Microsoft.Win32
             }
         }
 
-        static int GetRegistryKeyAccess(bool isWritable)
-        {
-            int winAccess;
-            if (!isWritable)
-            {
-                winAccess = Interop.mincore.RegistryOperations.KEY_READ;
-            }
-            else
-            {
-                winAccess = Interop.mincore.RegistryOperations.KEY_READ | Interop.mincore.RegistryOperations.KEY_WRITE;
-            }
-
-            return winAccess;
-        }
-
-        static bool IsWritable(int rights)
-        {
-            return (rights & (Interop.mincore.RegistryOperations.KEY_SET_VALUE |
-                              Interop.mincore.RegistryOperations.KEY_CREATE_SUB_KEY |
-                              (int)RegistryRights.Delete |
-                              (int)RegistryRights.TakeOwnership |
-                              (int)RegistryRights.ChangePermissions)) != 0;
-        }
-
-        static private void ValidateKeyName(string name)
+        private static void ValidateKeyName(string name)
         {
             Contract.Ensures(name != null);
             if (name == null)
             {
-                ThrowHelper.ThrowArgumentNullException("name");
+                ThrowHelper.ThrowArgumentNullException(nameof(name));
             }
 
             int nextSlash = name.IndexOf("\\", StringComparison.OrdinalIgnoreCase);
@@ -1682,21 +703,24 @@ namespace Microsoft.Win32
             while (nextSlash != -1)
             {
                 if ((nextSlash - current) > MaxKeyLength)
-                    ThrowHelper.ThrowArgumentException(SR.Arg_RegKeyStrLenBug);
-
+                {
+                    ThrowHelper.ThrowArgumentException(SR.Arg_RegKeyStrLenBug, nameof(name));
+                }
                 current = nextSlash + 1;
                 nextSlash = name.IndexOf("\\", current, StringComparison.OrdinalIgnoreCase);
             }
 
             if ((name.Length - current) > MaxKeyLength)
-                ThrowHelper.ThrowArgumentException(SR.Arg_RegKeyStrLenBug);
+            {
+                ThrowHelper.ThrowArgumentException(SR.Arg_RegKeyStrLenBug, nameof(name));
+            }
         }
 
         static private void ValidateKeyOptions(RegistryOptions options)
         {
             if (options < RegistryOptions.None || options > RegistryOptions.Volatile)
             {
-                ThrowHelper.ThrowArgumentException(SR.Argument_InvalidRegistryOptionsCheck, "options");
+                ThrowHelper.ThrowArgumentException(SR.Argument_InvalidRegistryOptionsCheck, nameof(options));
             }
         }
 
@@ -1704,15 +728,41 @@ namespace Microsoft.Win32
         {
             if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64)
             {
-                ThrowHelper.ThrowArgumentException(SR.Argument_InvalidRegistryViewCheck, "view");
+                ThrowHelper.ThrowArgumentException(SR.Argument_InvalidRegistryViewCheck, nameof(view));
             }
         }
-    }
 
-    [Flags]
-    public enum RegistryValueOptions
-    {
-        None = 0,
-        DoNotExpandEnvironmentNames = 1
+        private static RegistryRights GetRegistryKeyRights(bool isWritable)
+        {
+            return isWritable ?
+                RegistryRights.ReadKey | RegistryRights.WriteKey :
+                RegistryRights.ReadKey;
+        }
+
+        /// <summary>Retrieves the current state of the dirty property.</summary>
+        /// <remarks>A key is marked as dirty if any operation has occurred that modifies the contents of the key.</remarks>
+        /// <returns><b>true</b> if the key has been modified.</returns>
+        private bool IsDirty() => (_state & StateFlags.Dirty) != 0;
+
+        private bool IsSystemKey() => (_state & StateFlags.SystemKey) != 0;
+
+        private bool IsWritable() => (_state & StateFlags.WriteAccess) != 0;
+
+        private bool IsPerfDataKey() => (_state & StateFlags.PerfData) != 0;
+
+        private void SetDirty() => _state |= StateFlags.Dirty;
+
+        [Flags]
+        private enum StateFlags
+        {
+            /// <summary>Dirty indicates that we have munged data that should be potentially written to disk.</summary>
+            Dirty = 0x0001,
+            /// <summary>SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened" or "closed".</summary>
+            SystemKey = 0x0002,
+            /// <summary>Access</summary>
+            WriteAccess = 0x0004,
+            /// <summary>Indicates if this key is for HKEY_PERFORMANCE_DATA</summary>
+            PerfData = 0x0008
+        }
     }
 }
index 5877643..2971a0b 100644 (file)
@@ -16,4 +16,3 @@ namespace Microsoft.Win32
         None = unchecked((int)0xFFFFFFFF), //  mistakenly overrode this value.  
     }   // Now instead of using Interop.mincore.RegistryValues.REG_NONE we use "-1".
 }
-
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryValueOptions.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryValueOptions.cs
new file mode 100644 (file)
index 0000000..622a7dd
--- /dev/null
@@ -0,0 +1,15 @@
+// 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.
+
+using System;
+
+namespace Microsoft.Win32
+{
+    [Flags]
+    public enum RegistryValueOptions
+    {
+        None = 0,
+        DoNotExpandEnvironmentNames = 1
+    }
+}
index f7872b8..53eadba 100644 (file)
@@ -8,7 +8,7 @@ namespace Microsoft.Win32
 {
     public enum RegistryView
     {
-        Default = 0,                           // 0x0000 operate on the default registry view
+        Default = 0, // 0x0000 operate on the default registry view
         Registry64 = Interop.mincore.RegistryView.KEY_WOW64_64KEY, // 0x0100 operate on the 64-bit registry view
         Registry32 = Interop.mincore.RegistryView.KEY_WOW64_32KEY, // 0x0200 operate on the 32-bit registry view
     };
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.FileSystem.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.FileSystem.cs
new file mode 100644 (file)
index 0000000..04001a7
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Microsoft.Win32.SafeHandles
+{
+    public sealed partial class SafeRegistryHandle : SafeHandle
+    {
+        [SecurityCritical]
+        protected override bool ReleaseHandle()
+        {
+            // TODO: implement this if necessary
+            return true;
+        }
+    }
+}
diff --git a/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.Windows.cs b/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/SafeHandles/SafeRegistryHandle.Windows.cs
new file mode 100644 (file)
index 0000000..5ef4e6f
--- /dev/null
@@ -0,0 +1,18 @@
+// 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.
+
+using System.Runtime.InteropServices;
+using System.Security;
+
+namespace Microsoft.Win32.SafeHandles
+{
+    public sealed partial class SafeRegistryHandle : SafeHandle
+    {
+        [SecurityCritical]
+        protected override bool ReleaseHandle()
+        {
+            return (Interop.mincore.RegCloseKey(handle) == Interop.mincore.Errors.ERROR_SUCCESS);
+        }
+    }
+}
index eb64b1e..7e5479c 100644 (file)
@@ -4,16 +4,17 @@
 
 using System;
 using System.Runtime.InteropServices;
+using System.Security;
 
 namespace Microsoft.Win32.SafeHandles
 {
-    [System.Security.SecurityCritical]
-    public sealed class SafeRegistryHandle : SafeHandle
+    [SecurityCritical]
+    public sealed partial class SafeRegistryHandle : SafeHandle
     {
-        [System.Security.SecurityCritical]
+        [SecurityCritical]
         internal SafeRegistryHandle() : base(IntPtr.Zero, true) { }
 
-        [System.Security.SecurityCritical]
+        [SecurityCritical]
         public SafeRegistryHandle(IntPtr preexistingHandle, bool ownsHandle) : base(IntPtr.Zero, ownsHandle)
         {
             SetHandle(preexistingHandle);
@@ -21,16 +22,8 @@ namespace Microsoft.Win32.SafeHandles
 
         public override bool IsInvalid
         {
-            [System.Security.SecurityCritical]
-            get
-            { return handle == new IntPtr(0) || handle == new IntPtr(-1); }
-        }
-
-        [System.Security.SecurityCritical]
-        override protected bool ReleaseHandle()
-        {
-            return (Interop.mincore.RegCloseKey(handle) == Interop.mincore.Errors.ERROR_SUCCESS);
+            [SecurityCritical]
+            get { return handle == new IntPtr(0) || handle == new IntPtr(-1); }
         }
     }
 }
-
index 2157c34..b9e63b6 100644 (file)
@@ -14,26 +14,32 @@ namespace Microsoft.Win32
         {
             throw new ArgumentException(msg);
         }
+
         internal static void ThrowArgumentException(string msg, string argument)
         {
             throw new ArgumentException(msg, argument);
         }
+
         internal static void ThrowArgumentNullException(string argument)
         {
             throw new ArgumentNullException(argument);
         }
+
         internal static void ThrowInvalidOperationException(string msg)
         {
             throw new InvalidOperationException(msg);
         }
+
         internal static void ThrowSecurityException(string msg)
         {
             throw new System.Security.SecurityException(msg);
         }
+
         internal static void ThrowUnauthorizedAccessException(string msg)
         {
             throw new UnauthorizedAccessException(msg);
         }
+
         internal static void ThrowObjectDisposedException(string objectName, string msg)
         {
             throw new ObjectDisposedException(objectName, msg);