Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1665,18 +1665,6 @@ public sealed class StartProcessCommand : PSCmdlet, IDisposable
private ManualResetEvent _waithandle = null;
private bool _isDefaultSetParameterSpecified = false;

private bool _useShellExecute;
private readonly bool _isOnFullWinSku;

/// <summary>
/// Constructor
/// </summary>
public StartProcessCommand()
{
_isOnFullWinSku = Platform.IsWindows && !Platform.IsNanoServer && !Platform.IsIoT;
_useShellExecute = _isOnFullWinSku;
}

#region Parameters

/// <summary>
Expand Down Expand Up @@ -1877,7 +1865,7 @@ protected override void BeginProcessing()
string message = string.Empty;

// -Verb and -WindowStyle are not supported on non-Windows platforms as well as Windows headless SKUs
if (_isOnFullWinSku)
if (Platform.IsWindowsDesktop)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe cache the value in a variable instead of looking it up twice in the same method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the code to cache the value in Platform.IsWindowsDesktop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daxian-dbw This does not seem to be fixed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value is cached in a static field in Platform.IsWindowsDesktop property.

{
// Parameters '-NoNewWindow' and '-WindowStyle' are both valid on full windows SKUs.
if (_nonewwindow && _windowstyleSpecified)
Expand Down Expand Up @@ -1908,6 +1896,8 @@ protected override void BeginProcessing()

//create an instance of the ProcessStartInfo Class
ProcessStartInfo startInfo = new ProcessStartInfo();
//use ShellExecute by default if we are running on full windows SKUs
startInfo.UseShellExecute = Platform.IsWindowsDesktop;

//Path = Mandatory parameter -> Will not be empty.
try
Expand Down Expand Up @@ -1959,7 +1949,6 @@ protected override void BeginProcessing()
{
if (_isDefaultSetParameterSpecified)
{
_useShellExecute = false;
startInfo.UseShellExecute = false;
}

Expand All @@ -1970,10 +1959,10 @@ protected override void BeginProcessing()
LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine));
LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User));
}
#if !CORECLR

//WindowStyle
startInfo.WindowStyle = _windowstyle;
#endif

//NewWindow
if (_nonewwindow)
{
Expand Down Expand Up @@ -2054,15 +2043,14 @@ protected override void BeginProcessing()
}
}
}
#if !CORECLR // Properties 'Verb' and 'WindowStyle' are missing in CoreCLR
else if (ParameterSetName.Equals("UseShellExecute"))
{
//Verb
if (Verb != null) { startInfo.Verb = Verb; }
//WindowStyle
startInfo.WindowStyle = _windowstyle;
}
#endif

//Starts the Process
Process process = Start(startInfo);

Expand Down Expand Up @@ -2212,7 +2200,7 @@ private Process Start(ProcessStartInfo startInfo)
return process;
#else
Process process = null;
if (_useShellExecute)
if (startInfo.UseShellExecute)
{
process = StartWithShellExecute(startInfo);
}
Expand Down Expand Up @@ -2467,7 +2455,7 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo)
lpStartupInfo.dwFlags |= 0x00000001;

// On headless SKUs like NanoServer and IoT, window style can only be the default value 'Normal'.
switch (WindowStyle)
switch (startinfo.WindowStyle)
{
case ProcessWindowStyle.Normal:
//SW_SHOWNORMAL
Expand Down Expand Up @@ -2595,11 +2583,7 @@ private Process StartWithShellExecute(ProcessStartInfo startInfo)
Process result = null;
try
{
#if CORECLR
result = ShellExecuteHelper.Start(startInfo, WindowStyle, Verb);
#else
result = Process.Start(startInfo);
#endif
}
catch (Win32Exception ex)
{
Expand Down
19 changes: 19 additions & 0 deletions src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,29 @@ internal static bool IsInbox
}
}

/// <summary>
/// True if underlying system is Windows Desktop.
/// </summary>
public static bool IsWindowsDesktop
{
get
{
#if UNIX
return false;
#else
if (_isWindowsDesktop.HasValue) { return _isWindowsDesktop.Value; }

_isWindowsDesktop = !IsNanoServer && !IsIoT;
return _isWindowsDesktop.Value;
#endif
}
}

#if !UNIX
private static bool? _isNanoServer = null;
private static bool? _isIoT = null;
private static bool? _isInbox = null;
private static bool? _isWindowsDesktop = null;
#endif

