/********************************************************************++ Copyright (c) Microsoft Corporation. All rights reserved. --********************************************************************/ using System.IO; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation.Internal; using System.Management.Automation.Language; using System.Reflection; using PipelineResultTypes = System.Management.Automation.Runspaces.PipelineResultTypes; namespace System.Management.Automation { #region Auxiliary /// /// An interface that a /// or /// must implement to indicate that it has dynamic parameters. /// /// /// Dynamic parameters allow a /// or /// to define additional parameters based on the value of /// the formal arguments. For example, the parameters of /// "set-itemproperty" for the file system provider vary /// depending on whether the target object is a file or directory. /// /// /// /// /// public interface IDynamicParameters { /// /// Returns an instance of an object that defines the /// dynamic parameters for this /// or . /// /// /// This method should return an object that has properties and fields /// decorated with parameter attributes similar to a /// or . /// These attributes include , /// , argument transformation and /// validation attributes, etc. /// /// Alternately, it can return a /// /// instead. /// /// The or /// should hold on to a reference to the object which it returns from /// this method, since the argument values for the dynamic parameters /// specified by that object will be set in that object. /// /// This method will be called after all formal (command-line) /// parameters are set, but before /// is called and before any incoming pipeline objects are read. /// Therefore, parameters which allow input from the pipeline /// may not be set at the time this method is called, /// even if the parameters are mandatory. /// object GetDynamicParameters(); } /// /// Type used to define a parameter on a cmdlet script of function that /// can only be used as a switch. /// public struct SwitchParameter { private bool _isPresent; /// /// Returns true if the parameter was specified on the command line, false otherwise. /// /// True if the parameter was specified, false otherwise public bool IsPresent { get { return _isPresent; } } /// /// Implicit cast operator for casting SwitchParameter to bool. /// /// The SwitchParameter object to convert to bool /// The corresponding boolean value. public static implicit operator bool (SwitchParameter switchParameter) { return switchParameter.IsPresent; } /// /// Implicit cast operator for casting bool to SwitchParameter. /// /// The bool to convert to SwitchParameter /// The corresponding boolean value. public static implicit operator SwitchParameter(bool value) { return new SwitchParameter(value); } /// /// Explicit method to convert a SwitchParameter to a boolean value. /// /// The boolean equivalent of the SwitchParameter public bool ToBool() { return _isPresent; } /// /// Construct a SwitchParameter instance with a particular value. /// /// /// If true, it indicates that the switch is present, flase otherwise. /// public SwitchParameter(bool isPresent) { _isPresent = isPresent; } /// /// Static method that returns a instance of SwitchParameter that indicates that it is present. /// /// An instance of a switch parameter that will convert to true in a boolean context public static SwitchParameter Present { get { return new SwitchParameter(true); } } /// /// Compare this switch parameter to another object. /// /// An object to compare against /// True if the objects are the same value. public override bool Equals(object obj) { if (obj is bool) { return _isPresent == (bool)obj; } else if (obj is SwitchParameter) { return _isPresent == ((SwitchParameter)obj).IsPresent; } else { return false; } } /// /// Returns the hash code for this switch parameter. /// /// The hash code for this cobject. public override int GetHashCode() { return _isPresent.GetHashCode(); } /// /// Implement the == operator for switch parameters objects. /// /// first object to compare /// second object to compare /// True if they are the same public static bool operator ==(SwitchParameter first, SwitchParameter second) { return first.Equals(second); } /// /// Implement the != operator for switch parameters /// /// first object to compare /// second object to compare /// True if they are different public static bool operator !=(SwitchParameter first, SwitchParameter second) { return !first.Equals(second); } /// /// Implement the == operator for switch parameters and booleans. /// /// first object to compare /// second object to compare /// True if they are the same public static bool operator ==(SwitchParameter first, bool second) { return first.Equals(second); } /// /// Implement the != operator for switch parameters and booleans. /// /// first object to compare /// second object to compare /// True if they are different public static bool operator !=(SwitchParameter first, bool second) { return !first.Equals(second); } /// /// Implement the == operator for bool and switch parameters /// /// first object to compare /// second object to compare /// True if they are the same public static bool operator ==(bool first, SwitchParameter second) { return first.Equals(second); } /// /// Implement the != operator for bool and switch parameters /// /// first object to compare /// second object to compare /// True if they are different public static bool operator !=(bool first, SwitchParameter second) { return !first.Equals(second); } /// /// Returns the string representation for this object /// /// The string for this object. public override string ToString() { return _isPresent.ToString(); } } /// /// Interfaces that cmdlets can use to build script blocks and execute scripts. /// public class CommandInvocationIntrinsics { private ExecutionContext _context; private PSCmdlet _cmdlet; private MshCommandRuntime _commandRuntime; internal CommandInvocationIntrinsics(ExecutionContext context, PSCmdlet cmdlet) { _context = context; if (cmdlet != null) { _cmdlet = cmdlet; _commandRuntime = cmdlet.CommandRuntime as MshCommandRuntime; } } internal CommandInvocationIntrinsics(ExecutionContext context) : this(context, null) { } /// /// If an error occurred while executing the cmdlet, this will be set to true. /// public bool HasErrors { get { return _commandRuntime.PipelineProcessor.ExecutionFailed; } set { _commandRuntime.PipelineProcessor.ExecutionFailed = value; } } /// /// Returns a string with all of the variable and expression substitutions done. /// /// The string to expand. /// /// The expanded string. /// /// Thrown if a parse exception occurred during subexpression substitution. /// public string ExpandString(string source) { if (null != _cmdlet) _cmdlet.ThrowIfStopping(); return _context.Engine.Expand(source); } /// /// /// /// /// /// public CommandInfo GetCommand(string commandName, CommandTypes type) { return GetCommand(commandName, type, null); } /// /// Returns a command info for a given command name and type, using the specified arguments /// to resolve dynamic parameters. /// /// The command name to search for /// The command type to search for /// The command arguments used to resolve dynamic parameters /// A CommandInfo result that represents the resolved command public CommandInfo GetCommand(string commandName, CommandTypes type, object[] arguments) { CommandInfo result = null; try { CommandOrigin commandOrigin = CommandOrigin.Runspace; if (_cmdlet != null) { commandOrigin = _cmdlet.CommandOrigin; } else if (_context != null) { commandOrigin = _context.EngineSessionState.CurrentScope.ScopeOrigin; } result = CommandDiscovery.LookupCommandInfo(commandName, type, SearchResolutionOptions.None, commandOrigin, _context); if ((result != null) && (arguments != null) && (arguments.Length > 0)) { // We've been asked to retrieve dynamic parameters if (result.ImplementsDynamicParameters) { result = result.CreateGetCommandCopy(arguments); } } } catch (CommandNotFoundException) { } return result; } /// /// This event handler is called when a command is not found. /// If should have a single string parameter that is the name /// of the command and should return a CommandInfo object or null. By default /// it will search the module path looking for a module that exports the /// desired command. /// public System.EventHandler CommandNotFoundAction { get; set; } /// /// This event handler is called before the command lookup is done. /// If should have a single string parameter that is the name /// of the command and should return a CommandInfo object or null. /// public System.EventHandler PreCommandLookupAction { get; set; } /// /// This event handler is after the command lookup is done but before the event object is /// returned to the caller. This allows things like interning scripts to work. /// If should have a single string parameter that is the name /// of the command and should return a CommandInfo object or null. /// public System.EventHandler PostCommandLookupAction { get; set; } /// /// Returns the CmdletInfo object that corresponds to the name argument /// /// The name of the cmdlet to look for /// The cmdletInfo object if found, null otherwise public CmdletInfo GetCmdlet(string commandName) { return GetCmdlet(commandName, _context); } /// /// Returns the CmdletInfo object that corresponds to the name argument /// /// The name of the cmdlet to look for /// The execution context instance to use for lookup /// The cmdletInfo object if found, null otherwise internal static CmdletInfo GetCmdlet(string commandName, ExecutionContext context) { CmdletInfo current = null; CommandSearcher searcher = new CommandSearcher( commandName, SearchResolutionOptions.None, CommandTypes.Cmdlet, context); do { try { if (!searcher.MoveNext()) { break; } } catch (ArgumentException) { continue; } catch (PathTooLongException) { continue; } catch (FileLoadException) { continue; } catch (MetadataException) { continue; } catch (FormatException) { continue; } current = ((IEnumerator)searcher).Current as CmdletInfo; } while (true); return current; } /// /// Get the cmdlet info using the name of the cmdlet's implementing type. This bypasses /// session state and retrieves the command directly. Note that the help file and snapin/module /// info will both be null on returned object. /// /// the type name of the class implementing this cmdlet /// CmdletInfo for the cmdlet if found, null otherwise public CmdletInfo GetCmdletByTypeName(string cmdletTypeName) { if (string.IsNullOrEmpty(cmdletTypeName)) { throw PSTraceSource.NewArgumentNullException("cmdletTypeName"); } Exception e = null; Type cmdletType = TypeResolver.ResolveType(cmdletTypeName, out e); if (e != null) { throw e; } if (cmdletType == null) { return null; } CmdletAttribute ca = null; foreach (var attr in cmdletType.GetTypeInfo().GetCustomAttributes(true)) { ca = attr as CmdletAttribute; if (ca != null) break; } if (ca == null) { throw PSTraceSource.NewNotSupportedException(); } string noun = ca.NounName; string verb = ca.VerbName; string cmdletName = verb + "-" + noun; return new CmdletInfo(cmdletName, cmdletType, null, null, _context); } /// /// Returns a list of all cmdlets... /// /// public List GetCmdlets() { return GetCmdlets("*"); } /// /// Returns all cmdlets whose names match the pattern... /// /// A list of CmdletInfo objects... public List GetCmdlets(string pattern) { if (pattern == null) throw PSTraceSource.NewArgumentNullException("pattern"); List cmdlets = new List(); CmdletInfo current = null; CommandSearcher searcher = new CommandSearcher( pattern, SearchResolutionOptions.CommandNameIsPattern, CommandTypes.Cmdlet, _context); do { try { if (!searcher.MoveNext()) { break; } } catch (ArgumentException) { continue; } catch (PathTooLongException) { continue; } catch (FileLoadException) { continue; } catch (MetadataException) { continue; } catch (FormatException) { continue; } current = ((IEnumerator)searcher).Current as CmdletInfo; if (current != null) cmdlets.Add(current); } while (true); return cmdlets; } /// /// Searches for PowerShell commands, optionally using wildcard patterns /// and optionally return the full path to applications and scripts rather than /// the simple command name. /// /// The name of the command to use /// If true treat the name as a pattern to search for /// If true, return the full path to scripts and applications /// A list of command names... public List GetCommandName(string name, bool nameIsPattern, bool returnFullName) { if (name == null) { throw PSTraceSource.NewArgumentNullException("name"); } List commands = new List(); foreach (CommandInfo current in this.GetCommands(name, CommandTypes.All, nameIsPattern)) { if (current.CommandType == CommandTypes.Application) { string cmdExtension = System.IO.Path.GetExtension(current.Name); if (!String.IsNullOrEmpty(cmdExtension)) { // Only add the application in PATHEXT... foreach (string extension in CommandDiscovery.PathExtensions) { if (extension.Equals(cmdExtension, StringComparison.OrdinalIgnoreCase)) { if (returnFullName) { commands.Add(current.Definition); } else { commands.Add(current.Name); } } } } } else if (current.CommandType == CommandTypes.ExternalScript) { if (returnFullName) { commands.Add(current.Definition); } else { commands.Add(current.Name); } } else { commands.Add(current.Name); } } return commands; } /// /// Searches for PowerShell commands, optionally using wildcard patterns /// /// The name of the command to use /// Type of commands to support /// If true treat the name as a pattern to search for /// Collection of command names... public IEnumerable GetCommands(string name, CommandTypes commandTypes, bool nameIsPattern) { if (name == null) { throw PSTraceSource.NewArgumentNullException("name"); } SearchResolutionOptions options = nameIsPattern ? (SearchResolutionOptions.CommandNameIsPattern | SearchResolutionOptions.ResolveFunctionPatterns | SearchResolutionOptions.ResolveAliasPatterns) : SearchResolutionOptions.None; return GetCommands(name, commandTypes, options); } internal IEnumerable GetCommands(string name, CommandTypes commandTypes, SearchResolutionOptions options, CommandOrigin? commandOrigin = null) { CommandSearcher searcher = new CommandSearcher( name, options, commandTypes, _context); if (commandOrigin != null) { searcher.CommandOrigin = commandOrigin.Value; } do { try { if (!searcher.MoveNext()) { break; } } catch (ArgumentException) { continue; } catch (PathTooLongException) { continue; } catch (FileLoadException) { continue; } catch (MetadataException) { continue; } catch (FormatException) { continue; } CommandInfo commandInfo = ((IEnumerator)searcher).Current as CommandInfo; if (commandInfo != null) { yield return commandInfo; } } while (true); } /// /// Executes a piece of text as a script synchronously. /// /// The script text to evaluate /// A collection of MshCobjects generated by the script. /// Thrown if there was a parsing error in the script. /// Represents a script-level exception /// public Collection InvokeScript(string script) { return InvokeScript(script, true, PipelineResultTypes.None, null); } /// /// Executes a piece of text as a script synchronously. /// /// The script text to evaluate /// The arguments to the script /// A collection of MshCobjects generated by the script. /// Thrown if there was a parsing error in the script. /// Represents a script-level exception /// public Collection InvokeScript(string script, params object[] args) { return InvokeScript(script, true, PipelineResultTypes.None, args); } /// /// /// /// /// /// /// public Collection InvokeScript( SessionState sessionState, ScriptBlock scriptBlock, params object[] args) { if (scriptBlock == null) { throw PSTraceSource.NewArgumentNullException("scriptBlock"); } if (sessionState == null) { throw PSTraceSource.NewArgumentNullException("sessionState"); } SessionStateInternal _oldSessionState = _context.EngineSessionState; try { _context.EngineSessionState = sessionState.Internal; return InvokeScript(scriptBlock, false, PipelineResultTypes.None, null, args); } finally { _context.EngineSessionState = _oldSessionState; } } /// /// Invoke a scriptblock in the current runspace, controlling if it gets a new scope. /// /// If true, a new scope will be created /// The scriptblock to execute /// Optionall input to the command /// Arguments to pass to the scriptblock /// The result of the evaluation public Collection InvokeScript( bool useLocalScope, ScriptBlock scriptBlock, IList input, params object[] args) { if (scriptBlock == null) { throw PSTraceSource.NewArgumentNullException("scriptBlock"); } // Force the current runspace onto the callers thread - this is needed // if this API is going to be callable through the SessionStateProxy on the runspace. var old = System.Management.Automation.Runspaces.Runspace.DefaultRunspace; System.Management.Automation.Runspaces.Runspace.DefaultRunspace = _context.CurrentRunspace; try { return InvokeScript(scriptBlock, useLocalScope, PipelineResultTypes.None, input, args); } finally { System.Management.Automation.Runspaces.Runspace.DefaultRunspace = old; } } /// /// Executes a piece of text as a script synchronously using the options provided. /// /// The script to evaluate. /// If true, evaluate the script in its own scope. /// If false, the script will be evaluated in the current scope i.e. it will be "dotted" /// If set to Output, all output will be streamed /// to the output pipe of the calling cmdlet. If set to None, the result will be returned /// to the caller as a collection of PSObjects. No other flags are supported at this time and /// will result in an exception if used. /// The list of objects to use as input to the script. /// The array of arguments to the command. /// A collection of MshCobjects generated by the script. This will be /// empty if output was redirected. /// Thrown if there was a parsing error in the script. /// Represents a script-level exception /// Thrown if any redirect other than output is attempted /// public Collection InvokeScript(string script, bool useNewScope, PipelineResultTypes writeToPipeline, IList input, params object[] args) { if (script == null) throw new ArgumentNullException("script"); // Compile the script text into an executable script block. ScriptBlock sb = ScriptBlock.Create(_context, script); return InvokeScript(sb, useNewScope, writeToPipeline, input, args); } private Collection InvokeScript(ScriptBlock sb, bool useNewScope, PipelineResultTypes writeToPipeline, IList input, params object[] args) { if (null != _cmdlet) _cmdlet.ThrowIfStopping(); Cmdlet cmdletToUse = null; ScriptBlock.ErrorHandlingBehavior errorHandlingBehavior = ScriptBlock.ErrorHandlingBehavior.WriteToExternalErrorPipe; // Check if they want output if ((writeToPipeline & PipelineResultTypes.Output) == PipelineResultTypes.Output) { cmdletToUse = _cmdlet; writeToPipeline &= (~PipelineResultTypes.Output); } // Check if they want error if ((writeToPipeline & PipelineResultTypes.Error) == PipelineResultTypes.Error) { errorHandlingBehavior = ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe; writeToPipeline &= (~PipelineResultTypes.Error); } if (writeToPipeline != PipelineResultTypes.None) { // The only output types are Output and Error. throw PSTraceSource.NewNotImplementedException(); } // If the cmdletToUse is not null, then the result of the evaluation will be // streamed out the output pipe of the cmdlet. object rawResult; if (cmdletToUse != null) { sb.InvokeUsingCmdlet( contextCmdlet: cmdletToUse, useLocalScope: useNewScope, errorHandlingBehavior: errorHandlingBehavior, dollarUnder: AutomationNull.Value, input: input, scriptThis: AutomationNull.Value, args: args); rawResult = AutomationNull.Value; } else { rawResult = sb.DoInvokeReturnAsIs( useLocalScope: useNewScope, errorHandlingBehavior: errorHandlingBehavior, dollarUnder: AutomationNull.Value, input: input, scriptThis: AutomationNull.Value, args: args); } if (rawResult == AutomationNull.Value) { return new Collection(); } // If the result is already a collection of PSObjects, just return it... Collection result = rawResult as Collection; if (result != null) return result; result = new Collection(); IEnumerator list = null; list = LanguagePrimitives.GetEnumerator(rawResult); if (list != null) { while (list.MoveNext()) { object val = list.Current; result.Add(LanguagePrimitives.AsPSObjectOrNull(val)); } } else { result.Add(LanguagePrimitives.AsPSObjectOrNull(rawResult)); } return result; } /// /// Compile a string into a script block. /// /// The source text to compile /// The compiled script block /// public ScriptBlock NewScriptBlock(string scriptText) { if (null != _commandRuntime) _commandRuntime.ThrowIfStopping(); ScriptBlock result = ScriptBlock.Create(_context, scriptText); return result; } } //CommandInvocationIntrinsics #endregion Auxiliary /// /// Defines members used by Cmdlets. /// All Cmdlets must derive from /// . /// /// /// Do not attempt to create instances of /// /// or its subclasses. /// Instead, derive your own subclasses and mark them with /// , /// and when your assembly is included in a shell, the Engine will /// take care of instantiating your subclass. /// public abstract partial class PSCmdlet : Cmdlet { #region private_members internal bool HasDynamicParameters { get { return this is IDynamicParameters; } } #endregion private_members #region public members /// /// The name of the parameter set in effect. /// /// the parameter set name public string ParameterSetName { get { using (PSTransactionManager.GetEngineProtectionScope()) { return _ParameterSetName; } } } /// /// Contains information about the identity of this cmdlet /// and how it was invoked. /// /// public new InvocationInfo MyInvocation { get { using (PSTransactionManager.GetEngineProtectionScope()) { return base.MyInvocation; } } } /// /// If the cmdlet declares paging support (via ), /// then property contains arguments of the paging parameters. /// Otherwise property is null. /// public PagingParameters PagingParameters { get { using (PSTransactionManager.GetEngineProtectionScope()) { if (!this.CommandInfo.CommandMetadata.SupportsPaging) { return null; } if (_pagingParameters == null) { MshCommandRuntime mshCommandRuntime = this.CommandRuntime as MshCommandRuntime; if (mshCommandRuntime != null) { _pagingParameters = mshCommandRuntime.PagingParameters ?? new PagingParameters(mshCommandRuntime); } } return _pagingParameters; } } } private PagingParameters _pagingParameters; #region InvokeCommand private CommandInvocationIntrinsics _invokeCommand; /// /// Provides access to utility routines for executing scripts /// and creating script blocks. /// /// Returns an object exposing the utility routines. public CommandInvocationIntrinsics InvokeCommand { get { using (PSTransactionManager.GetEngineProtectionScope()) { return _invokeCommand ?? (_invokeCommand = new CommandInvocationIntrinsics(Context, this)); } } } //InvokeCommand #endregion InvokeCommand #endregion public members } }