MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections.Generic;5using System.Collections.ObjectModel;6using System.Diagnostics.Tracing;7using System.Globalization;8using System.IO;9using System.Linq;10using System.Management.Automation.Host;11using System.Management.Automation.Internal;12using System.Management.Automation.Runspaces;13using System.Security;14 15using Microsoft.PowerShell.Commands;16using Microsoft.Win32;17 18using Dbg = System.Management.Automation.Diagnostics;19 20namespace System.Management.Automation21{22 /// <summary>23 /// EventArgs for the ScriptCmdletVariableUpdate event.24 /// </summary>25 public class CommandLookupEventArgs : EventArgs26 {27 /// <summary>28 /// Constructor for event args object.29 /// </summary>30 /// <param name="commandName">The name of the command we're searching for.</param>31 /// <param name="commandOrigin">The origin of the command internal or runspace (external).</param>32 /// <param name="context">The execution context for this command.</param>33 internal CommandLookupEventArgs(string commandName, CommandOrigin commandOrigin, ExecutionContext context)34 {35 CommandName = commandName;36 CommandOrigin = commandOrigin;37 _context = context;38 }39 40 private readonly ExecutionContext _context;41 42 /// <summary>43 /// The name of the command we're looking for.44 /// </summary>45 public string CommandName { get; }46 47 /// <summary>48 /// The origin of the command internal or runspace (external)49 /// </summary>50 public CommandOrigin CommandOrigin { get; }51 52 /// <summary>53 /// If true on return from event handler, the search is stopped.54 /// </summary>55 public bool StopSearch { get; set; }56 57 /// <summary>58 /// The CommandInfo object for the command that was found.59 /// </summary>60 public CommandInfo Command { get; set; }61 62 /// <summary>63 /// Scriptblock to be returned as the found command. If it is set to64 /// null, then the command to return and the StopSearch flag will be reset.65 /// </summary>66 public ScriptBlock CommandScriptBlock67 {68 get69 {70 return _scriptBlock;71 }72 73 set74 {75 _scriptBlock = value;76 if (_scriptBlock != null)77 {78 string dynamicName = "LookupHandlerReplacementFor<<" + CommandName + ">>";79 Command = new FunctionInfo(dynamicName, _scriptBlock, _context);80 StopSearch = true;81 }82 else83 {84 Command = null;85 StopSearch = false;86 }87 }88 }89 90 private ScriptBlock _scriptBlock;91 }92 93 /// <summary>94 /// Defines the preference options for the Module Auto-loading feature.95 /// </summary>96 public enum PSModuleAutoLoadingPreference97 {98 /// <summary>99 /// Do not auto-load modules when a command is not found.100 /// </summary>101 None = 0,102 103 /// <summary>104 /// Only auto-load modules when a command is not found, and the command105 /// is module-qualified.106 /// </summary>107 ModuleQualified = 1,108 109 /// <summary>110 /// Auto-load modules when a command is not found.111 /// </summary>112 All = 2113 }114 115 /// <summary>116 /// CommandDiscovery...117 /// </summary>118 internal class CommandDiscovery119 {120 [TraceSource("CommandDiscovery", "Traces the discovery of cmdlets, scripts, functions, applications, etc.")]121 internal static readonly PSTraceSource discoveryTracer =122 PSTraceSource.GetTracer(123 "CommandDiscovery",124 "Traces the discovery of cmdlets, scripts, functions, applications, etc.",125 false);126 127 #region ctor128 129 /// <summary>130 /// Default constructor...131 /// </summary>132 /// <exception cref="ArgumentNullException">133 /// If <paramref name="context"/> is null.134 /// </exception>135 internal CommandDiscovery(ExecutionContext context)136 {137 if (context == null)138 {139 throw PSTraceSource.NewArgumentNullException(nameof(context));140 }141 142 Context = context;143 discoveryTracer.ShowHeaders = false;144 }145 146 /// <summary>147 /// Determines if the cmdlet is a cmdlet that shouldn't be in the discovery list.148 /// </summary>149 /// <param name="implementingType">150 /// Type implementing the cmdlet151 /// </param>152 /// <returns>153 /// True if the cmdlet is a special cmdlet that shouldn't be part of the discovery list. Or false otherwise.154 /// </returns>155 private static bool IsSpecialCmdlet(Type implementingType)156 {157 // These commands should never be put in the discovery list. They are an internal implementation158 // detail of the formatting and output component. That component uses these cmdlets by creating159 // an instance of the CommandProcessor class directly.160 return implementingType == typeof(OutLineOutputCommand) || implementingType == typeof(FormatDefaultCommand);161 }162 163 private CmdletInfo NewCmdletInfo(SessionStateCmdletEntry entry)164 {165 return NewCmdletInfo(entry, Context);166 }167 168 internal static CmdletInfo NewCmdletInfo(SessionStateCmdletEntry entry, ExecutionContext context)169 {170 CmdletInfo ci = new CmdletInfo(entry.Name, entry.ImplementingType, entry.HelpFileName, entry.PSSnapIn, context)171 {172 Visibility = entry.Visibility,173 Module = entry.Module174 };175 return ci;176 }177 178 internal static AliasInfo NewAliasInfo(SessionStateAliasEntry entry, ExecutionContext context)179 {180 AliasInfo ci = new AliasInfo(entry.Name, entry.Definition, context, entry.Options)181 {182 Visibility = entry.Visibility,183 Module = entry.Module184 };185 return ci;186 }187 188 /// <summary>189 /// Adds the CmdletInfo to the cmdlet cache in the current scope object.190 /// </summary>191 /// <param name="name">192 /// The name of the cmdlet to add.193 /// </param>194 /// <param name="newCmdletInfo">195 /// The CmdletInfo to add.196 /// </param>197 /// <param name="isGlobal">198 /// If true, the cmdlet is added to the Module Scope of the session state.199 /// </param>200 /// <exception cref="PSNotSupportedException">201 /// If a cmdlet with the same module and cmdlet name already exists202 /// but has a different implementing type.203 /// </exception>204 internal CmdletInfo AddCmdletInfoToCache(string name, CmdletInfo newCmdletInfo, bool isGlobal)205 {206 if (string.IsNullOrEmpty(name))207 {208 throw PSTraceSource.NewArgumentException(nameof(name));209 }210 211 if (newCmdletInfo == null)212 {213 throw PSTraceSource.NewArgumentNullException("cmdlet");214 }215 216 if (isGlobal)217 {218 // When cmdlet cache was not scope-based, we used to import cmdlets to the module scope.219 // We need to do the same as the default action (setting "isGlobal" is done as a default action in the caller)220 return Context.EngineSessionState.ModuleScope.AddCmdletToCache(newCmdletInfo.Name, newCmdletInfo, CommandOrigin.Internal, Context);221 }222 223 return Context.EngineSessionState.CurrentScope.AddCmdletToCache(newCmdletInfo.Name, newCmdletInfo, CommandOrigin.Internal, Context);224 }225 226 /// <summary>227 /// Add a SessionStateCmdletEntry to the cmdlet cache...228 /// </summary>229 /// <param name="entry"></param>230 internal void AddSessionStateCmdletEntryToCache(SessionStateCmdletEntry entry)231 {232 AddSessionStateCmdletEntryToCache(entry, /*local*/false);233 }234 235 /// <summary>236 /// Add a SessionStateCmdletEntry to the cmdlet cache...237 /// </summary>238 /// <param name="entry"></param>239 /// <param name="local"></param>240 internal void AddSessionStateCmdletEntryToCache(SessionStateCmdletEntry entry, bool local)241 {242 if (!IsSpecialCmdlet(entry.ImplementingType))243 {244 CmdletInfo nci = NewCmdletInfo(entry);245 AddCmdletInfoToCache(nci.Name, nci, !local);246 }247 }248 249 #endregion ctor250 251 #region internal methods252 253 /// <summary>254 /// Look up a command named by the argument string and return its CommandProcessorBase.255 /// </summary>256 /// <param name="commandName">257 /// The command name to lookup.258 /// </param>259 /// <param name="commandOrigin">Location where the command was dispatched from.</param>260 /// <param name="useLocalScope">261 /// True if command processor should use local scope to execute the command,262 /// False if not. Null if command discovery should default to something reasonable263 /// for the command discovered.264 /// </param>265 /// <param name="forCompletion">266 /// True if this for parameter completion and script requirements should be ignored.267 /// </param>268 /// <returns>269 /// </returns>270 /// <exception cref="CommandNotFoundException">271 /// If the command, <paramref name="commandName"/>, could not be found.272 /// </exception>273 /// <exception cref="System.Security.SecurityException">274 /// If the security manager is preventing the command from running.275 /// </exception>276 internal CommandProcessorBase LookupCommandProcessor(string commandName,277 CommandOrigin commandOrigin, bool? useLocalScope, bool forCompletion = false)278 {279 CommandProcessorBase processor = null;280 CommandInfo commandInfo = LookupCommandInfo(commandName, commandOrigin);281 282 if (commandInfo != null)283 {284 processor = LookupCommandProcessor(commandInfo, commandOrigin, useLocalScope, null, forCompletion);285 286 // commandInfo.Name might be different than commandName - restore the original invocation name287 processor.Command.MyInvocation.InvocationName = commandName;288 }289 290 return processor;291 }292 293 internal static void VerifyRequiredModules(ExternalScriptInfo scriptInfo, ExecutionContext context, bool forCompletion = false)294 {295 // Check Required Modules296 if (scriptInfo.RequiresModules != null)297 {298 foreach (var requiredModule in scriptInfo.RequiresModules)299 {300 ErrorRecord error = null;301 ModuleCmdletBase.LoadRequiredModule(302 context: context,303 currentModule: null,304 requiredModuleSpecification: requiredModule,305 moduleManifestPath: null,306 manifestProcessingFlags: ModuleCmdletBase.ManifestProcessingFlags.LoadElements | ModuleCmdletBase.ManifestProcessingFlags.WriteErrors,307 error: out error);308 if (!forCompletion && error is not null)309 {310 ScriptRequiresException scriptRequiresException =311 new ScriptRequiresException(312 scriptInfo.Name,313 new Collection<string> { requiredModule.GetRequiredModuleNotFoundVersionMessage() },314 "ScriptRequiresMissingModules",315 false,316 error);317 throw scriptRequiresException;318 }319 }320 }321 }322 323 private CommandProcessorBase CreateScriptProcessorForSingleShell(ExternalScriptInfo scriptInfo, ExecutionContext context, bool useLocalScope, SessionStateInternal sessionState, bool forCompletion = false)324 {325 VerifyScriptRequirements(scriptInfo, Context, forCompletion);326 327 if (!string.IsNullOrEmpty(scriptInfo.RequiresApplicationID))328 {329 ScriptRequiresException sre =330 new ScriptRequiresException(331 scriptInfo.Name,332 string.Empty,333 string.Empty,334 "RequiresShellIDInvalidForSingleShell");335 336 throw sre;337 }338 339 return CreateCommandProcessorForScript(scriptInfo, Context, useLocalScope, sessionState);340 }341 342 // This method verifies the following 3 elements of #Requires statement343 // #Requires -RunAsAdministrator344 // #Requires -PSVersion345 // #Requires -PSEdition346 // #Requires -Module347 internal static void VerifyScriptRequirements(ExternalScriptInfo scriptInfo, ExecutionContext context, bool forCompletion = false)348 {349 // When completing script parameters we don't care if these requirements are met.350 // VerifyRequiredModules will attempt to load the required modules which is useful for completion (so the correct types are loaded).351 if (!forCompletion)352 {353 VerifyElevatedPrivileges(scriptInfo);354 VerifyPSVersion(scriptInfo);355 VerifyPSEdition(scriptInfo);356 }357 358 VerifyRequiredModules(scriptInfo, context, forCompletion);359 }360 361 internal static void VerifyPSVersion(ExternalScriptInfo scriptInfo)362 {363 Version requiresPSVersion = scriptInfo.RequiresPSVersion;364 // in single shell mode365 if (requiresPSVersion != null)366 {367 if (!PSVersionInfo.IsValidPSVersion(requiresPSVersion))368 {369 ScriptRequiresException scriptRequiresException =370 new ScriptRequiresException(371 scriptInfo.Name,372 requiresPSVersion,373 PSVersionInfo.PSVersion.ToString(),374 "ScriptRequiresUnmatchedPSVersion");375 throw scriptRequiresException;376 }377 }378 }379 380 internal static void VerifyPSEdition(ExternalScriptInfo scriptInfo)381 {382 if (scriptInfo.RequiresPSEditions != null)383 {384 var isCurrentEditionListed = false;385 var isRequiresPSEditionSpecified = false;386 foreach (var edition in scriptInfo.RequiresPSEditions)387 {388 isRequiresPSEditionSpecified = true;389 isCurrentEditionListed = Utils.IsPSEditionSupported(edition);390 if (isCurrentEditionListed)391 {392 break;393 }394 }395 396 // Throw an error if required PowerShell editions are specified and without the current PowerShell Edition.397 //398 if (isRequiresPSEditionSpecified && !isCurrentEditionListed)399 {400 var specifiedEditionsString = string.Join(',', scriptInfo.RequiresPSEditions);401 var message = StringUtil.Format(DiscoveryExceptions.RequiresPSEditionNotCompatible,402 scriptInfo.Name,403 specifiedEditionsString,404 PSVersionInfo.PSEditionValue);405 var ex = new RuntimeException(message);406 ex.SetErrorId("ScriptRequiresUnmatchedPSEdition");407 ex.SetTargetObject(scriptInfo.Name);408 throw ex;409 }410 }411 }412 413 internal static void VerifyElevatedPrivileges(ExternalScriptInfo scriptInfo)414 {415 bool requiresElevation = scriptInfo.RequiresElevation;416 bool isAdministrator = Utils.IsAdministrator();417 if (requiresElevation && !isAdministrator)418 {419 ScriptRequiresException scriptRequiresException =420 new ScriptRequiresException(421 scriptInfo.Name,422 "ScriptRequiresElevation");423 throw scriptRequiresException;424 }425 }426 427 /// <summary>428 /// Look up a command using a CommandInfo object and return its CommandProcessorBase.429 /// </summary>430 /// <param name="commandInfo">431 /// The commandInfo for the command to lookup.432 /// </param>433 /// <param name="commandOrigin">Location where the command was dispatched from.</param>434 /// <param name="useLocalScope">435 /// True if command processor should use local scope to execute the command,436 /// False if not. Null if command discovery should default to something reasonable437 /// for the command discovered.438 /// </param>439 /// <param name="forCompletion">440 /// True if this for parameter completion and script requirements should be ignored.441 /// </param>442 /// <param name="sessionState">The session state the commandInfo should be run in.</param>443 /// <returns>444 /// </returns>445 /// <exception cref="CommandNotFoundException">446 /// If the command, <paramref name="commandName"/>, could not be found.447 /// </exception>448 /// <exception cref="System.Management.Automation.PSSecurityException">449 /// If the security manager is preventing the command from running.450 /// </exception>451 internal CommandProcessorBase LookupCommandProcessor(CommandInfo commandInfo,452 CommandOrigin commandOrigin, bool? useLocalScope, SessionStateInternal sessionState, bool forCompletion = false)453 {454 CommandProcessorBase processor = null;455 456 HashSet<string> processedAliases = new HashSet<string>(StringComparer.OrdinalIgnoreCase);457 458 while (commandInfo.CommandType == CommandTypes.Alias &&459 (!processedAliases.Contains(commandInfo.Name)) &&460 (commandOrigin == CommandOrigin.Internal || commandInfo.Visibility == SessionStateEntryVisibility.Public))461 {462 processedAliases.Add(commandInfo.Name);463 464 AliasInfo aliasCommandInfo = (AliasInfo)commandInfo;465 commandInfo = aliasCommandInfo.ResolvedCommand ??466 LookupCommandInfo(aliasCommandInfo.Definition, commandOrigin, Context);467 468 // If we didn't have the alias target already resolved, see if it can be loaded.469 470 if (commandInfo == null)471 {472 CommandNotFoundException e =473 new CommandNotFoundException(474 aliasCommandInfo.Name,475 null,476 "AliasNotResolvedException",477 DiscoveryExceptions.AliasNotResolvedException,478 aliasCommandInfo.UnresolvedCommandName);479 throw e;480 }481 }482 483 ShouldRun(Context, Context.EngineHostInterface, commandInfo, commandOrigin);484 485 switch (commandInfo.CommandType)486 {487 case CommandTypes.Application:488 processor = new NativeCommandProcessor((ApplicationInfo)commandInfo, Context);489 break;490 case CommandTypes.Cmdlet:491 processor = new CommandProcessor((CmdletInfo)commandInfo, Context);492 break;493 case CommandTypes.ExternalScript:494 ExternalScriptInfo scriptInfo = (ExternalScriptInfo)commandInfo;495 scriptInfo.SignatureChecked = true;496 try497 {498 processor = CreateScriptProcessorForSingleShell(scriptInfo, Context, useLocalScope ?? true, sessionState, forCompletion);499 }500 catch (ScriptRequiresSyntaxException reqSyntaxException)501 {502 CommandNotFoundException e =503 new CommandNotFoundException(reqSyntaxException.Message, reqSyntaxException);504 throw e;505 }506 507 break;508 case CommandTypes.Filter:509 case CommandTypes.Function:510 case CommandTypes.Configuration:511 FunctionInfo functionInfo = (FunctionInfo)commandInfo;512 processor = CreateCommandProcessorForScript(functionInfo, Context, useLocalScope ?? true, sessionState);513 break;514 case CommandTypes.Script:515 processor = CreateCommandProcessorForScript((ScriptInfo)commandInfo, Context, useLocalScope ?? true, sessionState);516 break;517 case CommandTypes.Alias:518 default:519 {520 CommandNotFoundException e =521 new CommandNotFoundException(522 commandInfo.Name,523 null,524 "CommandNotFoundException",525 DiscoveryExceptions.CommandNotFoundException);526 throw e;527 }528 }529 530 // Set the internal command origin member on the command object at this point...531 processor.Command.CommandOriginInternal = commandOrigin;532 533 processor.Command.MyInvocation.InvocationName = commandInfo.Name;534 535 return processor;536 }537 538 internal static void ShouldRun(ExecutionContext context, PSHost host, CommandInfo commandInfo, CommandOrigin commandOrigin)539 {540 // ShouldRunInternal throws PSSecurityException if run is not allowed541 try542 {543 if (commandOrigin == CommandOrigin.Runspace && commandInfo.Visibility != SessionStateEntryVisibility.Public)544 {545 CommandNotFoundException e = new CommandNotFoundException(546 commandInfo.Name, null, "CommandNotFoundException", DiscoveryExceptions.CommandNotFoundException);547 throw e;548 }549 550 context.AuthorizationManager.ShouldRunInternal(commandInfo, commandOrigin, host);551 }552 catch (PSSecurityException reason)553 {554 MshLog.LogCommandHealthEvent(context,555 reason,556 Severity.Warning);557 558 MshLog.LogCommandLifecycleEvent(context,559 CommandState.Terminated,560 commandInfo.Name);561 562 throw;563 }564 }565 566 private static CommandProcessorBase CreateCommandProcessorForScript(ScriptInfo scriptInfo, ExecutionContext context, bool useNewScope, SessionStateInternal sessionState)567 {568 sessionState ??= scriptInfo.ScriptBlock.SessionStateInternal ?? context.EngineSessionState;569 CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor(scriptInfo, context, useNewScope, true, sessionState);570 if (scriptAsCmdletProcessor != null)571 {572 return scriptAsCmdletProcessor;573 }574 575 return new DlrScriptCommandProcessor(scriptInfo, context, useNewScope, sessionState);576 }577 578 private static CommandProcessorBase CreateCommandProcessorForScript(ExternalScriptInfo scriptInfo, ExecutionContext context, bool useNewScope, SessionStateInternal sessionState)579 {580 sessionState ??= scriptInfo.ScriptBlock.SessionStateInternal ?? context.EngineSessionState;581 CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor(scriptInfo, context, useNewScope, true, sessionState);582 if (scriptAsCmdletProcessor != null)583 {584 return scriptAsCmdletProcessor;585 }586 587 return new DlrScriptCommandProcessor(scriptInfo, context, useNewScope, sessionState);588 }589 590 internal static CommandProcessorBase CreateCommandProcessorForScript(FunctionInfo functionInfo, ExecutionContext context, bool useNewScope, SessionStateInternal sessionState)591 {592 sessionState ??= functionInfo.ScriptBlock.SessionStateInternal ?? context.EngineSessionState;593 CommandProcessorBase scriptAsCmdletProcessor = GetScriptAsCmdletProcessor(functionInfo, context, useNewScope, false, sessionState);594 if (scriptAsCmdletProcessor != null)595 {596 return scriptAsCmdletProcessor;597 }598 599 return new DlrScriptCommandProcessor(functionInfo, context, useNewScope, sessionState);600 }601 602 internal static CommandProcessorBase CreateCommandProcessorForScript(ScriptBlock scriptblock, ExecutionContext context, bool useNewScope, SessionStateInternal sessionState)603 {604 sessionState ??= scriptblock.SessionStateInternal ?? context.EngineSessionState;605 606 if (scriptblock.UsesCmdletBinding)607 {608 FunctionInfo fi = new FunctionInfo(string.Empty, scriptblock, context);609 return GetScriptAsCmdletProcessor(fi, context, useNewScope, false, sessionState);610 }611 612 return new DlrScriptCommandProcessor(scriptblock, context, useNewScope, CommandOrigin.Internal, sessionState);613 }614 615 private static CommandProcessorBase GetScriptAsCmdletProcessor(IScriptCommandInfo scriptCommandInfo, ExecutionContext context, bool useNewScope, bool fromScriptFile, SessionStateInternal sessionState)616 {617 if (scriptCommandInfo.ScriptBlock == null || !scriptCommandInfo.ScriptBlock.UsesCmdletBinding)618 {619 return null;620 }621 622 sessionState ??= scriptCommandInfo.ScriptBlock.SessionStateInternal ?? context.EngineSessionState;623 624 return new CommandProcessor(scriptCommandInfo, context, useNewScope, fromScriptFile, sessionState);625 }626 627 /// <summary>628 /// Look up a command and return its CommandInfo.629 /// </summary>630 /// <param name="commandName">631 /// The command name to lookup.632 /// </param>633 /// <returns>634 /// An instance of a CommandInfo object that represents the635 /// command. If the command is resolved as an alias, an AliasInfo636 /// is returned with the ReferencedCommand info intact.637 /// </returns>638 /// <exception cref="CommandNotFoundException">639 /// If the command, <paramref name="commandName"/>, could not be found.640 /// </exception>641 internal CommandInfo LookupCommandInfo(string commandName)642 {643 return LookupCommandInfo(commandName, CommandOrigin.Internal);644 }645 646 internal CommandInfo LookupCommandInfo(string commandName, CommandOrigin commandOrigin)647 {648 return LookupCommandInfo(commandName, commandOrigin, Context);649 }650 651 internal static CommandInfo LookupCommandInfo(string commandName, CommandOrigin commandOrigin, ExecutionContext context)652 {653 return LookupCommandInfo(commandName, CommandTypes.All, SearchResolutionOptions.ResolveLiteralThenPathPatterns, commandOrigin, context);654 }655 656 internal static CommandInfo LookupCommandInfo(657 string commandName,658 CommandTypes commandTypes,659 SearchResolutionOptions searchResolutionOptions,660 CommandOrigin commandOrigin,661 ExecutionContext context)662 {663 if (string.IsNullOrEmpty(commandName))664 {665 return null;666 }667 668 bool etwEnabled = CommandDiscoveryEventSource.Log.IsEnabled();669 if (etwEnabled) CommandDiscoveryEventSource.Log.CommandLookupStart(commandName);670 671 CommandInfo result = null;672 string originalCommandName = commandName;673 674 Exception lastError = null;675 676 // Check to see if there is a pre-search look-up event handler...677 CommandLookupEventArgs eventArgs = null;678 EventHandler<CommandLookupEventArgs> preCommandLookupEvent = context.EngineIntrinsics.InvokeCommand.PreCommandLookupAction;679 680 if (preCommandLookupEvent != null)681 {682 discoveryTracer.WriteLine("Executing PreCommandLookupAction: {0}", commandName);683 try684 {685 context.CommandDiscovery.RegisterLookupCommandInfoAction("ActivePreLookup", originalCommandName);686 eventArgs = new CommandLookupEventArgs(originalCommandName, commandOrigin, context);687 preCommandLookupEvent.Invoke(originalCommandName, eventArgs);688 689 discoveryTracer.WriteLine("PreCommandLookupAction returned: {0}", eventArgs.Command);690 }691 catch (Exception)692 {693 }694 finally { context.CommandDiscovery.UnregisterLookupCommandInfoAction("ActivePreLookup", commandName); }695 }696 697 // Check the module auto-loading preference698 PSModuleAutoLoadingPreference moduleAutoLoadingPreference = GetCommandDiscoveryPreference(context, SpecialVariables.PSModuleAutoLoadingPreferenceVarPath, "PSModuleAutoLoadingPreference");699 700 if (eventArgs == null || !eventArgs.StopSearch)701 {702 do703 {704 discoveryTracer.WriteLine("Looking up command: {0}", commandName);705 706 // Use the CommandSearcher to find the first command. If there are duplicate707 // command names, then take the first one...708 709 result = TryNormalSearch(commandName, context, commandOrigin, searchResolutionOptions, commandTypes, ref lastError);710 711 if (result != null)712 break;713 714 // Try the module-qualified auto-loading (unless module auto-loading has been entirely disabled)715 if (moduleAutoLoadingPreference != PSModuleAutoLoadingPreference.None)716 {717 result = TryModuleAutoLoading(commandName, context, originalCommandName, commandOrigin, ref lastError);718 }719 720 if (result != null)721 break;722 723 // See if the this was not module-qualified. In that case, we should look for the first module724 // that contains the command and load that.725 if (moduleAutoLoadingPreference == PSModuleAutoLoadingPreference.All)726 {727 result = TryModuleAutoDiscovery(commandName, context, originalCommandName, commandOrigin,728 searchResolutionOptions, commandTypes, ref lastError);729 }730 731 // Otherwise, invoke the CommandNotFound handler732 result ??= InvokeCommandNotFoundHandler(commandName, context, originalCommandName, commandOrigin);733 } while (false);734 }735 else736 {737 if (eventArgs.Command != null)738 {739 result = eventArgs.Command;740 }741 }742 743 // If we resolved a command, give the PostCommandLookup a chance to change it744 if (result != null)745 {746 System.EventHandler<CommandLookupEventArgs> postAction = context.EngineIntrinsics.InvokeCommand.PostCommandLookupAction;747 if (postAction != null)748 {749 discoveryTracer.WriteLine("Executing PostCommandLookupAction: {0}", originalCommandName);750 try751 {752 context.CommandDiscovery.RegisterLookupCommandInfoAction("ActivePostCommand", originalCommandName);753 754 eventArgs = new CommandLookupEventArgs(originalCommandName, commandOrigin, context);755 eventArgs.Command = result;756 postAction.Invoke(originalCommandName, eventArgs);757 758 if (eventArgs != null)759 {760 result = eventArgs.Command;761 discoveryTracer.WriteLine("PreCommandLookupAction returned: {0}", eventArgs.Command);762 }763 }764 catch (Exception)765 {766 }767 finally768 {769 context.CommandDiscovery.UnregisterLookupCommandInfoAction("ActivePostCommand", originalCommandName);770 }771 }772 }773 774 // After all command lookup is done, throw a CommandNotFound if we have no result.775 if (result == null)776 {777 discoveryTracer.TraceError(778 "'{0}' is not recognized as a cmdlet, function, executable program or script file.",779 commandName);780 781 CommandNotFoundException e =782 new CommandNotFoundException(783 originalCommandName,784 lastError,785 "CommandNotFoundException",786 DiscoveryExceptions.CommandNotFoundException);787 throw e;788 }789 790 if (etwEnabled) CommandDiscoveryEventSource.Log.CommandLookupStop(commandName);791 792 return result;793 }794 795 internal static void AutoloadModulesWithJobSourceAdapters(System.Management.Automation.ExecutionContext context, CommandOrigin commandOrigin)796 {797 /* This function is used by *-Job cmdlets (JobCmdletBase.BeginProcessing(), StartJobCommand.BeginProcessing())798 It attempts to load modules from a fixed ModulesWithJobSourceAdapters list that currently has only `PSScheduledJob` module that is not PS-Core compatible.799 Because this function does not check the result of a (currently failing) `PSScheduledJob` module autoload, it provides no value.800 After discussion it was decided to comment out this code as it may be useful if ModulesWithJobSourceAdapters list changes in the future.801 802 if (!context.IsModuleWithJobSourceAdapterLoaded)803 {804 PSModuleAutoLoadingPreference moduleAutoLoadingPreference = GetCommandDiscoveryPreference(context, SpecialVariables.PSModuleAutoLoadingPreferenceVarPath, "PSModuleAutoLoadingPreference");805 if (moduleAutoLoadingPreference != PSModuleAutoLoadingPreference.None)806 {807 CmdletInfo cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Import-Module");808 if ((commandOrigin == CommandOrigin.Internal) ||809 ((cmdletInfo != null) && (cmdletInfo.Visibility == SessionStateEntryVisibility.Public)))810 {811 foreach (var module in System.Management.Automation.ExecutionContext.ModulesWithJobSourceAdapters)812 {813 List<PSModuleInfo> existingModule = context.Modules.GetModules(new string[] { module }, false);814 if (existingModule == null || existingModule.Count == 0)815 {816 Exception unUsedException = null;817 AutoloadSpecifiedModule(module, context, cmdletInfo.Visibility, out unUsedException);818 }819 }820 821 context.IsModuleWithJobSourceAdapterLoaded = true;822 }823 }824 }*/825 }826 827 internal static Collection<PSModuleInfo> AutoloadSpecifiedModule(string moduleName, ExecutionContext context, SessionStateEntryVisibility visibility, out Exception exception)828 {829 exception = null;830 Collection<PSModuleInfo> matchingModules = null;831 CommandInfo commandInfo = new CmdletInfo("Import-Module", typeof(ImportModuleCommand), null, null, context);832 commandInfo.Visibility = visibility;833 Command importModuleCommand = new Command(commandInfo);834 835 discoveryTracer.WriteLine("Attempting to load module: {0}", moduleName);836 837 PowerShell ps = null;838 try839 {840 ps = PowerShell.Create(RunspaceMode.CurrentRunspace)841 .AddCommand(importModuleCommand)842 .AddParameter("Name", moduleName)843 .AddParameter("Scope", StringLiterals.Global)844 .AddParameter("PassThru")845 .AddParameter("ErrorAction", ActionPreference.Ignore)846 .AddParameter("WarningAction", ActionPreference.Ignore)847 .AddParameter("InformationAction", ActionPreference.Ignore)848 .AddParameter("Verbose", false)849 .AddParameter("Debug", false);850 matchingModules = (Collection<PSModuleInfo>)ps.Invoke<PSModuleInfo>();851 }852 catch (Exception e)853 {854 exception = e;855 discoveryTracer.WriteLine("Encountered error importing module: {0}", e.Message);856 // Call-out to user code, catch-all OK857 }858 859 return matchingModules;860 }861 862 private static CommandInfo InvokeCommandNotFoundHandler(string commandName, ExecutionContext context, string originalCommandName, CommandOrigin commandOrigin)863 {864 CommandInfo result = null;865 CommandLookupEventArgs eventArgs;866 System.EventHandler<CommandLookupEventArgs> cmdNotFoundHandler = context.EngineIntrinsics.InvokeCommand.CommandNotFoundAction;867 if (cmdNotFoundHandler != null)868 {869 discoveryTracer.WriteLine("Executing CommandNotFoundAction: {0}", commandName);870 try871 {872 context.CommandDiscovery.RegisterLookupCommandInfoAction("ActiveCommandNotFound", originalCommandName);873 eventArgs = new CommandLookupEventArgs(originalCommandName, commandOrigin, context);874 cmdNotFoundHandler.Invoke(originalCommandName, eventArgs);875 result = eventArgs.Command;876 }877 catch (Exception)878 {879 }880 finally { context.CommandDiscovery.UnregisterLookupCommandInfoAction("ActiveCommandNotFound", originalCommandName); }881 }882 883 return result;884 }885 886 private static CommandInfo TryNormalSearch(string commandName,887 ExecutionContext context,888 CommandOrigin commandOrigin,889 SearchResolutionOptions searchResolutionOptions,890 CommandTypes commandTypes,891 ref Exception lastError)892 {893 CommandInfo result = null;894 895 CommandSearcher searcher =896 new CommandSearcher(897 commandName,898 searchResolutionOptions,899 commandTypes,900 context);901 searcher.CommandOrigin = commandOrigin;902 903 try904 {905 if (!searcher.MoveNext())906 {907 if (!commandName.Contains('-') && !commandName.Contains('\\'))908 {909 discoveryTracer.WriteLine(910 "The command [{0}] was not found, trying again with get- prepended",911 commandName);912 913 commandName = StringLiterals.DefaultCommandVerb + StringLiterals.CommandVerbNounSeparator + commandName;914 915 try916 {917 result = LookupCommandInfo(commandName, commandTypes, searchResolutionOptions, commandOrigin, context);918 }919 catch (CommandNotFoundException) { }920 }921 }922 else923 {924 result = ((IEnumerator<CommandInfo>)searcher).Current;925 }926 }927 catch (ArgumentException argException)928 {929 lastError = argException;930 }931 catch (PathTooLongException pathTooLong)932 {933 lastError = pathTooLong;934 }935 catch (FileLoadException fileLoadException)936 {937 lastError = fileLoadException;938 }939 catch (FormatException formatException)940 {941 lastError = formatException;942 }943 catch (MetadataException metadataException)944 {945 lastError = metadataException;946 }947 948 return result;949 }950 951 private static CommandInfo TryModuleAutoDiscovery(string commandName,952 ExecutionContext context,953 string originalCommandName,954 CommandOrigin commandOrigin,955 SearchResolutionOptions searchResolutionOptions,956 CommandTypes commandTypes,957 ref Exception lastError)958 {959 bool etwEnabled = CommandDiscoveryEventSource.Log.IsEnabled();960 if (etwEnabled) CommandDiscoveryEventSource.Log.ModuleAutoDiscoveryStart(commandName);961 962 CommandInfo result = null;963 try964 {965 // If commandName had a slash, it was module-qualified or path-qualified.966 // In that case, we should not return anything (module-qualified is handled967 // by the previous call to TryModuleAutoLoading().968 int colonOrBackslash = commandName.AsSpan().IndexOfAny('\\', ':');969 if (colonOrBackslash != -1)970 return null;971 972 CmdletInfo cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Get-Module");973 if (commandOrigin == CommandOrigin.Internal || cmdletInfo?.Visibility == SessionStateEntryVisibility.Public)974 {975 // Search for a module with a matching command, as long as the user would have the ability to976 // import the module.977 cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Import-Module");978 if (commandOrigin == CommandOrigin.Internal || cmdletInfo?.Visibility == SessionStateEntryVisibility.Public)979 {980 discoveryTracer.WriteLine("Executing non module-qualified search: {0}", commandName);981 context.CommandDiscovery.RegisterLookupCommandInfoAction("ActiveModuleSearch", commandName);982 983 // Get the available module files, preferring modules from $PSHOME so that user modules don't984 // override system modules during auto-loading985 if (etwEnabled) CommandDiscoveryEventSource.Log.SearchingForModuleFilesStart();986 var defaultAvailableModuleFiles = ModuleUtils.GetDefaultAvailableModuleFiles(isForAutoDiscovery: true, context);987 if (etwEnabled) CommandDiscoveryEventSource.Log.SearchingForModuleFilesStop();988 989 foreach (string modulePath in defaultAvailableModuleFiles)990 {991 // WinBlue:69141 - We need to get the full path here because the module path might be C:\Users\User1\DOCUME~1992 // While the exportedCommands are cached, they are cached with the full path993 string expandedModulePath = Path.GetFullPath(modulePath);994 string moduleShortName = Path.GetFileNameWithoutExtension(expandedModulePath);995 var exportedCommands = AnalysisCache.GetExportedCommands(expandedModulePath, false, context);996 997 if (exportedCommands == null) { continue; }998 999 // Skip if module only has class or other types and no commands.1000 if (exportedCommands.TryGetValue(commandName, out CommandTypes exportedCommandTypes))1001 {1002 discoveryTracer.WriteLine("Found in module: {0}", expandedModulePath);1003 Collection<PSModuleInfo> matchingModule = AutoloadSpecifiedModule(1004 expandedModulePath,1005 context,1006 cmdletInfo != null ? cmdletInfo.Visibility : SessionStateEntryVisibility.Private,1007 out lastError);1008 1009 if (matchingModule is null || matchingModule.Count == 0)1010 {1011 string errorMessage = lastError is null1012 ? StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModule, commandName, moduleShortName)1013 : StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModuleWithErrorMessage, commandName, moduleShortName, lastError.Message);1014 1015 throw new CommandNotFoundException(1016 originalCommandName,1017 lastError,1018 "CouldNotAutoloadMatchingModule",1019 errorMessage);1020 }1021 1022 result = LookupCommandInfo(commandName, commandTypes, searchResolutionOptions, commandOrigin, context);1023 }1024 1025 if (result != null)1026 {1027 break;1028 }1029 }1030 1031#if !CORECLR1032 // Close the progress pane that may have popped up from analyzing UNC paths.1033 if (context.CurrentCommandProcessor != null)1034 {1035 ProgressRecord analysisProgress = new ProgressRecord(0, Modules.ScriptAnalysisPreparing, " ");1036 analysisProgress.RecordType = ProgressRecordType.Completed;1037 context.CurrentCommandProcessor.CommandRuntime.WriteProgress(analysisProgress);1038 }1039#endif1040 }1041 }1042 }1043 catch (CommandNotFoundException) { throw; }1044 catch (Exception)1045 {1046 }1047 finally1048 {1049 context.CommandDiscovery.UnregisterLookupCommandInfoAction("ActiveModuleSearch", commandName);1050 }1051 1052 if (etwEnabled) CommandDiscoveryEventSource.Log.ModuleAutoDiscoveryStop(commandName);1053 1054 return result;1055 }1056 1057 private static CommandInfo TryModuleAutoLoading(string commandName, ExecutionContext context, string originalCommandName, CommandOrigin commandOrigin, ref Exception lastError)1058 {1059 CommandInfo result = null;1060 1061 // If commandName was module-qualified. In that case, we should load the module.1062 var colonOrBackslash = commandName.AsSpan().IndexOfAny('\\', ':');1063 1064 // If we don't see '\', there is no module specified, so no module to load.1065 // If we see ':' before '\', then we probably have a drive qualified path, not a module name1066 if (colonOrBackslash == -1 || commandName[colonOrBackslash] == ':')1067 return null;1068 1069 string moduleCommandName = commandName.Substring(colonOrBackslash + 1, commandName.Length - colonOrBackslash - 1);1070 string moduleName;1071 1072 // Now we check if there exists the second '\'1073 var secondBackslash = moduleCommandName.IndexOf('\\');1074 if (secondBackslash == -1)1075 {1076 moduleName = commandName.Substring(0, colonOrBackslash);1077 }1078 else1079 {1080 string versionString = moduleCommandName.Substring(0, secondBackslash);1081 // The second '\' could be version specified. eg: "Microsoft.PowerShell.Archive\1.0.0.0\Compress-Archive", we need to support this scenario1082 Version version;1083 if (Version.TryParse(versionString, out version))1084 {1085 moduleCommandName = moduleCommandName.Substring(secondBackslash + 1, moduleCommandName.Length - secondBackslash - 1);1086 moduleName = commandName.Substring(0, colonOrBackslash) + "\\" + versionString + "\\" + commandName.Substring(0, colonOrBackslash) + ".psd1";1087 }1088 else1089 {1090 moduleName = commandName.Substring(0, colonOrBackslash);1091 }1092 }1093 1094 if (string.IsNullOrEmpty(moduleName) || string.IsNullOrEmpty(moduleCommandName) || moduleName.EndsWith('.'))1095 return null;1096 1097 bool etwEnabled = CommandDiscoveryEventSource.Log.IsEnabled();1098 if (etwEnabled) CommandDiscoveryEventSource.Log.ModuleAutoLoadingStart(commandName);1099 1100 try1101 {1102 discoveryTracer.WriteLine("Executing module-qualified search: {0}", commandName);1103 context.CommandDiscovery.RegisterLookupCommandInfoAction("ActiveModuleSearch", commandName);1104 1105 // Verify that auto-loading is only done on for internal commands if it's not public1106 CmdletInfo cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Import-Module");1107 if ((commandOrigin == CommandOrigin.Internal) ||1108 ((cmdletInfo != null) && (cmdletInfo.Visibility == SessionStateEntryVisibility.Public)))1109 {1110 List<PSModuleInfo> existingModule = context.Modules.GetModules(new string[] { moduleName }, false);1111 PSModuleInfo discoveredModule = null;1112 1113 if (existingModule == null || existingModule.Count == 0)1114 {1115 discoveryTracer.WriteLine("Attempting to load module: {0}", moduleName);1116 Exception exception;1117 Collection<PSModuleInfo> importedModule = AutoloadSpecifiedModule(moduleName, context, cmdletInfo.Visibility, out exception);1118 lastError = exception;1119 1120 if ((importedModule == null) || (importedModule.Count == 0))1121 {1122 string error = StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportModule, moduleName);1123 CommandNotFoundException commandNotFound = new CommandNotFoundException(1124 originalCommandName,1125 lastError,1126 "CouldNotAutoLoadModule",1127 error);1128 throw commandNotFound;1129 }1130 1131 discoveredModule = importedModule[0];1132 }1133 else1134 {1135 discoveredModule = existingModule[0];1136 }1137 1138 CommandInfo exportedResult;1139 if (discoveredModule.ExportedCommands.TryGetValue(moduleCommandName, out exportedResult))1140 {1141 // Return the command if we found a module1142 result = exportedResult;1143 }1144 }1145 }1146 catch (CommandNotFoundException) { throw; }1147 catch (Exception)1148 {1149 }1150 finally { context.CommandDiscovery.UnregisterLookupCommandInfoAction("ActiveModuleSearch", commandName); }1151 1152 if (etwEnabled) CommandDiscoveryEventSource.Log.ModuleAutoLoadingStop(commandName);1153 return result;1154 }1155 1156 internal void RegisterLookupCommandInfoAction(string currentAction, string command)1157 {1158 HashSet<string> currentActionSet = null;1159 switch (currentAction)1160 {1161 case "ActivePreLookup": currentActionSet = _activePreLookup; break;1162 case "ActiveModuleSearch": currentActionSet = _activeModuleSearch; break;1163 case "ActiveCommandNotFound": currentActionSet = _activeCommandNotFound; break;1164 case "ActivePostCommand": currentActionSet = _activePostCommand; break;1165 }1166 1167 if (!currentActionSet.Add(command))1168 throw new InvalidOperationException();1169 }1170 1171 internal void UnregisterLookupCommandInfoAction(string currentAction, string command)1172 {1173 HashSet<string> currentActionSet = null;1174 switch (currentAction)1175 {1176 case "ActivePreLookup": currentActionSet = _activePreLookup; break;1177 case "ActiveModuleSearch": currentActionSet = _activeModuleSearch; break;1178 case "ActiveCommandNotFound": currentActionSet = _activeCommandNotFound; break;1179 case "ActivePostCommand": currentActionSet = _activePostCommand; break;1180 }1181 1182 currentActionSet.Remove(command);1183 }1184 1185 private readonly HashSet<string> _activePreLookup = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1186 private readonly HashSet<string> _activeModuleSearch = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1187 private readonly HashSet<string> _activeCommandNotFound = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1188 private readonly HashSet<string> _activePostCommand = new HashSet<string>(StringComparer.OrdinalIgnoreCase);1189 1190 /// <summary>1191 /// Gets the resolved paths contained in the PATH environment1192 /// variable.1193 /// </summary>1194 /// <returns>1195 /// The contents of the PATH environment variable split on System.IO.Path.PathSeparator.1196 /// </returns>1197 /// <remarks>1198 /// The result is an ordered list of paths with paths starting with "." unresolved until lookup time.1199 /// </remarks>1200 internal LookupPathCollection GetLookupDirectoryPaths()