summaryrefslogtreecommitdiff
path: root/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscorlib/src/Microsoft/Win32/RegistryKey.cs')
-rw-r--r--src/mscorlib/src/Microsoft/Win32/RegistryKey.cs2166
1 files changed, 2166 insertions, 0 deletions
diff --git a/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
new file mode 100644
index 0000000000..dcf31dc60c
--- /dev/null
+++ b/src/mscorlib/src/Microsoft/Win32/RegistryKey.cs
@@ -0,0 +1,2166 @@
+// 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.
+
+
+/*
+ Note on transaction support:
+ Eventually we will want to add support for NT's transactions to our
+ RegistryKey API's (possibly Whidbey M3?). When we do this, here's
+ the list of API's we need to make transaction-aware:
+
+ RegCreateKeyEx
+ RegDeleteKey
+ RegDeleteValue
+ RegEnumKeyEx
+ RegEnumValue
+ RegOpenKeyEx
+ RegQueryInfoKey
+ RegQueryValueEx
+ RegSetValueEx
+
+ We can ignore RegConnectRegistry (remote registry access doesn't yet have
+ transaction support) and RegFlushKey. RegCloseKey doesn't require any
+ additional work. .
+ */
+
+/*
+ Note on ACL support:
+ The key thing to note about ACL's is you set them on a kernel object like a
+ 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 workaround. 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 {
+
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Security;
+#if FEATURE_MACL
+ using System.Security.AccessControl;
+#endif
+ using System.Security.Permissions;
+ using System.Text;
+ using System.Threading;
+ using System.IO;
+ using System.Runtime.Remoting;
+ using System.Runtime.InteropServices;
+ using Microsoft.Win32.SafeHandles;
+ using System.Runtime.Versioning;
+ using System.Globalization;
+ using System.Diagnostics.Contracts;
+ using System.Diagnostics.CodeAnalysis;
+
+ /**
+ * Registry hive values. Useful only for GetRemoteBaseKey
+ */
+ [Serializable]
+[System.Runtime.InteropServices.ComVisible(true)]
+ 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),
+#if !FEATURE_CORECLR
+ DynData = unchecked((int)0x80000006),
+#endif
+ }
+
+ /**
+ * 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)
+ */
+#if FEATURE_REMOTING
+ [ComVisible(true)]
+ public sealed class RegistryKey : MarshalByRefObject, IDisposable
+#else
+ [ComVisible(true)]
+ public sealed class RegistryKey : IDisposable
+#endif
+ {
+
+ // 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));
+#if !FEATURE_CORECLR
+ internal static readonly IntPtr HKEY_DYN_DATA = new IntPtr(unchecked((int)0x80000006));
+#endif
+
+ // 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",
+#if !FEATURE_CORECLR
+ "HKEY_DYN_DATA"
+#endif
+ };
+
+ // MSDN defines the following limits for registry key names & values:
+ // Key Name: 255 characters
+ // Value name: 16,383 Unicode characters
+ // Value: either 1 MB or current available memory, depending on registry format.
+ private const int MaxKeyLength = 255;
+ private const int MaxValueLength = 16383;
+
+ [System.Security.SecurityCritical] // auto-generated
+ private volatile SafeRegistryHandle hkey = null;
+ private volatile int state = 0;
+ private volatile String keyName;
+ private volatile bool remoteKey = false;
+ private volatile RegistryKeyPermissionCheck checkMode;
+ private volatile RegistryView regView = RegistryView.Default;
+
+ /**
+ * RegistryInternalCheck values. Useful only for CheckPermission
+ */
+ private enum RegistryInternalCheck {
+ CheckSubKeyWritePermission = 0,
+ CheckSubKeyReadPermission = 1,
+ CheckSubKeyCreatePermission = 2,
+ CheckSubTreeReadPermission = 3,
+ CheckSubTreeWritePermission = 4,
+ CheckSubTreeReadWritePermission = 5,
+ CheckValueWritePermission = 6,
+ CheckValueCreatePermission = 7,
+ CheckValueReadPermission = 8,
+ CheckKeyReadPermission = 9,
+ CheckSubTreePermission = 10,
+ CheckOpenSubKeyWithWritablePermission = 11,
+ CheckOpenSubKeyPermission = 12
+ };
+
+
+ /**
+ * 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) {
+ }
+
+
+ /**
+ * 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] // auto-generated
+ 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;
+ }
+ if (writable) {
+ this.state |= STATE_WRITEACCESS;
+ }
+ if (isPerfData)
+ this.state |= STATE_PERF_DATA;
+ ValidateKeyView(view);
+ }
+
+ /**
+ * Closes this key, flushes it to disk if the contents have been modified.
+ */
+ public void Close() {
+ Dispose(true);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ private void Dispose(bool disposing) {
+ if (hkey != null) {
+
+ if (!IsSystemKey()) {
+ try {
+ hkey.Dispose();
+ }
+ catch (IOException){
+ // we don't really care if the handle is invalid at this point
+ }
+ finally
+ {
+ hkey = null;
+ }
+ }
+ else if (disposing && IsPerfDataKey()) {
+ // System keys should never be closed. However, we want to call RegCloseKey
+ // on HKEY_PERFORMANCE_DATA when called from PerformanceCounter.CloseSharedResources
+ // (i.e. when disposing is true) so that we release the PERFLIB cache and cause it
+ // to be refreshed (by re-reading the registry) when accessed subsequently.
+ // This is the only way we can see the just installed perf counter.
+ // NOTE: since HKEY_PERFORMANCE_DATA is process wide, there is inherent race condition in closing
+ // the key asynchronously. While Vista is smart enough to rebuild the PERFLIB resources
+ // in this situation the down level OSes are not. We have a small window between
+ // the dispose below and usage elsewhere (other threads). This is By Design.
+ // This is less of an issue when OS > NT5 (i.e Vista & higher), we can close the perfkey
+ // (to release & refresh PERFLIB resources) and the OS will rebuild PERFLIB as necessary.
+ SafeRegistryHandle.RegCloseKey(RegistryKey.HKEY_PERFORMANCE_DATA);
+ }
+ }
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void Flush() {
+ if (hkey != null) {
+ if (IsDirty()) {
+ Win32Native.RegFlushKey(hkey);
+ }
+ }
+ }
+
+#if FEATURE_CORECLR
+ void IDisposable.Dispose()
+#else
+ public void Dispose()
+#endif
+ {
+ 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.
+ */
+ [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
+ public RegistryKey CreateSubKey(String subkey) {
+ return CreateSubKey(subkey, checkMode);
+ }
+
+ [ComVisible(false)]
+ public RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck)
+ {
+ return CreateSubKeyInternal(subkey, permissionCheck, null, RegistryOptions.None);
+ }
+
+ [ComVisible(false)]
+ public RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
+ {
+ return CreateSubKeyInternal(subkey, permissionCheck, null, options);
+ }
+
+ [ComVisible(false)]
+ public RegistryKey CreateSubKey(String subkey, bool writable)
+ {
+ return CreateSubKeyInternal(subkey, writable ? RegistryKeyPermissionCheck.ReadWriteSubTree : RegistryKeyPermissionCheck.ReadSubTree, null, RegistryOptions.None);
+ }
+
+ [ComVisible(false)]
+ public RegistryKey CreateSubKey(String subkey, bool writable, RegistryOptions options)
+ {
+ return CreateSubKeyInternal(subkey, writable ? RegistryKeyPermissionCheck.ReadWriteSubTree : RegistryKeyPermissionCheck.ReadSubTree, null, options);
+ }
+
+
+#if FEATURE_MACL
+ [ComVisible(false)]
+ public unsafe RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity)
+ {
+ return CreateSubKeyInternal(subkey, permissionCheck, registrySecurity, RegistryOptions.None);
+ }
+
+ [ComVisible(false)]
+ public unsafe RegistryKey CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions registryOptions, RegistrySecurity registrySecurity)
+ {
+ return CreateSubKeyInternal(subkey, permissionCheck, registrySecurity, registryOptions);
+ }
+#endif
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ private unsafe RegistryKey CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, object registrySecurityObj, RegistryOptions registryOptions)
+ {
+ ValidateKeyOptions(registryOptions);
+ ValidateKeyName(subkey);
+ ValidateKeyMode(permissionCheck);
+ EnsureWriteable();
+ subkey = FixupName(subkey); // Fixup multiple slashes to a single slash
+
+ // only keys opened under read mode is not writable
+ if (!remoteKey) {
+ RegistryKey key = InternalOpenSubKey(subkey, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree));
+ if (key != null) { // Key already exits
+ CheckPermission(RegistryInternalCheck.CheckSubKeyWritePermission, subkey, false, RegistryKeyPermissionCheck.Default);
+ CheckPermission(RegistryInternalCheck.CheckSubTreePermission, subkey, false, permissionCheck);
+ key.checkMode = permissionCheck;
+ return key;
+ }
+ }
+
+ CheckPermission(RegistryInternalCheck.CheckSubKeyCreatePermission, subkey, false, RegistryKeyPermissionCheck.Default);
+
+ Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
+#if FEATURE_MACL
+ RegistrySecurity registrySecurity = (RegistrySecurity)registrySecurityObj;
+ // For ACL's, get the security descriptor from the RegistrySecurity.
+ if (registrySecurity != null) {
+ secAttrs = new Win32Native.SECURITY_ATTRIBUTES();
+ secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);
+
+ byte[] sd = registrySecurity.GetSecurityDescriptorBinaryForm();
+ // We allocate memory on the stack to improve the speed.
+ // So this part of code can't be refactored into a method.
+ byte* pSecDescriptor = stackalloc byte[sd.Length];
+ Buffer.Memcpy(pSecDescriptor, 0, sd, 0, sd.Length);
+ secAttrs.pSecurityDescriptor = pSecDescriptor;
+ }
+#endif
+ int disposition = 0;
+
+ // By default, the new key will be writable.
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegCreateKeyEx(hkey,
+ subkey,
+ 0,
+ null,
+ (int)registryOptions /* specifies if the key is volatile */,
+ GetRegistryKeyAccess(permissionCheck != RegistryKeyPermissionCheck.ReadSubTree) | (int)regView,
+ secAttrs,
+ out result,
+ out disposition);
+
+ if (ret == 0 && !result.IsInvalid) {
+ RegistryKey key = new RegistryKey(result, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree), false, remoteKey, false, regView);
+ CheckPermission(RegistryInternalCheck.CheckSubTreePermission, subkey, false, permissionCheck);
+ key.checkMode = permissionCheck;
+
+ 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?
+
+ BCLDebug.Assert(false, "Unexpected code path in RegistryKey::CreateSubKey");
+ return null;
+ }
+
+ /**
+ * 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) {
+ DeleteSubKey(subkey, true);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void DeleteSubKey(String subkey, bool throwOnMissingSubKey) {
+ ValidateKeyName(subkey);
+ EnsureWriteable();
+ subkey = FixupName(subkey); // Fixup multiple slashes to a single slash
+ CheckPermission(RegistryInternalCheck.CheckSubKeyWritePermission, subkey, false, RegistryKeyPermissionCheck.Default);
+
+ // Open the key we are deleting and check for children. Be sure to
+ // explicitly call close to avoid keeping an extra HKEY open.
+ //
+ RegistryKey key = InternalOpenSubKey(subkey,false);
+ if (key != null) {
+ try {
+ if (key.InternalSubKeyCount() > 0) {
+ ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_RegRemoveSubKey);
+ }
+ }
+ finally {
+ key.Close();
+ }
+
+ int ret;
+
+ try {
+ ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
+ }
+ catch (EntryPointNotFoundException) {
+ ret = Win32Native.RegDeleteKey(hkey, subkey);
+ }
+
+ if (ret!=0) {
+ if (ret == Win32Native.ERROR_FILE_NOT_FOUND) {
+ if (throwOnMissingSubKey)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent);
+ }
+ else
+ Win32Error(ret, null);
+ }
+ }
+ else { // there is no key which also means there is no subkey
+ if (throwOnMissingSubKey)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent);
+ }
+ }
+
+ /**
+ * Recursively deletes a subkey and any child subkeys.
+ *
+ * @param subkey SubKey to delete.
+ */
+ public void DeleteSubKeyTree(String subkey) {
+ DeleteSubKeyTree(subkey, true /*throwOnMissingSubKey*/);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public void DeleteSubKeyTree(String subkey, Boolean throwOnMissingSubKey) {
+ ValidateKeyName(subkey);
+
+ // Security concern: Deleting a hive's "" subkey would delete all
+ // of that hive's contents. Don't allow "".
+ if (subkey.Length==0 && IsSystemKey()) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyDelHive);
+ }
+
+ EnsureWriteable();
+
+ subkey = FixupName(subkey); // Fixup multiple slashes to a single slash
+ CheckPermission(RegistryInternalCheck.CheckSubTreeWritePermission, subkey, false, RegistryKeyPermissionCheck.Default);
+
+ RegistryKey key = InternalOpenSubKey(subkey, true);
+ if (key != null) {
+ try {
+ if (key.InternalSubKeyCount() > 0) {
+ String[] keys = key.InternalGetSubKeyNames();
+
+ for (int i=0; i<keys.Length; i++) {
+ key.DeleteSubKeyTreeInternal(keys[i]);
+ }
+ }
+ }
+ finally {
+ key.Close();
+ }
+
+ int ret;
+ try {
+ ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
+ }
+ catch (EntryPointNotFoundException) {
+ ret = Win32Native.RegDeleteKey(hkey, subkey);
+ }
+
+ if (ret!=0) Win32Error(ret, null);
+ }
+ else if(throwOnMissingSubKey) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent);
+ }
+ }
+
+ // 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] // auto-generated
+ private void DeleteSubKeyTreeInternal(string subkey) {
+ RegistryKey key = InternalOpenSubKey(subkey, true);
+ if (key != null) {
+ try {
+ if (key.InternalSubKeyCount() > 0) {
+ String[] keys = key.InternalGetSubKeyNames();
+
+ for (int i=0; i<keys.Length; i++) {
+ key.DeleteSubKeyTreeInternal(keys[i]);
+ }
+ }
+ }
+ finally {
+ key.Close();
+ }
+
+ int ret;
+ try {
+ ret = Win32Native.RegDeleteKeyEx(hkey, subkey, (int)regView, 0);
+ }
+ catch (EntryPointNotFoundException) {
+ ret = Win32Native.RegDeleteKey(hkey, subkey);
+ }
+ if (ret!=0) Win32Error(ret, null);
+ }
+ else {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyAbsent);
+ }
+ }
+
+ /**
+ * Deletes the specified value from this key.
+ *
+ * @param name Name of value to delete.
+ */
+ public void DeleteValue(String name) {
+ DeleteValue(name, true);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void DeleteValue(String name, bool throwOnMissingValue) {
+ EnsureWriteable();
+ CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default);
+ int errorCode = Win32Native.RegDeleteValue(hkey, name);
+
+ //
+ // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE
+ // This still means the name doesn't exist. We need to be consistent with previous OS.
+ //
+ if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE) {
+ if (throwOnMissingValue) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSubKeyValueAbsent);
+ }
+ // Otherwise, just return giving no indication to the user.
+ // (For compatibility)
+ }
+ // We really should throw an exception here if errorCode was bad,
+ // but we can't for compatibility reasons.
+ BCLDebug.Correctness(errorCode == 0, "RegDeleteValue failed. Here's your error code: "+errorCode);
+ }
+
+ /**
+ * Retrieves a new RegistryKey that represents the requested key. Valid
+ * values are:
+ *
+ * HKEY_CLASSES_ROOT,
+ * HKEY_CURRENT_USER,
+ * HKEY_LOCAL_MACHINE,
+ * HKEY_USERS,
+ * HKEY_PERFORMANCE_DATA,
+ * HKEY_CURRENT_CONFIG,
+ * HKEY_DYN_DATA.
+ *
+ * @param hKey HKEY_* to open.
+ *
+ * @return the RegistryKey requested.
+ */
+ [System.Security.SecurityCritical] // auto-generated
+ internal static RegistryKey GetBaseKey(IntPtr hKey) {
+ return GetBaseKey(hKey, RegistryView.Default);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal static RegistryKey GetBaseKey(IntPtr hKey, RegistryView view) {
+
+ int index = ((int)hKey) & 0x0FFFFFFF;
+ BCLDebug.Assert(index >= 0 && index < hkeyNames.Length, "index is out of range!");
+ BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!");
+
+ bool isPerf = hKey == HKEY_PERFORMANCE_DATA;
+ // only mark the SafeHandle as ownsHandle if the key is HKEY_PERFORMANCE_DATA.
+ SafeRegistryHandle srh = new SafeRegistryHandle(hKey, isPerf);
+
+ RegistryKey key = new RegistryKey(srh, true, true,false, isPerf, view);
+ key.checkMode = RegistryKeyPermissionCheck.Default;
+ key.keyName = hkeyNames[index];
+ return key;
+ }
+
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public static RegistryKey OpenBaseKey(RegistryHive hKey, RegistryView view) {
+ ValidateKeyView(view);
+ CheckUnmanagedCodePermission();
+ return GetBaseKey((IntPtr)((int)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,
+ * HKEY_DYN_DATA.
+ *
+ * @param hKey HKEY_* to open.
+ * @param machineName the machine to connect to
+ *
+ * @return the RegistryKey requested.
+ */
+ public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName) {
+ return OpenRemoteBaseKey(hKey, machineName, RegistryView.Default);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public static RegistryKey OpenRemoteBaseKey(RegistryHive hKey, String machineName, RegistryView view) {
+ if (machineName==null)
+ throw new ArgumentNullException("machineName");
+ int index = (int)hKey & 0x0FFFFFFF;
+ if (index < 0 || index >= hkeyNames.Length || ((int)hKey & 0xFFFFFFF0) != 0x80000000) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_RegKeyOutOfRange"));
+ }
+ ValidateKeyView(view);
+
+ CheckUnmanagedCodePermission();
+ // connect to the specified remote registry
+ SafeRegistryHandle foreignHKey = null;
+ int ret = Win32Native.RegConnectRegistry(machineName, new SafeRegistryHandle(new IntPtr((int)hKey), false), out foreignHKey);
+
+ if (ret == Win32Native.ERROR_DLL_INIT_FAILED)
+ // return value indicates an error occurred
+ throw new ArgumentException(Environment.GetResourceString("Arg_DllInitFailure"));
+
+ if (ret != 0)
+ Win32ErrorStatic(ret, null);
+
+ if (foreignHKey.IsInvalid)
+ // return value indicates an error occurred
+ throw new ArgumentException(Environment.GetResourceString("Arg_RegKeyNoRemoteConnect", machineName));
+
+ RegistryKey key = new RegistryKey(foreignHKey, true, false, true, ((IntPtr) hKey) == HKEY_PERFORMANCE_DATA, view);
+ key.checkMode = RegistryKeyPermissionCheck.Default;
+ key.keyName = hkeyNames[index];
+ return key;
+ }
+
+ /**
+ * Retrieves a subkey. If readonly is <b>true</b>, then the subkey is opened with
+ * read-only access.
+ *
+ * @param name Name or path of subkey to open.
+ * @param readonly Set to <b>true</b> if you only need readonly access.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+ public RegistryKey OpenSubKey(string name, bool writable ) {
+ ValidateKeyName(name);
+ EnsureNotDisposed();
+ name = FixupName(name); // Fixup multiple slashes to a single slash
+
+ CheckPermission(RegistryInternalCheck.CheckOpenSubKeyWithWritablePermission, name, writable, RegistryKeyPermissionCheck.Default);
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegOpenKeyEx(hkey,
+ name,
+ 0,
+ GetRegistryKeyAccess(writable) | (int)regView,
+ out result);
+
+ if (ret == 0 && !result.IsInvalid) {
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.checkMode = GetSubKeyPermissonCheck(writable);
+ key.keyName = keyName + "\\" + name;
+ return key;
+ }
+
+ // Return null if we didn't find the key.
+ if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) {
+ // We need to throw SecurityException here for compatibility reasons,
+ // although UnauthorizedAccessException will make more sense.
+ ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission);
+ }
+
+ return null;
+ }
+
+#if FEATURE_MACL
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public RegistryKey OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck) {
+ ValidateKeyMode(permissionCheck);
+ return InternalOpenSubKey(name, permissionCheck, GetRegistryKeyAccess(permissionCheck));
+ }
+
+ [System.Security.SecuritySafeCritical]
+ [ComVisible(false)]
+ public RegistryKey OpenSubKey(String name, RegistryRights rights)
+ {
+ return InternalOpenSubKey(name, this.checkMode, (int)rights);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public RegistryKey OpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights) {
+ return InternalOpenSubKey(name, permissionCheck, (int)rights);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private RegistryKey InternalOpenSubKey(String name, RegistryKeyPermissionCheck permissionCheck, int rights) {
+ ValidateKeyName(name);
+ ValidateKeyMode(permissionCheck);
+
+ ValidateKeyRights(rights);
+
+ EnsureNotDisposed();
+ name = FixupName(name); // Fixup multiple slashes to a single slash
+
+ CheckPermission(RegistryInternalCheck.CheckOpenSubKeyPermission, name, false, permissionCheck);
+ CheckPermission(RegistryInternalCheck.CheckSubTreePermission, name, false, permissionCheck);
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegOpenKeyEx(hkey, name, 0, (rights | (int)regView), out result);
+ if (ret == 0 && !result.IsInvalid) {
+ RegistryKey key = new RegistryKey(result, (permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree), false, remoteKey, false, regView);
+ key.keyName = keyName + "\\" + name;
+ key.checkMode = permissionCheck;
+ return key;
+ }
+
+ // Return null if we didn't find the key.
+ if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL) {
+ // We need to throw SecurityException here for compatiblity reason,
+ // although UnauthorizedAccessException will make more sense.
+ ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission);
+ }
+
+ return null;
+ }
+#endif
+
+ // 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] // auto-generated
+ internal RegistryKey InternalOpenSubKey(String name, bool writable) {
+ ValidateKeyName(name);
+ EnsureNotDisposed();
+
+ SafeRegistryHandle result = null;
+ int ret = Win32Native.RegOpenKeyEx(hkey,
+ name,
+ 0,
+ GetRegistryKeyAccess(writable) | (int)regView,
+ out result);
+
+ if (ret == 0 && !result.IsInvalid) {
+ RegistryKey key = new RegistryKey(result, writable, false, remoteKey, false, regView);
+ key.keyName = keyName + "\\" + name;
+ return key;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a subkey with read only permissions.
+ *
+ * @param name Name or path of subkey to open.
+ *
+ * @return the Subkey requested, or <b>null</b> if the operation failed.
+ */
+#if FEATURE_CORECLR
+ [System.Security.SecurityCritical]
+#endif
+ public RegistryKey OpenSubKey(String name) {
+ return OpenSubKey(name, false);
+ }
+
+ /**
+ * Retrieves the count of subkeys.
+ *
+ * @return a count of subkeys.
+ */
+ public int SubKeyCount {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
+ return InternalSubKeyCount();
+ }
+ }
+
+ [ComVisible(false)]
+ public RegistryView View {
+ [System.Security.SecuritySafeCritical]
+ get {
+ EnsureNotDisposed();
+ return regView;
+ }
+ }
+
+#if !FEATURE_CORECLR
+ [ComVisible(false)]
+ public SafeRegistryHandle Handle {
+ [System.Security.SecurityCritical]
+ [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ get {
+ EnsureNotDisposed();
+ int ret = Win32Native.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;
+ case "HKEY_DYN_DATA":
+ baseKey = HKEY_DYN_DATA;
+ break;
+ default:
+ Win32Error(ret, null);
+ break;
+ }
+ // open the base key so that RegistryKey.Handle will return a valid handle
+ SafeRegistryHandle result;
+ ret = Win32Native.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(Win32Native.GetMessage(ret), ret);
+ }
+ }
+
+ [System.Security.SecurityCritical]
+ [ComVisible(false)]
+ [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static RegistryKey FromHandle(SafeRegistryHandle handle) {
+ return FromHandle(handle, RegistryView.Default);
+ }
+
+ [System.Security.SecurityCritical]
+ [ComVisible(false)]
+ [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public static RegistryKey FromHandle(SafeRegistryHandle handle, RegistryView view) {
+ if (handle == null) throw new ArgumentNullException("handle");
+ ValidateKeyView(view);
+
+ return new RegistryKey(handle, true /* isWritable */, view);
+ }
+#endif
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal int InternalSubKeyCount() {
+ EnsureNotDisposed();
+
+ int subkeys = 0;
+ int junk = 0;
+ int ret = Win32Native.RegQueryInfoKey(hkey,
+ null,
+ null,
+ IntPtr.Zero,
+ ref subkeys, // subkeys
+ null,
+ null,
+ ref junk, // values
+ null,
+ null,
+ null,
+ null);
+
+ if (ret != 0)
+ Win32Error(ret, null);
+ return subkeys;
+ }
+
+ /**
+ * Retrieves an array of strings containing all the subkey names.
+ *
+ * @return all subkey names.
+ */
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+ public String[] GetSubKeyNames() {
+ CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
+ return InternalGetSubKeyNames();
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal unsafe String[] InternalGetSubKeyNames() {
+ EnsureNotDisposed();
+ int subkeys = InternalSubKeyCount();
+ String[] names = new String[subkeys]; // Returns 0-length array if empty.
+
+ if (subkeys > 0) {
+ char[] name = new char[MaxKeyLength + 1];
+
+ int namelen;
+
+ 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 initialised.
+ int ret = Win32Native.RegEnumKeyEx(hkey,
+ i,
+ namePtr,
+ ref namelen,
+ null,
+ null,
+ null,
+ null);
+ if (ret != 0)
+ Win32Error(ret, null);
+ names[i] = new String(namePtr);
+ }
+ }
+ }
+
+ return names;
+ }
+
+ /**
+ * Retrieves the count of values.
+ *
+ * @return a count of values.
+ */
+ public int ValueCount {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
+ return InternalValueCount();
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal int InternalValueCount() {
+ EnsureNotDisposed();
+ int values = 0;
+ int junk = 0;
+ int ret = Win32Native.RegQueryInfoKey(hkey,
+ null,
+ null,
+ IntPtr.Zero,
+ ref junk, // subkeys
+ null,
+ null,
+ ref values, // values
+ null,
+ null,
+ null,
+ null);
+ if (ret != 0)
+ Win32Error(ret, null);
+ return values;
+ }
+
+ /**
+ * Retrieves an array of strings containing all the value names.
+ *
+ * @return all value names.
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public unsafe String[] GetValueNames() {
+ CheckPermission(RegistryInternalCheck.CheckKeyReadPermission, null, false, RegistryKeyPermissionCheck.Default);
+ EnsureNotDisposed();
+
+ int values = InternalValueCount();
+ String[] names = new String[values];
+
+ if (values > 0) {
+ 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 = Win32Native.RegEnumValue(hkey,
+ i,
+ namePtr,
+ ref namelen,
+ IntPtr.Zero,
+ null,
+ null,
+ null);
+
+ if (ret != 0) {
+ // ignore ERROR_MORE_DATA if we're querying HKEY_PERFORMANCE_DATA
+ if (!(IsPerfDataKey() && ret == Win32Native.ERROR_MORE_DATA))
+ Win32Error(ret, null);
+ }
+
+ names[i] = new String(namePtr);
+ }
+ }
+ }
+
+ return names;
+ }
+
+ /**
+ * Retrieves the specified value. <b>null</b> is returned if the value
+ * doesn't exist.
+ *
+ * Note that <var>name</var> can be null or "", at which point the
+ * unnamed or default value of this Registry key is returned, if any.
+ *
+ * @param name Name of value to retrieve.
+ *
+ * @return the data associated with the value.
+ */
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public Object GetValue(String name) {
+ CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
+ 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.
+ */
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+ public Object GetValue(String name, Object defaultValue) {
+ CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
+ return InternalGetValue(name, defaultValue, false, true);
+ }
+
+ #if FEATURE_CORECLR
+ [System.Security.SecurityCritical] // auto-generated
+ #else
+ [System.Security.SecuritySafeCritical]
+ #endif
+ [ComVisible(false)]
+ public Object GetValue(String name, Object defaultValue, RegistryValueOptions options) {
+ if( options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)options), "options");
+ }
+ bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames);
+ CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
+ return InternalGetValue(name, defaultValue, doNotExpand, true);
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ internal Object InternalGetValue(String name, Object defaultValue, bool doNotExpand, bool checkSecurity) {
+ if (checkSecurity) {
+ // Name can be null! It's the most common use of RegQueryValueEx
+ EnsureNotDisposed();
+ }
+
+ Object data = defaultValue;
+ int type = 0;
+ int datasize = 0;
+
+ int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
+
+ if (ret != 0) {
+ if (IsPerfDataKey()) {
+ int size = 65000;
+ int sizeInput = size;
+
+ int r;
+ byte[] blob = new byte[size];
+ while (Win32Native.ERROR_MORE_DATA == (r = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref sizeInput))) {
+ if (size == Int32.MaxValue) {
+ // ERROR_MORE_DATA was returned however we cannot increase the buffer size beyond Int32.MaxValue
+ Win32Error(r, name);
+ }
+ else if (size > (Int32.MaxValue / 2)) {
+ // at this point in the loop "size * 2" would cause an overflow
+ size = Int32.MaxValue;
+ }
+ else {
+ size *= 2;
+ }
+ sizeInput = size;
+ blob = new byte[size];
+ }
+ if (r != 0)
+ Win32Error(r, name);
+ return blob;
+ }
+ else {
+ // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).
+ // Some OS's returned ERROR_MORE_DATA even in success cases, so we
+ // want to continue on through the function.
+ if (ret != Win32Native.ERROR_MORE_DATA)
+ return data;
+ }
+ }
+
+ if (datasize < 0) {
+ // unexpected code path
+ BCLDebug.Assert(false, "[InternalGetValue] RegQueryValue returned ERROR_SUCCESS but gave a negative datasize");
+ datasize = 0;
+ }
+
+
+ switch (type) {
+ case Win32Native.REG_NONE:
+ case Win32Native.REG_DWORD_BIG_ENDIAN:
+ case Win32Native.REG_BINARY: {
+ byte[] blob = new byte[datasize];
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
+ data = blob;
+ }
+ break;
+ case Win32Native.REG_QWORD:
+ { // also REG_QWORD_LITTLE_ENDIAN
+ if (datasize > 8) {
+ // prevent an AV in the edge case that datasize is larger than sizeof(long)
+ goto case Win32Native.REG_BINARY;
+ }
+ long blob = 0;
+ BCLDebug.Assert(datasize==8, "datasize==8");
+ // Here, datasize must be 8 when calling this
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
+
+ data = blob;
+ }
+ break;
+ case Win32Native.REG_DWORD:
+ { // also REG_DWORD_LITTLE_ENDIAN
+ if (datasize > 4) {
+ // prevent an AV in the edge case that datasize is larger than sizeof(int)
+ goto case Win32Native.REG_QWORD;
+ }
+ int blob = 0;
+ BCLDebug.Assert(datasize==4, "datasize==4");
+ // Here, datasize must be four when calling this
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, ref blob, ref datasize);
+
+ data = blob;
+ }
+ break;
+
+ case Win32Native.REG_SZ:
+ {
+ if (datasize % 2 == 1) {
+ // handle the case where the registry contains an odd-byte length (corrupt data?)
+ try {
+ datasize = checked(datasize + 1);
+ }
+ catch (OverflowException e) {
+ throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e);
+ }
+ }
+ char[] blob = new char[datasize/2];
+
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
+ if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) {
+ data = new String(blob, 0, blob.Length - 1);
+ }
+ else {
+ // in the very unlikely case the data is missing null termination,
+ // pass in the whole char[] to prevent truncating a character
+ data = new String(blob);
+ }
+ }
+ break;
+
+ case Win32Native.REG_EXPAND_SZ:
+ {
+ if (datasize % 2 == 1) {
+ // handle the case where the registry contains an odd-byte length (corrupt data?)
+ try {
+ datasize = checked(datasize + 1);
+ }
+ catch (OverflowException e) {
+ throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e);
+ }
+ }
+ char[] blob = new char[datasize/2];
+
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
+
+ if (blob.Length > 0 && blob[blob.Length - 1] == (char)0) {
+ data = new String(blob, 0, blob.Length - 1);
+ }
+ else {
+ // in the very unlikely case the data is missing null termination,
+ // pass in the whole char[] to prevent truncating a character
+ data = new String(blob);
+ }
+
+ if (!doNotExpand)
+ data = Environment.ExpandEnvironmentVariables((String)data);
+ }
+ break;
+ case Win32Native.REG_MULTI_SZ:
+ {
+ if (datasize % 2 == 1) {
+ // handle the case where the registry contains an odd-byte length (corrupt data?)
+ try {
+ datasize = checked(datasize + 1);
+ }
+ catch (OverflowException e) {
+ throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e);
+ }
+ }
+ char[] blob = new char[datasize/2];
+
+ ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, blob, ref datasize);
+
+ // make sure the string is null terminated before processing the data
+ if (blob.Length > 0 && blob[blob.Length - 1] != (char)0) {
+ try {
+ char[] newBlob = new char[checked(blob.Length + 1)];
+ for (int i = 0; i < blob.Length; i++) {
+ newBlob[i] = blob[i];
+ }
+ newBlob[newBlob.Length - 1] = (char)0;
+ blob = newBlob;
+ }
+ catch (OverflowException e) {
+ throw new IOException(Environment.GetResourceString("Arg_RegGetOverflowBug"), e);
+ }
+ blob[blob.Length - 1] = (char)0;
+ }
+
+
+ IList<String> strings = new List<String>();
+ int cur = 0;
+ int len = blob.Length;
+
+ while (ret == 0 && cur < len) {
+ int nextNull = cur;
+ while (nextNull < len && blob[nextNull] != (char)0) {
+ nextNull++;
+ }
+
+ if (nextNull < len) {
+ BCLDebug.Assert(blob[nextNull] == (char)0, "blob[nextNull] should be 0");
+ if (nextNull-cur > 0) {
+ strings.Add(new String(blob, cur, nextNull-cur));
+ }
+ else {
+ // we found an empty string. But if we're at the end of the data,
+ // it's just the extra null terminator.
+ if (nextNull != len-1)
+ strings.Add(String.Empty);
+ }
+ }
+ else {
+ strings.Add(new String(blob, cur, len-cur));
+ }
+ cur = nextNull+1;
+ }
+
+ data = new String[strings.Count];
+ strings.CopyTo((String[])data, 0);
+ }
+ break;
+ case Win32Native.REG_LINK:
+ default:
+ break;
+ }
+
+ return data;
+ }
+
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ [ComVisible(false)]
+ public RegistryValueKind GetValueKind(string name) {
+ CheckPermission(RegistryInternalCheck.CheckValueReadPermission, name, false, RegistryKeyPermissionCheck.Default);
+ EnsureNotDisposed();
+
+ int type = 0;
+ int datasize = 0;
+ int ret = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
+ if (ret != 0)
+ Win32Error(ret, null);
+ if (type == Win32Native.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;
+ }
+
+ private bool IsSystemKey() {
+ return (this.state & STATE_SYSTEMKEY) != 0;
+ }
+
+ private bool IsWritable() {
+ return (this.state & STATE_WRITEACCESS) != 0;
+ }
+
+ private bool IsPerfDataKey() {
+ return (this.state & STATE_PERF_DATA) != 0;
+ }
+
+ public String Name {
+ [System.Security.SecuritySafeCritical] // auto-generated
+ get {
+ EnsureNotDisposed();
+ 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) {
+ SetValue(name, value, RegistryValueKind.Unknown);
+ }
+
+ [System.Security.SecuritySafeCritical] //auto-generated
+ [ComVisible(false)]
+ public unsafe void SetValue(String name, Object value, RegistryValueKind valueKind) {
+ if (value==null)
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.value);
+
+ if (name != null && name.Length > MaxValueLength) {
+ throw new ArgumentException(Environment.GetResourceString("Arg_RegValStrLenBug"));
+ }
+
+ if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind))
+ throw new ArgumentException(Environment.GetResourceString("Arg_RegBadKeyKind"), "valueKind");
+
+ EnsureWriteable();
+
+ if (!remoteKey && ContainsRegistryValue(name)) { // Existing key
+ CheckPermission(RegistryInternalCheck.CheckValueWritePermission, name, false, RegistryKeyPermissionCheck.Default);
+ }
+ else { // Creating a new value
+ CheckPermission(RegistryInternalCheck.CheckValueCreatePermission, name, false, RegistryKeyPermissionCheck.Default);
+ }
+
+ if (valueKind == RegistryValueKind.Unknown) {
+ // this is to maintain compatibility with the old way of autodetecting the type.
+ // SetValue(string, object) will come through this codepath.
+ valueKind = CalculateValueKind(value);
+ }
+
+ int ret = 0;
+ try {
+ switch (valueKind) {
+ case RegistryValueKind.ExpandString:
+ case RegistryValueKind.String:
+ {
+ String data = value.ToString();
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ valueKind,
+ data,
+ checked(data.Length * 2 + 2));
+ break;
+ }
+
+ case RegistryValueKind.MultiString:
+ {
+ // Other thread might modify the input array after we calculate the buffer length.
+ // Make a copy of the input array to be safe.
+ string[] dataStrings = (string[])(((string[])value).Clone());
+ int sizeInBytes = 0;
+
+ // First determine the size of the array
+ //
+ for (int i=0; i<dataStrings.Length; i++) {
+ if (dataStrings[i] == null) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetStrArrNull);
+ }
+ sizeInBytes = checked(sizeInBytes + (dataStrings[i].Length+1) * 2);
+ }
+ sizeInBytes = checked(sizeInBytes + 2);
+
+ byte[] basePtr = new byte[sizeInBytes];
+ fixed(byte* b = basePtr) {
+ IntPtr currentPtr = new IntPtr( (void *) b);
+
+ // Write out the strings...
+ //
+ for (int i=0; i<dataStrings.Length; i++) {
+ // Assumes that the Strings are always null terminated.
+ String.InternalCopy(dataStrings[i],currentPtr,(checked(dataStrings[i].Length*2)));
+ currentPtr = new IntPtr((long)currentPtr + (checked(dataStrings[i].Length*2)));
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+ }
+
+ *(char*)(currentPtr.ToPointer()) = '\0';
+ currentPtr = new IntPtr((long)currentPtr + 2);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.MultiString,
+ basePtr,
+ sizeInBytes);
+ }
+ break;
+ }
+
+ case RegistryValueKind.None:
+ case RegistryValueKind.Binary:
+ byte[] dataBytes = (byte[]) value;
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ (valueKind == RegistryValueKind.None ? Win32Native.REG_NONE: RegistryValueKind.Binary),
+ dataBytes,
+ dataBytes.Length);
+ break;
+
+ case RegistryValueKind.DWord:
+ {
+ // We need to use Convert here because we could have a boxed type cannot be
+ // unboxed and cast at the same time. I.e. ((int)(object)(short) 5) will fail.
+ int data = Convert.ToInt32(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.DWord,
+ ref data,
+ 4);
+ break;
+ }
+
+ case RegistryValueKind.QWord:
+ {
+ long data = Convert.ToInt64(value, System.Globalization.CultureInfo.InvariantCulture);
+
+ ret = Win32Native.RegSetValueEx(hkey,
+ name,
+ 0,
+ RegistryValueKind.QWord,
+ ref data,
+ 8);
+ break;
+ }
+ }
+ }
+ catch (OverflowException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidOperationException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (FormatException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+ catch (InvalidCastException) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegSetMismatchedKind);
+ }
+
+ if (ret == 0) {
+ SetDirty();
+ }
+ else
+ Win32Error(ret, null);
+
+ }
+
+ private RegistryValueKind CalculateValueKind(Object value) {
+ // This logic matches what used to be in SetValue(string name, object value) in the v1.0 and v1.1 days.
+ // Even though we could add detection for an int64 in here, we want to maintain compatibility with the
+ // old behavior.
+ if (value is Int32)
+ return RegistryValueKind.DWord;
+ else if (value is Array) {
+ if (value is byte[])
+ return RegistryValueKind.Binary;
+ else if (value is String[])
+ return RegistryValueKind.MultiString;
+ else
+ throw new ArgumentException(Environment.GetResourceString("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] // auto-generated
+ public override String ToString() {
+ EnsureNotDisposed();
+ return keyName;
+ }
+
+#if FEATURE_MACL
+ public RegistrySecurity GetAccessControl() {
+ return GetAccessControl(AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public RegistrySecurity GetAccessControl(AccessControlSections includeSections) {
+ EnsureNotDisposed();
+ return new RegistrySecurity(hkey, keyName, includeSections);
+ }
+
+ [System.Security.SecuritySafeCritical] // auto-generated
+ public void SetAccessControl(RegistrySecurity registrySecurity) {
+ EnsureWriteable();
+ if (registrySecurity == null)
+ throw new ArgumentNullException("registrySecurity");
+
+ registrySecurity.Persist(hkey, keyName);
+ }
+#endif
+
+ /**
+ * 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] // auto-generated
+ internal void Win32Error(int errorCode, String str) {
+ switch (errorCode) {
+ case Win32Native.ERROR_ACCESS_DENIED:
+ if (str != null)
+ throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_RegistryKeyGeneric_Key", str));
+ else
+ throw new UnauthorizedAccessException();
+
+ case Win32Native.ERROR_INVALID_HANDLE:
+ /**
+ * For normal RegistryKey instances we dispose the SafeRegHandle and throw IOException.
+ * However, for HKEY_PERFORMANCE_DATA (on a local or remote machine) we avoid disposing the
+ * SafeRegHandle and only throw the IOException. This is to workaround reentrancy issues
+ * in PerformanceCounter.NextValue() where the API could throw {NullReference, ObjectDisposed, ArgumentNull}Exception
+ * on reentrant calls because of this error code path in RegistryKey
+ *
+ * Normally we'd make our caller synchronize access to a shared RegistryKey instead of doing something like this,
+ * however we shipped PerformanceCounter.NextValue() un-synchronized in v2.0RTM and customers have taken a dependency on
+ * this behavior (being able to simultaneously query multiple remote-machine counters on multiple threads, instead of
+ * having serialized access).
+ */
+ if (!IsPerfDataKey()) {
+ this.hkey.SetHandleAsInvalid();
+ this.hkey = null;
+ }
+ goto default;
+
+ case Win32Native.ERROR_FILE_NOT_FOUND:
+ throw new IOException(Environment.GetResourceString("Arg_RegKeyNotFound"), errorCode);
+
+ default:
+ throw new IOException(Win32Native.GetMessage(errorCode), errorCode);
+ }
+ }
+
+ [SecuritySafeCritical]
+ internal static void Win32ErrorStatic(int errorCode, String str) {
+ switch (errorCode) {
+ case Win32Native.ERROR_ACCESS_DENIED:
+ if (str != null)
+ throw new UnauthorizedAccessException(Environment.GetResourceString("UnauthorizedAccess_RegistryKeyGeneric_Key", str));
+ else
+ throw new UnauthorizedAccessException();
+
+ default:
+ throw new IOException(Win32Native.GetMessage(errorCode), errorCode);
+ }
+ }
+
+ internal static String FixupName(String name)
+ {
+ BCLDebug.Assert(name!=null,"[FixupName]name!=null");
+ if (name.IndexOf('\\') == -1)
+ return name;
+
+ StringBuilder sb = new StringBuilder(name);
+ FixupPath(sb);
+ int temp = sb.Length - 1;
+ if (temp >= 0 && sb[temp] == '\\') // Remove trailing slash
+ sb.Length = temp;
+ return sb.ToString();
+ }
+
+
+ private static void FixupPath(StringBuilder path)
+ {
+ Contract.Requires(path != null);
+ int length = path.Length;
+ bool fixup = false;
+ char markerChar = (char)0xFFFF;
+
+ int i = 1;
+ while (i < length - 1)
+ {
+ if (path[i] == '\\')
+ {
+ i++;
+ while (i < length)
+ {
+ if (path[i] == '\\')
+ {
+ path[i] = markerChar;
+ i++;
+ fixup = true;
+ }
+ else
+ break;
+ }
+
+ }
+ i++;
+ }
+
+ if (fixup)
+ {
+ i = 0;
+ int j = 0;
+ while (i < length)
+ {
+ if(path[i] == markerChar)
+ {
+ i++;
+ continue;
+ }
+ path[j] = path[i];
+ i++;
+ j++;
+ }
+ path.Length += j - i;
+ }
+
+ }
+
+ //
+ // Read/Write/Create SubKey Permission
+ //
+ private void GetSubKeyReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Read;
+ path = keyName + "\\" + subkeyName + "\\.";
+ }
+ private void GetSubKeyWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ // If we want to open a subkey of a read-only key as writeable, we need to do the check.
+ access = RegistryPermissionAccess.Write;
+ path = keyName + "\\" + subkeyName + "\\.";
+ }
+ private void GetSubKeyCreatePermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Create;
+ path = keyName + "\\" + subkeyName + "\\.";
+ }
+
+ //
+ // Read/Write/ReadWrite SubTree Permission
+ //
+ private void GetSubTreeReadPermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Read;
+ path = keyName + "\\" + subkeyName + "\\";
+ }
+ private void GetSubTreeWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Write;
+ path = keyName + "\\" + subkeyName + "\\";
+ }
+ private void GetSubTreeReadWritePermission(string subkeyName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Write | RegistryPermissionAccess.Read;
+ path = keyName + "\\" + subkeyName;
+ }
+
+ //
+ // Read/Write/Create Value Permission
+ //
+ private void GetValueReadPermission(string valueName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Read;
+ path = keyName+"\\"+valueName;
+ }
+ private void GetValueWritePermission(string valueName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Write;
+ path = keyName+"\\"+valueName;
+ }
+ private void GetValueCreatePermission(string valueName, out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Create;
+ path = keyName+"\\"+valueName;
+ }
+
+ // Read Key Permission
+ private void GetKeyReadPermission(out RegistryPermissionAccess access, out string path) {
+ access = RegistryPermissionAccess.Read;
+ path = keyName + "\\.";
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void CheckPermission(RegistryInternalCheck check, string item, bool subKeyWritable, RegistryKeyPermissionCheck subKeyCheck) {
+ bool demand = false;
+ RegistryPermissionAccess access = RegistryPermissionAccess.NoAccess;
+ string path = null;
+
+#if !FEATURE_CORECLR
+ if (CodeAccessSecurityEngine.QuickCheckForAllDemands()) {
+ return; // full trust fast path
+ }
+#endif // !FEATURE_CORECLR
+
+ switch (check) {
+ //
+ // Read/Write/Create SubKey Permission
+ //
+ case RegistryInternalCheck.CheckSubKeyReadPermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode == RegistryKeyPermissionCheck.Default, "Should be called from a key opened under default mode only!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ demand = true;
+ GetSubKeyReadPermission(item, out access, out path);
+ }
+ break;
+ case RegistryInternalCheck.CheckSubKeyWritePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating sub key under read-only key!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetSubKeyWritePermission(item, out access, out path);
+ }
+ }
+ break;
+ case RegistryInternalCheck.CheckSubKeyCreatePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating sub key under read-only key!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetSubKeyCreatePermission(item, out access, out path);
+ }
+ }
+ break;
+ //
+ // Read/Write/ReadWrite SubTree Permission
+ //
+ case RegistryInternalCheck.CheckSubTreeReadPermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetSubTreeReadPermission(item, out access, out path);
+ }
+ }
+ break;
+ case RegistryInternalCheck.CheckSubTreeWritePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow writing value to read-only key!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetSubTreeWritePermission(item, out access, out path);
+ }
+ }
+ break;
+ case RegistryInternalCheck.CheckSubTreeReadWritePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ // If we want to open a subkey of a read-only key as writeable, we need to do the check.
+ demand = true;
+ GetSubTreeReadWritePermission(item, out access, out path);
+ }
+ break;
+ //
+ // Read/Write/Create Value Permission
+ //
+ case RegistryInternalCheck.CheckValueReadPermission:
+ ///*** no remoteKey check ***///
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ // only need to check for default mode (dynamice check)
+ demand = true;
+ GetValueReadPermission(item, out access, out path);
+ }
+ break;
+ case RegistryInternalCheck.CheckValueWritePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow writing value to read-only key!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ // skip the security check if the key is opened under write mode
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetValueWritePermission(item, out access, out path);
+ }
+ }
+ break;
+ case RegistryInternalCheck.CheckValueCreatePermission:
+ if (remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ BCLDebug.Assert(checkMode != RegistryKeyPermissionCheck.ReadSubTree, "We shouldn't allow creating value under read-only key!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ // skip the security check if the key is opened under write mode
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ demand = true;
+ GetValueCreatePermission(item, out access, out path);
+ }
+ }
+ break;
+ //
+ // CheckKeyReadPermission
+ //
+ case RegistryInternalCheck.CheckKeyReadPermission:
+ ///*** no remoteKey check ***///
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ BCLDebug.Assert(item == null, "CheckKeyReadPermission should never have a non-null item parameter!");
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+
+ // only need to check for default mode (dynamice check)
+ demand = true;
+ GetKeyReadPermission(out access, out path);
+ }
+ break;
+ //
+ // CheckSubTreePermission
+ //
+ case RegistryInternalCheck.CheckSubTreePermission:
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ if( subKeyCheck == RegistryKeyPermissionCheck.ReadSubTree) {
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ if( remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ demand = true;
+ GetSubTreeReadPermission(item, out access, out path);
+ }
+ }
+ }
+ else if(subKeyCheck == RegistryKeyPermissionCheck.ReadWriteSubTree) {
+ if( checkMode != RegistryKeyPermissionCheck.ReadWriteSubTree) {
+ if( remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ demand = true;
+ GetSubTreeReadWritePermission(item, out access, out path);
+ }
+ }
+ }
+ break;
+
+ //
+ // CheckOpenSubKeyWithWritablePermission uses the 'subKeyWritable' parameter
+ //
+ case RegistryInternalCheck.CheckOpenSubKeyWithWritablePermission:
+ BCLDebug.Assert(subKeyCheck == RegistryKeyPermissionCheck.Default, "subKeyCheck should be Default (unused)");
+ // If the parent key is not opened under default mode, we have access already.
+ // If the parent key is opened under default mode, we need to check for permission.
+ if(checkMode == RegistryKeyPermissionCheck.Default) {
+ if( remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ demand = true;
+ GetSubKeyReadPermission(item, out access, out path);
+ }
+ break;
+ }
+ if( subKeyWritable && (checkMode == RegistryKeyPermissionCheck.ReadSubTree)) {
+ if( remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ demand = true;
+ GetSubTreeReadWritePermission(item, out access, out path);
+ }
+ break;
+ }
+ break;
+
+ //
+ // CheckOpenSubKeyPermission uses the 'subKeyCheck' parameter
+ //
+ case RegistryInternalCheck.CheckOpenSubKeyPermission:
+ BCLDebug.Assert(subKeyWritable == false, "subKeyWritable should be false (unused)");
+ if(subKeyCheck == RegistryKeyPermissionCheck.Default) {
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ if(remoteKey) {
+ CheckUnmanagedCodePermission();
+ }
+ else {
+ demand = true;
+ GetSubKeyReadPermission(item, out access, out path);
+ }
+ }
+ }
+ break;
+
+ default:
+ BCLDebug.Assert(false, "CheckPermission default switch case should never be hit!");
+ break;
+ }
+
+ if (demand) {
+ new RegistryPermission(access, path).Demand();
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ static private void CheckUnmanagedCodePermission() {
+#pragma warning disable 618
+ new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
+#pragma warning restore 618
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private bool ContainsRegistryValue(string name) {
+ int type = 0;
+ int datasize = 0;
+ int retval = Win32Native.RegQueryValueEx(hkey, name, null, ref type, (byte[])null, ref datasize);
+ return retval == 0;
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void EnsureNotDisposed(){
+ if (hkey == null) {
+ ThrowHelper.ThrowObjectDisposedException(keyName, ExceptionResource.ObjectDisposed_RegKeyClosed);
+ }
+ }
+
+ [System.Security.SecurityCritical] // auto-generated
+ private void EnsureWriteable() {
+ EnsureNotDisposed();
+ if (!IsWritable()) {
+ ThrowHelper.ThrowUnauthorizedAccessException(ExceptionResource.UnauthorizedAccess_RegistryNoWrite);
+ }
+ }
+
+ static int GetRegistryKeyAccess(bool isWritable) {
+ int winAccess;
+ if (!isWritable) {
+ winAccess = Win32Native.KEY_READ;
+ }
+ else {
+ winAccess = Win32Native.KEY_READ | Win32Native.KEY_WRITE;
+ }
+
+ return winAccess;
+ }
+
+ static int GetRegistryKeyAccess(RegistryKeyPermissionCheck mode) {
+ int winAccess = 0;
+ switch(mode) {
+ case RegistryKeyPermissionCheck.ReadSubTree:
+ case RegistryKeyPermissionCheck.Default:
+ winAccess = Win32Native.KEY_READ;
+ break;
+
+ case RegistryKeyPermissionCheck.ReadWriteSubTree:
+ winAccess = Win32Native.KEY_READ| Win32Native.KEY_WRITE;
+ break;
+
+ default:
+ BCLDebug.Assert(false, "unexpected code path");
+ break;
+ }
+
+ return winAccess;
+ }
+
+ private RegistryKeyPermissionCheck GetSubKeyPermissonCheck(bool subkeyWritable) {
+ if( checkMode == RegistryKeyPermissionCheck.Default) {
+ return checkMode;
+ }
+
+ if(subkeyWritable) {
+ return RegistryKeyPermissionCheck.ReadWriteSubTree;
+ }
+ else {
+ return RegistryKeyPermissionCheck.ReadSubTree;
+ }
+ }
+
+ static private void ValidateKeyName(string name) {
+ Contract.Ensures(name != null);
+ if (name == null) {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.name);
+ }
+
+ int nextSlash = name.IndexOf("\\", StringComparison.OrdinalIgnoreCase);
+ int current = 0;
+ while (nextSlash != -1) {
+ if ((nextSlash - current) > MaxKeyLength)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug);
+
+ current = nextSlash + 1;
+ nextSlash = name.IndexOf("\\", current, StringComparison.OrdinalIgnoreCase);
+ }
+
+ if ((name.Length - current) > MaxKeyLength)
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RegKeyStrLenBug);
+
+ }
+
+ static private void ValidateKeyMode(RegistryKeyPermissionCheck mode) {
+ if( mode < RegistryKeyPermissionCheck.Default || mode > RegistryKeyPermissionCheck.ReadWriteSubTree) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryKeyPermissionCheck, ExceptionArgument.mode);
+ }
+ }
+
+ static private void ValidateKeyOptions(RegistryOptions options) {
+ if (options < RegistryOptions.None || options > RegistryOptions.Volatile) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryOptionsCheck, ExceptionArgument.options);
+ }
+ }
+
+ static private void ValidateKeyView(RegistryView view) {
+ if (view != RegistryView.Default && view != RegistryView.Registry32 && view != RegistryView.Registry64) {
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidRegistryViewCheck, ExceptionArgument.view);
+ }
+ }
+
+
+#if FEATURE_MACL
+ static private void ValidateKeyRights(int rights) {
+ if(0 != (rights & ~((int)RegistryRights.FullControl))) {
+ // We need to throw SecurityException here for compatiblity reason,
+ // although UnauthorizedAccessException will make more sense.
+ ThrowHelper.ThrowSecurityException(ExceptionResource.Security_RegistryPermission);
+ }
+ }
+#endif
+ // Win32 constants for error handling
+ private const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
+ private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
+ private const int FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
+ }
+
+ [Flags]
+ public enum RegistryValueOptions {
+ None = 0,
+ DoNotExpandEnvironmentNames = 1
+ }
+
+ // the name for this API is meant to mimic FileMode, which has similar values
+
+ public enum RegistryKeyPermissionCheck {
+ Default = 0,
+ ReadSubTree = 1,
+ ReadWriteSubTree = 2
+ }
+}