GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Clean up ShellExecute code to use the .NET Core implementation (#4523) · PowerShell/PowerShell@384a9fe

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • src
    • Microsoft.PowerShell.Commands.Management/commands/management
      • Process.cs
    • System.Management.Automation
      • CoreCLR
        • CorePsPlatform.cs
      • engine
        • NativeCommandProcessor.cs
        • Utils.cs
      • help
        • HelpCommands.cs
      • namespaces
        • FileSystemProvider.cs
  • test/powershell
    • Language/Scripting/NativeExecution
      • NativeCommandProcessor.Tests.ps1
    • Modules/Microsoft.PowerShell.Utility
      • Invoke-Item.Tests.ps1
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs‎

Copy file name to clipboardExpand all lines: src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs
+8-24Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,18 +1665,6 @@ public sealed class StartProcessCommand : PSCmdlet, IDisposable
16651665
private ManualResetEvent _waithandle = null;
16661666
private bool _isDefaultSetParameterSpecified = false;
16671667

1668-
private bool _useShellExecute;
1669-
private readonly bool _isOnFullWinSku;
1670-
1671-
/// <summary>
1672-
/// Constructor
1673-
/// </summary>
1674-
public StartProcessCommand()
1675-
{
1676-
_isOnFullWinSku = Platform.IsWindows && !Platform.IsNanoServer && !Platform.IsIoT;
1677-
_useShellExecute = _isOnFullWinSku;
1678-
}
1679-
16801668
#region Parameters
16811669

16821670
/// <summary>
@@ -1877,7 +1865,7 @@ protected override void BeginProcessing()
18771865
string message = string.Empty;
18781866

18791867
// -Verb and -WindowStyle are not supported on non-Windows platforms as well as Windows headless SKUs
1880-
if (_isOnFullWinSku)
1868+
if (Platform.IsWindowsDesktop)
18811869
{
18821870
// Parameters '-NoNewWindow' and '-WindowStyle' are both valid on full windows SKUs.
18831871
if (_nonewwindow && _windowstyleSpecified)
@@ -1908,6 +1896,8 @@ protected override void BeginProcessing()
19081896

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

19121902
//Path = Mandatory parameter -> Will not be empty.
19131903
try
@@ -1959,7 +1949,6 @@ protected override void BeginProcessing()
19591949
{
19601950
if (_isDefaultSetParameterSpecified)
19611951
{
1962-
_useShellExecute = false;
19631952
startInfo.UseShellExecute = false;
19641953
}
19651954

@@ -1970,10 +1959,10 @@ protected override void BeginProcessing()
19701959
LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine));
19711960
LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User));
19721961
}
1973-
#if !CORECLR
1962+
19741963
//WindowStyle
19751964
startInfo.WindowStyle = _windowstyle;
1976-
#endif
1965+
19771966
//NewWindow
19781967
if (_nonewwindow)
19791968
{
@@ -2054,15 +2043,14 @@ protected override void BeginProcessing()
20542043
}
20552044
}
20562045
}
2057-
#if !CORECLR // Properties 'Verb' and 'WindowStyle' are missing in CoreCLR
20582046
else if (ParameterSetName.Equals("UseShellExecute"))
20592047
{
20602048
//Verb
20612049
if (Verb != null) { startInfo.Verb = Verb; }
20622050
//WindowStyle
20632051
startInfo.WindowStyle = _windowstyle;
20642052
}
2065-
#endif
2053+
20662054
//Starts the Process
20672055
Process process = Start(startInfo);
20682056

@@ -2212,7 +2200,7 @@ private Process Start(ProcessStartInfo startInfo)
22122200
return process;
22132201
#else
22142202
Process process = null;
2215-
if (_useShellExecute)
2203+
if (startInfo.UseShellExecute)
22162204
{
22172205
process = StartWithShellExecute(startInfo);
22182206
}
@@ -2467,7 +2455,7 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo)
24672455
lpStartupInfo.dwFlags |= 0x00000001;
24682456

24692457
// On headless SKUs like NanoServer and IoT, window style can only be the default value 'Normal'.
2470-
switch (WindowStyle)
2458+
switch (startinfo.WindowStyle)
24712459
{
24722460
case ProcessWindowStyle.Normal:
24732461
//SW_SHOWNORMAL
@@ -2595,11 +2583,7 @@ private Process StartWithShellExecute(ProcessStartInfo startInfo)
25952583
Process result = null;
25962584
try
25972585
{
2598-
#if CORECLR
2599-
result = ShellExecuteHelper.Start(startInfo, WindowStyle, Verb);
2600-
#else
26012586
result = Process.Start(startInfo);
2602-
#endif
26032587
}
26042588
catch (Win32Exception ex)
26052589
{
Collapse file

‎src/System.Management.Automation/CoreCLR/CorePsPlatform.cs‎

Copy file name to clipboardExpand all lines: src/System.Management.Automation/CoreCLR/CorePsPlatform.cs
+19Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,29 @@ internal static bool IsInbox
148148
}
149149
}
150150

