Exit code 0 does not show the result and passing argument for ps1 file in c#

Viewed 189

I am trying to run one of the feature of Windows Subsystem for Linux (WSL) via PowerShell in C# code. Even though, there is no error, I cannot see the result. In order to fix it, I have read and implemented some suggestion in other stackoverflow questions but still i cannot see the result. Another question is that is it possible to pass argument for following code. In MS website, there is no example regarding passing argument. I hope there is a way to do it.

Code is here:

PowerShell ps = PowerShell.Create();
ps.AddScript(File.ReadAllText(@"C:\Users\username\Desktop\test.ps1"));
ps.Invoke();  

The output of the Debug is :

The thread 0x4034 has exited with code 0 (0x0).
The thread 0x4b34 has exited with code 0 (0x0).
The thread 0x4f0 has exited with code 0 (0x0).
The program '[18876] WSL_CS.exe' has exited with code 0 (0x0).

But command prompt opened and closed very quickly.

In PowerShell, when I run the following code I can see the result.

powershell -ExecutionPolicy Bypass -File test.ps1 5
1 Answers

If you want to launch a visible console window that executes a script file and stays open (enters an interactive session after the script exits):

  • Do NOT use the PowerShell SDK and its PowerShell .NET class: Its purpose is programmatic control of PowerShell commands, not visible, interactive execution.

  • Instead, use the general-purpose Process class to create a child process that launches the PowerShell CLI in a visible console window (by default):

System.Diagnostics.Process.Start(
  "powershell.exe",  // The Windows PowerShell CLI
  @"-ExecutionPolicy Bypass -NoExit -NoLogo -File C:\Users\username\Desktop\test.ps1 5"
).WaitForExit();

Note how the argument(s) to pass to the script file are specified after the script-file path (5 in this example) - be sure to double-quote them as needed, such as when they contain embedded spaces.

Since everything after a -File (-f) is interpreted as the script-file path and the arguments to pass to the script, be sure to place these arguments last on the command line.

Alternatively, if you're creating a .NET Core / .NET 5+ application, you may specify all arguments individually, in which case .NET takes care of any required double-quoting for you:

System.Diagnostics.Process.Start(
  "powershell.exe",  // The Windows PowerShell CLI
   new string[] {
     "-ExecutionPolicy",
     "Bypass",
     "-NoExit",
     "-NoLogo",
     "-File",
     @"C:\Users\jdoe\Desktop\pg\pg.ps1",
     "5"  // Pass-through arguments.
   }
).WaitForExit();

Pitfall:

  • -NoExit is not honored if the script file passed to -File cannot be found.
    • This contrasts with passing a piece of PowerShell source code to the -Command (-c) parameter, where -NoExit is honored, even if execution of the source code results in errors).
    • This discrepancy is arguably a bug and has been reported in GitHub issue #10471.

The upshot is that if you launch the process not from an existing console window, a console window is created on demand, which automatically closes shortly after, namely after PowerShell has reported the error due to not finding the specified script file, making it all but impossible to see what happened.

The workaround is to use the -Command parameter instead, which keeps the window open unconditionally:

// Again, passing arguments individually is an option in .NET Core / .NET 5+
System.Diagnostics.Process.Start(
  "powershell.exe",  // The Windows PowerShell CLI
  @"-ExecutionPolicy Bypass -NoExit -NoLogo -Command & C:\Users\username\Desktop\test.ps1 5"
).WaitForExit();

Note the use of &, the call operator, to invoke the script (which isn't strictly necessary in this case, but generally is if the script-file path is quoted and/or contains variable references).

Also note that using -Command changes how the pass-through arguments are interpreted, which may or may not cause problems (not with a simple argument such as 5); in short: after command-line parsing, during which syntactic (unescaped) " chars. are stripped, PowerShell then interprets the resulting arguments as PowerShell source code, subjecting them to an additional layer of interpretation - see this answer for more information.

Related