2

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
  • 2
    Check `ps.HadErrors` and `ps.Streams.Error` after calling `Invoke()` – Mathias R. Jessen Jul 30 '21 at 11:33
  • @MathiasR.Jessen I have also tired these properties but command prompt has been closed very quick, the same situation is continuing unfortunately. – Oğuz KAHRAMAN Jul 30 '21 at 11:59
  • In your example when you run Powershell manually you add 5 as a parameter, where is the c# code to do that and what would happen if you ran the script in Powershell without that parameter, would it just exit? – Tom Jul 30 '21 at 15:02
  • @Tom yes it exited and Powershell turned into cmd prompt. – Oğuz KAHRAMAN Aug 02 '21 at 06:16
  • @mklement0 thank you so much. It seems working but I do not know how to pass an argument for this command. But i saw blue Powershell screen for a half second. – Oğuz KAHRAMAN Aug 02 '21 at 06:17
  • @Oğuz KAHRAMAN, you could refer to [the answer](https://stackoverflow.com/a/64020356/11507778) I write to pass the argument for the command. I can use the command to change the CreationTime of a txt file. – Jack J Jun Aug 02 '21 at 08:33
  • @JackJJun-MSFT thank you for the info. But i cannot see the Powershell and there is no error. My code is : `PowerShell ps = PowerShell.Create(); int myVar = 2; string script = string.Format(@"-noexit -file C:\Users\username\Desktop\test.ps1 {0}", myVar); ps.AddScript(script); ps.Invoke();` – Oğuz KAHRAMAN Aug 02 '21 at 09:24
  • @OğuzKAHRAMAN, this usually implies that the script file cannot be found - please see my answer. – mklement0 Aug 02 '21 at 13:44

1 Answers1

2

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775