151+
/// <summary>
152+
/// True if underlying system is Windows Desktop.
153+
/// </summary>
154+
public static bool IsWindowsDesktop
155+
{
156+
get
157+
{
158+
#if UNIX
159+
return false;
160+
#else
161+
if (_isWindowsDesktop.HasValue) { return _isWindowsDesktop.Value; }
162+
163+
_isWindowsDesktop = !IsNanoServer && !IsIoT;
164+
return _isWindowsDesktop.Value;
165+
#endif
166+
}
167+
}
168+
151169
#if !UNIX
152170
private static bool? _isNanoServer = null;
153171
private static bool? _isIoT = null;
154172
private static bool? _isInbox = null;
173+
private static bool? _isWindowsDesktop = null;
155174
#endif
156175

157176
// format files
Collapse file

‎src/System.Management.Automation/engine/NativeCommandProcessor.cs‎

Copy file name to clipboardExpand all lines: src/System.Management.Automation/engine/NativeCommandProcessor.cs
+69-46Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -304,20 +304,38 @@ internal override void Prepare(IDictionary psDefaultParameterValues)
304304
this.NativeParameterBinderController.BindParameters(arguments);
305305
}
306306

307-
InitNativeProcess();
307+
try
308+
{
309+
InitNativeProcess();
310+
}
311+
catch (Exception)
312+
{
313+
// Do cleanup in case of exception
314+
CleanUp();
315+
throw;
316+
}
308317
}
309318

310319
/// <summary>
311320
/// Executes the command. This method assumes that Prepare is already called.
312321
/// </summary>
313322
internal override void ProcessRecord()
314323
{
315-
while (Read())
324+
try
316325
{
317-
_inputWriter.Add(Command.CurrentPipelineObject);
318-
}
326+
while (Read())
327+
{
328+
_inputWriter.Add(Command.CurrentPipelineObject);
329+
}
319330

320-
ConsumeAvailableNativeProcessOutput(blocking: false);
331+
ConsumeAvailableNativeProcessOutput(blocking: false);
332+
}
333+
catch (Exception)
334+
{
335+
// Do cleanup in case of exception
336+
CleanUp();
337+
throw;
338+
}
321339
}
322340

323341
/// <summary>
@@ -342,6 +360,12 @@ internal override void ProcessRecord()
342360
/// </summary>
343361
private bool _isRunningInBackground;
344362

363+
/// <summary>
364+
/// Indicate if we have called 'NotifyBeginApplication()' on the host, so that
365+
/// we can call the counterpart 'NotifyEndApplication' as approriate.
366+
/// </summary>
367+
private bool _hasNotifiedBeginApplication;
368+
345369
/// <summary>
346370
/// This output queue helps us keep the output and error (if redirected) order correct.
347371
/// We could do a blocking read in the Complete block instead,
@@ -401,9 +425,10 @@ private void InitNativeProcess()
401425
// If this process is being run standalone, tell the host, which might want
402426
// to save off the window title or other such state as might be tweaked by
403427
// the native process
404-
if (!redirectOutput)
428+
if (_runStandAlone)
405429
{
406430
this.Command.Context.EngineHostInterface.NotifyBeginApplication();
431+
_hasNotifiedBeginApplication = true;
407432

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

464+
if (!Platform.IsWindows && startInfo.UseShellExecute)
465+
{
466+
// UseShellExecute is not properly supported on Unix. It runs the file with '/bin/sh'.
467+
// Before the behavior is improved (tracked by dotnet/corefx#19956), we use xdg-open/open as the default programs
468+
string executable = Platform.IsLinux ? "xdg-open" : /* OS X */ "open";
469+
startInfo.Arguments = "\"" + startInfo.FileName + "\" " + startInfo.Arguments;
470+
startInfo.FileName = executable;
471+
startInfo.UseShellExecute = false;
472+
}
473+
439474
try
440475
{
441-
_nativeProcess = new Process();
442-
_nativeProcess.StartInfo = startInfo;
476+
_nativeProcess = new Process() { StartInfo = startInfo };
443477
_nativeProcess.Start();
444478
}
445479
catch (Win32Exception)
446480
{
447-
#if CORECLR // Shell doesn't exist on OneCore, so a file cannot be associated with an executable,
448-
// and we cannot run an executable as 'ShellExecute' either.
449-
throw;
450-
#else
451-
// See if there is a file association for this command. If so
452-
// then we'll use that. If there's no file association, then
453-
// try shell execute...
481+
// On Unix platforms, nothing can be further done, so just throw
482+
// On headless Windows SKUs, there is no shell to fall back to, so just throw
483+
if (!Platform.IsWindowsDesktop) { throw; }
484+
485+
// on Windows desktops, see if there is a file association for this command. If so then we'll use that.
454486
string executable = FindExecutable(startInfo.FileName);
455487
bool notDone = true;
456488
if (!String.IsNullOrEmpty(executable))
@@ -495,7 +527,6 @@ private void InitNativeProcess()
495527
throw;
496528
}
497529
}
498-
#endif
499530
}
500531
}
501532

