-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Clean up ShellExecute code to use the .NET Core implementation #4523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82636e3
6ac56ab
f99c35a
7d375eb
5494c7f
16f459e
c7c9394
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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> | ||
|
|
@@ -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, | ||
|
|
@@ -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. | ||
|
|
@@ -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)) | ||
|
|
@@ -495,7 +527,6 @@ private void InitNativeProcess() | |
| throw; | ||
| } | ||
| } | ||
| #endif | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -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(); | ||
| } | ||
|
|
||
|
|
@@ -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; } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting - Can we use the one line pattern for
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
|
@@ -955,7 +978,6 @@ private static bool IsWindowsApplication(string fileName) | |
| // anything else - is a windows program... | ||
| return true; | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
| #endregion checkForConsoleApplication | ||
|
|
@@ -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(); | ||
|
|
@@ -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) | ||
| { | ||
|
|
@@ -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. | ||
|
|
@@ -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. | ||
|
|
@@ -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) | ||
|
|
@@ -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) | ||
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
|
||
|
|
@@ -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. | ||
|
|
@@ -1989,7 +2013,6 @@ public static void Hide() | |
| } | ||
| } | ||
| } | ||
| #endif | ||
|
|
||
| /// <summary> | ||
| /// Exception used to wrap the error coming from | ||
|
|
||
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.IsWindowsDesktopproperty.