Skip to content

Commit

Permalink
Report error when PowerShell built-in modules are missing (#16628)
Browse files Browse the repository at this point in the history
  • Loading branch information
daxian-dbw committed Jan 12, 2022
1 parent 1cb3553 commit cce64f4
Show file tree
Hide file tree
Showing 10 changed files with 426 additions and 240 deletions.
55 changes: 23 additions & 32 deletions src/System.Management.Automation/engine/CommandDiscovery.cs
Expand Up @@ -376,22 +376,16 @@ private static void VerifyRequiredSnapins(IEnumerable<PSSnapInSpecification> req

foreach (var requiresPSSnapIn in requiresPSSnapIns)
{
IEnumerable<PSSnapInInfo> loadedPSSnapIns = null;
loadedPSSnapIns = context.InitialSessionState.GetPSSnapIn(requiresPSSnapIn.Name);
if (loadedPSSnapIns == null || !loadedPSSnapIns.Any())
var loadedPSSnapIn = context.InitialSessionState.GetPSSnapIn(requiresPSSnapIn.Name);
if (loadedPSSnapIn is null)
{
if (requiresMissingPSSnapIns == null)
{
requiresMissingPSSnapIns = new Collection<string>();
}

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,
Dbg.Assert(loadedPSSnapIn.Version != null,
string.Format(
CultureInfo.InvariantCulture,
"Version is null for loaded PSSnapin {0}.", loadedPSSnapIn));
Expand All @@ -400,11 +394,7 @@ private static void VerifyRequiredSnapins(IEnumerable<PSSnapInSpecification> req
if (!AreInstalledRequiresVersionsCompatible(
requiresPSSnapIn.Version, loadedPSSnapIn.Version))
{
if (requiresMissingPSSnapIns == null)
{
requiresMissingPSSnapIns = new Collection<string>();
}

requiresMissingPSSnapIns ??= new Collection<string>();
requiresMissingPSSnapIns.Add(BuildPSSnapInDisplayName(requiresPSSnapIn));
}
}
Expand Down Expand Up @@ -1063,14 +1053,12 @@ private static CommandInfo InvokeCommandNotFoundHandler(string commandName, Exec
return null;

CmdletInfo cmdletInfo = context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Get-Module");
if ((commandOrigin == CommandOrigin.Internal) ||
((cmdletInfo != null) && (cmdletInfo.Visibility == SessionStateEntryVisibility.Public)))
if (commandOrigin == CommandOrigin.Internal || 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))))
if (commandOrigin == CommandOrigin.Internal || cmdletInfo?.Visibility == SessionStateEntryVisibility.Public)
{
discoveryTracer.WriteLine("Executing non module-qualified search: {0}", commandName);
context.CommandDiscovery.RegisterLookupCommandInfoAction("ActiveModuleSearch", commandName);
Expand All @@ -1085,30 +1073,33 @@ private static CommandInfo InvokeCommandNotFoundHandler(string commandName, Exec
{
// 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);
string expandedModulePath = Path.GetFullPath(modulePath);
string moduleShortName = 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))
if (exportedCommands.TryGetValue(commandName, out CommandTypes exportedCommandTypes))
{
Exception exception;
discoveryTracer.WriteLine("Found in module: {0}", expandedModulePath);
Collection<PSModuleInfo> matchingModule = AutoloadSpecifiedModule(expandedModulePath, context,
Collection<PSModuleInfo> matchingModule = AutoloadSpecifiedModule(
expandedModulePath,
context,
cmdletInfo != null ? cmdletInfo.Visibility : SessionStateEntryVisibility.Private,
out exception);
lastError = exception;
if ((matchingModule == null) || (matchingModule.Count == 0))
out lastError);

if (matchingModule is null || matchingModule.Count == 0)
{
string error = StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModule, commandName, moduleShortName);
CommandNotFoundException commandNotFound = new CommandNotFoundException(
string errorMessage = lastError is null
? StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModule, commandName, moduleShortName)
: StringUtil.Format(DiscoveryExceptions.CouldNotAutoImportMatchingModuleWithErrorMessage, commandName, moduleShortName, lastError.Message);

throw new CommandNotFoundException(
originalCommandName,
lastError,
"CouldNotAutoloadMatchingModule", error);
throw commandNotFound;
"CouldNotAutoloadMatchingModule",
errorMessage);
}

result = LookupCommandInfo(commandName, commandTypes, searchResolutionOptions, commandOrigin, context);
Expand Down
65 changes: 13 additions & 52 deletions src/System.Management.Automation/engine/InitialSessionState.cs
Expand Up @@ -1663,11 +1663,6 @@ public InitialSessionState Clone()

ss.DisableFormatUpdates = this.DisableFormatUpdates;

foreach (var s in this.defaultSnapins)
{
ss.defaultSnapins.Add(s);
}

foreach (var s in ImportedSnapins)
{
ss.ImportedSnapins.Add(s.Key, s.Value);
Expand Down Expand Up @@ -3801,34 +3796,26 @@ public PSSnapInInfo ImportPSSnapIn(string name, out PSSnapInException warning)

// Now actually load the snapin...
PSSnapInInfo snapin = ImportPSSnapIn(newPSSnapIn, out warning);
if (snapin != null)
{
ImportedSnapins.Add(snapin.Name, snapin);
}

return snapin;
}

internal PSSnapInInfo ImportCorePSSnapIn()
{
// Load Microsoft.PowerShell.Core as a snapin
// Load Microsoft.PowerShell.Core as a snapin.
PSSnapInInfo coreSnapin = PSSnapInReader.ReadCoreEngineSnapIn();
this.defaultSnapins.Add(coreSnapin);
try
{
PSSnapInException warning;
this.ImportPSSnapIn(coreSnapin, out warning);
}
catch (PSSnapInException)
{
throw;
}

ImportPSSnapIn(coreSnapin, out _);
return coreSnapin;
}

internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInException warning)
{
if (psSnapInInfo == null)
{
ArgumentNullException e = new ArgumentNullException(nameof(psSnapInInfo));
throw e;
}

// See if the snapin is already loaded. If has been then there will be an entry in the
// Assemblies list for it already...
bool reload = true;
Expand Down Expand Up @@ -3861,12 +3848,6 @@ internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInExce
Dictionary<string, List<SessionStateAliasEntry>> aliases = null;
Dictionary<string, SessionStateProviderEntry> providers = null;

if (psSnapInInfo == null)
{
ArgumentNullException e = new ArgumentNullException(nameof(psSnapInInfo));
throw e;
}

Assembly assembly = null;
string helpFile = null;

Expand Down Expand Up @@ -3985,37 +3966,18 @@ internal PSSnapInInfo ImportPSSnapIn(PSSnapInInfo psSnapInInfo, out PSSnapInExce
}
}

ImportedSnapins.Add(psSnapInInfo.Name, psSnapInInfo);
return psSnapInInfo;
}

