Make 'Start-Job' throw terminating error when PowerShell is being hosted (#9128)

This commit is contained in:
Dongbo Wang
2019-03-21 14:14:23 -07:00
committed by Travis Plunk
parent ea4bf50623
commit 775a2ebbdf
5 changed files with 78 additions and 27 deletions
@@ -14,17 +14,18 @@ namespace System.Management.Automation.Runspaces
/// </summary>
public sealed class PowerShellProcessInstance : IDisposable
{
#region Private Members
#region Fields
private readonly ProcessStartInfo _startInfo;
private static readonly string s_PSExePath;
private RunspacePool _runspacePool;
private readonly object _syncObject = new object();
private bool _started;
private bool _isDisposed;
private bool _processExited;
#endregion Private Members
internal static readonly string PwshExePath;
#endregion Fields
#region Constructors
@@ -33,11 +34,9 @@ namespace System.Management.Automation.Runspaces
static PowerShellProcessInstance()
{
#if UNIX
s_PSExePath = Path.Combine(Utils.DefaultPowerShellAppBase,
"pwsh");
PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh");
#else
s_PSExePath = Path.Combine(Utils.DefaultPowerShellAppBase,
"pwsh.exe");
PwshExePath = Path.Combine(Utils.DefaultPowerShellAppBase, "pwsh.exe");
#endif
}
@@ -49,16 +48,16 @@ namespace System.Management.Automation.Runspaces
/// <param name="useWow64"></param>
public PowerShellProcessInstance(Version powerShellVersion, PSCredential credential, ScriptBlock initializationScript, bool useWow64)
{
string psWow64Path = s_PSExePath;
string psWow64Path = PwshExePath;
if (useWow64)
{
string procArch = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
if ((!string.IsNullOrEmpty(procArch)) && (procArch.Equals("amd64", StringComparison.OrdinalIgnoreCase) ||
procArch.Equals("ia64", StringComparison.OrdinalIgnoreCase)))
if (!string.IsNullOrEmpty(procArch) && (procArch.Equals("amd64", StringComparison.OrdinalIgnoreCase) ||
procArch.Equals("ia64", StringComparison.OrdinalIgnoreCase)))
{
psWow64Path = s_PSExePath.ToLowerInvariant().Replace("\\system32\\", "\\syswow64\\");
psWow64Path = PwshExePath.ToLowerInvariant().Replace("\\system32\\", "\\syswow64\\");
if (!File.Exists(psWow64Path))
{
@@ -71,21 +70,7 @@ namespace System.Management.Automation.Runspaces
}
}
#if CORECLR
string processArguments = " -s -NoLogo -NoProfile";
#else
// Adding Version parameter to powershell
// Version parameter needs to go before all other parameters because the native layer looks for Version or
// PSConsoleFile parameters before parsing other parameters.
// The other parameters get parsed in the managed layer.
Version tempVersion = powerShellVersion ?? PSVersionInfo.PSVersion;
string processArguments = string.Format(CultureInfo.InvariantCulture,
"-Version {0}", new Version(tempVersion.Major, tempVersion.Minor));
processArguments = string.Format(CultureInfo.InvariantCulture,
"{0} -s -NoLogo -NoProfile", processArguments);
#endif
if (initializationScript != null)
{
@@ -103,7 +88,7 @@ namespace System.Management.Automation.Runspaces
// to 'false' in our use, we can ignore the 'WindowStyle' setting in the initialization below.
_startInfo = new ProcessStartInfo
{
FileName = useWow64 ? psWow64Path : s_PSExePath,
FileName = useWow64 ? psWow64Path : PwshExePath,
Arguments = processArguments,
UseShellExecute = false,
RedirectStandardInput = true,
@@ -781,7 +781,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
[Parameter(ValueFromPipelineByPropertyName = true,
ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
public string Subsystem { get; set; }
public virtual string Subsystem { get; set; }
#endregion
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
using System.IO;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Management.Automation.Remoting;
@@ -197,6 +198,22 @@ namespace Microsoft.PowerShell.Commands
get { return null; }
}
/// <summary>
/// Suppress HostName.
/// </summary>
public override string[] HostName
{
get { return null; }
}
/// <summary>
/// Suppress Subsystem.
/// </summary>
public override string Subsystem
{
get { return null; }
}
#endregion
/// <summary>
@@ -548,6 +565,25 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
protected override void BeginProcessing()
{
if (!File.Exists(PowerShellProcessInstance.PwshExePath))
{
// The pwsh executable file is not found under $PSHOME.
// This means that PowerShell is currently being hosted in another application,
// and 'Start-Job' is not supported by design in that scenario.
string message = StringUtil.Format(
RemotingErrorIdStrings.IPCPwshExecutableNotFound,
PowerShellProcessInstance.PwshExePath);
var exception = new PSNotSupportedException(message);
var errorRecord = new ErrorRecord(
exception,
"IPCPwshExecutableNotFound",
ErrorCategory.NotInstalled,
PowerShellProcessInstance.PwshExePath);
ThrowTerminatingError(errorRecord);
}
CommandDiscovery.AutoloadModulesWithJobSourceAdapters(this.Context, this.CommandOrigin);
if (ParameterSetName == DefinitionNameParameterSet)
@@ -787,6 +787,10 @@ Do you want to continue?</value>
<data name="IPCSupportsOnlyDefaultAuth" xml:space="preserve">
<value>The specified authentication mechanism "{0}" is not supported. Only "{1}" is supported for this operation.</value>
</data>
<data name="IPCPwshExecutableNotFound" xml:space="preserve">
<value>The pwsh executable cannot be found at "{0}".
Note that 'Start-Job' is not supported by design in scenarios where PowerShell is being hosted in other applications. Instead, usage of the 'ThreadJob' module is recommended in such scenarios.</value>
</data>
<data name="IPCWowComponentNotPresent" xml:space="preserve">
<value>The "{0}" executable file was not found. Verify that the WOW64 feature is installed.</value>
</data>
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System;
using System.Management.Automation;
using Xunit;
namespace PSTests.Sequential
{
// Not static because a test requires non-const variables
public static class PowerShellHostingScenario
{
// Test that it does not throw an exception
[Fact]
public static void TestStartJobThrowTerminatingException()
{
using (var ps = PowerShell.Create())
{
ps.AddCommand("Start-Job").AddParameter("ScriptBlock", ScriptBlock.Create("1+1"));
var ex = Assert.Throws<CmdletInvocationException>(() => ps.Invoke());
Assert.IsType<PSNotSupportedException>(ex.InnerException);
Assert.Equal("IPCPwshExecutableNotFound,Microsoft.PowerShell.Commands.StartJobCommand", ex.ErrorRecord.FullyQualifiedErrorId);
}
}
}
}