Files
PowerShell/src/System.Management.Automation/singleshell/Commands/MshSnapinCommands.cs
T

849 lines
30 KiB
C#
Raw Normal View History

/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
#pragma warning disable 1634, 1691 // Stops compiler from warning about unknown warnings
namespace Microsoft.PowerShell.Commands
{
#region PSSnapInCommandBase
/// <summary>
/// Base class for all the pssnapin related cmdlets.
/// </summary>
public abstract class PSSnapInCommandBase : PSCmdlet, IDisposable
{
#region IDisposable Members
/// <summary>
/// Set to true when object is disposed
/// </summary>
///
private bool _disposed;
/// <summary>
/// Dispose method unloads the app domain and the
/// resource reader if it was created.
/// </summary>
///
public void Dispose()
{
if (_disposed == false)
{
2016-07-29 13:02:49 -07:00
if (_resourceReader != null)
{
2016-07-29 13:02:49 -07:00
_resourceReader.Dispose();
_resourceReader = null;
}
GC.SuppressFinalize(this);
}
_disposed = true;
}
#endregion IDisposable Members
#region Cmdlet Methods
2016-07-29 13:02:49 -07:00
/// <summary>
/// Disposes the resource reader.
/// </summary>
///
protected override void EndProcessing()
{
2016-07-29 13:02:49 -07:00
if (_resourceReader != null)
{
2016-07-29 13:02:49 -07:00
_resourceReader.Dispose();
_resourceReader = null;
}
}
#endregion CmdletMethods
#region Internal Methods
/// <summary>
/// Runspace configuration for the current engine
/// </summary>
/// <remarks>
/// PSSnapIn cmdlets need <see cref="RunspaceConfigForSingleShell"/> object to work with.
/// </remarks>
internal RunspaceConfigForSingleShell Runspace
{
get
{
RunspaceConfigForSingleShell runSpace = Context.RunspaceConfiguration as RunspaceConfigForSingleShell;
if (runSpace == null)
{
return null;
}
return runSpace;
}
}
/// <summary>
/// Writes a non-terminating error onto the pipeline.
/// </summary>
/// <param name="targetObject">Object which caused this exception.</param>
/// <param name="errorId">ErrorId for this error.</param>
/// <param name="innerException">Complete exception object.</param>
/// <param name="category">ErrorCategory for this exception.</param>
internal void WriteNonTerminatingError(
Object targetObject,
string errorId,
Exception innerException,
ErrorCategory category)
{
WriteError(new ErrorRecord(innerException, errorId, category, targetObject));
}
/// <summary>
/// Searches the input list for the pattern supplied.
/// </summary>
/// <param name="searchList">Input list</param>
/// <param name="pattern">pattern with wildcards</param>
/// <returns>
/// A collection of string objects (representing PSSnapIn name)
/// that match the pattern.
/// </returns>
/// <remarks>
/// Please note that this method will use WildcardPattern class.
/// So it wont support all the 'regex' patterns
/// </remarks>
internal Collection<string>
SearchListForPattern(Collection<PSSnapInInfo> searchList,
string pattern)
{
Collection<string> listToReturn = new Collection<string>();
if (null == searchList)
{
// return an empty list
return listToReturn;
}
2016-07-29 13:02:49 -07:00
WildcardPattern matcher = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);
// We are doing WildCard search
foreach (PSSnapInInfo psSnapIn in searchList)
{
if (matcher.IsMatch(psSnapIn.Name))
{
listToReturn.Add(psSnapIn.Name);
}
}
// the returned list might contain 0 objects
return listToReturn;
}
/// <summary>
/// See if the snapin is already loaded..returns load snapin info if true, null otherwise.
/// </summary>
/// <returns></returns>
internal static PSSnapInInfo IsSnapInLoaded(Collection<PSSnapInInfo> loadedSnapins, PSSnapInInfo psSnapInInfo)
{
if (null == loadedSnapins)
{
return null;
}
foreach (PSSnapInInfo loadedPSSnapInInfo in loadedSnapins)
{
// See if the assembly-qualified names match and return the existing PSSnapInInfo
// if they do.
string loadedSnapInAssemblyName = loadedPSSnapInInfo.AssemblyName;
2016-07-29 13:02:49 -07:00
if (string.Equals(loadedPSSnapInInfo.Name, psSnapInInfo.Name, StringComparison.OrdinalIgnoreCase) &&
!string.IsNullOrEmpty(loadedSnapInAssemblyName)
&& string.Equals(loadedSnapInAssemblyName, psSnapInInfo.AssemblyName, System.StringComparison.OrdinalIgnoreCase))
{
return loadedPSSnapInInfo;
}
}
return null;
}
/// <summary>
/// Routine to get the list of loaded snapins...
/// </summary>
/// <returns></returns>
protected internal Collection<PSSnapInInfo> GetSnapIns(string pattern)
{
// If RunspaceConfiguration is not null, then return the list that it has
if (Runspace != null)
{
if (pattern != null)
return Runspace.ConsoleInfo.GetPSSnapIn(pattern, _shouldGetAll);
else
return Runspace.ConsoleInfo.PSSnapIns;
}
WildcardPattern matcher = null;
if (!String.IsNullOrEmpty(pattern))
{
bool doWildCardSearch = WildcardPattern.ContainsWildcardCharacters(pattern);
if (!doWildCardSearch)
{
// Verify PSSnapInID..
// This will throw if it not a valid name
PSSnapInInfo.VerifyPSSnapInFormatThrowIfError(pattern);
}
matcher = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase);
}
Collection<PSSnapInInfo> snapins = new Collection<PSSnapInInfo>();
if (_shouldGetAll)
{
foreach (PSSnapInInfo snapinKey in PSSnapInReader.ReadAll())
{
if (matcher == null || matcher.IsMatch(snapinKey.Name))
snapins.Add(snapinKey);
}
}
else
{
// Otherwise, just scan through the list of cmdlets and rebuild the table.
List<CmdletInfo> cmdlets = InvokeCommand.GetCmdlets();
Dictionary<PSSnapInInfo, bool> snapinTable = new Dictionary<PSSnapInInfo, bool>();
foreach (CmdletInfo cmdlet in cmdlets)
{
PSSnapInInfo snapin = cmdlet.PSSnapIn;
if (snapin != null && !snapinTable.ContainsKey(snapin))
snapinTable.Add(snapin, true);
}
foreach (PSSnapInInfo snapinKey in snapinTable.Keys)
{
if (matcher == null || matcher.IsMatch(snapinKey.Name))
snapins.Add(snapinKey);
}
}
return snapins;
}
/// <summary>
/// Use to indicate if all registered snapins should be listed by GetSnapins...
/// </summary>
protected internal bool ShouldGetAll
{
get { return _shouldGetAll; }
set { _shouldGetAll = value; }
}
private bool _shouldGetAll;
/// <summary>
/// A single instance of the resource indirect reader. This is used to load the
/// managed resource assemblies in a different app-domain so that they can be unloaded.
/// For perf reasons we only want to create one instance for the duration of the command
/// and be sure it gets disposed when the command completes.
/// </summary>
///
internal RegistryStringResourceIndirect ResourceReader
{
2016-08-02 22:25:13 -07:00
get {
return _resourceReader ?? (_resourceReader = RegistryStringResourceIndirect.GetResourceIndirectReader());
}
}
2016-07-29 13:02:49 -07:00
private RegistryStringResourceIndirect _resourceReader;
#endregion
}
#endregion
#region Add-PSSnapIn
/// <summary>
/// Class that implements add-pssnapin cmdlet.
/// </summary>
2016-08-23 21:21:31 -04:00
[Cmdlet(VerbsCommon.Add, "PSSnapin", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113281")]
[OutputType(typeof(PSSnapInInfo))]
2016-07-29 13:02:49 -07:00
public sealed class AddPSSnapinCommand : PSSnapInCommandBase
{
#region Parameters
/// <summary>
/// Property that gets/sets PSSnapIn Ids for the cmdlet.
/// </summary>
/// <value>An array of strings representing PSSnapIn ids.</value>
[Parameter(
Position = 0,
Mandatory = true,
ValueFromPipelineByPropertyName = true)]
public string[] Name
{
get
{
return _pssnapins;
}
set
{
_pssnapins = value;
}
}
private string[] _pssnapins;
/// <summary>
/// Gets or sets the Passthru flag for the operation.
/// If true, the PSSnapInInfo object is passed down the
/// output pipeline.
/// </summary>
[Parameter()]
public SwitchParameter PassThru
{
get
{
return _passThru;
}
set
{
_passThru = value;
}
}
private bool _passThru;
#endregion
#region Overrides
/// <summary>
/// Adds pssnapins to console file and loads the pssnapin dlls into
/// the current monad runtime.
/// </summary>
/// <remarks>
/// The new pssnapin information is not stored in the console file until
/// the file is saved.
/// </remarks>
protected override void ProcessRecord()
{
// Cache for the information stored in the registry
// update the cache the first time a wildcard is found..
Collection<PSSnapInInfo> listToSearch = null;
foreach (string pattern in _pssnapins)
{
Exception exception = null;
Collection<string> listToAdd = new Collection<string>();
try
{
// check whether there are any wildcard characters
bool doWildCardSearch = WildcardPattern.ContainsWildcardCharacters(pattern);
if (doWildCardSearch)
{
// wildcard found in the pattern
// Get all the possible candidates for current monad version
if (listToSearch == null)
{
// cache snapin registry information...
2016-07-29 13:02:49 -07:00
// For 3.0 PowerShell, we still use "1" as the registry version key for
// Snapin and Custom shell lookup/discovery.
// For 3.0 PowerShell, we use "3" as the registry version key only for Engine
// related data like ApplicationBase etc.
listToSearch = PSSnapInReader.ReadAll(PSVersionInfo.RegistryVersion1Key);
}
listToAdd = SearchListForPattern(listToSearch, pattern);
// listToAdd wont be null..
Diagnostics.Assert(listToAdd != null, "Pattern matching returned null");
if (listToAdd.Count == 0)
{
if (_passThru)
{
// passThru is specified and we have nothing to add...
WriteNonTerminatingError(pattern, "NoPSSnapInsFound",
PSTraceSource.NewArgumentException(pattern,
MshSnapInCmdletResources.NoPSSnapInsFound, pattern),
ErrorCategory.InvalidArgument);
}
continue;
}
}
else
{
listToAdd.Add(pattern);
}
// now add all the snapins for this pattern...
AddPSSnapIns(listToAdd);
}
catch (PSArgumentException ae)
{
exception = ae;
}
catch (System.Security.SecurityException se)
{
exception = se;
}
if (exception != null)
{
WriteNonTerminatingError(pattern,
"AddPSSnapInRead",
exception,
ErrorCategory.InvalidArgument);
}
}
}
#endregion
#region Private Methods
/// <summary>
/// Adds one or more snapins
/// </summary>
/// <param name="snapInList">List of snapin IDs</param>
/// <remarks>
/// This is a helper method and should not throw any
/// exceptions. All exceptions are caught and displayed
/// to the user using write* methods
/// </remarks>
private void AddPSSnapIns(Collection<string> snapInList)
{
if (snapInList == null)
{
// nothing to add
return;
}
//BUGBUG TODO - brucepay - this is a workaround for not being able to dynamically update
// the set of cmdlets in a runspace if there is no RunspaceConfiguration object.
// This is a temporary fix to unblock remoting tests and need to be corrected/completed
// before we can ship...
// If there is no RunspaceConfig object, then
// use an InitialSessionState object to gather and
// bind the cmdlets from the snapins...
if (Context.RunspaceConfiguration == null)
{
2016-07-29 13:02:49 -07:00
Collection<PSSnapInInfo> loadedSnapins = base.GetSnapIns(null);
InitialSessionState iss = InitialSessionState.Create();
bool isAtleastOneSnapinLoaded = false;
foreach (string snapIn in snapInList)
{
if (InitialSessionState.IsEngineModule(snapIn))
{
WriteNonTerminatingError(snapIn, "LoadSystemSnapinAsModule",
PSTraceSource.NewArgumentException(snapIn,
MshSnapInCmdletResources.LoadSystemSnapinAsModule, snapIn),
ErrorCategory.InvalidArgument);
}
else
{
PSSnapInException warning;
try
{
// Read snapin data
PSSnapInInfo newPSSnapIn = PSSnapInReader.Read(Utils.GetCurrentMajorVersion(), snapIn);
PSSnapInInfo psSnapInInfo = IsSnapInLoaded(loadedSnapins, newPSSnapIn);
// that means snapin is not already loaded ..so load the snapin
// now.
if (null == psSnapInInfo)
{
psSnapInInfo = iss.ImportPSSnapIn(snapIn, out warning);
isAtleastOneSnapinLoaded = true;
Context.InitialSessionState.ImportedSnapins.Add(psSnapInInfo.Name, psSnapInInfo);
}
// Write psSnapInInfo object only if passthru is specified.
if (_passThru)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
psSnapInInfo.LoadIndirectResources(ResourceReader);
WriteObject(psSnapInInfo);
}
}
catch (PSSnapInException pse)
{
WriteNonTerminatingError(snapIn, "AddPSSnapInRead", pse, ErrorCategory.InvalidData);
}
}
}
if (isAtleastOneSnapinLoaded)
{
// Now update the session state with the new stuff...
iss.Bind(Context, /*updateOnly*/ true);
}
return;
}
foreach (string psSnapIn in snapInList)
{
Exception exception = null;
try
{
PSSnapInException warning = null;
PSSnapInInfo psSnapInInfo = this.Runspace.AddPSSnapIn(psSnapIn, out warning);
if (warning != null)
{
WriteNonTerminatingError(psSnapIn, "AddPSSnapInRead", warning, ErrorCategory.InvalidData);
}
// Write psSnapInInfo object only if passthru is specified.
if (_passThru)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
psSnapInInfo.LoadIndirectResources(ResourceReader);
WriteObject(psSnapInInfo);
}
}
catch (PSArgumentException ae)
{
exception = ae;
}
catch (PSSnapInException sle)
{
exception = sle;
}
catch (System.Security.SecurityException se)
{
exception = se;
}
if (exception != null)
{
2016-07-29 13:02:49 -07:00
WriteNonTerminatingError(psSnapIn,
"AddPSSnapInRead",
exception,
ErrorCategory.InvalidArgument);
}
}
}
#endregion
}
#endregion
#region Remove-PSSnapIn
/// <summary>
/// Class that implements remove-pssnapin cmdlet.
/// </summary>
2016-08-23 21:21:31 -04:00
[Cmdlet(VerbsCommon.Remove, "PSSnapin", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113378")]
[OutputType(typeof(PSSnapInInfo))]
public sealed class RemovePSSnapinCommand : PSSnapInCommandBase
{
#region Parameters
/// <summary>
/// Property that gets/sets PSSnapIn Ids for the cmdlet.
/// </summary>
/// <value>An array of strings representing PSSnapIn ids.</value>
[Parameter(
Position = 0,
Mandatory = true,
ValueFromPipelineByPropertyName = true)]
public string[] Name
{
get
{
return _pssnapins;
}
set
{
_pssnapins = value;
}
}
private string[] _pssnapins;
/// <summary>
/// Gets or sets the Passthru flag for the operation.
/// If true, the PSSnapInInfo object is also passed
/// down the output pipeline.
/// </summary>
[Parameter()]
public SwitchParameter PassThru
{
get
{
return _passThru;
}
set
{
_passThru = value;
}
}
private bool _passThru;
#endregion
#region Overrides
/// <summary>
/// Removes pssnapins from the current console file.
/// </summary>
/// <remarks>
/// The pssnapin is not unloaded from the current engine. So all the cmdlets that are
/// represented by this pssnapin will continue to work.
/// </remarks>
protected override void ProcessRecord()
{
foreach (string psSnapIn in _pssnapins)
{
Collection<PSSnapInInfo> snapIns = GetSnapIns(psSnapIn);
// snapIns won't be null..
Diagnostics.Assert(snapIns != null, "GetSnapIns() returned null");
if (snapIns.Count == 0)
{
WriteNonTerminatingError(psSnapIn, "NoPSSnapInsFound",
PSTraceSource.NewArgumentException(psSnapIn,
MshSnapInCmdletResources.NoPSSnapInsFound, psSnapIn),
ErrorCategory.InvalidArgument);
continue;
}
foreach (PSSnapInInfo snapIn in snapIns)
{
// confirm the operation first
// this is always false if WhatIf is set
if (ShouldProcess(snapIn.Name))
{
Exception exception = null;
if (this.Runspace == null && this.Context.InitialSessionState != null)
{
try
{
// Check if this snapin can be removed
// Monad has specific restrictions on the mshsnapinid like
// mshsnapinid should be A-Za-z0-9.-_ etc.
PSSnapInInfo.VerifyPSSnapInFormatThrowIfError(snapIn.Name);
if (MshConsoleInfo.IsDefaultPSSnapIn(snapIn.Name, this.Context.InitialSessionState.defaultSnapins))
{
throw PSTraceSource.NewArgumentException(snapIn.Name, ConsoleInfoErrorStrings.CannotRemoveDefault, snapIn.Name);
}
// Handle the initial session state case...
InitialSessionState iss = InitialSessionState.Create();
PSSnapInException warning;
// Get the snapin information...
iss.ImportPSSnapIn(snapIn, out warning);
iss.Unbind(Context);
Context.InitialSessionState.ImportedSnapins.Remove(snapIn.Name);
}
catch (PSArgumentException ae)
{
exception = ae;
}
if (exception != null)
{
WriteNonTerminatingError(psSnapIn, "RemovePSSnapIn", exception, ErrorCategory.InvalidArgument);
}
}
else
{
try
{
PSSnapInException warning = null;
PSSnapInInfo psSnapInInfo = this.Runspace.RemovePSSnapIn(snapIn.Name, out warning);
if (warning != null)
{
WriteNonTerminatingError(snapIn.Name, "RemovePSSnapInRead", warning, ErrorCategory.InvalidData);
}
if (_passThru)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
psSnapInInfo.LoadIndirectResources(ResourceReader);
WriteObject(psSnapInInfo);
}
}
catch (PSArgumentException ae)
{
exception = ae;
}
if (exception != null)
{
WriteNonTerminatingError(psSnapIn, "RemovePSSnapIn", exception, ErrorCategory.InvalidArgument);
}
}
} // ShouldContinue
}
}
}
#endregion
}
#endregion
#region Get-PSSnapIn
/// <summary>
/// Class that implements get-pssnapin cmdlet.
/// </summary>
2016-08-23 21:21:31 -04:00
[Cmdlet(VerbsCommon.Get, "PSSnapin", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113330")]
[OutputType(typeof(PSSnapInInfo))]
public sealed class GetPSSnapinCommand : PSSnapInCommandBase
{
#region Parameters
/// <summary>
/// Name(s) of PSSnapIn(s).
/// </summary>
2016-07-29 13:02:49 -07:00
[Parameter(Position = 0, Mandatory = false)]
public string[] Name
{
get
{
return _pssnapins;
}
set
{
_pssnapins = value;
}
}
private string[] _pssnapins;
2016-07-29 13:02:49 -07:00
/// <summary>
/// Property that determines whether to get all pssnapins that are currently
/// registered ( in registry ).
/// </summary>
/// <value>
/// A boolean that determines whether to get all pssnapins that are currently
/// registered ( in registry ).
/// </value>
[Parameter(Mandatory = false)]
public SwitchParameter Registered
{
get
{
return ShouldGetAll;
}
set
{
ShouldGetAll = value;
}
}
#endregion
#region Overrides
/// <summary>
/// Constructs PSSnapInfo objects as requested by the user and writes them to the
/// output buffer.
/// </summary>
protected override void BeginProcessing()
{
if (_pssnapins != null)
{
foreach (string psSnapIn in _pssnapins)
{
Exception exception = null;
try
{
Collection<PSSnapInInfo> psSnapInInfoList = GetSnapIns(psSnapIn);
// psSnapInInfoList wont be null..
Diagnostics.Assert(psSnapInInfoList != null, "ConsoleInfo.GetPSSnapIn returned null");
if (psSnapInInfoList.Count == 0)
{
WriteNonTerminatingError(psSnapIn, "NoPSSnapInsFound",
PSTraceSource.NewArgumentException(psSnapIn,
MshSnapInCmdletResources.NoPSSnapInsFound, psSnapIn),
ErrorCategory.InvalidArgument);
continue;
}
foreach (PSSnapInInfo pssnapinInfo in psSnapInInfoList)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
pssnapinInfo.LoadIndirectResources(ResourceReader);
WriteObject(pssnapinInfo);
}
}
catch (System.Security.SecurityException se)
{
exception = se;
}
catch (PSArgumentException ae)
{
exception = ae;
}
if (exception != null)
{
WriteNonTerminatingError(psSnapIn, "GetPSSnapInRead", exception, ErrorCategory.InvalidArgument);
}
}
}
else if (ShouldGetAll)
{
Exception exception = null;
try
{
Collection<PSSnapInInfo> psSnapInInfoList = PSSnapInReader.ReadAll();
foreach (PSSnapInInfo pssnapinInfo in psSnapInInfoList)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
pssnapinInfo.LoadIndirectResources(ResourceReader);
WriteObject(pssnapinInfo);
}
}
catch (System.Security.SecurityException se)
{
exception = se;
}
catch (PSArgumentException ae)
{
exception = ae;
}
if (exception != null)
{
WriteNonTerminatingError(this, "GetPSSnapInRead", exception, ErrorCategory.InvalidArgument);
}
}
else
{
// this should never throw..
Collection<PSSnapInInfo> psSnapInInfoList = GetSnapIns(null);
foreach (PSSnapInInfo pssnapinInfo in psSnapInInfoList)
{
// Load the pssnapin info properties that are localizable and redirected in the registry
pssnapinInfo.LoadIndirectResources(ResourceReader);
WriteObject(pssnapinInfo);
}
}
}
#endregion
}
#endregion
}