@@ -304,20 +304,38 @@ internal override void Prepare(IDictionary psDefaultParameterValues) |
304 | 304 | this.NativeParameterBinderController.BindParameters(arguments); |
305 | 305 | } |
306 | 306 |
|
307 | | - InitNativeProcess(); |
| 307 | + try |
| 308 | + { |
| 309 | + InitNativeProcess(); |
| 310 | + } |
| 311 | + catch (Exception) |
| 312 | + { |
| 313 | + // Do cleanup in case of exception |
| 314 | + CleanUp(); |
| 315 | + throw; |
| 316 | + } |
308 | 317 | } |
309 | 318 |
|
310 | 319 | /// <summary> |
311 | 320 | /// Executes the command. This method assumes that Prepare is already called. |
312 | 321 | /// </summary> |
313 | 322 | internal override void ProcessRecord() |
314 | 323 | { |
315 | | - while (Read()) |
| 324 | + try |
316 | 325 | { |
317 | | - _inputWriter.Add(Command.CurrentPipelineObject); |
318 | | - } |
| 326 | + while (Read()) |
| 327 | + { |
| 328 | + _inputWriter.Add(Command.CurrentPipelineObject); |
| 329 | + } |
319 | 330 |
|
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 | + } |
321 | 339 | } |
322 | 340 |
|
323 | 341 | /// <summary> |
@@ -342,6 +360,12 @@ internal override void ProcessRecord() |
342 | 360 | /// </summary> |
343 | 361 | private bool _isRunningInBackground; |
344 | 362 |
|
| 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 | + |
345 | 369 | /// <summary> |
346 | 370 | /// This output queue helps us keep the output and error (if redirected) order correct. |
347 | 371 | /// We could do a blocking read in the Complete block instead, |
@@ -401,9 +425,10 @@ private void InitNativeProcess() |
401 | 425 | // If this process is being run standalone, tell the host, which might want |
402 | 426 | // to save off the window title or other such state as might be tweaked by |
403 | 427 | // the native process |
404 | | - if (!redirectOutput) |
| 428 | + if (_runStandAlone) |
405 | 429 | { |
406 | 430 | this.Command.Context.EngineHostInterface.NotifyBeginApplication(); |
| 431 | + _hasNotifiedBeginApplication = true; |
407 | 432 |
|
408 | 433 | // Also, store the Raw UI coordinates so that we can scrape the screen after |
409 | 434 | // if we are transcribing. |
@@ -436,21 +461,28 @@ private void InitNativeProcess() |
436 | 461 | throw new PipelineStoppedException(); |
437 | 462 | } |
438 | 463 |
|
| 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 | + |
439 | 474 | try |
440 | 475 | { |
441 | | - _nativeProcess = new Process(); |
442 | | - _nativeProcess.StartInfo = startInfo; |
| 476 | + _nativeProcess = new Process() { StartInfo = startInfo }; |
443 | 477 | _nativeProcess.Start(); |
444 | 478 | } |
445 | 479 | catch (Win32Exception) |
446 | 480 | { |
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. |
454 | 486 | string executable = FindExecutable(startInfo.FileName); |
455 | 487 | bool notDone = true; |
456 | 488 | if (!String.IsNullOrEmpty(executable)) |
@@ -495,7 +527,6 @@ private void InitNativeProcess() |
495 | 527 | throw; |
496 | 528 | } |
497 | 529 | } |
498 | | -#endif |
499 | 530 | } |
500 | 531 | } |
501 | 532 |
|
@@ -724,11 +755,7 @@ internal override void Complete() |
724 | 755 | } |
725 | 756 | finally |
726 | 757 | { |
727 | | - if (!_nativeProcess.StartInfo.RedirectStandardOutput) |
728 | | - { |
729 | | - this.Command.Context.EngineHostInterface.NotifyEndApplication(); |
730 | | - } |
731 | | - // Do the clean up... |
| 758 | + // Do some cleanup |
732 | 759 | CleanUp(); |
733 | 760 | } |
734 | 761 |
|
@@ -931,11 +958,7 @@ private static bool IsConsoleApplication(string fileName) |
931 | 958 | [ArchitectureSensitive] |
932 | 959 | private static bool IsWindowsApplication(string fileName) |
933 | 960 | { |
934 | | -#if UNIX |
935 | | - return false; |
936 | | -#else |
937 | | - if (Platform.IsNanoServer) |
938 | | - return false; |
| 961 | + if (!Platform.IsWindowsDesktop) { return false; } |
939 | 962 |
|
940 | 963 | SHFILEINFO shinfo = new SHFILEINFO(); |
941 | 964 | IntPtr type = SHGetFileInfo(fileName, 0, ref shinfo, (uint)Marshal.SizeOf(shinfo), SHGFI_EXETYPE); |
@@ -955,7 +978,6 @@ private static bool IsWindowsApplication(string fileName) |
955 | 978 | // anything else - is a windows program... |
956 | 979 | return true; |
957 | 980 | } |
958 | | -#endif |
959 | 981 | } |
960 | 982 |
|
961 | 983 | #endregion checkForConsoleApplication |
@@ -994,8 +1016,15 @@ internal void StopProcessing() |
994 | 1016 | /// </summary> |
995 | 1017 | private void CleanUp() |
996 | 1018 | { |
| 1019 | + // We need to call 'NotifyEndApplication' as appropriate during cleanup |
| 1020 | + if (_hasNotifiedBeginApplication) |
| 1021 | + { |
| 1022 | + this.Command.Context.EngineHostInterface.NotifyEndApplication(); |
| 1023 | + } |
| 1024 | + |
997 | 1025 | try |
998 | 1026 | { |
| 1027 | + // Dispose the process if it's already created |
999 | 1028 | if (_nativeProcess != null) |
1000 | 1029 | { |
1001 | 1030 | _nativeProcess.Dispose(); |
@@ -1104,11 +1133,14 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE |
1104 | 1133 | } |
1105 | 1134 | else |
1106 | 1135 | { |
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 | + |
1112 | 1144 | // We only want to ShellExecute something that is standalone... |
1113 | 1145 | if (!soloCommand) |
1114 | 1146 | { |
@@ -1117,7 +1149,6 @@ private ProcessStartInfo GetProcessStartInfo(bool redirectOutput, bool redirectE |
1117 | 1149 | } |
1118 | 1150 |
|
1119 | 1151 | startInfo.UseShellExecute = true; |
1120 | | -#endif |
1121 | 1152 | } |
1122 | 1153 |
|
1123 | 1154 | //For minishell value of -outoutFormat parameter depends on value of redirectOutput. |
@@ -1176,7 +1207,6 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr |
1176 | 1207 | redirectOutput = true; |
1177 | 1208 | redirectError = true; |
1178 | 1209 |
|
1179 | | - |
1180 | 1210 | // Figure out if we're going to run this process "standalone" i.e. without |
1181 | 1211 | // redirecting anything. This is a bit tricky as we always run redirected so |
1182 | 1212 | // 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 |
1243 | 1273 | redirectOutput = true; |
1244 | 1274 | redirectError = true; |
1245 | 1275 | } |
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)) |
1249 | 1277 | { |
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... |
1251 | 1280 | ConsoleVisibility.AllocateHiddenConsole(); |
1252 | 1281 |
|
1253 | 1282 | if (ConsoleVisibility.AlwaysCaptureApplicationIO) |
@@ -1256,9 +1285,8 @@ private void CalculateIORedirection(out bool redirectOutput, out bool redirectEr |
1256 | 1285 | redirectError = true; |
1257 | 1286 | } |
1258 | 1287 | } |
1259 | | -#endif |
1260 | | - if (!(redirectInput || redirectOutput)) |
1261 | | - _runStandAlone = true; |
| 1288 | + |
| 1289 | + _runStandAlone = !redirectInput && !redirectOutput && !redirectError; |
1262 | 1290 | } |
1263 | 1291 |
|
1264 | 1292 | private bool ValidateExtension(string path) |
@@ -1288,7 +1316,6 @@ private bool ValidateExtension(string path) |
1288 | 1316 | return false; |
1289 | 1317 | } |
1290 | 1318 |
|
1291 | | -#if !UNIX // Shell doesn't exist on OneCore, so documents cannot be associated with applications. |
1292 | 1319 | #region Interop for FindExecutable... |
1293 | 1320 |
|
1294 | 1321 | // Constant used to determine the buffer size for a path |
@@ -1368,7 +1395,6 @@ private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes |
1368 | 1395 | ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags); |
1369 | 1396 |
|
1370 | 1397 | #endregion |
1371 | | -#endif |
1372 | 1398 |
|
1373 | 1399 | #region Minishell Interop |
1374 | 1400 |
|
@@ -1843,8 +1869,6 @@ internal void Done() |
1843 | 1869 | } |
1844 | 1870 | } |
1845 | 1871 |
|
1846 | | -#if !CORECLR // There is no GUI application on OneCore, so powershell on OneCore should always have a console attached. |
1847 | | - |
1848 | 1872 | /// <summary> |
1849 | 1873 | /// Static class that allows you to show and hide the console window |
1850 | 1874 | /// associated with this process. |
@@ -1989,7 +2013,6 @@ public static void Hide() |
1989 | 2013 | } |
1990 | 2014 | } |
1991 | 2015 | } |
1992 | | -#endif |
1993 | 2016 |
|
1994 | 2017 | /// <summary> |
1995 | 2018 | /// Exception used to wrap the error coming from |
|