mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add two properties in `ProviderInfo` class: `ItemSeparator` and `AltItemSeparator`. On windows, the default values for those two properties are `ItemSeparator = '\'` and `AltItemSeparator = '/'`. On unix, the default values for those two properties are `ItemSeparator = '/'` and `AltItemSeparator = '\'`. Registry provider is the only exception, both properties for it have the value `\`.
4168 lines
152 KiB
C#
4168 lines
152 KiB
C#
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// Licensed under the MIT License.
|
|
#if !UNIX
|
|
|
|
using System;
|
|
using System.Collections.ObjectModel;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
using Microsoft.Win32;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Internal;
|
|
using System.Management.Automation.Provider;
|
|
using Dbg = System.Management.Automation;
|
|
using Microsoft.PowerShell.Commands.Internal;
|
|
|
|
namespace Microsoft.PowerShell.Commands
|
|
{
|
|
/// <summary>
|
|
/// Provider that provides access to Registry through cmdlets. This provider
|
|
/// implements <see cref="System.Management.Automation.Provider.NavigationCmdletProvider"/>,
|
|
/// <see cref="System.Management.Automation.Provider.IPropertyCmdletProvider"/>,
|
|
/// <see cref="System.Management.Automation.Provider.IDynamicPropertyCmdletProvider"/>,
|
|
/// <see cref="System.Management.Automation.Provider.ISecurityDescriptorCmdletProvider"/>
|
|
/// interfaces.
|
|
/// </summary>
|
|
/// <!--
|
|
///
|
|
/// INSTALLATION:
|
|
///
|
|
/// Type the following at an msh prompt:
|
|
///
|
|
/// new-PSProvider -Path "REG.cmdletprovider" -description "My registry navigation provider"
|
|
///
|
|
/// TO EXERCISE THE PROVIDER:
|
|
///
|
|
/// Get-PSDrive
|
|
/// set-location HKLM:\software
|
|
/// get-childitem
|
|
/// New-PSDrive -PSProvider REG -name HKCR -root HKEY_CLASSES_ROOT\CLSID
|
|
/// set-location HKCR:
|
|
/// get-childitem "{0000*"
|
|
///
|
|
/// The CmdletProvider attribute defines the name and capabilities of the provider.
|
|
/// The first parameter is the default friendly name for the provider. The second parameter
|
|
/// is the provider name which, along with some assembly information like version, company, etc.
|
|
/// is used as a fully-qualified provider name which can be used for disambiguation.
|
|
/// The third parameter states the capabilities of the provider.
|
|
///
|
|
/// -->
|
|
#if CORECLR // System.Transaction namespace is not in CoreClr.
|
|
[CmdletProvider(RegistryProvider.ProviderName, ProviderCapabilities.ShouldProcess)]
|
|
#else
|
|
[CmdletProvider(RegistryProvider.ProviderName, ProviderCapabilities.ShouldProcess | ProviderCapabilities.Transactions)]
|
|
#endif
|
|
[OutputType(typeof(string), ProviderCmdlet = ProviderCmdlet.MoveItemProperty)]
|
|
[OutputType(typeof(RegistryKey), typeof(string), ProviderCmdlet = ProviderCmdlet.GetChildItem)]
|
|
[OutputType(typeof(RegistryKey), ProviderCmdlet = ProviderCmdlet.GetItem)]
|
|
[OutputType(typeof(System.Security.AccessControl.RegistrySecurity), ProviderCmdlet = ProviderCmdlet.GetAcl)]
|
|
[OutputType(typeof(Microsoft.Win32.RegistryKey), ProviderCmdlet = ProviderCmdlet.GetChildItem)]
|
|
[OutputType(typeof(RegistryKey), ProviderCmdlet = ProviderCmdlet.GetItem)]
|
|
[OutputType(typeof(RegistryKey), typeof(string), typeof(Int32), typeof(Int64), ProviderCmdlet = ProviderCmdlet.GetItemProperty)]
|
|
[OutputType(typeof(RegistryKey), ProviderCmdlet = ProviderCmdlet.NewItem)]
|
|
public sealed partial class RegistryProvider :
|
|
NavigationCmdletProvider,
|
|
IPropertyCmdletProvider,
|
|
IDynamicPropertyCmdletProvider,
|
|
ISecurityDescriptorCmdletProvider
|
|
{
|
|
#region tracer
|
|
|
|
/// <summary>
|
|
/// An instance of the PSTraceSource class used for trace output
|
|
/// using "ProviderProvider" as the category.
|
|
/// </summary>
|
|
[Dbg.TraceSourceAttribute(
|
|
"RegistryProvider",
|
|
"The namespace navigation provider for the Windows Registry")]
|
|
private static Dbg.PSTraceSource s_tracer =
|
|
Dbg.PSTraceSource.GetTracer("RegistryProvider",
|
|
"The namespace navigation provider for the Windows Registry");
|
|
|
|
#endregion tracer
|
|
|
|
/// <summary>
|
|
/// Gets the name of the provider.
|
|
/// </summary>
|
|
public const string ProviderName = "Registry";
|
|
|
|
#region CmdletProvider overrides
|
|
|
|
/// <summary>
|
|
/// Gets the alternate item separator character for this provider.
|
|
/// </summary>
|
|
public override char AltItemSeparator => ItemSeparator;
|
|
|
|
#endregion
|
|
|
|
#region DriveCmdletProvider overrides
|
|
|
|
/// <summary>
|
|
/// Verifies that the new drive has a valid root.
|
|
/// </summary>
|
|
/// <returns>A PSDriveInfo object.</returns>
|
|
/// <!--
|
|
/// It also givesthe provider an opportunity to return a
|
|
/// derived class of PSDriveInfo which can contain provider specific
|
|
/// information about the drive.This may be done for performance
|
|
/// or reliability reasons or toprovide extra data to all calls
|
|
/// using the drive
|
|
/// -->
|
|
protected override PSDriveInfo NewDrive(PSDriveInfo drive)
|
|
{
|
|
if (drive == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("drive");
|
|
}
|
|
|
|
if (!ItemExists(drive.Root))
|
|
{
|
|
Exception e = new ArgumentException(RegistryProviderStrings.NewDriveRootDoesNotExist);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.InvalidArgument,
|
|
drive.Root));
|
|
}
|
|
|
|
return drive;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER registry drives during provider initialization.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// After the Start method is called on a provider, the InitializeDefaultDrives
|
|
/// method is called. This is an opportunity for the provider to
|
|
/// mount drives that are important to it. For instance, the Active Directory
|
|
/// provider might mount a drive for the defaultNamingContext if the
|
|
/// machine is joined to a domain. The FileSystem mounts all drives then available.
|
|
/// </remarks>
|
|
protected override Collection<PSDriveInfo> InitializeDefaultDrives()
|
|
{
|
|
Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>();
|
|
|
|
drives.Add(
|
|
new PSDriveInfo(
|
|
"HKLM",
|
|
ProviderInfo,
|
|
"HKEY_LOCAL_MACHINE",
|
|
RegistryProviderStrings.HKLMDriveDescription,
|
|
null));
|
|
|
|
drives.Add(
|
|
new PSDriveInfo(
|
|
"HKCU",
|
|
ProviderInfo,
|
|
"HKEY_CURRENT_USER",
|
|
RegistryProviderStrings.HKCUDriveDescription,
|
|
null));
|
|
|
|
return drives;
|
|
}
|
|
|
|
#endregion DriveCmdletProvider overrides
|
|
|
|
#region ItemCmdletProvider overrides
|
|
|
|
/// <summary>
|
|
/// Determines if the specified <paramref name="path"/> is syntactically and semantically valid.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to validate.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the path is valid, or False otherwise.
|
|
/// </returns>
|
|
protected override bool IsValidPath(string path)
|
|
{
|
|
bool result = true;
|
|
|
|
do // false loop
|
|
{
|
|
// There really aren't any illegal characters or syntactical patterns
|
|
// to validate, so just ensure that the path starts with one of the hive roots.
|
|
|
|
string root = NormalizePath(path);
|
|
root = root.TrimStart(StringLiterals.DefaultPathSeparator);
|
|
root = root.TrimEnd(StringLiterals.DefaultPathSeparator);
|
|
|
|
int pathSeparator = root.IndexOf(StringLiterals.DefaultPathSeparator);
|
|
|
|
if (pathSeparator != -1)
|
|
{
|
|
root = root.Substring(0, pathSeparator);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(root))
|
|
{
|
|
// An empty path means that we are at the root and should
|
|
// enumerate the hives. So that is a valid path.
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
if (GetHiveRoot(root) == null)
|
|
{
|
|
result = false;
|
|
}
|
|
} while (false);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the RegistryKey item at the specified <paramref name="path"/>
|
|
/// and writes it to the pipeline using the WriteObject method.
|
|
/// Any non-terminating exceptions are written to the WriteError method.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to retrieve.
|
|
/// </param>
|
|
protected override void GetItem(string path)
|
|
{
|
|
// Get the registry item
|
|
|
|
IRegistryWrapper result = GetRegkeyForPathWriteIfError(path, false);
|
|
|
|
if (result == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Write out the result
|
|
|
|
WriteRegistryItemObject(result, path);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets registry values at <paramref name="path "/> to the <paramref name="value"/> specified.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item that is to be set. Only registry values can be set using
|
|
/// this method.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// The new value for the registry value.
|
|
/// </param>
|
|
protected override void SetItem(string path, object value)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.SetItemAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.SetItemResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
value);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// Get the registry item
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check to see if the type was specified by the user
|
|
|
|
bool valueSet = false;
|
|
if (DynamicParameters != null)
|
|
{
|
|
RegistryProviderSetItemDynamicParameter dynParams =
|
|
DynamicParameters as RegistryProviderSetItemDynamicParameter;
|
|
|
|
if (dynParams != null)
|
|
{
|
|
try
|
|
{
|
|
// Convert the parameter to a RegistryValueKind
|
|
|
|
RegistryValueKind kind = dynParams.Type;
|
|
|
|
key.SetValue(null, value, kind);
|
|
valueSet = true;
|
|
}
|
|
catch (ArgumentException argException)
|
|
{
|
|
WriteError(new ErrorRecord(argException, argException.GetType().FullName, ErrorCategory.InvalidArgument, null));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!valueSet)
|
|
{
|
|
try
|
|
{
|
|
// Set the value
|
|
key.SetValue(null, value);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Write out the result
|
|
object result = value;
|
|
|
|
// Since SetValue can munge the data to a specified
|
|
// type (RegistryValueKind), retrieve the value again
|
|
// to output it in the correct form to the user.
|
|
result = ReadExistingKeyValue(key, null);
|
|
key.Close();
|
|
|
|
WriteItemObject(result, path, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the dynamic parameters for the SetItem method.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// Ignored.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// Ignored.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An instance of the <see cref="Microsoft.PowerShell.Commands.RegistryProviderSetItemDynamicParameter"/> class which
|
|
/// contains a parameter for the Type.
|
|
/// </returns>
|
|
protected override object SetItemDynamicParameters(string path, object value)
|
|
{
|
|
return new RegistryProviderSetItemDynamicParameter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears the item at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item that is to be cleared. Only registry values can be cleared using
|
|
/// this method.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// The registry provider implements this by removing all the values for the specified key.
|
|
/// The item that is cleared is written to the WriteObject method.
|
|
/// If the path is to a value, then an ArgumentException is written.
|
|
/// </remarks>
|
|
protected override void ClearItem(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
// Confirm the clear item with the user
|
|
|
|
string action = RegistryProviderStrings.ClearItemAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.ClearItemResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// Get the registry item
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string[] valueNames;
|
|
|
|
try
|
|
{
|
|
// Remove each value
|
|
valueNames = key.GetValueNames();
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, path));
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
|
|
for (int index = 0; index < valueNames.Length; ++index)
|
|
{
|
|
try
|
|
{
|
|
key.DeleteValue(valueNames[index]);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to delete the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to delete the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
|
|
// Write out the key
|
|
|
|
WriteRegistryItemObject(key, path);
|
|
}
|
|
}
|
|
|
|
#endregion ItemCmdletProvider overrides
|
|
|
|
#region ContainerCmdletProvider overrides
|
|
|
|
/// <summary>
|
|
/// Gets all the child keys and values of the key at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to get the child keys of.
|
|
/// </param>/
|
|
/// <param name="recurse">
|
|
/// Determines if the call should be recursive. If true, all subkeys of
|
|
/// the key at the specified path will be written. If false, only the
|
|
/// immediate children of the key at the specified path will be written.
|
|
/// </param>
|
|
/// <param name="depth">
|
|
/// Current depth of recursion; special case uint.MaxValue performs full recursion.
|
|
/// </param>
|
|
protected override void GetChildItems(
|
|
string path,
|
|
bool recurse,
|
|
uint depth)
|
|
{
|
|
s_tracer.WriteLine("recurse = {0}, depth = {1}", recurse, depth);
|
|
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (IsHiveContainer(path))
|
|
{
|
|
// If the path is empty or it is / or \, return all the hives
|
|
|
|
foreach (string hiveName in s_hiveNames)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetItem(hiveName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the key at the specified path
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, false);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Get all the subkeys of the specified path
|
|
|
|
string[] keyNames = key.GetSubKeyNames();
|
|
key.Close();
|
|
|
|
if (keyNames != null)
|
|
{
|
|
foreach (string subkeyName in keyNames)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(subkeyName))
|
|
{
|
|
string keypath = path;
|
|
|
|
try
|
|
{
|
|
// Generate the path for each key name
|
|
|
|
keypath = MakePath(path, subkeyName, childIsLeaf: true);
|
|
|
|
if (!string.IsNullOrEmpty(keypath))
|
|
{
|
|
// Call GetItem to retrieve the RegistryKey object
|
|
// and write it to the WriteObject method.
|
|
|
|
IRegistryWrapper resultKey = GetRegkeyForPath(keypath, false);
|
|
|
|
if (resultKey != null)
|
|
{
|
|
WriteRegistryItemObject(resultKey, keypath);
|
|
}
|
|
|
|
// Now recurse if necessary
|
|
|
|
if (recurse)
|
|
{
|
|
// Limiter for recursion
|
|
if (depth > 0) // this includes special case 'depth == uint.MaxValue' for unlimited recursion
|
|
{
|
|
GetChildItems(keypath, recurse, depth - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, keypath));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, keypath));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, keypath));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all the child key and value names of the key at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to get the child names from.
|
|
/// </param>
|
|
/// <param name="returnContainers">
|
|
/// Ignored since the registry provider does not implement filtering.
|
|
/// Normally, if this parameter is ReturnAllContainers then all subkeys should be
|
|
/// returned. If it is false, then only those subkeys that match the
|
|
/// filter should be returned.
|
|
/// </param>
|
|
protected override void GetChildNames(
|
|
string path,
|
|
ReturnContainers returnContainers)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (path.Length == 0)
|
|
{
|
|
// If the path is empty get the names of the hives
|
|
|
|
foreach (string hiveName in s_hiveNames)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WriteItemObject(hiveName, hiveName, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the key at the specified path
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, false);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Get the child key names
|
|
|
|
string[] results = key.GetSubKeyNames();
|
|
key.Close();
|
|
|
|
// Write the child key names to the WriteItemObject method
|
|
|
|
for (int index = 0; index < results.Length; ++index)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string childName = EscapeChildName(results[index]);
|
|
string childPath = MakePath(path, childName, childIsLeaf: true);
|
|
|
|
WriteItemObject(childName, childPath, true);
|
|
}
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
}
|
|
|
|
private const string charactersThatNeedEscaping = ".*?[]:";
|
|
|
|
/// <summary>
|
|
/// Escapes the characters in the registry key path that are used by globbing and
|
|
/// path.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to escape.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The escaped path.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// This method handles surrogate pairs. Please see msdn documentation
|
|
/// </remarks>
|
|
private static string EscapeSpecialChars(string path)
|
|
{
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
// Get the text enumerator..this will iterate through each character
|
|
// the character can be a surrogate pair
|
|
System.Globalization.TextElementEnumerator textEnumerator =
|
|
System.Globalization.StringInfo.GetTextElementEnumerator(path);
|
|
|
|
Dbg.Diagnostics.Assert(
|
|
textEnumerator != null,
|
|
string.Format(CultureInfo.CurrentCulture, "Cannot get a text enumerator for name {0}", path));
|
|
|
|
while (textEnumerator.MoveNext())
|
|
{
|
|
// Iterate through each element and findout whether
|
|
// any text needs escaping
|
|
string textElement = textEnumerator.GetTextElement();
|
|
|
|
// NTRAID#Windows Out of Band Releases-939036-2006/07/12-LeeHolm
|
|
// A single character can never contain a string of
|
|
// charactersThatNeedEscaping, so this method does nothing. The fix
|
|
// is to remove all calls to this escaping code, though, as this escaping
|
|
// should not be done.
|
|
if (textElement.Contains(charactersThatNeedEscaping))
|
|
{
|
|
// This text element needs espacing
|
|
result.Append("`");
|
|
}
|
|
|
|
result.Append(textElement);
|
|
}
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Escapes the characters in the registry key name that are used by globbing and
|
|
/// path.
|
|
/// </summary>
|
|
/// <param name="name">
|
|
/// The name to escape.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The escaped name.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// This method handles surrogate pairs. Please see msdn documentation
|
|
/// </remarks>
|
|
private static string EscapeChildName(string name)
|
|
{
|
|
StringBuilder result = new StringBuilder();
|
|
|
|
// Get the text enumerator..this will iterate through each character
|
|
// the character can be a surrogate pair
|
|
System.Globalization.TextElementEnumerator textEnumerator =
|
|
System.Globalization.StringInfo.GetTextElementEnumerator(name);
|
|
|
|
Dbg.Diagnostics.Assert(
|
|
textEnumerator != null,
|
|
string.Format(CultureInfo.CurrentCulture, "Cannot get a text enumerator for name {0}", name));
|
|
|
|
while (textEnumerator.MoveNext())
|
|
{
|
|
// Iterate through each element and findout whether
|
|
// any text needs escaping
|
|
string textElement = textEnumerator.GetTextElement();
|
|
|
|
// NTRAID#Windows Out of Band Releases-939036-2006/07/12-LeeHolm
|
|
// A single character can never contain a string of
|
|
// charactersThatNeedEscaping, so this method does nothing. The fix
|
|
// is to remove all calls to this escaping code, though, as this escaping
|
|
// should not be done.
|
|
if (textElement.Contains(charactersThatNeedEscaping))
|
|
{
|
|
// This text element needs espacing
|
|
result.Append("`");
|
|
}
|
|
|
|
result.Append(textElement);
|
|
}
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames the key at the specified <paramref name="path"/> to <paramref name="newName"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to rename.
|
|
/// </param>
|
|
/// <param name="newName">
|
|
/// The new name of the key.
|
|
/// </param>
|
|
protected override void RenameItem(
|
|
string path,
|
|
string newName)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(newName))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("newName");
|
|
}
|
|
|
|
s_tracer.WriteLine("newName = {0}", newName);
|
|
|
|
string parentPath = GetParentPath(path, null);
|
|
string newPath = MakePath(parentPath, newName);
|
|
|
|
// Make sure we aren't going to overwrite an existing item
|
|
|
|
bool exists = ItemExists(newPath);
|
|
|
|
if (exists)
|
|
{
|
|
Exception e = new ArgumentException(RegistryProviderStrings.RenameItemAlreadyExists);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.InvalidArgument,
|
|
newPath));
|
|
|
|
return;
|
|
}
|
|
// Confirm the rename item with the user
|
|
|
|
string action = RegistryProviderStrings.RenameItemAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.RenameItemResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
newPath);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// Implement rename as a move operation
|
|
|
|
MoveRegistryItem(path, newPath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new registry key or value at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the new key to create.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The type is ignored because this provider only creates
|
|
/// registry keys.
|
|
/// </param>
|
|
/// <param name="newItem">
|
|
/// The newItem is ignored because the provider creates the
|
|
/// key based on the path.
|
|
/// </param>
|
|
protected override void NewItem(
|
|
string path,
|
|
string type,
|
|
object newItem)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
// Confirm the new item with the user
|
|
|
|
string action = RegistryProviderStrings.NewItemAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.NewItemResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path);
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// Check to see if the key already exists
|
|
IRegistryWrapper resultKey = GetRegkeyForPath(path, false);
|
|
|
|
if (resultKey != null)
|
|
{
|
|
if (!Force)
|
|
{
|
|
Exception e = new System.IO.IOException(RegistryProviderStrings.KeyAlreadyExists);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.ResourceExists,
|
|
resultKey));
|
|
|
|
resultKey.Close();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Remove the existing key before creating the new one
|
|
resultKey.Close();
|
|
RemoveItem(path, false);
|
|
}
|
|
}
|
|
|
|
if (Force)
|
|
{
|
|
if (!CreateIntermediateKeys(path))
|
|
{
|
|
// We are unable to create Intermediate keys. Just return.
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Get the parent and child portions of the path
|
|
|
|
string parentPath = GetParentPath(path, null);
|
|
string childName = GetChildName(path);
|
|
|
|
// Get the key at the specified path
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(parentPath, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Create the new subkey
|
|
IRegistryWrapper newKey = key.CreateSubKey(childName);
|
|
key.Close();
|
|
|
|
try
|
|
{
|
|
// Set the default key value if the value and type were specified
|
|
|
|
if (newItem != null)
|
|
{
|
|
RegistryValueKind kind;
|
|
if (!ParseKind(type, out kind))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetRegistryValue(newKey, string.Empty, newItem, kind, path, false);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
// The key has been created, but the default value failed to be set.
|
|
// If possible, just write an error instead of failing the entire operation.
|
|
|
|
if ((exception is ArgumentException) ||
|
|
(exception is InvalidCastException) ||
|
|
(exception is System.IO.IOException) ||
|
|
(exception is System.Security.SecurityException) ||
|
|
(exception is System.UnauthorizedAccessException) ||
|
|
(exception is NotSupportedException))
|
|
{
|
|
ErrorRecord rec = new ErrorRecord(
|
|
exception,
|
|
exception.GetType().FullName,
|
|
ErrorCategory.WriteError,
|
|
newKey);
|
|
rec.ErrorDetails = new ErrorDetails(StringUtil.Format(RegistryProviderStrings.KeyCreatedValueFailed, childName));
|
|
WriteError(rec);
|
|
}
|
|
else
|
|
throw;
|
|
}
|
|
|
|
// Write the new key out.
|
|
WriteRegistryItemObject(newKey, path);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (ArgumentException argException)
|
|
{
|
|
WriteError(new ErrorRecord(argException, argException.GetType().FullName, ErrorCategory.InvalidArgument, path));
|
|
}
|
|
catch (NotSupportedException notSupportedException)
|
|
{
|
|
WriteError(new ErrorRecord(notSupportedException, notSupportedException.GetType().FullName, ErrorCategory.InvalidOperation, path));
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the specified registry key and all sub-keys.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to remove.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// Ignored. All removes are recursive because the
|
|
/// registry provider does not support filters.
|
|
/// </param>
|
|
protected override void RemoveItem(
|
|
string path,
|
|
bool recurse)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
s_tracer.WriteLine("recurse = {0}", recurse);
|
|
|
|
// Get the parent and child portions of the path
|
|
|
|
string parentPath = GetParentPath(path, null);
|
|
string childName = GetChildName(path);
|
|
|
|
// Get the parent key
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(parentPath, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Confirm the remove item with the user
|
|
|
|
string action = RegistryProviderStrings.RemoveKeyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.RemoveKeyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
try
|
|
{
|
|
key.DeleteSubKeyTree(childName);
|
|
}
|
|
catch (ArgumentException argumentException)
|
|
{
|
|
WriteError(new ErrorRecord(argumentException, argumentException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (NotSupportedException notSupportedException)
|
|
{
|
|
WriteError(new ErrorRecord(notSupportedException, notSupportedException.GetType().FullName, ErrorCategory.InvalidOperation, path));
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the key at the specified path exists.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to determine if it exists.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the key at the specified path exists, false otherwise.
|
|
/// </returns>
|
|
protected override bool ItemExists(string path)
|
|
{
|
|
bool result = false;
|
|
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (IsHiveContainer(path))
|
|
{
|
|
// an empty path, \ or / are valid because
|
|
// we will enumerate all the hives
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
IRegistryWrapper key = GetRegkeyForPath(path, false);
|
|
|
|
if (key != null)
|
|
{
|
|
result = true;
|
|
key.Close();
|
|
}
|
|
}
|
|
}
|
|
// Catch known non-terminating exceptions
|
|
catch (System.IO.IOException)
|
|
{
|
|
}
|
|
// In these cases, the item does exist
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
result = true;
|
|
}
|
|
catch (System.UnauthorizedAccessException)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the specified key has subkeys.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the key to determine if it has sub keys.
|
|
/// </param>
|
|
/// <returns>
|
|
/// True if the specified key has subkeys, false otherwise.
|
|
/// </returns>
|
|
protected override bool HasChildItems(string path)
|
|
{
|
|
bool result = false;
|
|
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
try
|
|
{
|
|
if (IsHiveContainer(path))
|
|
{
|
|
// An empty path will enumerate the hives
|
|
|
|
result = s_hiveNames.Length > 0;
|
|
}
|
|
else
|
|
{
|
|
IRegistryWrapper key = GetRegkeyForPath(path, false);
|
|
|
|
if (key != null)
|
|
{
|
|
result = key.SubKeyCount > 0;
|
|
key.Close();
|
|
}
|
|
}
|
|
}
|
|
catch (System.IO.IOException)
|
|
{
|
|
result = false;
|
|
}
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
result = false;
|
|
}
|
|
catch (System.UnauthorizedAccessException)
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the specified registry key to the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path of the registry key to copy.
|
|
/// </param>
|
|
/// <param name="destination">
|
|
/// The path to copy the key to.
|
|
/// </param>
|
|
/// <param name="recurse">
|
|
/// If true all subkeys should be copied. If false, only the
|
|
/// specified key should be copied.
|
|
/// </param>
|
|
protected override void CopyItem(
|
|
string path,
|
|
string destination,
|
|
bool recurse)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(destination))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("destination");
|
|
}
|
|
|
|
s_tracer.WriteLine("destination = {0}", destination);
|
|
s_tracer.WriteLine("recurse = {0}", recurse);
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, false);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
CopyRegistryKey(key, path, destination, recurse, true, false);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
private bool CopyRegistryKey(
|
|
IRegistryWrapper key,
|
|
string path,
|
|
string destination,
|
|
bool recurse,
|
|
bool streamResult,
|
|
bool streamFirstOnly)
|
|
{
|
|
bool result = true;
|
|
|
|
// Make sure we are not trying to do a recursive copy of a key
|
|
// to itself or a child of itself.
|
|
|
|
if (recurse)
|
|
{
|
|
if (ErrorIfDestinationIsSourceOrChildOfSource(path, destination))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Dbg.Diagnostics.Assert(
|
|
key != null,
|
|
"The key should have been validated by the caller");
|
|
|
|
Dbg.Diagnostics.Assert(
|
|
!string.IsNullOrEmpty(path),
|
|
"The path should have been validated by the caller");
|
|
|
|
Dbg.Diagnostics.Assert(
|
|
!string.IsNullOrEmpty(destination),
|
|
"The destination should have been validated by the caller");
|
|
|
|
s_tracer.WriteLine("destination = {0}", destination);
|
|
|
|
// Get the parent key of the destination
|
|
// If the destination already exists and is a key, then it becomes
|
|
// the container of the source. If the key doesn't already exist
|
|
// the parent of the destination path becomes the container of source.
|
|
|
|
IRegistryWrapper newParentKey = GetRegkeyForPath(destination, true);
|
|
string destinationName = GetChildName(path);
|
|
string destinationParent = destination;
|
|
|
|
if (newParentKey == null)
|
|
{
|
|
destinationParent = GetParentPath(destination, null);
|
|
destinationName = GetChildName(destination);
|
|
|
|
newParentKey = GetRegkeyForPathWriteIfError(destinationParent, true);
|
|
}
|
|
|
|
if (newParentKey == null)
|
|
{
|
|
// The key was not found.
|
|
// An error should have been written by GetRegkeyForPathWriteIfError
|
|
return false;
|
|
}
|
|
|
|
string destinationPath = MakePath(destinationParent, destinationName);
|
|
|
|
// Confirm the copy item with the user
|
|
|
|
string action = RegistryProviderStrings.CopyKeyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.CopyKeyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
destination);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// Create new key under the parent
|
|
|
|
IRegistryWrapper newKey = null;
|
|
try
|
|
{
|
|
newKey = newParentKey.CreateSubKey(destinationName);
|
|
}
|
|
catch (NotSupportedException e)
|
|
{
|
|
WriteError(new ErrorRecord(e, e.GetType().FullName, ErrorCategory.InvalidOperation, destinationName));
|
|
}
|
|
|
|
if (newKey != null)
|
|
{
|
|
// Now copy all the properties from the source to the destination
|
|
|
|
string[] valueNames = key.GetValueNames();
|
|
|
|
for (int index = 0; index < valueNames.Length; ++index)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
newParentKey.Close();
|
|
newKey.Close();
|
|
return false;
|
|
}
|
|
|
|
newKey.SetValue(
|
|
valueNames[index],
|
|
key.GetValue(valueNames[index], null, RegistryValueOptions.DoNotExpandEnvironmentNames),
|
|
key.GetValueKind(valueNames[index]));
|
|
}
|
|
|
|
if (streamResult)
|
|
{
|
|
// Write out the key that was copied
|
|
|
|
WriteRegistryItemObject(newKey, destinationPath);
|
|
|
|
if (streamFirstOnly)
|
|
{
|
|
streamResult = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
newParentKey.Close();
|
|
|
|
if (recurse)
|
|
{
|
|
// Copy all the subkeys
|
|
|
|
string[] subkeyNames = key.GetSubKeyNames();
|
|
|
|
for (int keyIndex = 0; keyIndex < subkeyNames.Length; ++keyIndex)
|
|
{
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Make the new path under the copy path.
|
|
|
|
string subKeyPath = MakePath(path, subkeyNames[keyIndex]);
|
|
string newSubKeyPath = MakePath(destinationPath, subkeyNames[keyIndex]);
|
|
|
|
IRegistryWrapper childKey = GetRegkeyForPath(subKeyPath, false);
|
|
|
|
bool subtreeResult = CopyRegistryKey(childKey, subKeyPath, newSubKeyPath, recurse, streamResult, streamFirstOnly);
|
|
|
|
childKey.Close();
|
|
|
|
if (!subtreeResult)
|
|
{
|
|
result = subtreeResult;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool ErrorIfDestinationIsSourceOrChildOfSource(
|
|
string sourcePath,
|
|
string destinationPath)
|
|
{
|
|
s_tracer.WriteLine("destinationPath = {0}", destinationPath);
|
|
|
|
// Note the paths have already been normalized so case-insensitive
|
|
// comparisons should be sufficient
|
|
|
|
bool result = false;
|
|
|
|
do
|
|
{
|
|
// See if the paths are equal
|
|
|
|
if (string.Compare(
|
|
sourcePath,
|
|
destinationPath,
|
|
StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
result = true;
|
|
break;
|
|
}
|
|
|
|
string newDestinationPath = GetParentPath(destinationPath, null);
|
|
|
|
if (string.IsNullOrEmpty(newDestinationPath))
|
|
{
|
|
// We reached the root so the destination must not be a child
|
|
// of the source
|
|
break;
|
|
}
|
|
|
|
if (string.Compare(
|
|
newDestinationPath,
|
|
destinationPath,
|
|
StringComparison.OrdinalIgnoreCase) == 0)
|
|
{
|
|
// We reached the root so the destination must not be a child
|
|
// of the source
|
|
break;
|
|
}
|
|
|
|
destinationPath = newDestinationPath;
|
|
} while (true);
|
|
|
|
if (result)
|
|
{
|
|
Exception e =
|
|
new ArgumentException(
|
|
RegistryProviderStrings.DestinationChildOfSource);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.InvalidArgument,
|
|
destinationPath));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#endregion ContainerCmdletProvider overrides
|
|
|
|
#region NavigationCmdletProvider overrides
|
|
|
|
/// <summary>
|
|
/// Determines if the key at the specified <paramref name="path"/> is a container.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to a key.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Since all registry keys are containers this method just checks
|
|
/// to see if the key exists and returns true if it is does or
|
|
/// false otherwise.
|
|
/// </returns>
|
|
protected override bool IsItemContainer(string path)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
bool result = false;
|
|
|
|
if (IsHiveContainer(path))
|
|
{
|
|
result = true;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
IRegistryWrapper key = GetRegkeyForPath(path, false);
|
|
|
|
if (key != null)
|
|
{
|
|
// All registry keys can be containers. Values are considered
|
|
// properties
|
|
key.Close();
|
|
result = true;
|
|
}
|
|
}
|
|
// Catch known exceptions that are not terminating
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (UnauthorizedAccessException unauthorizedAccess)
|
|
{
|
|
WriteError(new ErrorRecord(unauthorizedAccess, unauthorizedAccess.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the specified key.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path of the key to move.
|
|
/// </param>
|
|
/// <param name="destination">
|
|
/// The path to move the key to.
|
|
/// </param>
|
|
protected override void MoveItem(
|
|
string path,
|
|
string destination)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(destination))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("destination");
|
|
}
|
|
|
|
s_tracer.WriteLine("destination = {0}", destination);
|
|
|
|
// Confirm the rename item with the user
|
|
|
|
string action = RegistryProviderStrings.MoveItemAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.MoveItemResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
destination);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
MoveRegistryItem(path, destination);
|
|
}
|
|
}
|
|
|
|
private void MoveRegistryItem(string path, string destination)
|
|
{
|
|
// Implement move by copying the item and then removing it.
|
|
// The copy will write the item to the pipeline
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, false);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool continueWithRemove = false;
|
|
try
|
|
{
|
|
continueWithRemove = CopyRegistryKey(key, path, destination, true, true, true);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
|
|
key.Close();
|
|
|
|
string sourceParent = GetParentPath(path, null);
|
|
|
|
// If the destination is the same container as the source container don't do remove
|
|
// the source item because the source and destination are the same.
|
|
|
|
if (string.Equals(sourceParent, destination, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continueWithRemove = false;
|
|
}
|
|
|
|
if (continueWithRemove)
|
|
{
|
|
try
|
|
{
|
|
RemoveItem(path, true);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion NavigationCmdletProvider overrides
|
|
|
|
#region IPropertyCmdletProvider
|
|
|
|
/// <summary>
|
|
/// Gets the properties of the item specified by the <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to retrieve properties from.
|
|
/// </param>
|
|
/// <param name="providerSpecificPickList">
|
|
/// A list of properties that should be retrieved. If this parameter is null
|
|
/// or empty, all properties should be retrieved.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. An instance of PSObject representing the properties that were retrieved
|
|
/// should be passed to the WriteObject() method.
|
|
/// </returns>
|
|
public void GetProperty(
|
|
string path,
|
|
Collection<string> providerSpecificPickList)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get a set of matching properties on the key itself
|
|
IRegistryWrapper key;
|
|
Collection<string> filteredPropertyCollection;
|
|
GetFilteredRegistryKeyProperties(path,
|
|
providerSpecificPickList,
|
|
true,
|
|
false,
|
|
out key,
|
|
out filteredPropertyCollection);
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool valueAdded = false;
|
|
PSObject propertyResults = new PSObject();
|
|
foreach (string valueName in filteredPropertyCollection)
|
|
{
|
|
string notePropertyName = valueName;
|
|
if (string.IsNullOrEmpty(valueName))
|
|
{
|
|
// If the value name is empty then using "(default)"
|
|
// as the property name when adding the note, as
|
|
// PSObject does not allow an empty propertyName
|
|
|
|
notePropertyName = GetLocalizedDefaultToken();
|
|
}
|
|
|
|
propertyResults.Properties.Add(new PSNoteProperty(notePropertyName, key.GetValue(valueName)));
|
|
valueAdded = true;
|
|
}
|
|
|
|
key.Close();
|
|
|
|
if (valueAdded)
|
|
{
|
|
WritePropertyObject(propertyResults, path);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the specified properties of the item at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item to set the properties on.
|
|
/// </param>
|
|
/// <param name="propertyValue">
|
|
/// A PSObject which contains a collection of the name, type, value
|
|
/// of the properties to be set.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. An instance of PSObject representing the properties that were set
|
|
/// should be passed to the WriteObject() method.
|
|
/// </returns>
|
|
public void SetProperty(
|
|
string path,
|
|
PSObject propertyValue)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (propertyValue == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("propertyValue");
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
RegistryValueKind kind = RegistryValueKind.Unknown;
|
|
|
|
// Get the kind of the value using the dynamic parameters
|
|
|
|
if (DynamicParameters != null)
|
|
{
|
|
RegistryProviderSetItemDynamicParameter dynParams =
|
|
DynamicParameters as RegistryProviderSetItemDynamicParameter;
|
|
|
|
if (dynParams != null)
|
|
{
|
|
kind = dynParams.Type;
|
|
}
|
|
}
|
|
|
|
string action = RegistryProviderStrings.SetPropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.SetPropertyResourceTemplate;
|
|
|
|
foreach (PSMemberInfo property in propertyValue.Properties)
|
|
{
|
|
object newPropertyValue = property.Value;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
property.Name);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
try
|
|
{
|
|
SetRegistryValue(key, property.Name, newPropertyValue, kind, path);
|
|
}
|
|
catch (InvalidCastException invalidCast)
|
|
{
|
|
WriteError(new ErrorRecord(invalidCast, invalidCast.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to set the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, property.Name));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to set the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, property.Name));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, property.Name));
|
|
}
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// get-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="propertyValue">
|
|
/// A PSObject which contains a collection of the name, type, value
|
|
/// of the properties to be set.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object SetPropertyDynamicParameters(
|
|
string path,
|
|
PSObject propertyValue)
|
|
{
|
|
return new RegistryProviderSetItemDynamicParameter();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears a property of the item at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item on which to clear the property.
|
|
/// </param>
|
|
/// <param name="propertyToClear">
|
|
/// The name of the property to clear.
|
|
/// </param>
|
|
public void ClearProperty(
|
|
string path,
|
|
Collection<string> propertyToClear)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// get a set of matching properties on the key itself
|
|
IRegistryWrapper key;
|
|
Collection<string> filteredPropertyCollection;
|
|
GetFilteredRegistryKeyProperties(path,
|
|
propertyToClear,
|
|
false,
|
|
true,
|
|
out key,
|
|
out filteredPropertyCollection);
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string action = RegistryProviderStrings.ClearPropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.ClearPropertyResourceTemplate;
|
|
|
|
bool addedOnce = false;
|
|
PSObject result = new PSObject();
|
|
|
|
foreach (string valueName in filteredPropertyCollection)
|
|
{
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
valueName);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// reset the value of the property to its default value
|
|
object defaultValue = ResetRegistryKeyValue(key, valueName);
|
|
string propertyNameToAdd = valueName;
|
|
if (string.IsNullOrEmpty(valueName))
|
|
{
|
|
propertyNameToAdd = GetLocalizedDefaultToken();
|
|
}
|
|
|
|
result.Properties.Add(new PSNoteProperty(propertyNameToAdd, defaultValue));
|
|
addedOnce = true;
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
|
|
if (addedOnce)
|
|
{
|
|
WritePropertyObject(result, path);
|
|
}
|
|
}
|
|
|
|
#region Unimplemented methods
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// get-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="providerSpecificPickList">
|
|
/// A list of properties that should be retrieved. If this parameter is null
|
|
/// or empty, all properties should be retrieved.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object GetPropertyDynamicParameters(
|
|
string path,
|
|
Collection<string> providerSpecificPickList)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// clear-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="propertyToClear">
|
|
/// The name of the property to clear.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object ClearPropertyDynamicParameters(
|
|
string path,
|
|
Collection<string> propertyToClear)
|
|
{
|
|
return null;
|
|
}
|
|
#endregion Unimplemented methods
|
|
|
|
#endregion IPropertyCmdletProvider
|
|
|
|
#region IDynamicPropertyCmdletProvider
|
|
|
|
/// <summary>
|
|
/// Creates a new property on the specified item.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item on which the new property should be created.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the property that should be created.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The type of the property that should be created.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// The new value of the property that should be created.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. A PSObject representing the property that was created should
|
|
/// be passed to the WriteObject() method.
|
|
/// </returns>
|
|
/// <!--
|
|
/// Implement this method when you are providing access to a data store
|
|
/// that allows dynamic creation of properties.
|
|
/// -->
|
|
public void NewProperty(
|
|
string path,
|
|
string propertyName,
|
|
string type,
|
|
object value)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.NewPropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.NewPropertyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
propertyName);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
// convert the type to a RegistryValueKind
|
|
RegistryValueKind kind;
|
|
if (!ParseKind(type, out kind))
|
|
{
|
|
key.Close();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Check to see if the property already exists
|
|
// or overwrite if frce is on
|
|
if (Force || key.GetValue(propertyName) == null)
|
|
{
|
|
// Create the value
|
|
SetRegistryValue(key, propertyName, value, kind, path);
|
|
}
|
|
else
|
|
{
|
|
// The property already exists
|
|
|
|
System.IO.IOException e =
|
|
new System.IO.IOException(
|
|
RegistryProviderStrings.PropertyAlreadyExists);
|
|
WriteError(new ErrorRecord(e, e.GetType().FullName, ErrorCategory.ResourceExists, path));
|
|
key.Close();
|
|
return;
|
|
}
|
|
}
|
|
catch (ArgumentException argumentException)
|
|
{
|
|
WriteError(new ErrorRecord(argumentException, argumentException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (InvalidCastException invalidCast)
|
|
{
|
|
WriteError(new ErrorRecord(invalidCast, invalidCast.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a property on the item specified by the path.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item on which the property should be removed.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the property to be removed.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// Implement this method when you are providing access to a data store
|
|
/// that allows dynamic removal of properties.
|
|
/// </remarks>
|
|
public void RemoveProperty(
|
|
string path,
|
|
string propertyName)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
WildcardPattern propertyNamePattern =
|
|
WildcardPattern.Get(propertyName, WildcardOptions.IgnoreCase);
|
|
|
|
bool hadAMatch = false;
|
|
|
|
foreach (string valueName in key.GetValueNames())
|
|
{
|
|
if (
|
|
((!Context.SuppressWildcardExpansion) && (!propertyNamePattern.IsMatch(valueName))) ||
|
|
(Context.SuppressWildcardExpansion && (!string.Equals(valueName, propertyName, StringComparison.OrdinalIgnoreCase))))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hadAMatch = true;
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.RemovePropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.RemovePropertyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
valueName);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
string propertyNameToRemove = GetPropertyName(valueName);
|
|
|
|
try
|
|
{
|
|
// Remove the value
|
|
key.DeleteValue(propertyNameToRemove);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, propertyNameToRemove));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, propertyNameToRemove));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, propertyNameToRemove));
|
|
}
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
WriteErrorIfPerfectMatchNotFound(hadAMatch, path, propertyName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Renames a property of the item at the specified <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the item on which to rename the property.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The property to rename.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The new name of the property.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. A PSObject that represents the property that was renamed should be
|
|
/// passed to the WriteObject() method.
|
|
/// </returns>
|
|
public void RenameProperty(
|
|
string path,
|
|
string sourceProperty,
|
|
string destinationProperty)
|
|
{
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(path))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(path, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.RenamePropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.RenamePropertyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
path,
|
|
sourceProperty,
|
|
destinationProperty);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
try
|
|
{
|
|
MoveProperty(key, key, sourceProperty, destinationProperty);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, path));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies a property of the item at the specified <paramref name="path"/> to a new property on the
|
|
/// destination <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="sourcePath">
|
|
/// The path to the item on which to copy the property.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The name of the property to copy.
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The path to the item on which to copy the property to.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The destination property to copy to.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. A PSObject that represents the property that was copied should be
|
|
/// passed to the WriteObject() method.
|
|
/// </returns>
|
|
public void CopyProperty(
|
|
string sourcePath,
|
|
string sourceProperty,
|
|
string destinationPath,
|
|
string destinationProperty)
|
|
{
|
|
if (sourcePath == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("sourcePath");
|
|
}
|
|
|
|
if (destinationPath == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("destinationPath");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(sourcePath, destinationPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(sourcePath, false);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper destinationKey = GetRegkeyForPathWriteIfError(destinationPath, true);
|
|
if (destinationKey == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.CopyPropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.CopyPropertyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
sourcePath,
|
|
sourceProperty,
|
|
destinationPath,
|
|
destinationProperty);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
try
|
|
{
|
|
CopyProperty(key, destinationKey, sourceProperty, destinationProperty, true);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, sourcePath));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, sourcePath));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, sourcePath));
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves a property on an item specified by <paramref name="sourcePath"/>.
|
|
/// </summary>
|
|
/// <param name="sourcePath">
|
|
/// The path to the item on which to move the property.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The name of the property to move.
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The path to the item on which to move the property to.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The destination property to move to.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Nothing. A PSObject that represents the property that was moved should be
|
|
/// passed to the WriteObject() method.
|
|
/// </returns>
|
|
public void MoveProperty(
|
|
string sourcePath,
|
|
string sourceProperty,
|
|
string destinationPath,
|
|
string destinationProperty)
|
|
{
|
|
if (sourcePath == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("sourcePath");
|
|
}
|
|
|
|
if (destinationPath == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("destinationPath");
|
|
}
|
|
|
|
if (!CheckOperationNotAllowedOnHiveContainer(sourcePath, destinationPath))
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper key = GetRegkeyForPathWriteIfError(sourcePath, true);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
IRegistryWrapper destinationKey = GetRegkeyForPathWriteIfError(destinationPath, true);
|
|
if (destinationKey == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Confirm the set item with the user
|
|
|
|
string action = RegistryProviderStrings.MovePropertyAction;
|
|
|
|
string resourceTemplate = RegistryProviderStrings.MovePropertyResourceTemplate;
|
|
|
|
string resource =
|
|
string.Format(
|
|
Host.CurrentCulture,
|
|
resourceTemplate,
|
|
sourcePath,
|
|
sourceProperty,
|
|
destinationPath,
|
|
destinationProperty);
|
|
|
|
if (ShouldProcess(resource, action))
|
|
{
|
|
try
|
|
{
|
|
MoveProperty(key, destinationKey, sourceProperty, destinationProperty);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, sourcePath));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, sourcePath));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, sourcePath));
|
|
}
|
|
}
|
|
|
|
key.Close();
|
|
destinationKey.Close();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the parent path of the given <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to get the parent of.
|
|
/// </param>
|
|
/// <param name="root">
|
|
/// The root of the drive.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The parent path of the given path.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// Since the base class implementation of GetParentPath of HKLM:\foo would return
|
|
/// HKLM: we must add the \ back on.
|
|
/// </remarks>
|
|
protected override string GetParentPath(string path, string root)
|
|
{
|
|
string parentPath = base.GetParentPath(path, root);
|
|
|
|
// If the main path existed, we must do a semantic analysis
|
|
// to find the parent -- since path elements may contain
|
|
// path delimiters. We only need to do this comparison
|
|
// if the base implementation returns something in our namespace.
|
|
if (!string.Equals(parentPath, root, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
bool originalPathExists = ItemExists(path);
|
|
bool originalPathExistsWithRoot = false;
|
|
|
|
// This is an expensive test, only do it if we need to.
|
|
if (!originalPathExists)
|
|
originalPathExistsWithRoot = ItemExists(MakePath(root, path));
|
|
|
|
if ((!string.IsNullOrEmpty(parentPath)) && (originalPathExists || originalPathExistsWithRoot))
|
|
{
|
|
string parentPathToTest = parentPath;
|
|
|
|
do
|
|
{
|
|
parentPathToTest = parentPath;
|
|
if (originalPathExistsWithRoot)
|
|
parentPathToTest = MakePath(root, parentPath);
|
|
|
|
if (ItemExists(parentPathToTest))
|
|
break;
|
|
|
|
parentPath = base.GetParentPath(parentPath, root);
|
|
} while (!string.IsNullOrEmpty(parentPath));
|
|
}
|
|
}
|
|
|
|
return EnsureDriveIsRooted(parentPath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the child name for the given <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to get the leaf element of.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The leaf element of the given path.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// Since the base class implementation of GetChildName will return
|
|
/// normalized paths (with \), we must change them to forward slashes..
|
|
/// </remarks>
|
|
protected override string GetChildName(string path)
|
|
{
|
|
string childName = base.GetChildName(path);
|
|
return childName.Replace('\\', '/');
|
|
}
|
|
|
|
private static string EnsureDriveIsRooted(string path)
|
|
{
|
|
string result = path;
|
|
|
|
// Find the drive separator
|
|
|
|
int index = path.IndexOf(':');
|
|
|
|
if (index != -1)
|
|
{
|
|
// if the drive separator is the end of the path, add
|
|
// the root path separator back
|
|
|
|
if (index + 1 == path.Length)
|
|
{
|
|
result = path + StringLiterals.DefaultPathSeparator;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#region Unimplemented methods
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// new-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the property that should be created.
|
|
/// </param>
|
|
/// <param name="type">
|
|
/// The type of the property that should be created.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// The new value of the property that should be created.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object NewPropertyDynamicParameters(
|
|
string path,
|
|
string propertyName,
|
|
string type,
|
|
object value)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// remove-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the property that should be removed.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object RemovePropertyDynamicParameters(
|
|
string path,
|
|
string propertyName)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// rename-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The property to rename.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The new name of the property.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object RenamePropertyDynamicParameters(
|
|
string path,
|
|
string sourceProperty,
|
|
string destinationProperty)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// copy-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="sourcePath">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The name of the property to copy.
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The path to the item on which to copy the property to.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The destination property to copy to.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object CopyPropertyDynamicParameters(
|
|
string sourcePath,
|
|
string sourceProperty,
|
|
string destinationPath,
|
|
string destinationProperty)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gives the provider a chance to attach additional parameters to the
|
|
/// move-itemproperty cmdlet.
|
|
/// </summary>
|
|
/// <param name="sourcePath">
|
|
/// If the path was specified on the command line, this is the path
|
|
/// to the item to get the dynamic parameters for.
|
|
/// </param>
|
|
/// <param name="sourceProperty">
|
|
/// The name of the property to copy.
|
|
/// </param>
|
|
/// <param name="destinationPath">
|
|
/// The path to the item on which to copy the property to.
|
|
/// </param>
|
|
/// <param name="destinationProperty">
|
|
/// The destination property to copy to.
|
|
/// </param>
|
|
/// <returns>
|
|
/// An object that has properties and fields decorated with
|
|
/// parsing attributes similar to a cmdlet class.
|
|
/// </returns>
|
|
public object MovePropertyDynamicParameters(
|
|
string sourcePath,
|
|
string sourceProperty,
|
|
string destinationPath,
|
|
string destinationProperty)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
#endregion Unimplemented methods
|
|
|
|
#endregion IDynamicPropertyCmdletProvider
|
|
|
|
#region Private members
|
|
|
|
private void CopyProperty(
|
|
IRegistryWrapper sourceKey,
|
|
IRegistryWrapper destinationKey,
|
|
string sourceProperty,
|
|
string destinationProperty,
|
|
bool writeOnSuccess)
|
|
{
|
|
string realSourceProperty = GetPropertyName(sourceProperty);
|
|
string realDestinationProperty = GetPropertyName(destinationProperty);
|
|
|
|
object sourceValue = sourceKey.GetValue(sourceProperty);
|
|
RegistryValueKind sourceKind = sourceKey.GetValueKind(sourceProperty);
|
|
|
|
destinationKey.SetValue(destinationProperty, sourceValue, sourceKind);
|
|
|
|
if (writeOnSuccess)
|
|
{
|
|
WriteWrappedPropertyObject(sourceValue, realSourceProperty, sourceKey.Name);
|
|
}
|
|
}
|
|
|
|
private void MoveProperty(
|
|
IRegistryWrapper sourceKey,
|
|
IRegistryWrapper destinationKey,
|
|
string sourceProperty,
|
|
string destinationProperty)
|
|
{
|
|
string realSourceProperty = GetPropertyName(sourceProperty);
|
|
string realDestinationProperty = GetPropertyName(destinationProperty);
|
|
|
|
try
|
|
{
|
|
// If sourceProperty and destinationProperty happens to be the same
|
|
// then we shouldn't remove the property
|
|
bool continueWithRemove = true;
|
|
|
|
if (string.Equals(sourceKey.Name, destinationKey.Name, StringComparison.OrdinalIgnoreCase) &&
|
|
string.Equals(realSourceProperty, realDestinationProperty, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continueWithRemove = false;
|
|
}
|
|
|
|
// Move is implemented by copying the value and then deleting the original
|
|
// Copy property will throw an exception if it fails
|
|
|
|
CopyProperty(
|
|
sourceKey,
|
|
destinationKey,
|
|
realSourceProperty,
|
|
realDestinationProperty,
|
|
false);
|
|
|
|
// Delete sourceproperty only if it is not same as destination property
|
|
if (continueWithRemove)
|
|
{
|
|
sourceKey.DeleteValue(realSourceProperty);
|
|
}
|
|
|
|
object newValue = destinationKey.GetValue(realDestinationProperty);
|
|
WriteWrappedPropertyObject(newValue, destinationProperty, destinationKey.Name);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, sourceKey.Name));
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, sourceKey.Name));
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, sourceKey.Name));
|
|
return;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts all / in the path to \
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to normalize.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The path with all / normalized to \
|
|
/// </returns>
|
|
private string NormalizePath(string path)
|
|
{
|
|
string result = path;
|
|
|
|
if (!string.IsNullOrEmpty(path))
|
|
{
|
|
result = path.Replace(StringLiterals.AlternatePathSeparator, StringLiterals.DefaultPathSeparator);
|
|
|
|
// Remove relative path tokens
|
|
if (HasRelativePathTokens(path))
|
|
{
|
|
result = NormalizeRelativePath(result, null);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private bool HasRelativePathTokens(string path)
|
|
{
|
|
return (
|
|
(path.IndexOf("\\", StringComparison.OrdinalIgnoreCase) == 0) ||
|
|
path.Contains("\\.\\") ||
|
|
path.Contains("\\..\\") ||
|
|
path.EndsWith("\\..", StringComparison.OrdinalIgnoreCase) ||
|
|
path.EndsWith("\\.", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("..\\", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith(".\\", StringComparison.OrdinalIgnoreCase) ||
|
|
path.StartsWith("~", StringComparison.OrdinalIgnoreCase));
|
|
}
|
|
|
|
private void GetFilteredRegistryKeyProperties(string path,
|
|
Collection<string> propertyNames,
|
|
bool getAll,
|
|
bool writeAccess,
|
|
out IRegistryWrapper key,
|
|
out Collection<string> filteredCollection)
|
|
{
|
|
bool expandAll = false;
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
filteredCollection = new Collection<string>();
|
|
key = GetRegkeyForPathWriteIfError(path, writeAccess);
|
|
|
|
if (key == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If properties were not specified, get all the values
|
|
|
|
if (propertyNames == null)
|
|
{
|
|
propertyNames = new Collection<string>();
|
|
}
|
|
|
|
if (propertyNames.Count == 0 && getAll)
|
|
{
|
|
propertyNames.Add("*");
|
|
expandAll = true;
|
|
}
|
|
|
|
string[] valueNames;
|
|
try
|
|
{
|
|
valueNames = key.GetValueNames();
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.ReadError, path));
|
|
return;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return;
|
|
}
|
|
|
|
foreach (string requestedValueName in propertyNames)
|
|
{
|
|
WildcardPattern valueNameMatcher =
|
|
WildcardPattern.Get(
|
|
requestedValueName,
|
|
|
|
WildcardOptions.IgnoreCase);
|
|
|
|
bool hadAMatch = false;
|
|
|
|
foreach (string valueName in valueNames)
|
|
{
|
|
string valueNameToMatch = valueName;
|
|
|
|
// Need to convert the default value name to "(default)"
|
|
if (string.IsNullOrEmpty(valueName))
|
|
{
|
|
// Only do the conversion if the caller isn't asking for
|
|
// "" or null.
|
|
|
|
if (!string.IsNullOrEmpty(requestedValueName))
|
|
{
|
|
valueNameToMatch = GetLocalizedDefaultToken();
|
|
}
|
|
}
|
|
|
|
if (
|
|
expandAll ||
|
|
((Context.SuppressWildcardExpansion == false) && (valueNameMatcher.IsMatch(valueNameToMatch))) ||
|
|
((Context.SuppressWildcardExpansion == true) && (string.Equals(valueNameToMatch, requestedValueName, StringComparison.OrdinalIgnoreCase))))
|
|
{
|
|
if (string.IsNullOrEmpty(valueNameToMatch))
|
|
{
|
|
// If the value name is empty then using "(default)"
|
|
// as the property name when adding the note, as
|
|
// PSObject does not allow an empty propertyName
|
|
|
|
valueNameToMatch = GetLocalizedDefaultToken();
|
|
}
|
|
|
|
hadAMatch = true;
|
|
filteredCollection.Add(valueName);
|
|
}
|
|
}
|
|
|
|
WriteErrorIfPerfectMatchNotFound(hadAMatch, path, requestedValueName);
|
|
}
|
|
}
|
|
|
|
private void WriteErrorIfPerfectMatchNotFound(bool hadAMatch, string path, string requestedValueName)
|
|
{
|
|
if (!hadAMatch && !WildcardPattern.ContainsWildcardCharacters(requestedValueName))
|
|
{
|
|
// we did not have any match and the requested name did not have
|
|
// any globbing characters (perfect match attempted)
|
|
// we need to write an error
|
|
|
|
string formatString = RegistryProviderStrings.PropertyNotAtPath;
|
|
Exception e =
|
|
new PSArgumentException(
|
|
string.Format(
|
|
CultureInfo.CurrentCulture,
|
|
formatString,
|
|
requestedValueName,
|
|
path),
|
|
(Exception)null);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.InvalidArgument,
|
|
requestedValueName));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// IT resets the a registry key value to its default.
|
|
/// </summary>
|
|
/// <param name="key">Key whose value has to be reset.</param>
|
|
/// <param name="valueName">Name of the value to reset.</param>
|
|
/// <returns>Default value the key was set to.</returns>
|
|
private object ResetRegistryKeyValue(IRegistryWrapper key, string valueName)
|
|
{
|
|
RegistryValueKind valueKind = key.GetValueKind(valueName);
|
|
object defaultValue = null;
|
|
|
|
switch (valueKind)
|
|
{
|
|
// NOTICE: we assume that an unknown type is treated as
|
|
// the same as a binary blob
|
|
case RegistryValueKind.Binary:
|
|
case RegistryValueKind.Unknown:
|
|
{
|
|
defaultValue = Array.Empty<byte>();
|
|
}
|
|
|
|
break;
|
|
case RegistryValueKind.DWord:
|
|
{
|
|
defaultValue = (int)0;
|
|
}
|
|
|
|
break;
|
|
case RegistryValueKind.ExpandString:
|
|
case RegistryValueKind.String:
|
|
{
|
|
defaultValue = string.Empty;
|
|
}
|
|
|
|
break;
|
|
case RegistryValueKind.MultiString:
|
|
{
|
|
defaultValue = Array.Empty<string>();
|
|
}
|
|
|
|
break;
|
|
case RegistryValueKind.QWord:
|
|
{
|
|
defaultValue = (long)0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
key.SetValue(valueName, defaultValue, valueKind);
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to set the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.WriteError, valueName));
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to set the value. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, valueName));
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, valueName));
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the given path is the top container path (the one containing the hives)
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// path to check
|
|
/// </param>
|
|
/// <returns>
|
|
/// true if the path is empty, a \ or a /, else false
|
|
/// </returns>
|
|
private bool IsHiveContainer(string path)
|
|
{
|
|
bool result = false;
|
|
if (path == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("path");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(path) ||
|
|
(string.Compare(path, "\\", StringComparison.OrdinalIgnoreCase) == 0) ||
|
|
(string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) == 0))
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the container. if the container is the hive container (Registry::\)
|
|
/// it throws an exception.
|
|
/// </summary>
|
|
/// <param name="path">Path to check.</param>
|
|
/// <returns>False if the operation is not allowed.</returns>
|
|
private bool CheckOperationNotAllowedOnHiveContainer(string path)
|
|
{
|
|
if (IsHiveContainer(path))
|
|
{
|
|
string message = RegistryProviderStrings.ContainerInvalidOperationTemplate;
|
|
|
|
InvalidOperationException ex = new InvalidOperationException(message);
|
|
WriteError(new ErrorRecord(ex, "InvalidContainer", ErrorCategory.InvalidArgument, path));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the container. if the container is the hive container (Registry::\)
|
|
/// it throws an exception.
|
|
/// </summary>
|
|
/// <param name="sourcePath">Source path to check.</param>
|
|
/// <param name="destinationPath">Destination path to check.</param>
|
|
private bool CheckOperationNotAllowedOnHiveContainer(string sourcePath, string destinationPath)
|
|
{
|
|
if (IsHiveContainer(sourcePath))
|
|
{
|
|
string message = RegistryProviderStrings.SourceContainerInvalidOperationTemplate;
|
|
InvalidOperationException ex = new InvalidOperationException(message);
|
|
WriteError(new ErrorRecord(ex, "InvalidContainer", ErrorCategory.InvalidArgument, sourcePath));
|
|
return false;
|
|
}
|
|
|
|
if (IsHiveContainer(destinationPath))
|
|
{
|
|
string message =
|
|
RegistryProviderStrings.DestinationContainerInvalidOperationTemplate;
|
|
InvalidOperationException ex = new InvalidOperationException(message);
|
|
WriteError(new ErrorRecord(ex, "InvalidContainer", ErrorCategory.InvalidArgument, destinationPath));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the appropriate hive root name for the specified path.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to get the hive root name from.
|
|
/// </param>
|
|
/// <returns>
|
|
/// A registry key for the hive root specified by the path.
|
|
/// </returns>
|
|
private IRegistryWrapper GetHiveRoot(string path)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
if (TransactionAvailable())
|
|
{
|
|
for (int k = 0; k < s_wellKnownHivesTx.Length; k++)
|
|
{
|
|
if (string.Equals(path, s_hiveNames[k], StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(path, s_hiveShortNames[k], StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
using (CurrentPSTransaction)
|
|
{
|
|
return new TransactedRegistryWrapper(s_wellKnownHivesTx[k], this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int k = 0; k < s_wellKnownHives.Length; k++)
|
|
{
|
|
if (string.Equals(path, s_hiveNames[k], StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(path, s_hiveShortNames[k], StringComparison.OrdinalIgnoreCase))
|
|
return new RegistryWrapper(s_wellKnownHives[k]);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates the parent for the keypath specified by <paramref name="path"/>.
|
|
/// </summary>
|
|
/// <param name="path">RegistryKey path.</param>
|
|
/// <returns>
|
|
/// True if key is created or already exist,False otherwise.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// This method wont call ShouldProcess. Callers should do this before
|
|
/// calling this method.
|
|
/// </remarks>
|
|
private bool CreateIntermediateKeys(string path)
|
|
{
|
|
bool result = false;
|
|
|
|
// Check input.
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
try
|
|
{
|
|
// 1. Normalize path ( for "//","." etc )
|
|
// 2. Open the root
|
|
// 3. Create subkey
|
|
|
|
path = NormalizePath(path);
|
|
|
|
int index = path.IndexOf("\\", StringComparison.Ordinal);
|
|
if (index == 0)
|
|
{
|
|
// The user may precede a path with \
|
|
path = path.Substring(1);
|
|
index = path.IndexOf("\\", StringComparison.Ordinal);
|
|
}
|
|
|
|
if (index == -1)
|
|
{
|
|
// we are at root..there is no subkey to create
|
|
// just return
|
|
|
|
return true;
|
|
}
|
|
|
|
string keyRoot = path.Substring(0, index);
|
|
|
|
// NormalizePath will trim "\" at the end. So there is always something
|
|
// after index. Asserting just in case..
|
|
Dbg.Diagnostics.Assert(index + 1 < path.Length, "Bad path");
|
|
string remainingPath = path.Substring(index + 1);
|
|
|
|
IRegistryWrapper rootKey = GetHiveRoot(keyRoot);
|
|
|
|
if (remainingPath.Length == 0 || rootKey == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
// Create new subkey..and close
|
|
IRegistryWrapper subKey = rootKey.CreateSubKey(remainingPath);
|
|
|
|
if (subKey != null)
|
|
{
|
|
subKey.Close();
|
|
}
|
|
else
|
|
{
|
|
// SubKey is null
|
|
// Unable to create intermediate keys
|
|
throw PSTraceSource.NewArgumentException("path");
|
|
}
|
|
|
|
result = true;
|
|
}
|
|
catch (ArgumentException argumentException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
WriteError(new ErrorRecord(argumentException, argumentException.GetType().FullName, ErrorCategory.OpenError, path));
|
|
return result;
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.OpenError, path));
|
|
return result;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return result;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return result;
|
|
}
|
|
catch (NotSupportedException notSupportedException)
|
|
{
|
|
WriteError(new ErrorRecord(notSupportedException, notSupportedException.GetType().FullName, ErrorCategory.InvalidOperation, path));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A private helper method that retrieves a RegistryKey for the specified
|
|
/// path and if an exception is thrown retrieving the key, an error is written
|
|
/// and null is returned.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the registry key to retrieve.
|
|
/// </param>
|
|
/// <param name="writeAccess">
|
|
/// If write access is required the key then this should be true. If false,
|
|
/// the key will be opened with read access only.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The RegistryKey associated with the specified path.
|
|
/// </returns>
|
|
private IRegistryWrapper GetRegkeyForPathWriteIfError(string path, bool writeAccess)
|
|
{
|
|
IRegistryWrapper result = null;
|
|
try
|
|
{
|
|
result = GetRegkeyForPath(path, writeAccess);
|
|
|
|
if (result == null)
|
|
{
|
|
// The key was not found, write out an error.
|
|
|
|
ArgumentException exception =
|
|
new ArgumentException(
|
|
RegistryProviderStrings.KeyDoesNotExist);
|
|
WriteError(new ErrorRecord(exception, exception.GetType().FullName, ErrorCategory.InvalidArgument, path));
|
|
|
|
return null;
|
|
}
|
|
}
|
|
catch (ArgumentException argumentException)
|
|
{
|
|
WriteError(new ErrorRecord(argumentException, argumentException.GetType().FullName, ErrorCategory.OpenError, path));
|
|
return result;
|
|
}
|
|
catch (System.IO.IOException ioException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(ioException, ioException.GetType().FullName, ErrorCategory.OpenError, path));
|
|
return result;
|
|
}
|
|
catch (System.Security.SecurityException securityException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(securityException, securityException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return result;
|
|
}
|
|
catch (System.UnauthorizedAccessException unauthorizedAccessException)
|
|
{
|
|
// An exception occurred while trying to get the key. Write
|
|
// out the error.
|
|
|
|
WriteError(new ErrorRecord(unauthorizedAccessException, unauthorizedAccessException.GetType().FullName, ErrorCategory.PermissionDenied, path));
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// A private helper method that retrieves a RegistryKey for the specified
|
|
/// path.
|
|
/// </summary>
|
|
/// <param name="path">
|
|
/// The path to the registry key to retrieve.
|
|
/// </param>
|
|
/// <param name="writeAccess">
|
|
/// If write access is required the key then this should be true. If false,
|
|
/// the key will be opened with read access only.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The RegistryKey associated with the specified path.
|
|
/// </returns>
|
|
private IRegistryWrapper GetRegkeyForPath(string path, bool writeAccess)
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
// The key was not found, write out an error.
|
|
|
|
ArgumentException exception =
|
|
new ArgumentException(
|
|
RegistryProviderStrings.KeyDoesNotExist);
|
|
throw exception;
|
|
}
|
|
|
|
// Making sure to obey the StopProcessing.
|
|
if (Stopping)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
s_tracer.WriteLine("writeAccess = {0}", writeAccess);
|
|
|
|
IRegistryWrapper result = null;
|
|
|
|
do // false loop
|
|
{
|
|
int index = path.IndexOf("\\", StringComparison.Ordinal);
|
|
|
|
if (index == 0)
|
|
{
|
|
// The user may proceed a path with \
|
|
|
|
path = path.Substring(1);
|
|
index = path.IndexOf("\\", StringComparison.Ordinal);
|
|
}
|
|
|
|
if (index == -1)
|
|
{
|
|
result = GetHiveRoot(path);
|
|
break;
|
|
}
|
|
|
|
string keyRoot = path.Substring(0, index);
|
|
string remainingPath = path.Substring(index + 1);
|
|
|
|
IRegistryWrapper resultRoot = GetHiveRoot(keyRoot);
|
|
|
|
if (remainingPath.Length == 0 || resultRoot == null)
|
|
{
|
|
result = resultRoot;
|
|
break;
|
|
}
|
|
|
|
try
|
|
{
|
|
result = resultRoot.OpenSubKey(remainingPath, writeAccess);
|
|
}
|
|
catch (NotSupportedException e)
|
|
{
|
|
WriteError(new ErrorRecord(e, e.GetType().FullName, ErrorCategory.InvalidOperation, path));
|
|
}
|
|
|
|
// If we could not open the key, see if we can find the subkey that matches.
|
|
if (result == null)
|
|
{
|
|
IRegistryWrapper currentKey = resultRoot;
|
|
IRegistryWrapper tempKey = null;
|
|
|
|
// While there is still more to process
|
|
while (!string.IsNullOrEmpty(remainingPath))
|
|
{
|
|
bool foundSubkey = false;
|
|
|
|
foreach (string subKey in currentKey.GetSubKeyNames())
|
|
{
|
|
string normalizedSubkey = subKey;
|
|
|
|
// Check if the remaining path starts with the subkey name
|
|
if (!remainingPath.Equals(subKey, StringComparison.OrdinalIgnoreCase) &&
|
|
!remainingPath.StartsWith(subKey + StringLiterals.DefaultPathSeparator, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// Actually normalize the subkey and then check again
|
|
normalizedSubkey = NormalizePath(subKey);
|
|
|
|
if (!remainingPath.Equals(normalizedSubkey, StringComparison.OrdinalIgnoreCase) &&
|
|
!remainingPath.StartsWith(normalizedSubkey + StringLiterals.DefaultPathSeparator, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
tempKey = currentKey.OpenSubKey(subKey, writeAccess);
|
|
currentKey.Close();
|
|
currentKey = tempKey;
|
|
|
|
foundSubkey = true;
|
|
|
|
remainingPath = remainingPath.Equals(normalizedSubkey, StringComparison.OrdinalIgnoreCase)
|
|
? string.Empty
|
|
: remainingPath.Substring((normalizedSubkey + StringLiterals.DefaultPathSeparator).Length);
|
|
|
|
break;
|
|
}
|
|
|
|
if (!foundSubkey)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return currentKey;
|
|
}
|
|
} while (false);
|
|
|
|
return result;
|
|
}
|
|
|
|
// NB: The HKEY_DYN_DATA hive is left out of the following lists because
|
|
// it is only available on Win98/ME and we do not support that platform.
|
|
|
|
private static readonly string[] s_hiveNames = new string[] {
|
|
"HKEY_LOCAL_MACHINE",
|
|
"HKEY_CURRENT_USER",
|
|
"HKEY_CLASSES_ROOT",
|
|
"HKEY_CURRENT_CONFIG",
|
|
"HKEY_USERS",
|
|
"HKEY_PERFORMANCE_DATA"
|
|
};
|
|
|
|
private static readonly string[] s_hiveShortNames = new string[] {
|
|
"HKLM",
|
|
"HKCU",
|
|
"HKCR",
|
|
"HKCC",
|
|
"HKU",
|
|
"HKPD"
|
|
};
|
|
|
|
private static readonly RegistryKey[] s_wellKnownHives = new RegistryKey[] {
|
|
Registry.LocalMachine,
|
|
Registry.CurrentUser,
|
|
Registry.ClassesRoot,
|
|
Registry.CurrentConfig,
|
|
Registry.Users,
|
|
Registry.PerformanceData
|
|
};
|
|
|
|
private static readonly TransactedRegistryKey[] s_wellKnownHivesTx = new TransactedRegistryKey[] {
|
|
TransactedRegistry.LocalMachine,
|
|
TransactedRegistry.CurrentUser,
|
|
TransactedRegistry.ClassesRoot,
|
|
TransactedRegistry.CurrentConfig,
|
|
TransactedRegistry.Users
|
|
};
|
|
|
|
/// <summary>
|
|
/// Sets or creates a registry value on a key.
|
|
/// </summary>
|
|
/// <param name="key">
|
|
/// The key to set or create the value on.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the value to set or create.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// The new data for the value.
|
|
/// </param>
|
|
/// <param name="kind">
|
|
/// The RegistryValueKind of the value.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the key that the value is being set on.
|
|
/// </param>
|
|
private void SetRegistryValue(IRegistryWrapper key, string propertyName, object value, RegistryValueKind kind, string path)
|
|
{
|
|
SetRegistryValue(key, propertyName, value, kind, path, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets or creates a registry value on a key.
|
|
/// </summary>
|
|
/// <param name="key">
|
|
/// The key to set or create the value on.
|
|
/// </param>
|
|
/// <param name="propertyName">
|
|
/// The name of the value to set or create.
|
|
/// </param>
|
|
/// <param name="value">
|
|
/// The new data for the value.
|
|
/// </param>
|
|
/// <param name="kind">
|
|
/// The RegistryValueKind of the value.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the key that the value is being set on.
|
|
/// </param>
|
|
/// <param name="writeResult">
|
|
/// If true, the value that is set will be written out.
|
|
/// </param>
|
|
private void SetRegistryValue(
|
|
IRegistryWrapper key,
|
|
string propertyName,
|
|
object value,
|
|
RegistryValueKind kind,
|
|
string path,
|
|
bool writeResult)
|
|
{
|
|
Dbg.Diagnostics.Assert(
|
|
key != null,
|
|
"Caller should have verified key");
|
|
|
|
string propertyNameToSet = GetPropertyName(propertyName);
|
|
|
|
RegistryValueKind existingKind = RegistryValueKind.Unknown;
|
|
|
|
// If user does not specify a kind: get the valuekind if the property
|
|
// already exists
|
|
if (kind == RegistryValueKind.Unknown)
|
|
{
|
|
existingKind = GetValueKindForProperty(key, propertyNameToSet);
|
|
}
|
|
|
|
// try to do a conversion based on the existing kind, if we
|
|
// were able to retrieve one
|
|
if (existingKind != RegistryValueKind.Unknown)
|
|
{
|
|
try
|
|
{
|
|
value = ConvertValueToKind(value, existingKind);
|
|
kind = existingKind;
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
// failed attempt, we reset to unknown to let the
|
|
// default conversion process take over
|
|
existingKind = RegistryValueKind.Unknown;
|
|
}
|
|
}
|
|
|
|
// set the kind as defined by the user
|
|
if (existingKind == RegistryValueKind.Unknown)
|
|
{
|
|
// we use to kind passed in, either because we had
|
|
// a valid one or because we failed to retrieve an existing kind to match
|
|
if (kind == RegistryValueKind.Unknown)
|
|
{
|
|
// set the kind based on value
|
|
if (value != null)
|
|
{
|
|
kind = GetValueKindFromObject(value);
|
|
}
|
|
else
|
|
{
|
|
// if no value and unknown kind, then default to empty string
|
|
kind = RegistryValueKind.String;
|
|
}
|
|
}
|
|
|
|
value = ConvertValueToKind(value, kind);
|
|
}
|
|
|
|
key.SetValue(propertyNameToSet, value, kind);
|
|
|
|
if (writeResult)
|
|
{
|
|
// Now write out the value
|
|
object newValue = key.GetValue(propertyNameToSet);
|
|
|
|
WriteWrappedPropertyObject(newValue, propertyName, path);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to wrap property values when sent to the pipeline into an PSObject;
|
|
/// it adds the name of the property as a note.
|
|
/// </summary>
|
|
/// <param name="value">The property to be written.</param>
|
|
/// <param name="propertyName">Name of the property being written.</param>
|
|
/// <param name="path">The path of the item being written.</param>
|
|
private void WriteWrappedPropertyObject(object value, string propertyName, string path)
|
|
{
|
|
PSObject result = new PSObject();
|
|
|
|
string propertyNameToAdd = propertyName;
|
|
if (string.IsNullOrEmpty(propertyName))
|
|
{
|
|
propertyNameToAdd = GetLocalizedDefaultToken();
|
|
}
|
|
|
|
result.Properties.Add(new PSNoteProperty(propertyNameToAdd, value));
|
|
|
|
WritePropertyObject(result, path);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Uses LanguagePrimitives.ConvertTo to convert the value to the type that is appropriate
|
|
/// for the specified RegistryValueKind.
|
|
/// </summary>
|
|
/// <param name="value">
|
|
/// The value to convert.
|
|
/// </param>
|
|
/// <param name="kind">
|
|
/// The RegistryValueKind type to convert the value to.
|
|
/// </param>
|
|
/// <returns>
|
|
/// The converted value.
|
|
/// </returns>
|
|
private static object ConvertValueToKind(object value, RegistryValueKind kind)
|
|
{
|
|
switch (kind)
|
|
{
|
|
case RegistryValueKind.Binary:
|
|
value = (value != null)
|
|
? (byte[])LanguagePrimitives.ConvertTo(
|
|
value,
|
|
typeof(byte[]),
|
|
CultureInfo.CurrentCulture)
|
|
: new byte[] { };
|
|
break;
|
|
|
|
case RegistryValueKind.DWord:
|
|
{
|
|
if (value != null)
|
|
{
|
|
try
|
|
{
|
|
value = (int)LanguagePrimitives.ConvertTo(value, typeof(int), CultureInfo.CurrentCulture);
|
|
}
|
|
catch (PSInvalidCastException)
|
|
{
|
|
value = (UInt32)LanguagePrimitives.ConvertTo(value, typeof(UInt32), CultureInfo.CurrentCulture);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
}; break;
|
|
|
|
case RegistryValueKind.ExpandString:
|
|
value = (value != null)
|
|
? (string)LanguagePrimitives.ConvertTo(
|
|
value,
|
|
typeof(string),
|
|
CultureInfo.CurrentCulture)
|
|
: string.Empty;
|
|
break;
|
|
|
|
case RegistryValueKind.MultiString:
|
|
value = (value != null)
|
|
? (string[])LanguagePrimitives.ConvertTo(
|
|
value,
|
|
typeof(string[]),
|
|
CultureInfo.CurrentCulture)
|
|
: new string[] { };
|
|
break;
|
|
|
|
case RegistryValueKind.QWord:
|
|
{
|
|
if (value != null)
|
|
{
|
|
try
|
|
{
|
|
value = (long)LanguagePrimitives.ConvertTo(value, typeof(long), CultureInfo.CurrentCulture);
|
|
}
|
|
catch (PSInvalidCastException)
|
|
{
|
|
value = (UInt64)LanguagePrimitives.ConvertTo(value, typeof(UInt64), CultureInfo.CurrentCulture);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
}; break;
|
|
|
|
case RegistryValueKind.String:
|
|
value = (value != null)
|
|
? (string)LanguagePrimitives.ConvertTo(
|
|
value,
|
|
typeof(string),
|
|
CultureInfo.CurrentCulture)
|
|
: string.Empty;
|
|
break;
|
|
|
|
// If kind is Unknown then just leave the value as-is.
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to infer the RegistryValueKind from an object.
|
|
/// </summary>
|
|
/// <param name="value">Object whose RegistryValueKind has to be determined.</param>
|
|
/// <returns>Corresponding RegistryValueKind.</returns>
|
|
private static RegistryValueKind GetValueKindFromObject(object value)
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw PSTraceSource.NewArgumentNullException("value");
|
|
}
|
|
|
|
RegistryValueKind result = RegistryValueKind.Unknown;
|
|
|
|
Type valueType = value.GetType();
|
|
if (valueType == typeof(byte[]))
|
|
{
|
|
result = RegistryValueKind.Binary;
|
|
}
|
|
else if (valueType == typeof(int))
|
|
{
|
|
result = RegistryValueKind.DWord;
|
|
}
|
|
|
|
if (valueType == typeof(string))
|
|
{
|
|
result = RegistryValueKind.String;
|
|
}
|
|
|
|
if (valueType == typeof(string[]))
|
|
{
|
|
result = RegistryValueKind.MultiString;
|
|
}
|
|
|
|
if (valueType == typeof(long))
|
|
{
|
|
result = RegistryValueKind.QWord;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to get RegistryValueKind for a Property.
|
|
/// </summary>
|
|
/// <param name="key">RegistryKey containing property.</param>
|
|
/// <param name="valueName">Property for which RegistryValueKind is requested.</param>
|
|
/// <returns>RegistryValueKind of the property. If the property does not exit,returns RegistryValueKind.Unknown.</returns>
|
|
private static RegistryValueKind GetValueKindForProperty(IRegistryWrapper key, string valueName)
|
|
{
|
|
try
|
|
{
|
|
return key.GetValueKind(valueName);
|
|
}
|
|
catch (System.ArgumentException)
|
|
{
|
|
// RegistryKey that contains the specified value does not exist
|
|
}
|
|
catch (System.IO.IOException)
|
|
{
|
|
}
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
}
|
|
catch (System.UnauthorizedAccessException)
|
|
{
|
|
}
|
|
|
|
return RegistryValueKind.Unknown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper to read back an existing registry key value.
|
|
/// </summary>
|
|
/// <param name="key">Key to read the value from.</param>
|
|
/// <param name="valueName">Name of the value to read.</param>
|
|
/// <returns>Value of the key, null if it could not retrieve
|
|
/// it because known exceptions were thrown, else an exception is percolated up
|
|
/// </returns>
|
|
private static object ReadExistingKeyValue(IRegistryWrapper key, string valueName)
|
|
{
|
|
try
|
|
{
|
|
// Since SetValue can munge the data to a specified
|
|
// type (RegistryValueKind), retrieve the value again
|
|
// to output it in the correct form to the user.
|
|
|
|
return key.GetValue(valueName, null, RegistryValueOptions.DoNotExpandEnvironmentNames);
|
|
}
|
|
catch (System.IO.IOException)
|
|
{
|
|
}
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
}
|
|
catch (System.UnauthorizedAccessException)
|
|
{
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wraps a registry item in a PSObject and sets the TreatAs to
|
|
/// Microsoft.Win32.RegistryKey. This way values will be presented
|
|
/// in the same format as keys.
|
|
/// </summary>
|
|
/// <param name="key">
|
|
/// The registry key to be written out.
|
|
/// </param>
|
|
/// <param name="path">
|
|
/// The path to the item being written out.
|
|
/// </param>
|
|
private void WriteRegistryItemObject(
|
|
IRegistryWrapper key,
|
|
string path)
|
|
{
|
|
if (key == null)
|
|
{
|
|
Dbg.Diagnostics.Assert(
|
|
key != null,
|
|
"The RegistryProvider should never attempt to write out a null value");
|
|
|
|
// Don't error, but don't write out anything either.
|
|
return;
|
|
}
|
|
|
|
// Escape any wildcard characters in the path
|
|
path = EscapeSpecialChars(path);
|
|
|
|
// Wrap the key in an PSObject
|
|
PSObject outputObject = PSObject.AsPSObject(key.RegistryKey);
|
|
|
|
// Add the registry values to the PSObject
|
|
string[] valueNames = key.GetValueNames();
|
|
|
|
for (int index = 0; index < valueNames.Length; ++index)
|
|
{
|
|
if (string.IsNullOrEmpty(valueNames[index]))
|
|
{
|
|
// The first unnamed value becomes the default value
|
|
valueNames[index] = GetLocalizedDefaultToken();
|
|
break;
|
|
}
|
|
}
|
|
|
|
outputObject.AddOrSetProperty("Property", valueNames);
|
|
|
|
WriteItemObject(outputObject, path, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Takes a string and tries to parse it into a RegistryValueKind enum
|
|
/// type.
|
|
/// If the conversion fails, WriteError() is called.
|
|
/// </summary>
|
|
/// <param name="type">
|
|
/// The type as specified by the user that should be parsed into a RegistryValueKind enum.
|
|
/// </param>
|
|
/// <param name="kind">Output for the RegistryValueKind for the string.</param>
|
|
/// <returns>
|
|
/// true if the conversion succeeded
|
|
/// </returns>
|
|
private bool ParseKind(string type, out RegistryValueKind kind)
|
|
{
|
|
kind = RegistryValueKind.Unknown;
|
|
|
|
if (string.IsNullOrEmpty(type))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool success = true;
|
|
Exception innerException = null;
|
|
try
|
|
{
|
|
// Convert the parameter to a RegistryValueKind
|
|
kind = (RegistryValueKind)Enum.Parse(typeof(RegistryValueKind), type, true);
|
|
}
|
|
catch (InvalidCastException invalidCast)
|
|
{
|
|
innerException = invalidCast;
|
|
}
|
|
catch (ArgumentException argException)
|
|
{
|
|
innerException = argException;
|
|
}
|
|
|
|
if (innerException != null)
|
|
{
|
|
success = false;
|
|
|
|
string formatString =
|
|
RegistryProviderStrings.TypeParameterBindingFailure;
|
|
Exception e =
|
|
new ArgumentException(
|
|
string.Format(
|
|
CultureInfo.CurrentCulture,
|
|
formatString,
|
|
type,
|
|
typeof(RegistryValueKind).FullName),
|
|
innerException);
|
|
WriteError(new ErrorRecord(
|
|
e,
|
|
e.GetType().FullName,
|
|
ErrorCategory.InvalidArgument,
|
|
type));
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the default value name token from the resource.
|
|
/// In English that token is "(default)" without the quotes.
|
|
/// </summary>
|
|
/// <returns>
|
|
/// A string containing the default value name.
|
|
/// </returns>
|
|
private string GetLocalizedDefaultToken()
|
|
{
|
|
// This shouldn't be localized as it will break scripts
|
|
|
|
string defaultValueName = "(default)";
|
|
return defaultValueName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts an empty or null userEnteredPropertyName to the localized
|
|
/// string for the default property name.
|
|
/// </summary>
|
|
/// <param name="userEnteredPropertyName">
|
|
/// The property name to convert.
|
|
/// </param>
|
|
/// <returns>
|
|
/// If userEnteredPropertyName is null or empty, the localized default
|
|
/// property name is returned, else the userEnteredPropertyName is returned.
|
|
/// </returns>
|
|
private string GetPropertyName(string userEnteredPropertyName)
|
|
{
|
|
string result = userEnteredPropertyName;
|
|
|
|
if (!string.IsNullOrEmpty(userEnteredPropertyName))
|
|
{
|
|
var stringComparer = Host.CurrentCulture.CompareInfo;
|
|
|
|
if (stringComparer.Compare(
|
|
userEnteredPropertyName,
|
|
GetLocalizedDefaultToken(),
|
|
CompareOptions.IgnoreCase) == 0)
|
|
{
|
|
result = null;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endregion Private members
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines dynamic parameters for the registry provider.
|
|
/// </summary>
|
|
public class RegistryProviderSetItemDynamicParameter
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the Type parameter as a dynamic parameter for
|
|
/// the registry provider's SetItem method.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// The only acceptable values for this parameter are those found
|
|
/// in the RegistryValueKind enum
|
|
/// </remarks>
|
|
[Parameter(ValueFromPipelineByPropertyName = true)]
|
|
public RegistryValueKind Type { get; set; } = RegistryValueKind.Unknown;
|
|
}
|
|
}
|
|
#endif // !UNIX
|