7

I'd like to pass an associative array from C# to Powershell. As an example I'd like to execute this powershell line of code:


PS C:\> get-command | select name, @{N="Foo";E={"Bar"}} -first 3

Name                                                        Foo
----                                                        ---
Add-Content                                                 Bar
Add-History                                                 Bar
Add-Member                                                  Bar

I'd like to do this via a Pipeline of distinct Commands as opposed to a single command marked as a script. Here's the code:


Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();

pipeline.Commands.Add("get-command");

Command c = new Command("select-object");
List properties = new List();
properties.Add("name");
properties.Add("@{N=\"Foo\";E={\"Bar\"}}");
c.Parameters.Add("Property", properties.ToArray());
c.Parameters.Add("First", 3);
pipeline.Commands.Add(c);

pipeline.Commands.Add("Out-String");

Collection retval = pipeline.Invoke();
runspace.Close();

StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in retval)
    Console.WriteLine(obj.ToString());

But that associative array being passed in as a parameter to Select-Object isn't being parsed correctly. This is what comes out the other side:


PS C:\test> c:\test\Bin\Debug\test.exe

Name                                     @{N="Foo";E={"Bar"}}
----                                     --------------------
Add-Content
Add-History
Add-Member

What's wrong with how I'm setting up the Select-Object command parameters?

1 Answer 1

13

Creating a pipeline through c# and creating a pipeline with native powershell script have one major difference that is actually quite subtle: the parameter binder.

if I write a version of your code in pure script, I will get the same error: the hashtable literal is treated as a string value.

ps> $ps = $ps.Commands.Add("get-process")
ps> $ps = $ps.Commands.Add("select-object")
ps> $ps.Commands[1].Parameters.Add("Property", @("Name", '@{N="Foo";E={"Bar"}}'))

In this case, the command receives an array of two strings, the "name" and the hashtable literal string. This will be broken in exactly the same way as your C#. Now, take a look at the right way to do it (in script) - let me rewrite line 3:

ps> $ps.Commands[1].Parameters.Add("Property", @("Name", @{N="Foo";E={"Bar"}}))

So what changed? I removed the quotes around the hashtable -- I am passing a hashtable as the 2nd element of the object array! So, to get your C# example to work, you need to do what the parameter binder does for us at the command line (which is quite a lot!). Replace:

properties.Add("@{N=\"Foo\";E={\"Bar\"}}");

with

properties.Add(
    new Hashtable {
        {"N", "Foo"},
        {"E", System.Mananagement.Automation.ScriptBlock.Create("\"Foo\"")}
    }
);

I hope this clears it up for you. The parameter binder is probably the least visible but most powerful part of the powershell experience.

-Oisin

Sign up to request clarification or add additional context in comments.

4 Comments

type on 'Mananagement', also "error CS0117: 'System.Management.Automation.ScriptBlock' does not contain a definition for 'Create'" -- other than that, works great
ah, you must be using v1 powershell (the create static is on v2). sorry!
That's what I figured. Solution accepted anyway. I adapted what you provided to get what I needed and applied it to the real app. Works great. Best ever.
Thank you! I was constantly instantiating a script block as ScriptBlock.Create("{ $_ }") when instead I needed ScriptBlock.Create("$_")

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.