// format files
Expand Down
115 changes: 69 additions & 46 deletions src/System.Management.Automation/engine/NativeCommandProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -304,20 +304,38 @@ internal override void Prepare(IDictionary psDefaultParameterValues)
this.NativeParameterBinderController.BindParameters(arguments);
}

InitNativeProcess();
try
{
InitNativeProcess();
}
catch (Exception)
{
// Do cleanup in case of exception
CleanUp();
throw;
}
}

/// <summary>
/// Executes the command. This method assumes that Prepare is already called.
/// </summary>
internal override void ProcessRecord()
{
while (Read())
try
{
_inputWriter.Add(Command.CurrentPipelineObject);
}
while (Read())
{
_inputWriter.Add(Command.CurrentPipelineObject);
}

ConsumeAvailableNativeProcessOutput(blocking: false);
ConsumeAvailableNativeProcessOutput(blocking: false);
}
catch (Exception)
{
// Do cleanup in case of exception
CleanUp();
throw;
}
}

/// <summary>
Expand All @@ -342,6 +360,12 @@ internal override void ProcessRecord()
/// </summary>
private bool _isRunningInBackground;

/// <summary>
/// Indicate if we have called 'NotifyBeginApplication()' on the host, so that
/// we can call the counterpart 'NotifyEndApplication' as approriate.
/// </summary>
private bool _hasNotifiedBeginApplication;

/// <summary>
/// This output queue helps us keep the output and error (if redirected) order correct.
/// We could do a blocking read in the Complete block instead,
Expand Down Expand Up @@ -401,9 +425,10 @@ private void InitNativeProcess()
// If this process is being run standalone, tell the host, which might want
// to save off the window title or other such state as might be tweaked by
// the native process
if (!redirectOutput)
if (_runStandAlone)
{
this.Command.Context.EngineHostInterface.NotifyBeginApplication();
_hasNotifiedBeginApplication = true;

// Also, store the Raw UI coordinates so that we can scrape the screen after
// if we are transcribing.
Expand Down Expand Up @@ -436,21 +461,28 @@ private void InitNativeProcess()
throw new PipelineStoppedException();
}

if (!Platform.IsWindows && startInfo.UseShellExecute)
{
// UseShellExecute is not properly supported on Unix. It runs the file with '/bin/sh'.
// Before the behavior is improved (tracked by dotnet/corefx#19956), we use xdg-open/open as the default programs
string executable = Platform.IsLinux ? "xdg-open" : /* OS X */ "open";
startInfo.Arguments = "\"" + startInfo.FileName + "\" " + startInfo.Arguments;
startInfo.FileName = executable;
startInfo.UseShellExecute = false;
}

try
{
_nativeProcess = new Process();
_nativeProcess.StartInfo = startInfo;
_nativeProcess = new Process() { StartInfo = startInfo };
_nativeProcess.Start();
}
catch (Win32Exception)
{
#if CORECLR // Shell doesn't exist on OneCore, so a file cannot be associated with an executable,
// and we cannot run an executable as 'ShellExecute' either.
throw;
#else
// See if there is a file association for this command. If so
// then we'll use that. If there's no file association, then
// try shell execute...
// On Unix platforms, nothing can be further done, so just throw
// On headless Windows SKUs, there is no shell to fall back to, so just throw
if (!Platform.IsWindowsDesktop) { throw; }

// on Windows desktops, see if there is a file association for this command. If so then we'll use that.
string executable = FindExecutable(startInfo.FileName);
bool notDone = true;
if (!String.IsNullOrEmpty(executable))
Expand Down Expand Up @@ -495,7 +527,6 @@ private void InitNativeProcess()
throw;
}
}
#endif
}
}

Expand Down Expand Up @@ -724,11 +755,7 @@ internal override void Complete()
}
finally
{
if (!_nativeProcess.StartInfo.RedirectStandardOutput)
{
this.Command.Context.EngineHostInterface.NotifyEndApplication();
}
// Do the clean up...
// Do some cleanup
CleanUp();
}

