Calling Powershell from C# doesn't use environment variables - c#

I'm calling a powershell script from my C# application using:
System.Diagnostics.Process.Start("powershell.exe", " -executionpolicy unrestricted -file myscript.ps1");
Within this script is a call to Java (which relies on the CLASSPATH & PATH system variables). This is where the script fails and reports that:
java is not recognized as an internal command or external command
I believe this is because the system variables for PATH are not being used when I'm executing Powershell from C#. Any help ?
FYI, if I open Powershell manually and run the command it works correctly.

Related

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

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

Powershell Cmdlet Invocation Inconsistency

I am trying to call the specific cmdlet "Get-VpnConnection" from a PowerShell instance in a Windows service (.NET Framework 4.7).
I have been able to call this cmdlet successfully in a command-line project (also .NET Framework 4.7) and have been able to call other cmdlets from within this service (e.g. "Get-Date"). I have selected the highest account permissions for the package installer (LocalSystem).
Here is an excerpt of what I've tried:
using (PowerShell ps = PowerShell.Create())
{
ps.AddCommand("Get-VpnConnection");
Collection<PSObject> results = ps.Invoke();
WriteToFile(results.Count.ToString());
}
When I run this from within the service, WriteToFile (a method I've created to write to a log text file in the program's base directory) will write "0" to the log whereas for "Get-Date" it would write "1". When I run this from within the main function of a command-line project, I get "1" as expected. No exceptions are thrown and no PowerShell Stream (Error or Verbose) contains anything. It's almost as if the cmdlet isn't being recognized as valid since providing a gibberish argument to AddCommand will produce an identical result.
Thank you for you time.

PowerShell script run from C# is lacking several modules

var runspace = RunspaceFactory.CreateRunspace()
runspace.Open();
var ps = PowerShell.Create()
ps.Runspace = runspace;
ps.AddCommand(...);
ps.Invoke()
Some cmdlets that exist in a regular shell are missing from the runtime started by C#, e.g. all the commands from the Microsoft.PowerShell.LocalAccounts module :
Error: System.Management.Automation.CommandNotFoundException: The term 'New-LocalUser' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
at System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception exception)
at System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
By comparing the Get-Module -ListAvailable output from a regular shell and a C# started one I noticed multiple modules are missing :
AppBackgroundTask
AssignedAccess
BitLocker
ConfigCI
Defender
Microsoft.PowerShell.LocalAccounts
MMAgent
NetworkSwitchManager
PcsvDevice
PSWorkflow, PSWorkflowUtility
SmbShare, SmbWitness
StartLayout
WindowsSearch
The version ($PSVersionTable) is reported as the same in both shells : 5.1.14393.693
Edit
Seems to be the same issue & solution : Why do powershell modules not import when running powershell via start process in c#?
You can import modules directly on your script instead of trying to do that from C#.
Import-module bitlocker
For example.

How to run PowerShell script using System.Diagnostics.Process.Start in c#?

I'm writing this:
using System.Diagnostics;
Process.Start("C:\\CodeProjects\\C#\\WindowsPowerShell\\v1.0\\powershell_ise.exe", "-File .\\mp4_to_flac.ps1");
All this does is open up the script in Windows PowerShell ISE. But I also want it to RUN! So what other arguments do I have to pass in so that it executes?
Process.Start method Reference
You don't want to run powershell_ise.exe but powershell.exe. From a dos command prompt you can just prefix your command or script with #powershell, but for a process you're going to want to use something like
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
(that's mine on Win 8.1), yours should be somewhere near there if you're on a different version.
The chocolatey guys throw in these switches
-NoProfile -ExecutionPolicy unrestricted
when executing powershell from the command prompt, you might have to do that also depending on how your execution policy is set.
You could call it like so:
Process.Start(".\\mp4_to_flac.ps1");
and make sure all your windows settings are correct, so a double click on the file will execute it.

The term 'connect-QADService' is not recognized as the name of a cmdlet... inside C# function

Can someone, please, help to understand why the following code inside a Csharp function
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipe = runspace.CreatePipeline();
Command connectToActDir = new Command("connect-QADService");
connectToActDir.Parameters.Add("Service", "'ip:port'");
connectToActDir.Parameters.Add("ConnectionAccount", "'user.local\administrator'");
connectToActDir.Parameters.Add("ConnectionPassword", "'password'");
pipe.Commands.Add(connectToActDir);
pipe.Invoke();
causes the following error
The term 'connect-QADService' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
while ActiveRoles cmdlets are installed and running the equivalent command in power shell succeeds. Do I miss some dll in project references?
Thanks
The Quest ActiveRoles cmdlets are not loaded into a PowerShell session by default, because they're not part of PowerShell.
PowerShell 3 will automatically load modules when needed. I don't know if this holds true when using a RunSpace as I've never used PowerShell this way.
Do you load the Quest snap-in or module in your PowerShell profile? You'll need to do the equivalent in your C# code.

Categories