1

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.'

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?

6
  • 6
    Powershell files aren't like .bat files - you have to feed the .ps1 file into the powershell executable. From an OS level, you don't run a ps1 file, you just run powershell.exe with that script as an argument. If you change your code so that it runs powershell with your ps1 file as an arg it should work. Commented Dec 5, 2022 at 17:35
  • do you mean to use proc.StartInfo.FileName = @"powershell.exe 'absolute/path/to/test.ps1'";? Weirdly it says The system cannot find the file specified.' Do you know why is that? Commented Dec 5, 2022 at 17:43
  • 2
    FileName should be just the path to the executable you want, so it'll be the location of powershell.exe on your computer. Check out this answer for an example: stackoverflow.com/a/30846513/6457336 Commented Dec 5, 2022 at 17:48
  • There are two executables for powershell 1) ps.exe (command line) 2) powershell.exe (interactive) They can be found in following folder : C:\Windows\System32\WindowsPowerShell Commented Dec 5, 2022 at 18:15
  • 1
    @jdweng, there is no ps.exe. For Windows PowerShell, the single CLI - both for interactive and automated invocations is powershell.exe (C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe). For PowerShell (Core), the cross-platform, install-on-demand PowerShell edition, the single CLI is pwsh.exe (Windows) / pwsh (Unix-like platforms). While there are standard installation directories if you use the official installers, you're free to install it anywhere, possibly multiple versions side by side. Commented Dec 5, 2022 at 18:35

1 Answer 1

1

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;
}
Sign up to request clarification or add additional context in comments.

6 Comments

why are batch files still the only script-type file that are treated as executable files? I thought that Microsoft is trying to replace CMD with PowerShell, so at least ps1 script should also be treated as executable files?
@Ooker, that's a good question, but it seems that Microsoft went the opposite direction with PowerShell right from the beginning: you also cannot run .ps1 files by opening (double-clicking) them in File Explorer / on the desktop, even though that works with batch files. If this were to change, also at the WinAPI level - my guess is it that it won't - an additional conceptual challenge is that now there are two PowerShell editions, so the system would have to default to the edition that is guaranteed to be there, which is the legacy, maintenance-only edition, namely Windows PowerShell
Is there any reason for not making ps1 files as executables at the beginning?
@Ooker, I presume the reason is the same as not allowing execution of .ps1 script files by default even from inside PowerShell (on client editions of Windows), based on execution policies (which your CLI call is also subject to). That is, Microsoft apparently deems execution of .ps1 script files risky, requiring deliberate action to enable it.
@Ooker, it fundamentally isn't, and I can't speak to what caused the change of heart (PowerShell came along much, much later than cmd.exe). I can only speculate that perhaps they thought that PowerShell's advanced capabilities make it easier to do nefarious things.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.