Expand Down Expand Up @@ -931,11 +958,7 @@ private static bool IsConsoleApplication(string fileName)
[ArchitectureSensitive]
private static bool IsWindowsApplication(string fileName)
{
#if UNIX
return false;
#else
if (Platform.IsNanoServer)
return false;
if (!Platform.IsWindowsDesktop) { return false; }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting - Can we use the one line pattern for if?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, as long as the if block is a single simple statement like this one.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closed.


SHFILEINFO shinfo = new SHFILEINFO();
IntPtr type = SHGetFileInfo(fileName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_EXETYPE);
Expand All @@ -955,7 +978,6 @@ private static bool IsWindowsApplication(string fileName)
// anything else - is a windows program...
return true;
}
#endif
}

#endregion checkForConsoleApplication
Expand Down Expand Up @@ -994,8 +1016,15 @@ internal void StopProcessing()
/// </summary>
private void CleanUp()
{
// We need to call 'NotifyEndApplication' as appropriate during cleanup
if (_hasNotifiedBeginApplication)
{
this.Command.Context.EngineHostInterface.NotifyEndApplication();
}

try
{
// Dispose the process if it's already created
if (_nativeProcess != null)
{
_nativeProcess.Dispose();
Expand Down Expand Up @@ -1104,11 +1133,14 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE
}
else
{
#if CORECLR // Shell doesn't exist on OneCore, so documents cannot be associated with an application.
// Therefore, we cannot run document directly on OneCore.
throw InterpreterError.NewInterpreterException(this.Path, typeof(RuntimeException),
this.Command.InvocationExtent, "CantActivateDocumentInPowerShellCore", ParserStrings.CantActivateDocumentInPowerShellCore, this.Path);
#else
if (Platform.IsNanoServer || Platform.IsIoT)
{
// Shell doesn't exist on headless SKUs, so documents cannot be associated with an application.
// Therefore, we cannot run document in this case.
throw InterpreterError.NewInterpreterException(this.Path, typeof(RuntimeException),
this.Command.InvocationExtent, "CantActivateDocumentInPowerShellCore", ParserStrings.CantActivateDocumentInPowerShellCore, this.Path);
}

// We only want to ShellExecute something that is standalone...
if (!soloCommand)
{
Expand All @@ -1117,7 +1149,6 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE
}

startInfo.UseShellExecute = true;
#endif
}

//For minishell value of -outoutFormat parameter depends on value of redirectOutput.
Expand Down Expand Up @@ -1176,7 +1207,6 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr
redirectOutput = true;
redirectError = true;


// Figure out if we're going to run this process "standalone" i.e. without
// redirecting anything. This is a bit tricky as we always run redirected so
// we have to see if the redirection is actually being done at the topmost level or not.
Expand Down Expand Up @@ -1243,11 +1273,10 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr
redirectOutput = true;
redirectError = true;
}
#if !CORECLR // UI doesn't exist on OneCore, so all applications running on an OneCore client should be console applications.
// The powershell on the OneCore client should already have a console attached.
else if (IsConsoleApplication(this.Path))
else if (Platform.IsWindowsDesktop && IsConsoleApplication(this.Path))
{
// Allocate a console if there isn't one attached already...
// On Windows desktops, if the command to run is a console application,
// then allocate a console if there isn't one attached already...
ConsoleVisibility.AllocateHiddenConsole();

if (ConsoleVisibility.AlwaysCaptureApplicationIO)
Expand All @@ -1256,9 +1285,8 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr
redirectError = true;
}
}
#endif
if (!(redirectInput || redirectOutput))
_runStandAlone = true;

_runStandAlone = !redirectInput && !redirectOutput && !redirectError;
}

private bool ValidateExtension(string path)
Expand Down Expand Up @@ -1288,7 +1316,6 @@ private bool ValidateExtension(string path)
return false;
}

#if !UNIX // Shell doesn't exist on OneCore, so documents cannot be associated with applications.
#region Interop for FindExecutable...

// Constant used to determine the buffer size for a path
Expand Down Expand Up @@ -1368,7 +1395,6 @@ private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes
ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

#endregion
#endif

#region Minishell Interop

Expand Down Expand Up @@ -1843,8 +1869,6 @@ internal void Done()
}
}

#if !CORECLR // There is no GUI application on OneCore, so powershell on OneCore should always have a console attached.

/// <summary>
/// Static class that allows you to show and hide the console window
/// associated with this process.
Expand Down Expand Up @@ -1989,7 +2013,6 @@ public static void Hide()
}
}
}
#endif

/// <summary>
/// Exception used to wrap the error coming from
Expand Down
Loading