@@ -724,11 +755,7 @@ internal override void Complete()
724755
}
725756
finally
726757
{
727-
if (!_nativeProcess.StartInfo.RedirectStandardOutput)
728-
{
729-
this.Command.Context.EngineHostInterface.NotifyEndApplication();
730-
}
731-
// Do the clean up...
758+
// Do some cleanup
732759
CleanUp();
733760
}
734761

@@ -931,11 +958,7 @@ private static bool IsConsoleApplication(string fileName)
931958
[ArchitectureSensitive]
932959
private static bool IsWindowsApplication(string fileName)
933960
{
934-
#if UNIX
935-
return false;
936-
#else
937-
if (Platform.IsNanoServer)
938-
return false;
961+
if (!Platform.IsWindowsDesktop) { return false; }
939962

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

961983
#endregion checkForConsoleApplication
@@ -994,8 +1016,15 @@ internal void StopProcessing()
9941016
/// </summary>
9951017
private void CleanUp()
9961018
{
1019+
// We need to call 'NotifyEndApplication' as appropriate during cleanup
1020+
if (_hasNotifiedBeginApplication)
1021+
{
1022+
this.Command.Context.EngineHostInterface.NotifyEndApplication();
1023+
}
1024+
9971025
try
9981026
{
1027+
// Dispose the process if it's already created
9991028
if (_nativeProcess != null)
10001029
{
10011030
_nativeProcess.Dispose();
@@ -1104,11 +1133,14 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE
11041133
}
11051134
else
11061135
{
1107-
#if CORECLR // Shell doesn't exist on OneCore, so documents cannot be associated with an application.
1108-
// Therefore, we cannot run document directly on OneCore.
1109-
throw InterpreterError.NewInterpreterException(this.Path, typeof(RuntimeException),
1110-
this.Command.InvocationExtent, "CantActivateDocumentInPowerShellCore", ParserStrings.CantActivateDocumentInPowerShellCore, this.Path);
1111-
#else
1136+
if (Platform.IsNanoServer || Platform.IsIoT)
1137+
{
1138+
// Shell doesn't exist on headless SKUs, so documents cannot be associated with an application.
1139+
// Therefore, we cannot run document in this case.
1140+
throw InterpreterError.NewInterpreterException(this.Path, typeof(RuntimeException),
1141+
this.Command.InvocationExtent, "CantActivateDocumentInPowerShellCore", ParserStrings.CantActivateDocumentInPowerShellCore, this.Path);
1142+
}
1143+
11121144
// We only want to ShellExecute something that is standalone...
11131145
if (!soloCommand)
11141146
{
@@ -1117,7 +1149,6 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE
11171149
}
11181150

11191151
startInfo.UseShellExecute = true;
1120-
#endif
11211152
}
11221153

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

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

12531282
if (ConsoleVisibility.AlwaysCaptureApplicationIO)
@@ -1256,9 +1285,8 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr
12561285
redirectError = true;
12571286
}
12581287
}
1259-
#endif
1260-
if (!(redirectInput || redirectOutput))
1261-
_runStandAlone = true;
1288+
1289+
_runStandAlone = !redirectInput && !redirectOutput && !redirectError;
12621290
}
12631291

12641292
private bool ValidateExtension(string path)
@@ -1288,7 +1316,6 @@ private bool ValidateExtension(string path)
12881316
return false;
12891317
}
12901318

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

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

13701397
#endregion
1371-
#endif
13721398

13731399
#region Minishell Interop
13741400

@@ -1843,8 +1869,6 @@ internal void Done()
18431869
}
18441870
}
18451871

1846-
#if !CORECLR // There is no GUI application on OneCore, so powershell on OneCore should always have a console attached.
1847-
18481872
/// <summary>
18491873
/// Static class that allows you to show and hide the console window
18501874
/// associated with this process.
@@ -1989,7 +2013,6 @@ public static void Hide()
19892013
}
19902014
}
19912015
}
1992-
#endif
19932016

19942017
/// <summary>
19952018
/// Exception used to wrap the error coming from

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.