MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4//5// NOTE: A vast majority of this code was copied from BCL in6// ndp\clr\src\BCL\Microsoft\Win32\RegistryKey.cs.7// Namespace: Microsoft.Win328//9/*10 Note on transaction support:11 Eventually we will want to add support for NT's transactions to our12 TransactedRegistryKey API's (possibly Whidbey M3?). When we do this, here's13 the list of API's we need to make transaction-aware:14 15 RegCreateKeyEx16 RegDeleteKey17 RegDeleteValue18 RegEnumKeyEx19 RegEnumValue20 RegOpenKeyEx21 RegQueryInfoKey22 RegQueryValueEx23 RegSetValueEx24 25 We can ignore RegConnectRegistry (remote registry access doesn't yet have26 transaction support) and RegFlushKey. RegCloseKey doesn't require any27 additional work. .28 */29 30/*31 Note on ACL support:32 The key thing to note about ACL's is you set them on a kernel object like a33 registry key, then the ACL only gets checked when you construct handles to34 them. So if you set an ACL to deny read access to yourself, you'll still be35 able to read with that handle, but not with new handles.36 37 Another peculiarity is a Terminal Server app compatibility hack. The OS38 will second guess your attempt to open a handle sometimes. If a certain39 combination of Terminal Server app compat registry keys are set, then the40 OS will try to reopen your handle with lesser permissions if you couldn't41 open it in the specified mode. So on some machines, we will see handles that42 may not be able to read or write to a registry key. It's very strange. But43 the real test of these handles is attempting to read or set a value in an44 affected registry key.45 46 For reference, at least two registry keys must be set to particular values47 for this behavior:48 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\RegistryExtensionFlags, the least significant bit must be 1.49 HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\TSAppCompat must be 150 There might possibly be an interaction with yet a third registry key as well.51 52*/53 54using BCLDebug = System.Diagnostics.Debug;55 56namespace Microsoft.PowerShell.Commands.Internal57{58 using System;59 using System.Collections.Generic;60 using System.Security;61 using System.Security.AccessControl;62 using System.Security.Permissions;63 using System.Text;64 using System.IO;65 using System.Runtime.InteropServices;66 using Microsoft.Win32;67 using System.Runtime.Versioning;68 using System.Globalization;69 using System.Transactions;70 using System.Diagnostics.CodeAnalysis;71 72 // Putting this in a separate internal class to avoid OACR warning DoNotDeclareReadOnlyMutableReferenceTypes.73 internal sealed class BaseRegistryKeys74 {75 // We could use const here, if C# supported ELEMENT_TYPE_I fully.76 internal static readonly IntPtr HKEY_CLASSES_ROOT = new IntPtr(unchecked((int)0x80000000));77 internal static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001));78 internal static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002));79 internal static readonly IntPtr HKEY_USERS = new IntPtr(unchecked((int)0x80000003));80 internal static readonly IntPtr HKEY_CURRENT_CONFIG = new IntPtr(unchecked((int)0x80000005));81 }82 83 /// <summary>84 /// Registry encapsulation. To get an instance of a TransactedRegistryKey use the85 /// Registry class's static members then call OpenSubKey.86 ///87 /// @see Registry88 /// @security(checkDllCalls=off)89 /// @security(checkClassLinking=on)90 /// </summary>91 [ComVisible(true)]92 // Suppressed because these objects are written to the pipeline so need to be accessible.93 [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")]94 public sealed class TransactedRegistryKey : MarshalByRefObject, IDisposable95 {96 private const string resBaseName = "RegistryProviderStrings";97 98 // Dirty indicates that we have munged data that should be potentially99 // written to disk.100 //101 private const int STATE_DIRTY = 0x0001;102 103 // SystemKey indicates that this is a "SYSTEMKEY" and shouldn't be "opened"104 // or "closed".105 //106 private const int STATE_SYSTEMKEY = 0x0002;107 108 // Access109 //110 private const int STATE_WRITEACCESS = 0x0004;111 112 // Names of keys. This array must be in the same order as the HKEY values listed above.113 //114 private static readonly string[] s_hkeyNames = new string[] {115 "HKEY_CLASSES_ROOT",116 "HKEY_CURRENT_USER",117 "HKEY_LOCAL_MACHINE",118 "HKEY_USERS",119 "HKEY_PERFORMANCE_DATA",120 "HKEY_CURRENT_CONFIG",121 "HKEY_DYN_DATA"122 };123 124 // MSDN defines the following limits for registry key names & values:125 // Key Name: 255 characters126 // Value name: Win9x: 255 NT: 16,383 Unicode characters, or 260 ANSI chars127 // Value: either 1 MB or current available memory, depending on registry format.128 private const int MaxKeyLength = 255;129 private const int MaxValueNameLength = 16383;130 private const int MaxValueDataLength = 1024 * 1024;131 132 private SafeRegistryHandle _hkey = null;133 private int _state = 0;134 private string _keyName;135 private RegistryKeyPermissionCheck _checkMode;136 private System.Transactions.Transaction _myTransaction;137 private SafeTransactionHandle _myTransactionHandle;138 139 // This is a wrapper around RegOpenKeyTransacted that implements a workaround140 // to TxF bug number 181242 After calling RegOpenKeyTransacted, it calls RegQueryInfoKey.141 // If that call fails with ERROR_INVALID_TRANSACTION, we have possibly run into bug 181242. To workaround142 // this, we open the key without a transaction and then open it again with143 // a transaction and return THAT hkey.144 private int RegOpenKeyTransactedWrapper(SafeRegistryHandle hKey, string lpSubKey,145 int ulOptions, int samDesired, out SafeRegistryHandle hkResult,146 SafeTransactionHandle hTransaction, IntPtr pExtendedParameter)147 {148 int error = Win32Native.ERROR_SUCCESS;149 SafeRegistryHandle hKeyToReturn = null;150 151 error = Win32Native.RegOpenKeyTransacted(_hkey, lpSubKey, ulOptions, samDesired, out hKeyToReturn, hTransaction, pExtendedParameter);152 153 if (Win32Native.ERROR_SUCCESS == error && !hKeyToReturn.IsInvalid)154 {155 // This is a check and workaround for TxR bug 181242. If we try to use the transacted hKey we just opened156 // for a call to RegQueryInfoKey and get back a ERROR_INVALID_TRANSACTION error, then the key might be a symbolic link and TxR didn't157 // do the open correctly. The workaround is to open it non-transacted, then open it again transacted without158 // a subkey string. If we get some error other than ERROR_INVALID_TRANSACTION from RegQueryInfoKey, just ignore it for now.159 int subkeyCount = 0;160 int valueCount = 0;161 error = Win32Native.RegQueryInfoKey(hKeyToReturn,162 null,163 null,164 Win32Native.NULL,165 ref subkeyCount, // subkeys166 null,167 null,168 ref valueCount, // values169 null,170 null,171 null,172 null);173 if (Win32Native.ERROR_INVALID_TRANSACTION == error)174 {175 SafeRegistryHandle nonTxKey = null;176 SafeRegistryHandle txKey = null;177 error = Win32Native.RegOpenKeyEx(_hkey, lpSubKey, ulOptions, samDesired, out nonTxKey);178 // If we got some error on this open, just ignore it and continue on with the handle179 // we got on the original RegOpenKeyTransacted.180 if (Win32Native.ERROR_SUCCESS == error)181 {182 // Now do an RegOpenKeyTransacted with the non-transacted key and no "subKey" parameter.183 error = Win32Native.RegOpenKeyTransacted(nonTxKey, null, ulOptions, samDesired, out txKey, hTransaction, pExtendedParameter);184 if (Win32Native.ERROR_SUCCESS == error)185 {186 // Let's use this hkey instead.187 hKeyToReturn.Dispose();188 hKeyToReturn = txKey;189 }190 191 nonTxKey.Dispose();192 nonTxKey = null;193 }194 }195 }196 197 hkResult = hKeyToReturn;198 return error;199 }200 201 /**202 * Creates a TransactedRegistryKey.203 *204 * This key is bound to hkey, if writable is <b>false</b> then no write operations205 * will be allowed. If systemkey is set then the hkey won't be released206 * when the object is GC'ed.207 */208 private TransactedRegistryKey(SafeRegistryHandle hkey, bool writable, bool systemkey,209 System.Transactions.Transaction transaction, SafeTransactionHandle txHandle)210 {211 _hkey = hkey;212 _keyName = string.Empty;213 if (systemkey)214 {215 _state |= STATE_SYSTEMKEY;216 }217 218 if (writable)219 {220 _state |= STATE_WRITEACCESS;221 }222 // We want to take our own clone so we can dispose it when we want and223 // aren't susceptible to the caller disposing it.224 if (transaction != null)225 {226 _myTransaction = transaction.Clone();227 _myTransactionHandle = txHandle;228 }229 else230 {231 _myTransaction = null;232 _myTransactionHandle = null;233 }234 }235 236 private SafeTransactionHandle GetTransactionHandle()237 {238 SafeTransactionHandle safeTransactionHandle = null;239 240 // If myTransaction is not null and is not the same as Transaction.Current241 // this is an invalid operation. The transaction within which the RegistryKey object was created242 // needs to be the same as the transaction being used now.243 if (_myTransaction != null)244 {245 if (!_myTransaction.Equals(Transaction.Current))246 {247 throw new InvalidOperationException(RegistryProviderStrings.InvalidOperation_MustUseSameTransaction);248 }249 else250 {251 safeTransactionHandle = _myTransactionHandle;252 }253 }254 else // we want to use Transaction.Current for the transaction.255 {256 safeTransactionHandle = SafeTransactionHandle.Create();257 }258 259 return safeTransactionHandle;260 }261 262 /// <summary>TransactedRegistryKey.Close263 /// <para>Closes this key, flushes it to disk if the contents have been modified.264 /// Utilizes Transaction.Current for its transaction.</para>265 /// </summary>266 public void Close()267 {268 Dispose(true);269 }270 271 private void Dispose(bool disposing)272 {273 if (_hkey != null)274 {275 if (!IsSystemKey())276 {277 try278 {279 _hkey.Dispose();280 }281 catch (IOException)282 {283 // we don't really care if the handle is invalid at this point284 }285 finally286 {287 _hkey = null;288 }289 }290 }291 292 if (_myTransaction != null)293 {294 // Dispose the transaction because we cloned it.295 try296 {297 _myTransaction.Dispose();298 }299 catch (TransactionException)300 {301 // ignore.302 }303 finally304 {305 _myTransaction = null;306 }307 }308 }309 310 /// <summary>TransactedRegistryKey.Flush311 /// <para>Flushes this key. Utilizes Transaction.Current for its transaction.</para>312 /// </summary>313 public void Flush()314 {315 // Require a transaction. This will throw for "Base" keys because they aren't associated with a transaction.316 VerifyTransaction();317 if (_hkey != null)318 {319 if (IsDirty())320 {321 int ret = Win32Native.RegFlushKey(_hkey);322 if (Win32Native.ERROR_SUCCESS != ret)323 {324 throw new IOException(Win32Native.GetMessage(ret), ret);325 }326 }327 }328 }329 330 /// <summary>TransactedRegistryKey.Dispose331 /// <para>Disposes this key. Utilizes Transaction.Current for its transaction.</para>332 /// </summary>333 public void Dispose()334 {335 Dispose(true);336 }337 338 /// <summary>339 /// <para>Creates a new subkey, or opens an existing one.340 /// Utilizes Transaction.Current for its transaction.</para>341 /// </summary>342 /// <param name='subkey'>Name or path to subkey to create or open. Cannot be null or an empty string,343 /// otherwise an ArgumentException is thrown.</param>344 /// <returns>A TransactedRegistryKey object for the subkey, which is associated with Transaction.Current.345 /// returns null if the operation failed.</returns>346 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey347 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]348 public TransactedRegistryKey CreateSubKey(string subkey)349 {350 return CreateSubKey(subkey, _checkMode);351 }352 353 /// <summary>354 /// <para>Creates a new subkey, or opens an existing one.355 /// Utilizes Transaction.Current for its transaction.</para>356 /// </summary>357 /// <returns>A TransactedRegistryKey object for the subkey, which is associated with Transaction.Current.358 /// returns null if the operation failed.</returns>359 /// <param name='subkey'>Name or path to subkey to create or open. Cannot be null or an empty string,360 /// otherwise an ArgumentException is thrown.</param>361 /// <param name='permissionCheck'>One of the Microsoft.Win32.RegistryKeyPermissionCheck values that362 /// specifies whether the key is opened for read or read/write access.</param>363 [ComVisible(false)]364 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey365 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]366 public TransactedRegistryKey CreateSubKey(string subkey, RegistryKeyPermissionCheck permissionCheck)367 {368 return CreateSubKeyInternal(subkey, permissionCheck, (TransactedRegistrySecurity)null);369 }370 371 /// <summary>372 /// <para>Creates a new subkey, or opens an existing one.373 /// Utilizes Transaction.Current for its transaction.</para>374 /// </summary>375 /// <returns>A TransactedRegistryKey object for the subkey, which is associated with Transaction.Current.376 /// returns null if the operation failed.</returns>377 /// <param name='subkey'>Name or path to subkey to create or open. Cannot be null or an empty string,378 /// otherwise an ArgumentException is thrown.</param>379 /// <param name='permissionCheck'>One of the Microsoft.Win32.RegistryKeyPermissionCheck values that380 /// specifies whether the key is opened for read or read/write access.</param>381 /// <param name='registrySecurity'>A TransactedRegistrySecurity object that specifies the access control security for the new key.</param>382 [ComVisible(false)]383 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey384 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]385 public unsafe TransactedRegistryKey CreateSubKey(string subkey, RegistryKeyPermissionCheck permissionCheck, TransactedRegistrySecurity registrySecurity)386 {387 return CreateSubKeyInternal(subkey, permissionCheck, registrySecurity);388 }389 390 [ComVisible(false)]391 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey392 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]393 private unsafe TransactedRegistryKey CreateSubKeyInternal(string subkey, RegistryKeyPermissionCheck permissionCheck, object registrySecurityObj)394 {395 ValidateKeyName(subkey);396 // RegCreateKeyTransacted requires a non-empty key name, so let's deal with that here.397 if (string.Empty == subkey)398 {399 throw new ArgumentException(RegistryProviderStrings.Arg_RegKeyStrEmpty);400 }401 402 ValidateKeyMode(permissionCheck);403 EnsureWriteable();404 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash405 406 // only keys opened under read mode is not writable407 TransactedRegistryKey existingKey = InternalOpenSubKey(subkey, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree));408 if (existingKey != null)409 { // Key already exits410 CheckSubKeyWritePermission(subkey);411 CheckSubTreePermission(subkey, permissionCheck);412 existingKey._checkMode = permissionCheck;413 return existingKey;414 }415 416 CheckSubKeyCreatePermission(subkey);417 418 Win32Native.SECURITY_ATTRIBUTES secAttrs = null;419 TransactedRegistrySecurity registrySecurity = registrySecurityObj as TransactedRegistrySecurity;420 // For ACL's, get the security descriptor from the RegistrySecurity.421 if (registrySecurity != null)422 {423 secAttrs = new Win32Native.SECURITY_ATTRIBUTES();424 secAttrs.nLength = (int)Marshal.SizeOf(secAttrs);425 426 byte[] sd = registrySecurity.GetSecurityDescriptorBinaryForm();427 // We allocate memory on the stack to improve the speed.428 // So this part of code can't be refactored into a method.429 byte* pSecDescriptor = stackalloc byte[sd.Length];430 Microsoft.PowerShell.Commands.Internal.Buffer.memcpy(sd, 0, pSecDescriptor, 0, sd.Length);431 secAttrs.pSecurityDescriptor = pSecDescriptor;432 }433 434 int disposition = 0;435 436 // By default, the new key will be writable.437 SafeRegistryHandle result = null;438 int ret = 0;439 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();440 441 ret = Win32Native.RegCreateKeyTransacted(_hkey,442 subkey,443 0,444 null,445 0,446 GetRegistryKeyAccess(permissionCheck != RegistryKeyPermissionCheck.ReadSubTree),447 secAttrs,448 out result,449 out disposition,450 safeTransactionHandle,451 IntPtr.Zero452 );453 454 if (ret == 0 && !result.IsInvalid)455 {456 TransactedRegistryKey key = new TransactedRegistryKey(result, (permissionCheck != RegistryKeyPermissionCheck.ReadSubTree), false,457 Transaction.Current, safeTransactionHandle);458 CheckSubTreePermission(subkey, permissionCheck);459 key._checkMode = permissionCheck;460 461 if (subkey.Length == 0)462 key._keyName = _keyName;463 else464 key._keyName = _keyName + "\\" + subkey;465 return key;466 }467 else if (ret != 0) // syscall failed, ret is an error code.468 Win32Error(ret, _keyName + "\\" + subkey); // Access denied?469 470 BCLDebug.Assert(false, "Unexpected code path in RegistryKey::CreateSubKey");471 return null;472 }473 474 /// <summary>475 /// <para>Deletes the specified subkey. Will throw an exception if the subkey has476 /// subkeys. To delete a tree of subkeys use, DeleteSubKeyTree.477 /// Utilizes Transaction.Current for its transaction.</para>478 /// <exception cref="InvalidOperationException">Thrown if the subkey as child subkeys.</exception>479 /// </summary>480 /// <param name='subkey'>The subkey to delete.</param>481 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey482 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]483 public void DeleteSubKey(string subkey)484 {485 DeleteSubKey(subkey, true);486 }487 488 /// <summary>489 /// <para>Deletes the specified subkey. Will throw an exception if the subkey has490 /// subkeys. To delete a tree of subkeys use, DeleteSubKeyTree.491 /// Utilizes Transaction.Current for its transaction.</para>492 /// <exception cref="InvalidOperationException">Thrown if the subkey as child subkeys.</exception>493 /// <exception cref="ArgumentException">Thrown if true is specified for throwOnMissingSubKey and the494 /// specified subkey does not exist.</exception>495 /// </summary>496 /// <param name='subkey'>The subkey to delete.</param>497 /// <param name='throwOnMissingSubKey'>Specify true if an ArgumentException should be thrown if498 /// the specified subkey does not exist. If false is specified, a missing subkey does not throw499 /// an exception.</param>500 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey501 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]502 public void DeleteSubKey(string subkey, bool throwOnMissingSubKey)503 {504 ValidateKeyName(subkey);505 EnsureWriteable();506 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash507 CheckSubKeyWritePermission(subkey);508 509 // Open the key we are deleting and check for children. Be sure to510 // explicitly call close to avoid keeping an extra HKEY open.511 //512 TransactedRegistryKey key = InternalOpenSubKey(subkey, false);513 if (key != null)514 {515 try516 {517 if (key.InternalSubKeyCount() > 0)518 {519 throw new InvalidOperationException(RegistryProviderStrings.InvalidOperation_RegRemoveSubKey);520 }521 }522 finally523 {524 key.Close();525 }526 527 int ret = 0;528 529 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();530 ret = Win32Native.RegDeleteKeyTransacted(_hkey, subkey, 0, 0, safeTransactionHandle, IntPtr.Zero);531 532 if (ret != 0)533 {534 if (ret == Win32Native.ERROR_FILE_NOT_FOUND)535 {536 if (throwOnMissingSubKey)537 {538 throw new ArgumentException(RegistryProviderStrings.ArgumentException_RegSubKeyAbsent);539 }540 }541 else542 Win32Error(ret, null);543 }544 }545 else546 { // there is no key which also means there is no subkey547 if (throwOnMissingSubKey)548 throw new ArgumentException(RegistryProviderStrings.ArgumentException_RegSubKeyAbsent);549 }550 }551 552 /// <summary>553 /// <para>Recursively deletes a subkey and any child subkeys.554 /// Utilizes Transaction.Current for its transaction.</para>555 /// </summary>556 /// <param name="subkey">The subkey to delete.</param>557 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey558 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]559 public void DeleteSubKeyTree(string subkey)560 {561 ValidateKeyName(subkey);562 563 // Security concern: Deleting a hive's "" subkey would delete all564 // of that hive's contents. Don't allow "".565 if ((string.IsNullOrEmpty(subkey) || subkey.Length == 0) && IsSystemKey())566 {567 throw new ArgumentException(RegistryProviderStrings.ArgRegKeyDelHive);568 }569 570 EnsureWriteable();571 572 int ret = 0;573 574 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();575 subkey = FixupName(subkey); // Fixup multiple slashes to a single slash576 CheckSubTreeWritePermission(subkey);577 578 TransactedRegistryKey key = InternalOpenSubKey(subkey, true);579 if (key != null)580 {581 try582 {583 if (key.InternalSubKeyCount() > 0)584 {585 string[] keys = key.InternalGetSubKeyNames();586 587 for (int i = 0; i < keys.Length; i++)588 {589 key.DeleteSubKeyTreeInternal(keys[i]);590 }591 }592 }593 finally594 {595 key.Close();596 }597 598 ret = Win32Native.RegDeleteKeyTransacted(_hkey, subkey, 0, 0, safeTransactionHandle, IntPtr.Zero);599 if (ret != 0)600 {601 Win32Error(ret, null);602 }603 }604 else605 {606 throw new ArgumentException(RegistryProviderStrings.Arg_RegSubKeyAbsent);607 }608 }609 610 // An internal version which does no security checks or argument checking. Skipping the611 // security checks should give us a slight perf gain on large trees.612 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey613 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]614 private void DeleteSubKeyTreeInternal(string subkey)615 {616 int ret = 0;617 618 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();619 TransactedRegistryKey key = InternalOpenSubKey(subkey, true);620 if (key != null)621 {622 try623 {624 if (key.InternalSubKeyCount() > 0)625 {626 string[] keys = key.InternalGetSubKeyNames();627 628 for (int i = 0; i < keys.Length; i++)629 {630 key.DeleteSubKeyTreeInternal(keys[i]);631 }632 }633 }634 finally635 {636 key.Close();637 }638 639 ret = Win32Native.RegDeleteKeyTransacted(_hkey, subkey, 0, 0, safeTransactionHandle, IntPtr.Zero);640 if (ret != 0)641 {642 Win32Error(ret, null);643 }644 }645 else646 {647 throw new ArgumentException(RegistryProviderStrings.Arg_RegSubKeyAbsent);648 }649 }650 651 /// <summary>652 /// <para>Deletes the specified value from this key.653 /// Utilizes Transaction.Current for its transaction.</para>654 /// </summary>655 /// <param name="name">Name of the value to delete.</param>656 public void DeleteValue(string name)657 {658 DeleteValue(name, true);659 }660 661 /// <summary>662 /// <para>Deletes the specified value from this key.663 /// Utilizes Transaction.Current for its transaction.</para>664 /// </summary>665 /// <param name="name">Name of the value to delete.</param>666 /// <param name="throwOnMissingValue">Specify true if an ArgumentException should be thrown if667 /// the specified value does not exist. If false is specified, a missing value does not throw668 /// an exception.</param>669 public void DeleteValue(string name, bool throwOnMissingValue)670 {671 EnsureWriteable();672 CheckValueWritePermission(name);673 // Require a transaction. This will throw for "Base" keys because they aren't associated with a transaction.674 VerifyTransaction();675 int errorCode = Win32Native.RegDeleteValue(_hkey, name);676 677 //678 // From windows 2003 server, if the name is too long we will get error code ERROR_FILENAME_EXCED_RANGE679 // This still means the name doesn't exist. We need to be consistent with previous OS.680 //681 if (errorCode == Win32Native.ERROR_FILE_NOT_FOUND || errorCode == Win32Native.ERROR_FILENAME_EXCED_RANGE)682 {683 if (throwOnMissingValue)684 {685 throw new ArgumentException(RegistryProviderStrings.Arg_RegSubKeyValueAbsent);686 }687 else688 {689 errorCode = Win32Native.ERROR_SUCCESS;690 }691 }692 693 if (Win32Native.ERROR_SUCCESS != errorCode)694 {695 Win32Error(errorCode, null);696 }697 }698 699 /**700 * Retrieves a new TransactedRegistryKey that represents the requested key. Valid701 * values are:702 *703 * HKEY_CLASSES_ROOT,704 * HKEY_CURRENT_USER,705 * HKEY_LOCAL_MACHINE,706 * HKEY_USERS,707 * HKEY_PERFORMANCE_DATA,708 * HKEY_CURRENT_CONFIG,709 * HKEY_DYN_DATA.710 *711 * @param hKey HKEY_* to open.712 *713 * @return the TransactedRegistryKey requested.714 */715 internal static TransactedRegistryKey GetBaseKey(IntPtr hKey)716 {717 int index = ((int)hKey) & 0x0FFFFFFF;718 BCLDebug.Assert(index >= 0 && index < s_hkeyNames.Length, "index is out of range!");719 BCLDebug.Assert((((int)hKey) & 0xFFFFFFF0) == 0x80000000, "Invalid hkey value!");720 721 SafeRegistryHandle srh = new SafeRegistryHandle(hKey, false);722 723 // For Base keys, there is no transaction associated with the HKEY.724 TransactedRegistryKey key = new TransactedRegistryKey(srh, true, true, null, null);725 key._checkMode = RegistryKeyPermissionCheck.Default;726 key._keyName = s_hkeyNames[index];727 return key;728 }729 730 /// <summary>731 /// <para>Retrieves a subkey. If readonly is true, then the subkey is opened with732 /// read-only access.733 /// Utilizes Transaction.Current for its transaction.</para>734 /// </summary>735 /// <returns>The subkey requested or null if the operation failed.</returns>736 /// <param name="name">Name or path of the subkey to open.</param>737 /// <param name="writable">Set to true of you only need readonly access.</param>738 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey739 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]740 public TransactedRegistryKey OpenSubKey(string name, bool writable)741 {742 ValidateKeyName(name);743 EnsureNotDisposed();744 name = FixupName(name); // Fixup multiple slashes to a single slash745 746 CheckOpenSubKeyPermission(name, writable);747 SafeRegistryHandle result = null;748 int ret = 0;749 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();750 751 ret = RegOpenKeyTransactedWrapper(_hkey, name, 0, GetRegistryKeyAccess(writable), out result, safeTransactionHandle, IntPtr.Zero);752 753 if (ret == 0 && !result.IsInvalid)754 {755 TransactedRegistryKey key = new TransactedRegistryKey(result, writable, false, Transaction.Current, safeTransactionHandle);756 key._checkMode = GetSubKeyPermissionCheck(writable);757 key._keyName = _keyName + "\\" + name;758 return key;759 }760 761 // Return null if we didn't find the key.762 if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL)763 {764 // We need to throw SecurityException here for compatibility reasons,765 // although UnauthorizedAccessException will make more sense.766 throw new SecurityException(RegistryProviderStrings.Security_RegistryPermission);767 }768 769 return null;770 }771 772 /// <summary>773 /// <para>Retrieves a subkey.774 /// Utilizes Transaction.Current for its transaction.</para>775 /// </summary>776 /// <returns>The subkey requested or null if the operation failed.</returns>777 /// <param name="name">Name or path of the subkey to open.</param>778 /// <param name="permissionCheck">One of the Microsoft.Win32.RegistryKeyPermissionCheck values that specifies779 /// whether the key is opened for read or read/write access.</param>780 [ComVisible(false)]781 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey782 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]783 public TransactedRegistryKey OpenSubKey(string name, RegistryKeyPermissionCheck permissionCheck)784 {785 ValidateKeyMode(permissionCheck);786 return InternalOpenSubKey(name, permissionCheck, GetRegistryKeyAccess(permissionCheck));787 }788 789 /// <summary>790 /// <para>Retrieves a subkey.791 /// Utilizes Transaction.Current for its transaction.</para>792 /// </summary>793 /// <returns>The subkey requested or null if the operation failed.</returns>794 /// <param name="name">Name or path of the subkey to open.</param>795 /// <param name="permissionCheck">One of the Microsoft.Win32.RegistryKeyPermissionCheck values that specifies796 /// whether the key is opened for read or read/write access.</param>797 /// <param name="rights">A bitwise combination of Microsoft.Win32.RegistryRights values that specifies the desired security access.</param>798 [ComVisible(false)]799 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey800 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]801 public TransactedRegistryKey OpenSubKey(string name, RegistryKeyPermissionCheck permissionCheck, RegistryRights rights)802 {803 return InternalOpenSubKey(name, permissionCheck, (int)rights);804 }805 806 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey807 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]808 private TransactedRegistryKey InternalOpenSubKey(string name, RegistryKeyPermissionCheck permissionCheck, int rights)809 {810 ValidateKeyName(name);811 ValidateKeyMode(permissionCheck);812 ValidateKeyRights(rights);813 EnsureNotDisposed();814 name = FixupName(name); // Fixup multiple slashes to a single slash815 816 CheckOpenSubKeyPermission(name, permissionCheck);817 SafeRegistryHandle result = null;818 int ret = 0;819 820 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();821 822 ret = RegOpenKeyTransactedWrapper(_hkey, name, 0, rights, out result, safeTransactionHandle, IntPtr.Zero);823 824 if (ret == 0 && !result.IsInvalid)825 {826 TransactedRegistryKey key = new TransactedRegistryKey(result, (permissionCheck == RegistryKeyPermissionCheck.ReadWriteSubTree), false,827 Transaction.Current, safeTransactionHandle);828 key._keyName = _keyName + "\\" + name;829 key._checkMode = permissionCheck;830 return key;831 }832 833 // Return null if we didn't find the key.834 if (ret == Win32Native.ERROR_ACCESS_DENIED || ret == Win32Native.ERROR_BAD_IMPERSONATION_LEVEL)835 {836 // We need to throw SecurityException here for compatibility reason,837 // although UnauthorizedAccessException will make more sense.838 throw new SecurityException(RegistryProviderStrings.Security_RegistryPermission);839 }840 841 return null;842 }843 844 // This required no security checks. This is to get around the Deleting SubKeys which only require845 // write permission. They call OpenSubKey which required read. Now instead call this function w/o security checks846 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey847 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]848 internal TransactedRegistryKey InternalOpenSubKey(string name, bool writable)849 {850 ValidateKeyName(name);851 EnsureNotDisposed();852 853 int winAccess = GetRegistryKeyAccess(writable);854 SafeRegistryHandle result = null;855 int ret = 0;856 SafeTransactionHandle safeTransactionHandle = GetTransactionHandle();857 858 ret = RegOpenKeyTransactedWrapper(_hkey, name, 0, winAccess, out result, safeTransactionHandle, IntPtr.Zero);859 860 if (ret == 0 && !result.IsInvalid)861 {862 TransactedRegistryKey key = new TransactedRegistryKey(result, writable, false, Transaction.Current, safeTransactionHandle);863 key._keyName = _keyName + "\\" + name;864 return key;865 }866 867 return null;868 }869 870 /// <summary>871 /// <para>Retrieves a subkey for readonly access.872 /// Utilizes Transaction.Current for its transaction.</para>873 /// </summary>874 /// <returns>The subkey requested or null if the operation failed.</returns>875 /// <param name="name">Name or path of the subkey to open.</param>876 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey877 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]878 public TransactedRegistryKey OpenSubKey(string name)879 {880 return OpenSubKey(name, false);881 }882 883 /// <summary>884 /// <para>Retrieves the count of subkeys.885 /// Utilizes Transaction.Current for its transaction.</para>886 /// </summary>887 /// <returns>The count of subkeys.</returns>888 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey889 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]890 public int SubKeyCount891 {892 get893 {894 CheckKeyReadPermission();895 return InternalSubKeyCount();896 }897 }898 899 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey900 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]901 internal int InternalSubKeyCount()902 {903 EnsureNotDisposed();904 // Don't require a transaction. We don't want to throw for "Base" keys.905 906 int subkeys = 0;907 int junk = 0;908 int ret = Win32Native.RegQueryInfoKey(_hkey,909 null,910 null,911 Win32Native.NULL,912 ref subkeys, // subkeys913 null,914 null,915 ref junk, // values916 null,917 null,918 null,919 null);920 921 if (ret != 0)922 Win32Error(ret, null);923 return subkeys;924 }925 926 /// <summary>927 /// <para>Retrieves an array of strings containing all the subkey names.928 /// Utilizes Transaction.Current for its transaction.</para>929 /// </summary>930 /// <returns>A string array containing all the subkey names.</returns>931 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey932 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]933 public string[] GetSubKeyNames()934 {935 CheckKeyReadPermission();936 return InternalGetSubKeyNames();937 }938 939 // Suppressed to be consistent with naming in Microsoft.Win32.RegistryKey940 [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly")]941 internal string[] InternalGetSubKeyNames()942 {943 EnsureNotDisposed();944 // Don't require a transaction. We don't want to throw for "Base" keys.945 int subkeys = InternalSubKeyCount();946 string[] names = new string[subkeys]; // Returns 0-length array if empty.947 948 if (subkeys > 0)949 {950 StringBuilder name = new StringBuilder(256);951 int namelen;952 953 for (int i = 0; i < subkeys; i++)954 {955 namelen = name.Capacity; // Don't remove this. The API's doesn't work if this is not properly initialised.956 int ret = Win32Native.RegEnumKeyEx(_hkey,957 i,958 name,959 out namelen,960 null,961 null,962 null,963 null);964 if (ret != 0)965 Win32Error(ret, null);966 names[i] = name.ToString();967 }968 }969 970 return names;971 }972 973 /// <summary>974 /// <para>Retrieves the count of values.975 /// Utilizes Transaction.Current for its transaction.</para>976 /// </summary>977 /// <returns>A count of values.</returns>978 public int ValueCount979 {980 get981 {982 CheckKeyReadPermission();983 return InternalValueCount();984 }985 }986 987 internal int InternalValueCount()988 {989 EnsureNotDisposed();990 // Don't require a transaction. We don't want to throw for "Base" keys.991 int values = 0;992 int junk = 0;993 int ret = Win32Native.RegQueryInfoKey(_hkey,994 null,995 null,996 Win32Native.NULL,997 ref junk, // subkeys998 null,999 null,1000 ref values, // values1001 null,1002 null,1003 null,1004 null);1005 if (ret != 0)1006 Win32Error(ret, null);1007 return values;1008 }1009 1010 /// <summary>1011 /// <para>Retrieves an array of strings containing all the value names.1012 /// Utilizes Transaction.Current for its transaction.</para>1013 /// </summary>1014 /// <returns>All the value names.</returns>1015 public string[] GetValueNames()1016 {1017 CheckKeyReadPermission();1018 EnsureNotDisposed();1019 // Don't require a transaction. We don't want to throw for "Base" keys.1020 1021 int values = InternalValueCount();1022 string[] names = new string[values];1023 1024 if (values > 0)1025 {1026 StringBuilder name = new StringBuilder(256);1027 int namelen;1028 int currentlen;1029 int ret;1030 1031 for (int i = 0; i < values; i++)1032 {1033 currentlen = name.Capacity;1034 ret = Win32Native.ERROR_MORE_DATA;1035 1036 // loop while we get error_more_data or until we have exceeded1037 // the max name length.1038 while (Win32Native.ERROR_MORE_DATA == ret)1039 {1040 namelen = currentlen;1041 ret = Win32Native.RegEnumValue(_hkey,1042 i,1043 name,1044 ref namelen,1045 Win32Native.NULL,1046 null,1047 null,1048 null);1049 1050 if (ret != 0)1051 {1052 if (ret != Win32Native.ERROR_MORE_DATA)1053 Win32Error(ret, null);1054 1055 // We got ERROR_MORE_DATA. Let's see if we can make the buffer1056 // bigger.1057 if (MaxValueNameLength == currentlen)1058 Win32Error(ret, null);1059 1060 currentlen = currentlen * 2;1061 if (MaxValueNameLength < currentlen)1062 currentlen = MaxValueNameLength;1063 1064 // Allocate a new buffer.1065 name = new StringBuilder(currentlen);1066 }1067 }1068 1069 names[i] = name.ToString();1070 }1071 }1072 1073 return names;1074 }1075 1076 /// <summary>1077 /// <para>Retrieves the specified value. null is returned if the value1078 /// doesn't exist. Utilizes Transaction.Current for its transaction.</para>1079 /// <para>Note that name can be null or "", at which point the1080 /// unnamed or default value of this Registry key is returned, if any.</para>1081 /// </summary>1082 /// <returns>The data associated with the value.</returns>1083 /// <param name="name">Name of value to retrieve.</param>1084 public object GetValue(string name)1085 {1086 CheckValueReadPermission(name);1087 return InternalGetValue(name, null, false, true);1088 }1089 1090 /// <summary>1091 /// <para>Retrieves the specified value. null is returned if the value1092 /// doesn't exist. Utilizes Transaction.Current for its transaction.</para>1093 /// <para>Note that name can be null or "", at which point the1094 /// unnamed or default value of this Registry key is returned, if any.</para>1095 /// </summary>1096 /// <returns>The data associated with the value.</returns>1097 /// <param name="name">Name of value to retrieve.</param>1098 /// <param name="defaultValue">Value to return if name doesn't exist.</param>1099 public object GetValue(string name, object defaultValue)1100 {1101 CheckValueReadPermission(name);1102 return InternalGetValue(name, defaultValue, false, true);1103 }1104 1105 /// <summary>1106 /// <para>Retrieves the specified value. null is returned if the value1107 /// doesn't exist. Utilizes Transaction.Current for its transaction.</para>1108 /// <para>Note that name can be null or "", at which point the1109 /// unnamed or default value of this Registry key is returned, if any.</para>1110 /// </summary>1111 /// <returns>The data associated with the value.</returns>1112 /// <param name="name">Name of value to retrieve.</param>1113 /// <param name="defaultValue">Value to return if name doesn't exist.</param>1114 /// <param name="options">One of the Microsoft.Win32.RegistryValueOptions values that specifies1115 /// optional processing of the retrieved value.</param>1116 [ComVisible(false)]1117 public object GetValue(string name, object defaultValue, RegistryValueOptions options)1118 {1119 if (options < RegistryValueOptions.None || options > RegistryValueOptions.DoNotExpandEnvironmentNames)1120 {1121 string resourceTemplate = RegistryProviderStrings.Arg_EnumIllegalVal;1122 string resource = string.Format(CultureInfo.CurrentCulture, resourceTemplate, options.ToString());1123 throw new ArgumentException(resource);1124 }1125 1126 bool doNotExpand = (options == RegistryValueOptions.DoNotExpandEnvironmentNames);1127 CheckValueReadPermission(name);1128 return InternalGetValue(name, defaultValue, doNotExpand, true);1129 }1130 1131 internal object InternalGetValue(string name, object defaultValue, bool doNotExpand, bool checkSecurity)1132 {1133 if (checkSecurity)1134 {1135 // Name can be null! It's the most common use of RegQueryValueEx1136 EnsureNotDisposed();1137 }1138 1139 // Don't require a transaction. We don't want to throw for "Base" keys.1140 1141 object data = defaultValue;1142 int type = 0;1143 int datasize = 0;1144 1145 int ret = Win32Native.RegQueryValueEx(_hkey, name, null, ref type, (byte[])null, ref datasize);1146 1147 if (ret != 0)1148 {1149 // For stuff like ERROR_FILE_NOT_FOUND, we want to return null (data).1150 // Some OS's returned ERROR_MORE_DATA even in success cases, so we1151 // want to continue on through the function.1152 if (ret != Win32Native.ERROR_MORE_DATA)1153 return data;1154 }1155 1156 switch (type)1157 {1158 case Win32Native.REG_DWORD_BIG_ENDIAN:1159 case Win32Native.REG_BINARY:1160 {1161 byte[] blob = new byte[datasize];1162 ret = Win32Native.RegQueryValueEx(_hkey, name, null, ref type, blob, ref datasize);1163 data = blob;1164 }1165 1166 break;1167 case Win32Native.REG_QWORD:1168 { // also REG_QWORD_LITTLE_ENDIAN1169 if (datasize > 8)1170 {1171 // prevent an AV in the edge case that datasize is larger than sizeof(long)1172 goto case Win32Native.REG_BINARY;1173 }1174 1175 long blob = 0;1176 BCLDebug.Assert(datasize == 8, "datasize==8");1177 // Here, datasize must be 8 when calling this1178 ret = Win32Native.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);1179 1180 data = blob;1181 }1182 1183 break;1184 case Win32Native.REG_DWORD:1185 { // also REG_DWORD_LITTLE_ENDIAN1186 if (datasize > 4)1187 {1188 // prevent an AV in the edge case that datasize is larger than sizeof(int)1189 goto case Win32Native.REG_QWORD;1190 }1191 1192 int blob = 0;1193 BCLDebug.Assert(datasize == 4, "datasize==4");1194 // Here, datasize must be four when calling this1195 ret = Win32Native.RegQueryValueEx(_hkey, name, null, ref type, ref blob, ref datasize);1196 1197 data = blob;1198 }1199 1200 break;