2016-03-30 14:20:52 -07:00
/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System.Collections.Generic ;
using System.Collections.ObjectModel ;
using System.Diagnostics.Tracing ;
using System.Globalization ;
using System.IO ;
using System.Linq ;
using System.Management.Automation.Internal ;
using System.Management.Automation.Runspaces ;
using System.Security ;
using Dbg = System . Management . Automation . Diagnostics ;
using System.Management.Automation.Host ;
using Microsoft.PowerShell.Commands ;
using Microsoft.Win32 ;
namespace System.Management.Automation
{
/// <summary>
/// EventArgs for the ScriptCmdletVariableUpdate event
/// </summary>
public class CommandLookupEventArgs : EventArgs
{
/// <summary>
/// Constructor for event args object
/// </summary>
/// <param name="commandName">The name of the command we're searching for</param>
/// <param name="commandOrigin">The origin of the command internal or runspace (external)</param>
/// <param name="context">The execution context for this command</param>
internal CommandLookupEventArgs ( string commandName , CommandOrigin commandOrigin , ExecutionContext context )
{
this . commandName = commandName ;
this . commandOrigin = commandOrigin ;
this . context = context ;
}
ExecutionContext context ;
/// <summary>
/// The name of the command we're looking for
/// </summary>
public string CommandName { get { return this . commandName ; } }
string commandName ;
/// <summary>
/// The origin of the command internal or runspace (external)
/// </summary>
public CommandOrigin CommandOrigin { get { return this . commandOrigin ; } }
CommandOrigin commandOrigin ;
/// <summary>
/// If true on return from event handler, the search is stopped.
/// </summary>
public bool StopSearch { get ; set ; }
/// <summary>
/// The CommandInfo obejct for the command that was found.
/// </summary>
public CommandInfo Command { get ; set ; }
/// <summary>
/// Scriptblock to be returned as the found command. If it is set to
/// null, then the command to return and the StopSearch flag will be reset.
/// </summary>
public ScriptBlock CommandScriptBlock
{
get { return scriptBlock ; }
set
{
scriptBlock = value ;
if ( scriptBlock != null )
{
string dynamicName = "LookupHandlerReplacementFor<<" + commandName + ">>" ;
Command = new FunctionInfo ( dynamicName , scriptBlock , context );
StopSearch = true ;
}
else
{
Command = null ;
StopSearch = false ;
}
}
}
ScriptBlock scriptBlock ;
}
/// <summary>
/// Defines the preference options for the Module Auto-loading feature
/// </summary>
public enum PSModuleAutoLoadingPreference
{
/// <summary>
/// Do not auto-load modules when a command is not found.
/// </summary>
None = 0 ,
/// <summary>
/// Only auto-load modules when a command is not found, and the command
/// is module-qualified.
/// </summary>
ModuleQualified = 1 ,
/// <summary>
/// Auto-load modules when a command is not found.
/// </summary>
All = 2
}
/// <summary>
/// CommandDiscovery...
/// </summary>
internal class CommandDiscovery
{
[TraceSource("CommandDiscovery", "Traces the discovery of cmdlets, scripts, functions, applications, etc.")]
static internal PSTraceSource discoveryTracer =
PSTraceSource . GetTracer (
"CommandDiscovery" ,
"Traces the discovery of cmdlets, scripts, functions, applications, etc." ,
false );
#region ctor
/// <summary>
/// Default constructor...
/// </summary>
///
/// <exception cref="ArgumentNullException">
/// If <paramref name="context"/> is null.
/// </exception>
///
internal CommandDiscovery ( ExecutionContext context )
{
if ( context == null )
{
throw PSTraceSource . NewArgumentNullException ( "context" );
}
_context = context ;
discoveryTracer . ShowHeaders = false ;
// Cache the ScriptInfo for the scripts defined in the RunspaceConfiguration
cachedScriptInfo =
new Dictionary < string , ScriptInfo >( StringComparer . OrdinalIgnoreCase );
LoadScriptInfo ();
}
private void AddCmdletToCache ( CmdletConfigurationEntry entry )
{
if (! IsSpecialCmdlet ( entry . ImplementingType ))
{
CmdletInfo newCmdletInfo = NewCmdletInfo ( entry , SessionStateEntryVisibility . Public );
AddCmdletInfoToCache ( newCmdletInfo . Name , newCmdletInfo , isGlobal : true );
}
}
/// <summary>
/// Determines if the cmdlet is a cmdlet that shouldn't be in the discovery list.
/// </summary>
///
/// <param name="implementingType">
/// Type implementing the cmdlet
/// </param>
///
/// <returns>
/// True if the cmdlet is a special cmdlet that shouldn't be part of the discovery list. Or false otherwise.
/// </returns>
///
private bool IsSpecialCmdlet ( Type implementingType )
{
// These commands should never be put in the discovery list. They are an internal implementation
// detail of the formatting and output component. That component uses these cmdlets by creating
// an instance of the CommandProcessor class directly.
return implementingType == typeof ( OutLineOutputCommand ) || implementingType == typeof ( FormatDefaultCommand );
}
private CmdletInfo NewCmdletInfo ( CmdletConfigurationEntry entry , SessionStateEntryVisibility visibility )
{
CmdletInfo ci = new CmdletInfo ( entry . Name , entry . ImplementingType , entry . HelpFileName , entry . PSSnapIn , _context );
ci . Visibility = visibility ;
return ci ;
}
private CmdletInfo NewCmdletInfo ( SessionStateCmdletEntry entry )
{
return NewCmdletInfo ( entry , _context );
}
internal static CmdletInfo NewCmdletInfo ( SessionStateCmdletEntry entry , ExecutionContext context )
{
CmdletInfo ci = new CmdletInfo ( entry . Name , entry . ImplementingType , entry . HelpFileName , entry . PSSnapIn , context )
{
Visibility = entry . Visibility ,
Module = entry . Module
};
return ci ;
}
internal static AliasInfo NewAliasInfo ( SessionStateAliasEntry entry , ExecutionContext context )
{
AliasInfo ci = new AliasInfo ( entry . Name , entry . Definition , context , entry . Options )
{
Visibility = entry . Visibility ,
Module = entry . Module
};
return ci ;
}
/// <summary>
/// Adds the CmdletInfo to the cmdlet cache in the current scope object.
/// </summary>
///
/// <param name="name">
/// The name of the cmdlet to add.
/// </param>
///
/// <param name="newCmdletInfo">
/// The CmdletInfo to add.
/// </param>
///
/// <param name="isGlobal">
/// If true, the cmdlet is added to the Module Scope of the session state.
/// </param>
///
/// <exception cref="PSNotSupportedException">
/// If a cmdlet with the same module and cmdlet name already exists
/// but has a different implementing type.
/// </exception>
///
internal CmdletInfo AddCmdletInfoToCache ( string name , CmdletInfo newCmdletInfo , bool isGlobal )
{
if ( String . IsNullOrEmpty ( name ))
{
throw PSTraceSource . NewArgumentException ( "name" );
}
if ( newCmdletInfo == null )
{
throw PSTraceSource . NewArgumentNullException ( "cmdlet" );
}
if ( isGlobal )
{
// When cmdlet cache was not scope-based, we used to import cmdlets to the module scope.
// We need to do the same as the default action (setting "isGlobal" is done as a default action in the caller)
return _context . EngineSessionState . ModuleScope . AddCmdletToCache ( newCmdletInfo . Name , newCmdletInfo , CommandOrigin . Internal , _context );
}
return _context . EngineSessionState . CurrentScope . AddCmdletToCache ( newCmdletInfo . Name , newCmdletInfo , CommandOrigin . Internal , _context );
}
/// <summary>
/// Add a SessionStateCmdletEntry to the cmdlet cache...
/// </summary>
/// <param name="entry"></param>
internal void AddSessionStateCmdletEntryToCache ( SessionStateCmdletEntry entry )
{
AddSessionStateCmdletEntryToCache ( entry , /*local*/ false );
}
/// <summary>
/// Add a SessionStateCmdletEntry to the cmdlet cache...
/// </summary>
/// <param name="entry"></param>
/// <param name="local"></param>
internal void AddSessionStateCmdletEntryToCache ( SessionStateCmdletEntry entry , bool local )
{
if (! IsSpecialCmdlet ( entry . ImplementingType ))
{
CmdletInfo nci = NewCmdletInfo ( entry );
AddCmdletInfoToCache ( nci . Name , nci , ! local );
}
}
private void LoadScriptInfo ()
{
if ( _context . RunspaceConfiguration != null )
{
foreach ( ScriptConfigurationEntry entry in _context . RunspaceConfiguration . Scripts )
{
try
{
cachedScriptInfo . Add ( entry . Name , new ScriptInfo ( entry . Name , ScriptBlock . Create ( _context , entry . Definition ), _context ));
}
catch ( ArgumentException )
{
PSNotSupportedException notSupported =
PSTraceSource . NewNotSupportedException (
DiscoveryExceptions . DuplicateScriptName ,
entry . Name );
throw notSupported ;
}
}
}
}
#endregion ctor
#region internal methods
/// <summary>
/// Look up a command named by the argument string and return its CommandProcessorBase.
/// </summary>
///
/// <param name="commandName">
/// The command name to lookup.
/// </param>
///
/// <param name="commandOrigin"> Location where the command was dispatched from. </param>
///
/// <param name="useLocalScope">
/// True if command processor should use local scope to execute the command,
/// False if not. Null if command discovery should default to something reasonable
/// for the command discovered.
/// </param>
/// <returns>
///
/// </returns>
///
/// <exception cref="CommandNotFoundException">
/// If the command, <paramref name="commandName"/>, could not be found.
/// </exception>
///
/// <exception cref="System.Security.SecurityException">
/// If the security manager is preventing the command from running.
/// </exception>
///
internal CommandProcessorBase LookupCommandProcessor ( string commandName ,
CommandOrigin commandOrigin , bool? useLocalScope )
{
CommandInfo commandInfo = null ;
#if false
if ( tokenCache . ContainsKey ( commandName ))
{
commandInfo = tokenCache [ commandName ];
}
else
{
commandInfo = LookupCommandInfo ( commandName );
if ( commandInfo . CommandType == CommandTypes . Alias )
{
commandInfo = (( AliasInfo ) commandInfo ). ResolvedCommand ;
}
tokenCache [ commandName ] = commandInfo ;
}
#else
commandInfo = LookupCommandInfo ( commandName , commandOrigin );
#endif
CommandProcessorBase processor = LookupCommandProcessor ( commandInfo , commandOrigin , useLocalScope , null );
// commandInfo.Name might be different than commandName - restore the original invocation name
processor . Command . MyInvocation . InvocationName = commandName ;
return processor ;
}
//Minishell ExternalScriptInfo scriptInfo
internal CommandProcessorBase CreateScriptProcessorForMiniShell ( ExternalScriptInfo scriptInfo , bool useLocalScope , SessionStateInternal sessionState )
{
VerifyScriptRequirements ( scriptInfo , Context );
if ( String . IsNullOrEmpty ( scriptInfo . RequiresApplicationID ))
{
if ( scriptInfo . RequiresPSSnapIns != null && scriptInfo . RequiresPSSnapIns . Any ())
{
Collection < string > requiresMissingPSSnapIns = GetPSSnapinNames ( scriptInfo . RequiresPSSnapIns );
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException (
scriptInfo . Name ,
requiresMissingPSSnapIns ,
"ScriptRequiresMissingPSSnapIns" ,
true );
throw scriptRequiresException ;
}
return CreateCommandProcessorForScript ( scriptInfo , _context , useLocalScope , sessionState );
}
else
{
if ( String . Equals (
_context . ShellID ,
scriptInfo . RequiresApplicationID ,
StringComparison . OrdinalIgnoreCase ))
{
return CreateCommandProcessorForScript ( scriptInfo , _context , useLocalScope , sessionState );
}
else
{
// Throw a runtime exception
string shellPath = GetShellPathFromRegistry ( scriptInfo . RequiresApplicationID );
ScriptRequiresException sre =
new ScriptRequiresException (
scriptInfo . Name ,
scriptInfo . RequiresApplicationID ,
shellPath ,
"ScriptRequiresUnmatchedShellId" );
throw sre ;
}
}
}
internal static void VerifyRequiredModules ( ExternalScriptInfo scriptInfo , ExecutionContext context )
{
// Check Required Modules
if ( scriptInfo . RequiresModules != null )
{
foreach ( var requiredModule in scriptInfo . RequiresModules )
{
ErrorRecord error = null ;
ModuleCmdletBase . LoadRequiredModule (
context : context ,
currentModule : null ,
requiredModuleSpecification : requiredModule ,
moduleManifestPath : null ,
manifestProcessingFlags : ModuleCmdletBase . ManifestProcessingFlags . LoadElements | ModuleCmdletBase . ManifestProcessingFlags . WriteErrors ,
error : out error );
if ( error != null )
{
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException (
scriptInfo . Name ,
new Collection < string > { requiredModule . Name },
"ScriptRequiresMissingModules" ,
false ,
error );
throw scriptRequiresException ;
}
}
}
}
private static Collection < String > GetPSSnapinNames ( IEnumerable < PSSnapInSpecification > PSSnapins )
{
Collection < string > result = new Collection < string >();
foreach ( var PSSnapin in PSSnapins )
{
result . Add ( BuildPSSnapInDisplayName ( PSSnapin ));
}
return result ;
}
CommandProcessorBase CreateScriptProcessorForSingleShell ( ExternalScriptInfo scriptInfo , ExecutionContext context , bool useLocalScope , SessionStateInternal sessionState )
{
VerifyScriptRequirements ( scriptInfo , Context );
IEnumerable < PSSnapInSpecification > requiresPSSnapIns = scriptInfo . RequiresPSSnapIns ;
if ( requiresPSSnapIns != null && requiresPSSnapIns . Any ())
{
Collection < string > requiresMissingPSSnapIns = null ;
VerifyRequiredSnapins ( requiresPSSnapIns , context , out requiresMissingPSSnapIns );
if ( requiresMissingPSSnapIns != null )
{
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException (
scriptInfo . Name ,
requiresMissingPSSnapIns ,
"ScriptRequiresMissingPSSnapIns" ,
true );
throw scriptRequiresException ;
}
}
else
{
// If there were no PSSnapins required but there is a shellID required, then we need
// to error
if (! String . IsNullOrEmpty ( scriptInfo . RequiresApplicationID ))
{
ScriptRequiresException sre =
new ScriptRequiresException (
scriptInfo . Name ,
String . Empty ,
String . Empty ,
"RequiresShellIDInvalidForSingleShell" );
throw sre ;
}
}
return CreateCommandProcessorForScript ( scriptInfo , _context , useLocalScope , sessionState );
}
private static void VerifyRequiredSnapins ( IEnumerable < PSSnapInSpecification > requiresPSSnapIns , ExecutionContext context , out Collection < string > requiresMissingPSSnapIns )
{
requiresMissingPSSnapIns = null ;
bool isHostedWithInitialSessionState = false ;
RunspaceConfigForSingleShell rs = null ;
if ( context . InitialSessionState != null )
{
isHostedWithInitialSessionState = true ;
}
else if ( context . RunspaceConfiguration != null )
{
rs = context . RunspaceConfiguration as RunspaceConfigForSingleShell ;
Dbg . Assert ( rs != null , "RunspaceConfiguration should not be null" );
}
else
{
Dbg . Assert ( false , "PowerShell should be hosted with either InitialSessionState or RunspaceConfiguration" );
}
foreach ( var requiresPSSnapIn in requiresPSSnapIns )
{
IEnumerable < PSSnapInInfo > loadedPSSnapIns = null ;
if ( isHostedWithInitialSessionState )
{
loadedPSSnapIns = context . InitialSessionState . GetPSSnapIn ( requiresPSSnapIn . Name );
}
else
{
loadedPSSnapIns = rs . ConsoleInfo . GetPSSnapIn ( requiresPSSnapIn . Name , false );
}
if ( loadedPSSnapIns == null || loadedPSSnapIns . Count () == 0 )
{
if ( requiresMissingPSSnapIns == null )
{
requiresMissingPSSnapIns = new Collection < string >();
}
requiresMissingPSSnapIns . Add ( BuildPSSnapInDisplayName ( requiresPSSnapIn ));
}
else
{
// the requires PSSnapin is loaded. now check the PSSnapin version
PSSnapInInfo loadedPSSnapIn = loadedPSSnapIns . First ();
Diagnostics . Assert ( loadedPSSnapIn . Version != null ,
string . Format (
CultureInfo . InvariantCulture ,
"Version is null for loaded PSSnapin {0}." , loadedPSSnapIn ));
if ( requiresPSSnapIn . Version != null )
{
if (! AreInstalledRequiresVersionsCompatible (
requiresPSSnapIn . Version , loadedPSSnapIn . Version ))
{
if ( requiresMissingPSSnapIns == null )
{
requiresMissingPSSnapIns = new Collection < string >();
}
requiresMissingPSSnapIns . Add ( BuildPSSnapInDisplayName ( requiresPSSnapIn ));
}
}
}
}
}
// This method verifies the following 3 elements of #Requires statement
// #Requires -RunAsAdministrator
// #Requires -PSVersion
// #Requires -PSEdition
// #Requires -Module
internal static void VerifyScriptRequirements ( ExternalScriptInfo scriptInfo , ExecutionContext context )
{
VerifyElevatedPriveleges ( scriptInfo );
VerifyPSVersion ( scriptInfo );
VerifyPSEdition ( scriptInfo );
VerifyRequiredModules ( scriptInfo , context );
}
internal static void VerifyPSVersion ( ExternalScriptInfo scriptInfo )
{
Version requiresPSVersion = scriptInfo . RequiresPSVersion ;
// in single shell mode
if ( requiresPSVersion != null )
{
if (! Utils . IsPSVersionSupported ( requiresPSVersion ))
{
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException (
scriptInfo . Name ,
requiresPSVersion ,
PSVersionInfo . PSVersion . ToString (),
"ScriptRequiresUnmatchedPSVersion" );
throw scriptRequiresException ;
}
}
}
internal static void VerifyPSEdition ( ExternalScriptInfo scriptInfo )
{
2016-03-31 17:27:44 -07:00
if ( scriptInfo . RequiresPSEditions != null )
2016-03-30 14:20:52 -07:00
{
2016-03-31 17:27:44 -07:00
var isCurrentEditionListed = false ;
var isRequiresPSEditionSpecified = false ;
foreach ( var edition in scriptInfo . RequiresPSEditions )
{
isRequiresPSEditionSpecified = true ;
isCurrentEditionListed = Utils . IsPSEditionSupported ( edition );
if ( isCurrentEditionListed )
{
break ;
}
}
// Throw an error if required PowerShell editions are specified and without the current PowerShell Edition.
//
if ( isRequiresPSEditionSpecified && ! isCurrentEditionListed )
{
var specifiedEditionsString = string . Join ( "," , scriptInfo . RequiresPSEditions );
var message = StringUtil . Format ( DiscoveryExceptions . RequiresPSEditionNotCompatible ,
scriptInfo . Name ,
specifiedEditionsString ,
PSVersionInfo . PSEdition );
var ex = new RuntimeException ( message );
ex . SetErrorId ( "ScriptRequiresUnmatchedPSEdition" );
ex . SetTargetObject ( scriptInfo . Name );
throw ex ;
}
2016-03-30 14:20:52 -07:00
}
}
internal static void VerifyElevatedPriveleges ( ExternalScriptInfo scriptInfo )
{
bool requiresElevation = scriptInfo . RequiresElevation ;
bool isAdministrator = Utils . IsAdministrator ();
if ( requiresElevation && ! isAdministrator )
{
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException (
scriptInfo . Name ,
"ScriptRequiresElevation" );
throw scriptRequiresException ;
}
}
#region comment out RequiresNetFrameworkVersion feature 8 / 10 / 2010
/*
* The "#requires -NetFrameworkVersion" feature is CUT OFF.
* This method will be reenabled will be CUT OFF too
/*
internal static void VerifyNetFrameworkVersion(ExternalScriptInfo scriptInfo)
{
Version requiresNetFrameworkVersion = scriptInfo.RequiresNetFrameworkVersion;
if (requiresNetFrameworkVersion != null)
{
if (!Utils.IsNetFrameworkVersionSupported(requiresNetFrameworkVersion))
{
ScriptRequiresException scriptRequiresException =
new ScriptRequiresException(
scriptInfo.Name,
scriptInfo.NetFrameworkVersionLineNumber,
requiresNetFrameworkVersion,
"ScriptRequiresUnmatchedNetFrameworkVersion");
throw scriptRequiresException;
}
}
}
*/
#endregion
/// <summary>
/// used to determine compatibility between the versions in the requires statement and
/// the installed version. The version can be PSSnapin or msh
/// </summary>
/// <param name="requires">versions in the requires statement</param>
/// <param name="installed">version installed</param>
/// <returns>
/// true if requires and installed's major version match and requires' minor version
/// is smaller than or equal to installed's
/// </returns>
/// <remarks>
/// In PowerShell V2, script requiring PowerShell 1.0 will fail.
/// </remarks>
private static bool AreInstalledRequiresVersionsCompatible ( Version requires , Version installed )
{
return requires . Major == installed . Major && requires . Minor <= installed . Minor ;
}
private static string BuildPSSnapInDisplayName ( PSSnapInSpecification PSSnapin )
{
return PSSnapin . Version == null ?
PSSnapin . Name :
StringUtil . Format ( DiscoveryExceptions . PSSnapInNameVersion ,
PSSnapin . Name , PSSnapin . Version );
}
/// <summary>
/// Look up a command using a CommandInfo object and return its CommandProcessorBase.
/// </summary>
///
/// <param name="commandInfo">
/// The commandInfo for the command to lookup.
/// </param>
///
/// <param name="commandOrigin"> Location where the command was dispatched from. </param>
/// <param name="useLocalScope">
/// True if command processor should use local scope to execute the command,
/// False if not. Null if command discovery should default to something reasonable
/// for the command discovered.
/// </param>
/// <param name="sessionState">The session state the commandInfo should be run in.</param>
/// <returns>
///
/// </returns>
///
/// <exception cref="CommandNotFoundException">
/// If the command, <paramref name="commandName"/>, could not be found.
/// </exception>
///
/// <exception cref="System.Management.Automation.PSSecurityException">
/// If the security manager is preventing the command from running.
/// </exception>
///
internal CommandProcessorBase LookupCommandProcessor ( CommandInfo commandInfo ,
CommandOrigin commandOrigin , bool? useLocalScope , SessionStateInternal sessionState )
{
CommandProcessorBase processor = null ;
HashSet < string > processedAliases = new HashSet < string >( StringComparer . OrdinalIgnoreCase );
while ( commandInfo . CommandType == CommandTypes . Alias &&
(! processedAliases . Contains ( commandInfo . Name )) &&
( commandOrigin == CommandOrigin . Internal || commandInfo . Visibility == SessionStateEntryVisibility . Public ))
{
processedAliases . Add ( commandInfo . Name );
AliasInfo aliasCommandInfo = ( AliasInfo ) commandInfo ;
commandInfo = aliasCommandInfo . ResolvedCommand ;
// If we didn't have the alias target already resolved, see if it can be loaded.
if ( commandInfo == null )
{
commandInfo = LookupCommandInfo ( aliasCommandInfo . Definition , commandOrigin , this . _context );
}
if ( commandInfo == null )
{
CommandNotFoundException e =
new CommandNotFoundException (
aliasCommandInfo . Name ,
null ,
"AliasNotResolvedException" ,
DiscoveryExceptions . AliasNotResolvedException ,
aliasCommandInfo . UnresolvedCommandName );
throw e ;
}
}
ShouldRun ( _context , _context . EngineHostInterface , commandInfo , commandOrigin );
switch ( commandInfo . CommandType )
{
case CommandTypes . Application :
processor = new NativeCommandProcessor (( ApplicationInfo ) commandInfo , _context );
break ;
case CommandTypes . Cmdlet :
processor = new CommandProcessor (( CmdletInfo ) commandInfo , _context );
break ;
case CommandTypes . ExternalScript :
ExternalScriptInfo scriptInfo = ( ExternalScriptInfo ) commandInfo ;
scriptInfo . SignatureChecked = true ;
try
{
if (! _context . IsSingleShell )
{
// in minishell mode
processor = CreateScriptProcessorForMiniShell ( scriptInfo , useLocalScope ?? true , sessionState );
}
else
{
// single shell mode
processor = CreateScriptProcessorForSingleShell ( scriptInfo , _context , useLocalScope ?? true , sessionState );
}
}
catch ( ScriptRequiresSyntaxException reqSyntaxException )
{
CommandNotFoundException e =
new CommandNotFoundException ( reqSyntaxException . Message , reqSyntaxException );
throw e ;
}
catch ( PSArgumentException argException )
{
CommandNotFoundException e =
new CommandNotFoundException (
commandInfo . Name ,
argException ,
"ScriptRequiresInvalidFormat" ,
DiscoveryExceptions . ScriptRequiresInvalidFormat );
throw e ;
}
break ;
case CommandTypes . Filter :
case CommandTypes . Function :
case CommandTypes . Workflow :
case CommandTypes . Configuration :
FunctionInfo functionInfo = ( FunctionInfo ) commandInfo ;
processor = CreateCommandProcessorForScript ( functionInfo , _context , useLocalScope ?? true , sessionState );
break ;
case CommandTypes . Script :
processor = CreateCommandProcessorForScript (( ScriptInfo ) commandInfo , _context , useLocalScope ?? true , sessionState );
break ;
case CommandTypes . Alias :
default :
{
CommandNotFoundException e =
new CommandNotFoundException (
commandInfo . Name ,
null ,
"CommandNotFoundException" ,
DiscoveryExceptions . CommandNotFoundException );
throw e ;
}
}
// Set the internal command origin member on the command object at this point...
processor . Command . CommandOriginInternal = commandOrigin ;
processor . Command . MyInvocation . InvocationName = commandInfo . Name ;
return processor ;
} // LookupCommandProcessor
internal static void ShouldRun ( ExecutionContext context , PSHost host , CommandInfo commandInfo , CommandOrigin commandOrigin )
{
//ShouldRunInternal throws PSSecurityException if run is not allowed
try
{
if ( commandOrigin == CommandOrigin . Runspace && commandInfo . Visibility != SessionStateEntryVisibility . Public )
{
CommandNotFoundException e = new CommandNotFoundException (
commandInfo . Name , null , "CommandNotFoundException" , DiscoveryExceptions . CommandNotFoundException );
throw e ;
}
context . AuthorizationManager . ShouldRunInternal ( commandInfo , commandOrigin , host );
}
catch ( PSSecurityException reason )
{
MshLog . LogCommandHealthEvent ( context ,
reason ,
Severity . Warning );
MshLog . LogCommandLifecycleEvent ( context ,
CommandState . Terminated ,
commandInfo . Name );
throw ;
}
}
static private CommandProcessorBase CreateCommandProcessorForScript ( ScriptInfo scriptInfo , ExecutionContext context , bool useNewScope , SessionStateInternal sessionState )
{
sessionState = sessionState ?? scriptInfo . ScriptBlock . SessionStateInternal ?? context . EngineSessionState ;
CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor ( scriptInfo , context , useNewScope , true , sessionState );
if ( scriptAsCmdletProcessor != null )
{
return scriptAsCmdletProcessor ;
}
return new DlrScriptCommandProcessor ( scriptInfo , context , useNewScope , sessionState );
}
static private CommandProcessorBase CreateCommandProcessorForScript ( ExternalScriptInfo scriptInfo , ExecutionContext context , bool useNewScope , SessionStateInternal sessionState )
{
sessionState = sessionState ?? scriptInfo . ScriptBlock . SessionStateInternal ?? context . EngineSessionState ;
CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor ( scriptInfo , context , useNewScope , true , sessionState );
if ( scriptAsCmdletProcessor != null )
{
return scriptAsCmdletProcessor ;
}
return new DlrScriptCommandProcessor ( scriptInfo , context , useNewScope , sessionState );
}
static internal CommandProcessorBase CreateCommandProcessorForScript ( FunctionInfo functionInfo , ExecutionContext context , bool useNewScope , SessionStateInternal sessionState )
{
sessionState = sessionState ?? functionInfo . ScriptBlock . SessionStateInternal ?? context . EngineSessionState ;
CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor ( functionInfo , context , useNewScope , false , sessionState );
if ( scriptAsCmdletProcessor != null )
{
return scriptAsCmdletProcessor ;
}
return new DlrScriptCommandProcessor ( functionInfo , context , useNewScope , sessionState );
}
static internal CommandProcessorBase CreateCommandProcessorForScript ( ScriptBlock scriptblock , ExecutionContext context , bool useNewScope , SessionStateInternal sessionState )
{
sessionState = sessionState ?? scriptblock . SessionStateInternal ?? context . EngineSessionState ;
if ( scriptblock . UsesCmdletBinding )
{
FunctionInfo fi = new FunctionInfo ( "" , scriptblock , context );
return GetScriptAsCmdletProcessor ( fi , context , useNewScope , false , sessionState );
}
return new DlrScriptCommandProcessor ( scriptblock , context , useNewScope , CommandOrigin . Internal , sessionState );
}
static private CommandProcessorBase GetScriptAsCmdletProcessor ( IScriptCommandInfo scriptCommandInfo , ExecutionContext context , bool useNewScope , bool fromScriptFile , SessionStateInternal sessionState )
{
if ( scriptCommandInfo . ScriptBlock == null || ! scriptCommandInfo . ScriptBlock . UsesCmdletBinding )
{
return null ;
}
sessionState = sessionState ?? scriptCommandInfo . ScriptBlock . SessionStateInternal ?? context . EngineSessionState ;
return new CommandProcessor ( scriptCommandInfo , context , useNewScope , fromScriptFile , sessionState );
}
/// <summary>
/// Look up a command and return its CommandInfo.
/// </summary>
///
/// <param name="commandName">
/// The command name to lookup.
/// </param>
///
/// <returns>
/// An instance of a CommandInfo object that represents the
/// command. If the command is resolved as an alias, an AliasInfo
/// is returned with the ReferencedCommand info intact.
/// </returns>
///
/// <exception cref="CommandNotFoundException">
/// If the command, <paramref name="commandName"/>, could not be found.
/// </exception>
///
internal CommandInfo LookupCommandInfo ( string commandName )
{
return LookupCommandInfo ( commandName , CommandOrigin . Internal );
}
internal CommandInfo LookupCommandInfo ( string commandName , CommandOrigin commandOrigin )
{
return LookupCommandInfo ( commandName , commandOrigin , _context );
}
internal static CommandInfo LookupCommandInfo ( string commandName , CommandOrigin commandOrigin , ExecutionContext context )
{
return LookupCommandInfo ( commandName , CommandTypes . All , SearchResolutionOptions . None , commandOrigin , context );
}
internal static CommandInfo LookupCommandInfo (
string commandName ,
CommandTypes commandTypes ,
SearchResolutionOptions searchResolutionOptions ,
CommandOrigin commandOrigin ,
ExecutionContext context )
{
if ( String . IsNullOrEmpty ( commandName ))
{
return null ;
}
bool etwEnabled = CommandDiscoveryEventSource . Log . IsEnabled ();
if ( etwEnabled ) CommandDiscoveryEventSource . Log . CommandLookupStart ( commandName );
CommandInfo result = null ;
string originalCommandName = commandName ;
Exception lastError = null ;
// Check to see if there is a pre-search look-up event handler...
CommandLookupEventArgs eventArgs = null ;
EventHandler < CommandLookupEventArgs > preCommandLookupEvent = context . EngineIntrinsics . InvokeCommand . PreCommandLookupAction ;
if ( preCommandLookupEvent != null )
{
discoveryTracer . WriteLine ( "Executing PreCommandLookupAction: {0}" , commandName );
try
{
context . CommandDiscovery . RegisterLookupCommandInfoAction ( "ActivePreLookup" , originalCommandName );
eventArgs = new CommandLookupEventArgs ( originalCommandName , commandOrigin , context );
preCommandLookupEvent . Invoke ( originalCommandName , eventArgs );
discoveryTracer . WriteLine ( "PreCommandLookupAction returned: {0}" , eventArgs . Command );
}
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
}
finally { context . CommandDiscovery . UnregisterLookupCommandInfoAction ( "ActivePreLookup" , commandName ); }
}
// Check the module auto-loading preference
PSModuleAutoLoadingPreference moduleAutoLoadingPreference = GetCommandDiscoveryPreference ( context , SpecialVariables . PSModuleAutoLoadingPreferenceVarPath , "PSModuleAutoLoadingPreference" );
if ( eventArgs == null || eventArgs . StopSearch != true )
{
do
{
discoveryTracer . WriteLine ( "Looking up command: {0}" , commandName );
// Use the CommandSearcher to find the first command. If there are duplicate
// command names, then take the first one...
result = TryNormalSearch ( commandName , context , commandOrigin , searchResolutionOptions , commandTypes , ref lastError );
if ( result != null )
break ;
// Try the module-qualified auto-loading (unless module auto-loading has been entirely disabled)
if ( moduleAutoLoadingPreference != PSModuleAutoLoadingPreference . None )
{
result = TryModuleAutoLoading ( commandName , context , originalCommandName , commandOrigin , result ,
ref lastError );
}
if ( result != null )
break ;
// See if the this was not module-qualified. In that case, we should look for the first module
// that contains the command and load that.
if ( moduleAutoLoadingPreference == PSModuleAutoLoadingPreference . All )
{
result = TryModuleAutoDiscovery ( commandName , context , originalCommandName , commandOrigin ,
searchResolutionOptions , commandTypes , ref lastError );
}
// Otherwise, invoke the CommandNotFound handler
if ( result == null )
{
result = InvokeCommandNotFoundHandler ( commandName , context , originalCommandName , commandOrigin ,
result );
}
} while ( false );
}
else
{
if ( eventArgs . Command != null )
{
result = eventArgs . Command ;
}
}
// If we resolved a command, give the PostCommandLookup a chance to change it
if ( result != null )
{
System . EventHandler < CommandLookupEventArgs > postAction = context . EngineIntrinsics . InvokeCommand . PostCommandLookupAction ;
if ( postAction != null )
{
discoveryTracer . WriteLine ( "Executing PostCommandLookupAction: {0}" , originalCommandName );
try
{
context . CommandDiscovery . RegisterLookupCommandInfoAction ( "ActivePostCommand" , originalCommandName );
eventArgs = new CommandLookupEventArgs ( originalCommandName , commandOrigin , context );
eventArgs . Command = result ;
postAction . Invoke ( originalCommandName , eventArgs );
if ( eventArgs != null )
{
result = eventArgs . Command ;
discoveryTracer . WriteLine ( "PreCommandLookupAction returned: {0}" , eventArgs . Command );
}
}
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
}
finally
{
context . CommandDiscovery . UnregisterLookupCommandInfoAction ( "ActivePostCommand" , originalCommandName );
}
}
}
// After all command lookup is done, throw a CommandNotFound if we have no result.
if ( result == null )
{
discoveryTracer . TraceError (
"'{0}' is not recognized as a cmdlet, function, operable program or script file." ,
commandName );
CommandNotFoundException e =
new CommandNotFoundException (
originalCommandName ,
lastError ,
"CommandNotFoundException" ,
DiscoveryExceptions . CommandNotFoundException );
throw e ;
}
if ( etwEnabled ) CommandDiscoveryEventSource . Log . CommandLookupStop ( commandName );
return result ;
}
internal static void AutoloadModulesWithJobSourceAdapters ( System . Management . Automation . ExecutionContext context , CommandOrigin commandOrigin )
{
if (! context . IsModuleWithJobSourceAdapterLoaded )
{
PSModuleAutoLoadingPreference moduleAutoLoadingPreference = GetCommandDiscoveryPreference ( context , SpecialVariables . PSModuleAutoLoadingPreferenceVarPath , "PSModuleAutoLoadingPreference" );
if ( moduleAutoLoadingPreference != PSModuleAutoLoadingPreference . None )
{
CmdletInfo cmdletInfo = context . SessionState . InvokeCommand . GetCmdlet ( "Microsoft.PowerShell.Core\\Import-Module" );
if (( commandOrigin == CommandOrigin . Internal ) ||
(( cmdletInfo != null ) && ( cmdletInfo . Visibility == SessionStateEntryVisibility . Public )))
{
foreach ( var module in System . Management . Automation . ExecutionContext . ModulesWithJobSourceAdapters )
{
List < PSModuleInfo > existingModule = context . Modules . GetModules ( new string [] { module }, false );
if ( existingModule == null || existingModule . Count == 0 )
{
Exception unUsedException = null ;
AutoloadSpecifiedModule ( module , context , cmdletInfo . Visibility , out unUsedException );
}
}
context . IsModuleWithJobSourceAdapterLoaded = true ;
}
}
}
}
internal static Collection < PSModuleInfo > AutoloadSpecifiedModule ( string moduleName , ExecutionContext context , SessionStateEntryVisibility visibility , out Exception exception )
{
exception = null ;
Collection < PSModuleInfo > matchingModules = null ;
CommandInfo commandInfo = new CmdletInfo ( "Import-Module" , typeof ( ImportModuleCommand ), null , null , context );
commandInfo . Visibility = visibility ;
Command importModuleCommand = new Command ( commandInfo );
discoveryTracer . WriteLine ( "Attempting to load module: {0}" , moduleName );
PowerShell ps = null ;
try
{
ps = PowerShell . Create ( RunspaceMode . CurrentRunspace )
. AddCommand ( importModuleCommand )
. AddParameter ( "Name" , moduleName )
. AddParameter ( "Scope" , StringLiterals . Global )
. AddParameter ( "PassThru" )
. AddParameter ( "ErrorAction" , ActionPreference . Ignore )
. AddParameter ( "WarningAction" , ActionPreference . Ignore )
. AddParameter ( "InformationAction" , ActionPreference . Ignore )
. AddParameter ( "Verbose" , false )
. AddParameter ( "Debug" , false );
matchingModules = ( Collection < PSModuleInfo >) ps . Invoke < PSModuleInfo >();
}
catch ( Exception e )
{
exception = e ;
discoveryTracer . WriteLine ( "Encountered error importing module: {0}" , e . Message );
//Call-out to user code, catch-all OK
CommandProcessorBase . CheckForSevereException ( e );
}
return matchingModules ;
}
private static CommandInfo InvokeCommandNotFoundHandler ( string commandName , ExecutionContext context , string originalCommandName , CommandOrigin commandOrigin , CommandInfo result )
{
CommandLookupEventArgs eventArgs ;
System . EventHandler < CommandLookupEventArgs > cmdNotFoundHandler = context . EngineIntrinsics . InvokeCommand . CommandNotFoundAction ;
if ( cmdNotFoundHandler != null )
{
discoveryTracer . WriteLine ( "Executing CommandNotFoundAction: {0}" , commandName );
try
{
context . CommandDiscovery . RegisterLookupCommandInfoAction ( "ActiveCommandNotFound" , originalCommandName );
eventArgs = new CommandLookupEventArgs ( originalCommandName , commandOrigin , context );
cmdNotFoundHandler . Invoke ( originalCommandName , eventArgs );
result = eventArgs . Command ;
}
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
}
finally { context . CommandDiscovery . UnregisterLookupCommandInfoAction ( "ActiveCommandNotFound" , originalCommandName ); }
}
return result ;
}
private static CommandInfo TryNormalSearch ( string commandName ,
ExecutionContext context ,
CommandOrigin commandOrigin ,
SearchResolutionOptions searchResolutionOptions ,
CommandTypes commandTypes ,
ref Exception lastError )
{
CommandInfo result = null ;
CommandSearcher searcher =
new CommandSearcher (
commandName ,
searchResolutionOptions ,
commandTypes ,
context );
searcher . CommandOrigin = commandOrigin ;
try
{
if (! searcher . MoveNext ())
{
if (! commandName . Contains ( "-" ) && ! commandName . Contains ( "\\" ))
{
discoveryTracer . WriteLine (
"The command [{0}] was not found, trying again with get- prepended" ,
commandName );
commandName = StringLiterals . DefaultCommandVerb + StringLiterals . CommandVerbNounSeparator + commandName ;
try
{
result = LookupCommandInfo ( commandName , commandTypes , searchResolutionOptions , commandOrigin , context );
}
catch ( CommandNotFoundException ) { }
}
}
else
{
result = (( IEnumerator < CommandInfo >) searcher ). Current ;
}
}
catch ( ArgumentException argException )
{
lastError = argException ;
}
catch ( PathTooLongException pathTooLong )
{
lastError = pathTooLong ;
}
catch ( FileLoadException fileLoadException )
{
lastError = fileLoadException ;
}
catch ( FormatException formatException )
{
lastError = formatException ;
}
catch ( MetadataException metadataException )
{
lastError = metadataException ;
}
return result ;
}
private static CommandInfo TryModuleAutoDiscovery ( string commandName ,
ExecutionContext context ,
string originalCommandName ,
CommandOrigin commandOrigin ,
SearchResolutionOptions searchResolutionOptions ,
CommandTypes commandTypes ,
ref Exception lastError )
{
bool etwEnabled = CommandDiscoveryEventSource . Log . IsEnabled ();
if ( etwEnabled ) CommandDiscoveryEventSource . Log . ModuleAutoDiscoveryStart ( commandName );
CommandInfo result = null ;
bool cleanupModuleAnalysisAppDomain = false ;
try
{
// If commandName had a slash, it was module-qualified or path-qualified.
// In that case, we should not return anything (module-qualified is handled
// by the previous call to TryModuleAutoLoading().
int colonOrBackslash = commandName . IndexOfAny ( Utils . Separators . ColonOrBackslash );
if ( colonOrBackslash != - 1 )
return null ;
CmdletInfo cmdletInfo = context . SessionState . InvokeCommand . GetCmdlet ( "Microsoft.PowerShell.Core\\Get-Module" );
if (( commandOrigin == CommandOrigin . Internal ) ||
(( cmdletInfo != null ) && ( cmdletInfo . Visibility == SessionStateEntryVisibility . Public )))
{
// Search for a module with a matching command, as long as the user would have the ability to
// import the module.
cmdletInfo = context . SessionState . InvokeCommand . GetCmdlet ( "Microsoft.PowerShell.Core\\Import-Module" );
if ((( commandOrigin == CommandOrigin . Internal ) ||
(( cmdletInfo != null ) && ( cmdletInfo . Visibility == SessionStateEntryVisibility . Public ))))
{
discoveryTracer . WriteLine ( "Executing non module-qualified search: {0}" , commandName );
context . CommandDiscovery . RegisterLookupCommandInfoAction ( "ActiveModuleSearch" , commandName );
cleanupModuleAnalysisAppDomain = context . TakeResponsibilityForModuleAnalysisAppDomain ();
// Get the available module files, prefering modules from $PSHOME so that user modules don't
// override system modules during auto-loading
if ( etwEnabled ) CommandDiscoveryEventSource . Log . SearchingForModuleFilesStart ();
var defaultAvailableModuleFiles = ModuleUtils . GetDefaultAvailableModuleFiles ( true , true , context );
if ( etwEnabled ) CommandDiscoveryEventSource . Log . SearchingForModuleFilesStop ();
foreach ( string modulePath in defaultAvailableModuleFiles )
{
// WinBlue:69141 - We need to get the full path here because the module path might be C:\Users\User1\DOCUME~1
// While the exportedCommands are cached, they are cached with the full path
string expandedModulePath = IO . Path . GetFullPath ( modulePath );
string moduleShortName = System . IO . Path . GetFileNameWithoutExtension ( expandedModulePath );
var exportedCommands = AnalysisCache . GetExportedCommands ( expandedModulePath , false , context );
if ( exportedCommands == null ) { continue ; }
CommandTypes exportedCommandTypes ;
//Skip if module only has class or other types and no commands.
if ( exportedCommands . TryGetValue ( commandName , out exportedCommandTypes ))
{
Exception exception ;
discoveryTracer . WriteLine ( "Found in module: {0}" , expandedModulePath );
Collection < PSModuleInfo > matchingModule = AutoloadSpecifiedModule ( expandedModulePath , context ,
cmdletInfo != null ? cmdletInfo . Visibility : SessionStateEntryVisibility . Private ,
out exception );
lastError = exception ;
if (( matchingModule == null ) || ( matchingModule . Count == 0 ))
{
string error = StringUtil . Format ( DiscoveryExceptions . CouldNotAutoImportMatchingModule , commandName , moduleShortName );
CommandNotFoundException commandNotFound = new CommandNotFoundException (
originalCommandName ,
lastError ,
"CouldNotAutoloadMatchingModule" , error );
throw commandNotFound ;
}
result = LookupCommandInfo ( commandName , commandTypes , searchResolutionOptions , commandOrigin , context );
}
if ( result != null )
{
break ;
}
}
// Close the progress pane that may have popped up from analyzing UNC paths.
2016-04-06 12:01:07 -07:00
// Porting note: we don't like this message
2016-04-08 15:02:51 -07:00
if (! Platform . IsCore && context . CurrentCommandProcessor != null )
2016-03-30 14:20:52 -07:00
{
ProgressRecord analysisProgress = new ProgressRecord ( 0 , Modules . ScriptAnalysisPreparing , " " );
analysisProgress . RecordType = ProgressRecordType . Completed ;
context . CurrentCommandProcessor . CommandRuntime . WriteProgress ( analysisProgress );
}
}
}
}
catch ( CommandNotFoundException ) { throw ; }
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
}
finally
{
context . CommandDiscovery . UnregisterLookupCommandInfoAction ( "ActiveModuleSearch" , commandName );
if ( cleanupModuleAnalysisAppDomain )
{
context . ReleaseResponsibilityForModuleAnalysisAppDomain ();
}
}
if ( etwEnabled ) CommandDiscoveryEventSource . Log . ModuleAutoDiscoveryStop ( commandName );
return result ;
}
private static CommandInfo TryModuleAutoLoading ( string commandName , ExecutionContext context , string originalCommandName , CommandOrigin commandOrigin , CommandInfo result , ref Exception lastError )
{
// If commandName was module-qualified. In that case, we should load the module.
var colonOrBackslash = commandName . IndexOfAny ( Utils . Separators . ColonOrBackslash );
// If we don't see '\', there is no module specified, so no module to load.
// If we see ':' before '\', then we probably have a drive qualified path, not a module name
if ( colonOrBackslash == - 1 || commandName [ colonOrBackslash ] == ':' )
return null ;
string moduleCommandName = commandName . Substring ( colonOrBackslash + 1 , commandName . Length - colonOrBackslash - 1 );
string moduleName ;
// Now we check if there exists the second '\'
var secondBackslash = moduleCommandName . IndexOfAny ( Utils . Separators . Backslash );
if ( secondBackslash == - 1 )
{
moduleName = commandName . Substring ( 0 , colonOrBackslash );
}
else
{
string versionString = moduleCommandName . Substring ( 0 , secondBackslash );
// The second '\' could be version specified. eg: "Microsoft.PowerShell.Archive\1.0.0.0\Compress-Archive", we need to support this scenario
Version version ;
if ( Version . TryParse ( versionString , out version ))
{
moduleCommandName = moduleCommandName . Substring ( secondBackslash + 1 , moduleCommandName . Length - secondBackslash - 1 );
moduleName = commandName . Substring ( 0 , colonOrBackslash ) + "\\" + versionString + "\\" + commandName . Substring ( 0 , colonOrBackslash ) + ".psd1" ;
}
else
{
moduleName = commandName . Substring ( 0 , colonOrBackslash );
}
}
if ( String . IsNullOrEmpty ( moduleName ) || String . IsNullOrEmpty ( moduleCommandName ) || moduleName . EndsWith ( "." , StringComparison . Ordinal ))
return null ;
bool etwEnabled = CommandDiscoveryEventSource . Log . IsEnabled ();
if ( etwEnabled ) CommandDiscoveryEventSource . Log . ModuleAutoLoadingStart ( commandName );
try
{
discoveryTracer . WriteLine ( "Executing module-qualified search: {0}" , commandName );
context . CommandDiscovery . RegisterLookupCommandInfoAction ( "ActiveModuleSearch" , commandName );
// Verify that auto-loading is only done on for internal commands if it's not public
CmdletInfo cmdletInfo = context . SessionState . InvokeCommand . GetCmdlet ( "Microsoft.PowerShell.Core\\Import-Module" );
if (( commandOrigin == CommandOrigin . Internal ) ||
(( cmdletInfo != null ) && ( cmdletInfo . Visibility == SessionStateEntryVisibility . Public )))
{
List < PSModuleInfo > existingModule = context . Modules . GetModules ( new string [] { moduleName }, false );
PSModuleInfo discoveredModule = null ;
if ( existingModule == null || existingModule . Count == 0 )
{
discoveryTracer . WriteLine ( "Attempting to load module: {0}" , moduleName );
Exception exception ;
Collection < PSModuleInfo > importedModule = AutoloadSpecifiedModule ( moduleName , context , cmdletInfo . Visibility , out exception );
lastError = exception ;
if (( importedModule == null ) || ( importedModule . Count == 0 ))
{
string error = StringUtil . Format ( DiscoveryExceptions . CouldNotAutoImportModule , moduleName );
CommandNotFoundException commandNotFound = new CommandNotFoundException (
originalCommandName ,
lastError ,
"CouldNotAutoLoadModule" ,
error );
throw commandNotFound ;
}
discoveredModule = importedModule [ 0 ];
}
else
{
discoveredModule = existingModule [ 0 ];
}
CommandInfo exportedResult ;
if ( discoveredModule . ExportedCommands . TryGetValue ( moduleCommandName , out exportedResult ))
{
// Return the command if we found a module
result = exportedResult ;
}
}
}
catch ( CommandNotFoundException ) { throw ; }
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
}
finally { context . CommandDiscovery . UnregisterLookupCommandInfoAction ( "ActiveModuleSearch" , commandName ); }
if ( etwEnabled ) CommandDiscoveryEventSource . Log . ModuleAutoLoadingStop ( commandName );
return result ;
}
internal void RegisterLookupCommandInfoAction ( string currentAction , string command )
{
HashSet < string > currentActionSet = null ;
switch ( currentAction )
{
case "ActivePreLookup" : currentActionSet = activePreLookup ; break ;
case "ActiveModuleSearch" : currentActionSet = activeModuleSearch ; break ;
case "ActiveCommandNotFound" : currentActionSet = activeCommandNotFound ; break ;
case "ActivePostCommand" : currentActionSet = activePostCommand ; break ;
}
if ( currentActionSet . Contains ( command ))
throw new InvalidOperationException ();
else
currentActionSet . Add ( command );
}
internal void UnregisterLookupCommandInfoAction ( string currentAction , string command )
{
HashSet < string > currentActionSet = null ;
switch ( currentAction )
{
case "ActivePreLookup" : currentActionSet = activePreLookup ; break ;
case "ActiveModuleSearch" : currentActionSet = activeModuleSearch ; break ;
case "ActiveCommandNotFound" : currentActionSet = activeCommandNotFound ; break ;
case "ActivePostCommand" : currentActionSet = activePostCommand ; break ;
}
if ( currentActionSet . Contains ( command ))
currentActionSet . Remove ( command );
}
HashSet < string > activePreLookup = new HashSet < string >( StringComparer . OrdinalIgnoreCase );
HashSet < string > activeModuleSearch = new HashSet < string >( StringComparer . OrdinalIgnoreCase );
HashSet < string > activeCommandNotFound = new HashSet < string >( StringComparer . OrdinalIgnoreCase );
HashSet < string > activePostCommand = new HashSet < string >( StringComparer . OrdinalIgnoreCase );
/// <summary>
/// Gets a CommandPathSearch constructed with the specified patterns and
/// using the PATH as the lookup directories
/// </summary>
///
/// <param name="patterns">
/// The patterns to search for. These patterns must be in the form taken
/// by DirectoryInfo.GetFiles().
/// </param>
///
/// <returns>
/// An instance of CommandPathSearch that is initialized with the specified
/// patterns and using the PATH as the lookup directories.
/// </returns>
internal IEnumerable < string > GetCommandPathSearcher ( IEnumerable < string > patterns )
{
// Get the PATH environment variable
IEnumerable < string > lookupPathArray = GetLookupDirectoryPaths ();
// Construct the CommandPathSearch object and return it.
return new CommandPathSearch ( patterns , lookupPathArray , _context );
} // GetCommandPathSearcher
/// <summary>
/// Gets the resolved paths contained in the PATH environment
/// variable.
/// </summary>
///
/// <returns>
2016-04-06 12:01:07 -07:00
/// The contents of the PATH environment variable split on System.IO.Path.PathSeparator.
2016-03-30 14:20:52 -07:00
/// </returns>
///
/// <remarks>
/// The result is an ordered list of paths with paths starting with "." unresolved until lookup time.
/// </remarks>
///
internal IEnumerable < string > GetLookupDirectoryPaths ()
{
LookupPathCollection result = new LookupPathCollection ();
string path = Environment . GetEnvironmentVariable ( "PATH" );
discoveryTracer . WriteLine (
"PATH: {0}" ,
path );
bool isPathCacheValid =
path != null &&
String . Equals ( pathCacheKey , path , StringComparison . OrdinalIgnoreCase ) &&
cachedPath != null ;
if (! isPathCacheValid )
{
// Reset the cached lookup paths
cachedLookupPaths = null ;
// Tokenize the path and cache it
pathCacheKey = path ;
if ( pathCacheKey != null )
{
2016-04-06 12:01:07 -07:00
string [] tokenizedPath = pathCacheKey . Split ( new char [] { System . IO . Path . PathSeparator }, StringSplitOptions . RemoveEmptyEntries );
2016-03-30 14:20:52 -07:00
cachedPath = new Collection < string >();
foreach ( string directory in tokenizedPath )
{
string tempDir = directory . TrimStart ();
cachedPath . Add ( tempDir );
result . Add ( tempDir );
}
}
}
else
{
result . AddRange ( cachedPath );
}
// Cache the new lookup paths
if ( cachedLookupPaths == null )
{
cachedLookupPaths = result ;
}
return cachedLookupPaths ;
} // GetLookupDirectoryPaths
/// <summary>
/// The cached list of lookup paths. It can be invalidated by
/// the PATH changing.
/// </summary>
private LookupPathCollection cachedLookupPaths ;
/// <summary>
/// The key that determines if the cached PATH can be used.
/// </summary>
private string pathCacheKey ;
/// <summary>
/// The cache of the tokenized PATH directories
/// </summary>
private Collection < string > cachedPath ;
#endregion internal members
#region environment variable helpers
/// <summary>
/// Gets the PATHEXT environment variable extensions and tokenizes them.
/// </summary>
///
internal static string [] PathExtensionsWithPs1Prepended
{
get
{
var pathExt = Environment . GetEnvironmentVariable ( "PATHEXT" );
if (! string . Equals ( pathExt , pathExtCacheKey , StringComparison . OrdinalIgnoreCase ) ||
cachedPathExtCollection == null )
{
InitPathExtCache ( pathExt );
}
return cachedPathExtCollectionWithPs1 ;
} // get
} // PathExtensions
/// <summary>
/// Gets the PATHEXT environment variable extensions and tokenizes them.
/// </summary>
///
internal static string [] PathExtensions
{
get
{
var pathExt = Environment . GetEnvironmentVariable ( "PATHEXT" );
if (! string . Equals ( pathExt , pathExtCacheKey , StringComparison . OrdinalIgnoreCase ) ||
cachedPathExtCollection == null )
{
InitPathExtCache ( pathExt );
}
return cachedPathExtCollection ;
} // get
} // PathExtensions
private static void InitPathExtCache ( string pathExt )
{
lock ( lockObject )
{
cachedPathExtCollection = pathExt != null
2016-04-06 12:01:07 -07:00
? pathExt . Split ( new char [] { System . IO . Path . PathSeparator }, StringSplitOptions . RemoveEmptyEntries )
2016-03-30 14:20:52 -07:00
: Utils . EmptyArray < string >();
cachedPathExtCollectionWithPs1 = new string [ cachedPathExtCollection . Length + 1 ];
cachedPathExtCollectionWithPs1 [ 0 ] = StringLiterals . PowerShellScriptFileExtension ;
Array . Copy ( cachedPathExtCollection , 0 , cachedPathExtCollectionWithPs1 , 1 , cachedPathExtCollection . Length );
pathExtCacheKey = pathExt ;
}
}
#endregion environment variable helpers
#region private members
private static object lockObject = new object ();
private static string pathExtCacheKey ;
private static string [] cachedPathExtCollection ;
private static string [] cachedPathExtCollectionWithPs1 ;
/// <summary>
/// Gets the cmdlet information for the specified name.
/// </summary>
///
/// <param name="cmdletName">
/// The name of the cmdlet to return the information for.
/// </param>
/// <param name="searchAllScopes">
/// True if we should search all scopes, false if we should stop after finding the first.
/// </param>
///
/// <returns>
/// The CmdletInfo for the cmdlet for all the cmdlets with the specified name.
/// </returns>
///
/// <exception cref="ArgumentException">
/// If <paramref name="cmdletName"/> is null or empty.
/// </exception>
///
internal IEnumerator < CmdletInfo > GetCmdletInfo ( string cmdletName , bool searchAllScopes )
{
Dbg . Assert (! String . IsNullOrEmpty ( cmdletName ), "Caller should verify the cmdletName" );
PSSnapinQualifiedName commandName = PSSnapinQualifiedName . GetInstance ( cmdletName );
if ( commandName == null )
{
yield break ;
}
// Check the current cmdlet cache then check the top level
// if we aren't already at the top level.
SessionStateScopeEnumerator scopeEnumerator =
new SessionStateScopeEnumerator ( _context . EngineSessionState . CurrentScope );
foreach ( SessionStateScope scope in scopeEnumerator )
{
List < CmdletInfo > cmdlets ;
if (! scope . CmdletTable . TryGetValue ( commandName . ShortName , out cmdlets ))
{
continue ;
}
foreach ( var cmdletInfo in cmdlets )
{
if (! String . IsNullOrEmpty ( commandName . PSSnapInName ))
{
if ( String . Equals ( cmdletInfo . ModuleName , commandName . PSSnapInName , StringComparison . OrdinalIgnoreCase ))
{
yield return cmdletInfo ;
if (! searchAllScopes )
{
yield break ;
}
}
// The engine cmdlets get imported (via Import-Module) once when PowerShell starts and the cmdletInfo is added to PSSnapinHelpers._cmdletcache(static) with ModuleName
// as "System.Management.Automation.dll" instead of the actual snapin name. The next time we load something in an InitialSessionState, we look at this _cmdletcache and
// if the the assembly is already loaded, we just return the cmdlets back. So, the CmdletInfo has moduleName has "System.Management.Automation.dll". So, when M3P Activity
// tries to access Microsoft.PowerShell.Core\\Get-Command, it cannot. So, adding an additional check to return the correct cmdletInfo for cmdlets from core modules.
else if ( InitialSessionState . IsEngineModule ( cmdletInfo . ModuleName ))
{
if ( String . Equals (
cmdletInfo . ModuleName ,
InitialSessionState . GetNestedModuleDllName ( commandName . PSSnapInName ),
StringComparison . OrdinalIgnoreCase ))
{
yield return cmdletInfo ;
if (! searchAllScopes )
{
yield break ;
}
}
}
}
else
{
yield return cmdletInfo ;
if (! searchAllScopes )
{
yield break ;
}
}
}
}
} // GetCmdletInfo
private bool _cmdletCacheInitialized = false ;
/// <summary>
/// Called by the RunspaceConfiguration when a PSSnapIn gets added to the
/// console to update the list of available cmdlets.
/// </summary>
///
internal void UpdateCmdletCache ()
{
if (! _cmdletCacheInitialized )
{
foreach ( CmdletConfigurationEntry entry in _context . RunspaceConfiguration . Cmdlets )
{
AddCmdletToCache ( entry );
}
_cmdletCacheInitialized = true ;
return ;
}
foreach ( CmdletConfigurationEntry entry in _context . RunspaceConfiguration . Cmdlets . UpdateList )
{
if ( entry == null )
{
continue ;
}
switch ( entry . Action )
{
case UpdateAction . Add :
AddCmdletToCache ( entry );
break ;
case UpdateAction . Remove :
RemoveCmdletFromCache ( entry );
break ;
default :
break ;
}
}
} // UpdateCmdletCache
/// <summary>
/// Removes a cmdlet from the cmdlet cache.
/// </summary>
///
/// <param name="entry">
/// The configuration entry for the cmdlet which is being removed.
/// </param>
///
private void RemoveCmdletFromCache ( CmdletConfigurationEntry entry )
{
IDictionary < string , List < CmdletInfo >> cmdletTable = _context . EngineSessionState . GetCmdletTable ();
List < CmdletInfo > cacheEntry ;
if ( cmdletTable . TryGetValue ( entry . Name , out cacheEntry ))
{
int removalIndex = GetCmdletRemovalIndex ( cacheEntry , entry . PSSnapIn == null ? String . Empty : entry . PSSnapIn . Name );
if ( removalIndex >= 0 )
{
string name = cacheEntry [ removalIndex ]. Name ;
cacheEntry . RemoveAt ( removalIndex );
_context . EngineSessionState . RemoveCmdlet ( name , removalIndex , true );
}
// Remove the entry from the cache if there are no more cmdlets
if ( cacheEntry . Count == 0 )
{
_context . EngineSessionState . RemoveCmdletEntry ( entry . Name , true );
}
}
}
private int GetCmdletRemovalIndex ( List < CmdletInfo > cacheEntry , string PSSnapin )
{
int removalIndex = - 1 ;
for ( int index = 0 ; index < cacheEntry . Count ; ++ index )
{
if ( String . Equals ( cacheEntry [ index ]. ModuleName , PSSnapin , StringComparison . OrdinalIgnoreCase ))
{
removalIndex = index ;
break ;
}
}
return removalIndex ;
}
/// <summary>
/// Gets the cached ScriptInfo for a command using the script name.
/// </summary>
///
/// <param name="name">
/// The name of the script.
/// </param>
///
/// <returns>
/// A reference to the ScriptInfo for the command if its in the cache,
/// or null otherwise.
/// </returns>
///
internal ScriptInfo GetScriptInfo ( string name )
{
Dbg . Assert (
! String . IsNullOrEmpty ( name ),
"The caller should verify the name" );
ScriptInfo result ;
cachedScriptInfo . TryGetValue ( name , out result );
return result ;
} // GetScriptInfo
/// <summary>
/// Gets the script cache
/// </summary>
///
internal Dictionary < string , ScriptInfo > ScriptCache
{
get { return cachedScriptInfo ; }
}
/// <summary>
/// The cache for the ScriptInfo.
/// </summary>
///
private Dictionary < string , ScriptInfo > cachedScriptInfo ;
internal ExecutionContext Context
{
get { return _context ; }
}
private ExecutionContext _context ;
/// <summary>
/// Reads the path for the appropriate shellID from the registry.
/// </summary>
///
/// <param name="shellID">
/// The ID of the shell to retrieve the path for.
/// </param>
///
/// <returns>
/// The path to the shell represented by the shellID.
/// </returns>
///
/// <remarks>
/// The shellID must be registered in the Windows Registry in either
/// the HKEY_CURRENT_USER or HKEY_LOCAL_MACHINE hive under
/// Software/Microsoft/MSH/<ShellID> and are searched in that order.
/// </remarks>
///
internal static string GetShellPathFromRegistry ( string shellID )
{
2016-04-06 12:01:07 -07:00
if (! Platform . HasRegistrySupport ())
{
return null ;
}
2016-03-30 14:20:52 -07:00
string result = null ;
try
{
RegistryKey shellKey = Registry . LocalMachine . OpenSubKey ( Utils . GetRegistryConfigurationPath ( shellID ));
if ( shellKey != null )
{
// verify the value kind as a string
RegistryValueKind kind = shellKey . GetValueKind ( "path" );
if ( kind == RegistryValueKind . ExpandString ||
kind == RegistryValueKind . String )
{
result = shellKey . GetValue ( "path" ) as string ;
}
}
}
// Ignore these exceptions and return an empty or null result
catch ( SecurityException )
{
}
catch ( IOException )
{
}
catch ( ArgumentException )
{
}
return result ;
}
internal static PSModuleAutoLoadingPreference GetCommandDiscoveryPreference ( ExecutionContext context , VariablePath variablePath , string environmentVariable )
{
Dbg . Assert ( context != null , "context cannot be Null" );
Dbg . Assert ( variablePath != null , "variablePath must be non empty" );
Dbg . Assert (! string . IsNullOrEmpty ( environmentVariable ), "environmentVariable must be non empty" );
if ( context == null )
{
throw PSTraceSource . NewArgumentNullException ( "context" );
}
// check the PSVariable
object result = context . GetVariableValue ( variablePath );
try
{
if ( result != null )
{
return LanguagePrimitives . ConvertTo < PSModuleAutoLoadingPreference >( result );
}
// check the environment variable
String psEnvironmentVariable = Environment . GetEnvironmentVariable ( environmentVariable );
if (! String . IsNullOrEmpty ( psEnvironmentVariable ))
{
return LanguagePrimitives . ConvertTo < PSModuleAutoLoadingPreference >( psEnvironmentVariable );
}
}
catch ( Exception e )
{
CommandProcessorBase . CheckForSevereException ( e );
return PSModuleAutoLoadingPreference . All ;
}
return PSModuleAutoLoadingPreference . All ;
}
#endregion
}
/// <summary>
/// A helper collection of strings that doesn't allow duplicate strings. Comparison
/// is case-insensitive and done in the invariant culture.
/// </summary>
internal class LookupPathCollection : Collection < string >
{
/// <summary>
/// Default constructor
/// </summary>
internal LookupPathCollection () : base () { }
/// <summary>
/// Constructs a LookupPathCollection object and adds all the items
/// in the supplied collection to it.
/// </summary>
///
/// <param name="collection">
/// A set of items to be added to the collection.
/// </param>
///
internal LookupPathCollection ( IEnumerable < string > collection ) : base ()
{
foreach ( string item in collection )
{
Add ( item );
}
}
/// <summary>
/// Adds the specified string to the collection if its not already
/// a member of the collection.
/// </summary>
///
/// <param name="item">
/// The string to add to the collection.
/// </param>
///
/// <returns>
/// The index at which the string was added or -1 if it was not added.
/// </returns>
///
public new int Add ( string item )
{
int result = - 1 ;
if (! Contains ( item ))
{
base . Add ( item );
result = base . IndexOf ( item );
}
return result ;
}
/// <summary>
/// Adds all the strings in the specified collection to this collection
/// </summary>
///
/// <param name="collection">
/// The collection of strings to add.
/// </param>
///
/// <remarks>
/// Only the strings that are not already in the collection will be added.
/// </remarks>
///
internal void AddRange ( ICollection < string > collection )
{
foreach ( string name in collection )
{
Add ( name );
}
}
/// <summary>
/// Determines if the string already exists in the collection
/// using a invariant culture case insensitive comparison.
/// </summary>
///
/// <param name="item">
/// The string to check for existence.
/// </param>
///
/// <returns>
/// True if the string already exists in the collection.
/// </returns>
///
public new bool Contains ( string item )
{
bool result = false ;
foreach ( string name in this )
{
if ( String . Equals ( item , name , StringComparison . OrdinalIgnoreCase ))
{
result = true ;
break ;
}
}
return result ;
}
/// <summary>
/// Returns a collection of all the indexes that are relative paths.
/// </summary>
///
/// <returns>
/// A collection of all the indexes that are relative paths.
/// </returns>
///
internal Collection < int > IndexOfRelativePath ()
{
Collection < int > result = new Collection < int >();
for ( int index = 0 ; index < this . Count ; ++ index )
{
string path = this [ index ];
if (! String . IsNullOrEmpty ( path ) &&
path . StartsWith ( "." , StringComparison . CurrentCulture ))
{
result . Add ( index );
}
}
return result ;
} // IndexOfRelativePath
/// <summary>
/// Finds the first index of the specified string. The string
/// is compared in the invariant culture using a case-insensitive comparison.
/// </summary>
///
/// <param name="item">
/// The string to look for.
/// </param>
///
/// <returns>
/// The index of the string in the collection or -1 if it was not found.
/// </returns>
///
/// <exception cref="ArgumentException">
/// If <paramref name="item"/> is null or empty.
/// </exception>
///
public new int IndexOf ( string item )
{
if ( String . IsNullOrEmpty ( item ))
{
throw PSTraceSource . NewArgumentException ( "item" );
}
int result = - 1 ;
for ( int index = 0 ; index < this . Count ; ++ index )
{
if ( String . Equals ( this [ index ], item , StringComparison . OrdinalIgnoreCase ))
{
result = index ;
break ;
}
}
return result ;
}
} // LookupPathCollection
// Guid is {ea9e8155-5042-5537-0b73-8c0e6b53f398}
[EventSource(Name = "Microsoft-PowerShell-CommandDiscovery")]
internal class CommandDiscoveryEventSource : EventSource
{
internal static CommandDiscoveryEventSource Log = new CommandDiscoveryEventSource ();
public void CommandLookupStart ( string CommandName ) { WriteEvent ( 1 , CommandName ); }
public void CommandLookupStop ( string CommandName ) { WriteEvent ( 2 , CommandName ); }
public void ModuleAutoLoadingStart ( string CommandName ) { WriteEvent ( 3 , CommandName ); }
public void ModuleAutoLoadingStop ( string CommandName ) { WriteEvent ( 4 , CommandName ); }
public void ModuleAutoDiscoveryStart ( string CommandName ) { WriteEvent ( 5 , CommandName ); }
public void ModuleAutoDiscoveryStop ( string CommandName ) { WriteEvent ( 6 , CommandName ); }
public void SearchingForModuleFilesStart () { WriteEvent ( 7 ); }
public void SearchingForModuleFilesStop () { WriteEvent ( 8 ); }
public void GetModuleExportedCommandsStart ( string ModulePath ) { WriteEvent ( 9 , ModulePath ); }
public void GetModuleExportedCommandsStop ( string ModulePath ) { WriteEvent ( 10 , ModulePath ); }
public void ModuleManifestAnalysisResult ( string ModulePath , bool Success ) { WriteEvent ( 11 , ModulePath , Success ); }
public void ModuleManifestAnalysisException ( string ModulePath , string Exception ) { WriteEvent ( 12 , ModulePath , Exception ); }
}
}