简体   繁体   中英

Why does process.start() only accept batch, not PowerShell script?

In this script, if I use a batch file, it works:

 private async void cmdRunBatchFile2_Click(object sender, EventArgs e)
        {
            cmdRunBatchFile2.Enabled = false;
            await Task.Run(() => {
                var proc = new Process();
                proc.StartInfo.FileName = @"test.bat";
                proc.StartInfo.RedirectStandardOutput = true;
                proc.StartInfo.RedirectStandardError = true;
                proc.StartInfo.CreateNoWindow = true;
                if (proc.Start())
                {
                //do something
                }
                proc.WaitForExit();
            });
            cmdRunBatchFile2.Enabled = true;
        }

However if I change it to test.ps1 , it returns this error: System.ComponentModel.Win32Exception: 'An error occurred trying to start process 'test.ps1' with working directory XYZ. The specified executable is not a valid application for this OS platform.' System.ComponentModel.Win32Exception: 'An error occurred trying to start process 'test.ps1' with working directory XYZ. The specified executable is not a valid application for this OS platform.'

After reading .Net Core 2.0 Process.Start throws "The specified executable is not a valid application for this OS platform" , I then blindly try adding

proc.StartInfo.UseShellExecute = true;

This yields another error: System.InvalidOperationException: 'The Process object must have the UseShellExecute property set to false in order to redirect IO streams.'

Do you know why is that?

First things first: An alternative , more efficient and flexible way to execute PowerShell code from a .NET executable is to use the PowerShell SDK , which enables in-process execution with full .NET type support - see this answer for an example.


Batch files ( .bat , .cmd files) have special status on Windows : They are the only script -type files that the Windows API - which underlies the .NET APIs - treats as if they were directly executable, binary files , by implicitly passing such files to cmd.exe /c for execution.

All other script files , which by definition require a scripting engine (shell) to execute, can not be executed this way, and instead you must specify their associated scripting engine / shell CLI as the executable in the .FileName property of the System.Diagnostics.ProcessStartInfo class, followed by the appropriate engine / shell-specific command-line parameter that requests execution of a script file, if necessary, and the script file's path itself.

  • Note:
    • If you use .UseShellExecute = true , you'll lose the ability to capture the launched process' output and to control its window (creation) style.
    • With .UseShellExecute = true , you can specify a script file directly in .FileName , but there's no guarantee that it will execute , given that is the default GUI shell operation that is then performed (which never runs in the current console window), which for script files often means opening them for editing .

In order to execute a .ps1 script via powershell.exe , the Windows PowerShell CLI, use powershell.exe -file .

Therefore:

private async void cmdRunBatchFile2_Click(object sender, EventArgs e)
{
  cmdRunPs1File.Enabled = false;
  await Task.Run(() =>
  {
    var proc = new Process();
    // Specify the PowerShell CLI as the executable.
    proc.StartInfo.FileName = @"powershell.exe";
    // Specify arguments, i.e. the .ps1 script to invoke, via -File.
    // Note: This assumes that 'test.ps1` is located in the process' current dir.
    // Consider placing '-NoProfile' before '-File', to suppress
    // profile loading, both for speed and a predictable execution environment.
    proc.Start.Info.Arguments = @"-File test.ps1";
    // !! .UseShellExecute must be false in order to be able to 
    // !! capture output in memory and to use .CreateNoWindow = true
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;
    proc.StartInfo.CreateNoWindow = true;
    if (proc.Start())
    {
      //do something
    }
    proc.WaitForExit();
  });
  cmdRunPs1File.Enabled = true;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM