Passing and invoking System.Action<> from powershell to c# - c#

I am working on a c# program using System.Speech.Recognition to recognize speech and run PowerShell commands according to what is said.
I have the following powershell script that represents a macro for creating a speech command:
Add-Type -Path ".\GAVPI.Lib.dll"
Add-Type -Path ".\GAVPI.Lib.Logging.dll"
[Action[GAVPI.Lib.Logging.Parameter]]$speechRecognized = {
param($i)
[System.Windows.MessageBox]::Show("test")
}
$parameter = New-Object -TypeName GAVPI.Lib.Logging.Parameter -ArgumentList
("parameter", "value")
$phrase = New-Object -TypeName GAVPI.Lib.Core.Triggers.Phrase -ArgumentList
("default","test", $speechRecognized, $parameter)
return $phrase
This phrase object is used to tell what commands can be said and recognized. It successfully is passed to c# like this:
var list = new List<Phrase>();
var ps = PowerShell.Create();
var run = RunspaceFactory.CreateRunspace();
ps.Runspace = run;
run.Open();
var script = ps.AddScript(".\\PowershellTemplate.ps1", true);
var result = ps.Invoke();
foreach (var psObject in result)
{
if (psObject.BaseObject is Phrase)
{
list.Add((Phrase)psObject.BaseObject);
}
}
return list;
When a command is recognized,the Phrase class invokes the Action:
public override void Run(Parameter selectedparameter)
{
if (parAction != null)
{
parAction.Invoke(selectedparameter);
}
}
private Action<Parameter> parAction;
When the program is run, if you say "test parameter" it invokes the action<parameter> in the phrase class, which invokes the script block in powershell.
I get the following exception at the parAction.Invoke(selectedparameter); line:
There is no Runspace available to run scripts in this thread. You can
provide one in the DefaultRunspace property of the
System.Management.Automation.Runspaces.Runspace type. The script block
you attempted to invoke was:
param($i)...w("test")
How do I pass the runspace to the Sysem.Action<Parameter> so that it can run the PowerShell script block?

Related

C# Powershell not returning any Get-ChildItem results

When I run powershell from my MVC application, i get no results from the Get-ChildItem –Path IIS:\AppPools command.
If i run it directly in powershell it works fine. I have set the site to run on administrator account, thinking that maybe it cant "see" the pools, but no luck.
This is just a test too, accessing the pools, as what i was origionally struggling with was turning off an application pool, as it "could not be found". with Get-WebAppPoolState -name $service
public static string test()
{
using (var powershell = PowerShell.Create())
{
using (Runspace runspace = RunspaceFactory.CreateRunspace())
{
using (RunspaceInvoke invoker = new RunspaceInvoke())
{
invoker.Invoke("Set-ExecutionPolicy Unrestricted");
invoker.Invoke("Import-Module WebAdministration");
runspace.Open();
powershell.Runspace = runspace;
powershell.AddScript(#"Import-Module WebAdministration
Get-ChildItem –Path IIS:\AppPools");
var results = powershell.Invoke();
var result = "";
result += ResultsToString(results); // results is a blank list
result += String.Join(",", powershell.Streams.Error.ToList().Select(x => x.Exception.Message));
return result;
}
}
}
}
Any ideas where the issue lies? code, permissions?

C# unable to execute the powershell script

using (Runspace runspace = System.Management.Automation.Runspaces.RunspaceFactory.CreateRunspace())
{
runspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
Command lic1 = new Command("Out-String");
var script1 = "$s = Get-LocalUser -Name test2";
var script2 = "Remove-LocalUser -InputObject $s";
string accessScriptPath = $"{script1} ;\n {script2}";
powershell.Commands.AddCommand(lic1);
powershell.Commands.AddCommand(new Command(accessScriptPath));
var results = powershell.Invoke();
PowerShellErrorCheck(powershell, results);
powershell.Commands.Clear();
}
}
I wants to execute the command and it was assigned to a variable and trying to access in next line but system throw exception. '$s = Get-LocalUser -Name test2 ;
Remove-LocalUser -InputObject $s' 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.
Mainly I was trying the above code was to simulate the below powershell code to c#
$x = Get-CsRgsAgentGroup -Identity service:ApplicationServer:atl-cs-001.litwareinc.com -Name "Help Desk"
$x.AgentsByUri.Add("sip:kenmyer#litwareinc.com")
Set-CsRgsAgentGroup -Instance $x