internal List<PSSnapInInfo> GetPSSnapIn(string psSnapinName)
internal PSSnapInInfo GetPSSnapIn(string psSnapinName)
{
List<PSSnapInInfo> loadedSnapins = null;
foreach (var defaultSnapin in defaultSnapins)
{
if (defaultSnapin.Name.Equals(psSnapinName, StringComparison.OrdinalIgnoreCase))
{
if (loadedSnapins == null)
{
loadedSnapins = new List<PSSnapInInfo>();
}

loadedSnapins.Add(defaultSnapin);
}
}

PSSnapInInfo importedSnapin = null;
if (ImportedSnapins.TryGetValue(psSnapinName, out importedSnapin))
if (ImportedSnapins.TryGetValue(psSnapinName, out PSSnapInInfo importedSnapin))
{
if (loadedSnapins == null)
{
loadedSnapins = new List<PSSnapInInfo>();
}

loadedSnapins.Add(importedSnapin);
return importedSnapin;
}

return loadedSnapins;
return null;
}

[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom")]
Expand Down Expand Up @@ -4895,7 +4857,6 @@ internal static void RemoveAllDrivesForProvider(ProviderInfo pi, SessionStateInt

internal static readonly string CoreSnapin = "Microsoft.PowerShell.Core";
internal static readonly string CoreModule = "Microsoft.PowerShell.Core";
internal Collection<PSSnapInInfo> defaultSnapins = new Collection<PSSnapInInfo>();

// The list of engine modules to create warnings when you try to remove them
internal static readonly HashSet<string> EngineModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
Expand Down

0 comments on commit cce64f4

Please sign in to comment.