I have a simple function in C# to call a PowerShell command and return the output and it works fine. However I want to have to be able to return values for custom functions that I add to PowerShell. Here is my C# code:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
PowerShell ps = PowerShell.Create();
ps.Runspace = runspace;
ps.Commands.AddScript("MyCustomCommand");
ICollection<PSObject> results = ps.Invoke();
When I run PowerShell locally I have the profiles set up in the location C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
so when I open PowerShell and input MyCustomCommand
it returns a string:
Function MyCustomCommand {
[CmdletBinding()]
param()
return "someoutput"
}
However when I make that same MyCustomCommand call from C# it's not found. How can I add this profile permanently in a way that C# will also return that same output when calling the command in the same way that happens when I open an instance of PowerShell on my desktop?
Update I used the modules solution recommended by @start-automating but I was not able to run due to the execution policy so I modified it:
ps.Runspace = runspace;
ps.AddCommand("Set-ExecutionPolicy").AddArgument("Unrestricted");
ps.AddCommand("Import-Module").AddArgument("MyNewModuleName");
ps.Commands.AddScript("MyCustomCommand");
CodePudding user response:
What's Going Wrong?
It can't run your command in C# because your command is not yet loaded.
Ok, So How Do You Load it?
You're already using a [Runspace]
. This is good. The [Runspace]
is what you'll need to load the function into. There are two ways to do this:
- Use
[InitialSessionState]
This is the recommended route. [InitialSessionState]
is used to populate the initial state of a PowerShell [Runspace]
. Here you can load modules, declare commands, add variables, etc. The easiest way to handle things would be to have them be in a module in the first place.
InitialSessionState iss = InitialSessionState.CreateDefault2();
// Note: .CreateDefault2 is the recommended default of a session state
// Note: .CreateDefault reflects the default session state in v2-v4 timeframes
iss.ImportPSModule(new[] { "NameOfModule" });
iss.ImportPSModulesFromPath("PathToModule");
Runspace runspace = RunspaceFactory.CreateRunspace(iss);
You can also declare individual functions by hand, using a SessionStateFunctionEntry
InitialSessionState iss = InitialSessionState.CreateDefault2();
iss.Commands.Add(new SessionStateFunctionEntry("MyFunction","MyDefinition"));
Using either technique, this should populate your runspace with the commands you need.
- .AddCommand/.AddScript with useLocalScope set to $false
The other way you can approach this is by running a command that will change the [Runspace]
. By default, when you .AddCommand or .AddScript to a [PowerShell]
object, it will use a "local" scope. This means any changes you make (commands you add, modules you import, variables you set, etc) will be "locally scoped", and will not be available the next time you run in the [Runspace]
.
To make this work the way you'd want, to use .AddScript / .AddCommand with useLocalScope (the second argument), set to false.
Here's a quick PowerShell proof of concept:
[PowerShell]::Create().AddScript('$x = 1',$false).AddStatement().AddScript('$x').Invoke()
Hope this Helps