Opening Runspace Pool using custom classes (C#) throws error

I have to add multithreading capabilities to a PowerShell script. Unfortunately I get an error when I try to open an runspace pool using custom C# classes in the InitialSessionState object. The following example is a DEMO code only, not the original script. You can copy&paste it and it will run without any modifications needed. What is really strange is, that all seems to work correctly despite the error message coming up when opening the runspace pool.
This is the error message (translated):
Error while loading the extended type data file: Error in type data "FooBar.BarClass": The TypeData must have: "Members", "TypeConverters", "TypeAdapters" or "StandardMembers".
I have already worked and tested for hours and have no idea what is the reason for that message. And yes, I know that there are very good libraries and cmdlets available for multithreading, but I cannot use them for different reasons.
# Simple c-sharp classes
Add-Type -TypeDef #"
namespace FooBar {
public class FooClass {
public string Foo() {
return "Foo";
}
}
public class BarClass {
public string Bar() {
return "Bar";
}
}
}
"#
function callFooBar {
[FooBar.FooClass]$foo = New-Object FooBar.FooClass
[FooBar.BarClass]$bar = New-Object FooBar.BarClass
$foo.Foo() + $bar.Bar()
}
$scriptBlock = {
Write-Output ( callFooBar )
}
# Setting up an initial session state object
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
# Getting the function definition for the functions to add
$functionDefinition = Get-Content function:\callFooBar
$functionEntry = New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry -ArgumentList 'callFooBar', $functionDefinition
# And add it to the iss object
[void]$initialSessionState.Commands.Add($functionEntry)
# Get the type data for the custom types to add
$typeData = New-Object System.Management.Automation.Runspaces.TypeData -ArgumentList 'FooBar.FooClass'
$typeEntry = New-Object System.Management.Automation.Runspaces.SessionStateTypeEntry -ArgumentList $typeData, $false
# And add it to the iss object
[void]$initialSessionState.Types.Add($typeEntry)
# Get the type data for the custom types to add
$typeData = New-Object System.Management.Automation.Runspaces.TypeData -ArgumentList 'FooBar.BarClass'
$typeEntry = New-Object System.Management.Automation.Runspaces.SessionStateTypeEntry -ArgumentList $typeData, $false
# And add it to the iss object
[void]$initialSessionState.Types.Add($typeEntry)
# Create Runspace pool
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, ([int]$env:NUMBER_OF_PROCESSORS + 1), $initialSessionState, $Host)
$RunspacePool.ApartmentState = 'MTA'
[void]$RunspacePool.Open() # <<<< Error occurs here!
[System.Collections.Generic.List[object]]$Jobs = #()
1..2 | % {
$job = [System.Management.Automation.PowerShell]::Create($initialSessionState)
$job.RunspacePool = $RunspacePool
[void]$job.AddScript($scriptBlock)
$jobs += New-Object PSObject -Property #{
RunNum = $_
Pipe = $job
Result = $job.BeginInvoke()
}
}
do {
} while ($jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"
$Results = #()
foreach ($job in $jobs) {
$Results += $job.Pipe.EndInvoke($job.Result)
}
$Results
$RunspacePool.Close()
$RunspacePool.Dispose()
#mklement0 Your hint leads me to the right direction! Custom C# classes with type-definition must be added in a different way to the runspacepool. This was working now (after a few hours testing with all possible runspace methods...):
# Get the script data for the custom c# class to add
$scriptDefinition = New-Object System.Management.Automation.Runspaces.ScriptConfigurationEntry -ArgumentList 'FooBar.FooClass', $false
$scriptEntry = New-Object System.Management.Automation.Runspaces.SessionStateScriptEntry -ArgumentList $scriptDefinition
# And add it to the iss object
[void]$initialSessionState.Commands.Add($scriptEntry)
# Get the script data for the custom c# class to add
$scriptDefinition = New-Object System.Management.Automation.Runspaces.ScriptConfigurationEntry -ArgumentList 'FooBar.BarClass', $false
$scriptEntry = New-Object System.Management.Automation.Runspaces.SessionStateScriptEntry -ArgumentList $scriptDefinition
# And add it to the iss object
[void]$initialSessionState.Commands.Add($scriptEntry)
Not by type, but with script and command. Thank you for your hint.

Pass multiple variables to PowerShell from C#

I'm invoking a PowerShell script from C#, I'm using a hashtable to pass through parameters however when it comes to invoking the PowerShell script I get
A positional parameter cannot be found that accepts argument
The PowerShell script has two parameters. The hashtable has two keys with one value each. PowerShell script below:
param([string]$username,[string]$path)
#Gets SID
$objUser = New-Object System.Security.Principal.NTAccount($username)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$SID = $strSID.Value
# delets user
net user $username /DELETE
# removes folder
rmdir /q $path
Remove-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$SID"
C# that calls the PowerShell script:
class RunScript
{
public static void FireScript(String script, Hashtable var)
{
RunspaceConfiguration runspaceConfiguration = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(runspaceConfiguration);
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
String scriptfile = "..\\..\\Resources\\" + script + ".ps1";
Command myCommand = new Command(scriptfile, false);
foreach (DictionaryEntry entry in var)
{
CommandParameter testParam = new CommandParameter(entry.Key.ToString(),entry.Value);
//CommandParameter testParam = new CommandParameter(null, entry.Value);
myCommand.Parameters.Add(testParam);
}
pipeline.Commands.Add(myCommand);
Collection<PSObject> psObjects;
psObjects = pipeline.Invoke();
runspace.Close();
}
}
Code that calls firescript:
Hashtable var = new Hashtable();
var.Add("username","testb");
var.Add("path", "C:\\Documents and Settings\\testb");
RunScript.FireScript("remove user",var);
I think that you need to set this parameter attribute: ValueFromPipeline which conform to this link represents an 'Optional named parameter. True indicates that the cmdlet parameter takes its value from a pipeline object. Specify this keyword if the cmdlet accesses the complete object, not just a property of the object. The default is false.'
You can check also check this link for some examples. The code could be like this:
param(
[parameter(Position=0, ValueFromPipeline=$true)][string]$username
[parameter(Position=1, ValueFromPipeline=$true)][string]$path
)

Error when running powershell command remotely

I want to run Powershell command on remote machine. This is method that I am using (localhost:131 is because I use tunnel to remote machine's port 5985):
public string RunRemotePowerShellCommand(string command)
{
System.Security.SecureString password = new System.Security.SecureString();
foreach (char c in _password.ToCharArray())
{
password.AppendChar(c);
}
string schema = "http://schemas.microsoft.com/powershell/Microsoft.Powershell";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(false,
"localhost", 131, "/wsman", schema, new PSCredential(_domain + #"\" + _userName, password));
using (Runspace remoteRunspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
remoteRunspace.Open();
using (PowerShell powershell = PowerShell.Create())
{
powershell.Runspace = remoteRunspace;
powershell.AddCommand(command);
powershell.Invoke();
Collection<PSObject> results = powershell.Invoke();
// convert the script result into a single string
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
return stringBuilder.ToString();
}
}
}
I'm trying to run following command:
D:\FolderName\scriptName.ps1 -action editbinding -component "comp1","comp2","comp3","comp4"
Like this:
RunRemotePowerShellCommand(#"D:\FolderName\scriptName.ps1 -action editbinding -component ""comp1"",""comp2"",""comp3"",""comp4""");
but I get:
Error: System.Management.Automation.RemoteException: The term 'D:\FolderName\scriptName.ps1 -action editbinding -component "comp1","comp2","comp3","comp4"' is not recognized as a name of cmdlet, function, script file, or operable program. Check the spelling of the name, or if the path is included, verify that the path is correct and try again.
The method works fine with simple commands, and the command that I want to run is ok when I run it on remote machine.
Thanks in advance.
Regards,
Dusan
You need to use the powershell.AddParameter() method to add the parameters for your command. The AddCommand() call should name just the command: cmdlet name, function name, path to script, etc. From the docs:
PowerShell ps = PowerShell.Create();
ps.AddCommand("Get-Process");
ps.AddArgument("wmi*");
ps.AddCommand("Sort-Object");
ps.AddParameter("descending");
ps.AddArgument("id");
I has a similar requirement.
My solution was to create a powershell function in C# code and use it over the powershell remote session like.
using System;
using System.Management.Automation;
namespace PowerShellTest
{
class Program
{
static void Main(string[] args)
{
string func = #"function Test { Write-Host 'hello' };";
PowerShell ps = PowerShell.Create();
ps.AddScript(func);
ps.Invoke();
ps.AddCommand("Test");
ps.Invoke();
Console.WriteLine("Successfully executed function");
Console.ReadLine();
}
}
}

Categories

Resources