Monday, February 05, 2007 7:11 PM bart

A first introduction to Windows PowerShell runspaces

Dear blogging friends, I'm back from some blog silence due to other priorities during last weeks . Although it will take some time to get back up to speed in the blogosphere, I decided to leave you no longer hungry for some cool stuff and posted this simple warming-up sample of Windows PowerShell runspaces.


Runspaces in a few words

One of the coolest things about Windows PowerShell is the fact that the entire command line processor and pipeline infrastructure can be hosted by applications other than the PowerShell.exe executable itself. Basically, that's what the MMC 3.0 layering on top of Windows PowerShell is all about, which has been fully realized in the Exchange Server 2007 product. In this and upcoming posts I want to show you how you can leverage the power of all this magic in your own applications, allowing you to realize such a layering yourself: build cmdlets and host the PS engine!


A simple snap-in

In order to illustrate the use of a runspace, we need to create some snap-in first (or you could reuse an existing one). Please revisit my previous posts on Windows PowerShell for more information on the creation of cmdlets; in this post I'll assume you know what a cmdlet is and how you create a parameterized one. Below you can find the code for a snap-in containing two cmdlets which are rather self-explanatory:

using System; using System.Collections.Generic; using System.Text; using System.Management.Automation; using System.ComponentModel; namespace DemoSnapIn { [Cmdlet(VerbsCommon.Get, "Greeting")] public class SayHelloCmdlet : PSCmdlet { private string name; [Parameter(Mandatory = true, Position = 0)] public string Name { get { return name; } set { name = value; } } protected override void ProcessRecord() { this.WriteObject("Hello " + name); } } [Cmdlet(VerbsCommon.Get, "Permutations")] public class PermutationGeneratorCmdlet : PSCmdlet { private string _string; [Parameter(Mandatory = true, Position = 0)] public string String { get { return _string; } set { _string = value; } } protected override void ProcessRecord() { foreach (string s in GetPermutations(_string)) WriteObject(s); } static void Swap(StringBuilder sb, int i, int j) { char t = sb[i]; sb[i] = sb[j]; sb[j] = t; } static IEnumerable<string> GetPermutations(string s) { int len = s.Length; StringBuilder sb = new StringBuilder(len); sb.Append(s); return GetPermutations(sb, 0, len - 1); } static IEnumerable<string> GetPermutations(StringBuilder sb, int b, int e) { if (b == e) yield return sb.ToString(); else { for (int i = 0; i <= e - b; i++) { Swap(sb, b, b + i); foreach (string r in GetPermutations(sb, b + 1, e)) yield return r; Swap(sb, b + i, b); } } } } [RunInstaller(true)] public class MySnapIn : PSSnapIn { public override string Name { get { return "DemoSnapIn"; } } public override string Description { get { return "Sample snap-in"; } } public override string Vendor { get { return "Bart"; } } } }

Note: the permutation searcher is just a simple straightforward implementation to illustrate cmdlets that produce multiple results; please ignore the algorithm used which has a bit of a quick-n-dirty fashion :-)

Put the code above in a C# class library project, add a reference to System.Management.Automation (you might want to copy the System.Management.Automation.dll file from the GAC to some folder in order to reference it - recall the GAC can be found under %windir%\assembly\GAC_MSIL through the command-line); compile the whole thing (preferably with a strong name) and open up a VS2005 command-line (under Windows Vista launched using "Run as Administrator" in order to have the necessary privileges to write to the registry, a task executed by installutil.exe). In the command-prompt, navigate to the bin\Debug or bin\Release folder (according to the build type you chose) and execute installutil.exe -i DemoSnapIn.dll (assuming the assembly is named DemoSnapIn.dll of course):

Next, open Windows PowerShell and check whether the registration was successful by calling get-pssnapin -registered:

You should be able to call both the get-greetings and get-permutations cmdlets once you have added the snap-in to the shell:


Hosting a runspace

Time to call the cmdlets from inside another application. To illustrate this, create a new console application in C#, add a reference to System.Management.Automation (you might want to copy the System.Management.Automation.dll file from the GAC to some folder in order to reference it - recall the GAC can be found under %windir%\assembly\GAC_MSIL through the command-line). Next, add the following code:

using System; using System.Management.Automation.Runspaces; using System.Management.Automation; namespace DemoCmdHost { class Program { const string name = "Bart"; const string perm = "Abc"; static void Main(string[] args) { RunspaceConfiguration config = RunspaceConfiguration.Create(); PSSnapInException ex; config.AddPSSnapIn("DemoSnapIn", out ex); if (ex != null) throw ex; using (Runspace rs = RunspaceFactory.CreateRunspace(config)) { rs.Open(); Pipeline p = rs.CreatePipeline(); Command cmd = new Command("get-greeting"); cmd.Parameters.Add("Name", name); p.Commands.Add(cmd); Console.WriteLine(p.Invoke()[0].BaseObject as string); } using (Runspace rs = RunspaceFactory.CreateRunspace(config)) { rs.Open(); Pipeline p = rs.CreatePipeline(); Command cmd = new Command("get-permutations"); cmd.Parameters.Add("String", perm); p.Commands.Add(cmd); foreach (PSObject o in p.Invoke()) Console.WriteLine(o.BaseObject); } } } }

That's it! Here's the result you should get:

More will follow later. Take a look at the Windows SDK documentation for Windows PowerShell too.


Calling all Windows Vista users - Download Windows PowerShell now

Over here: | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:


# re: A first introduction to Windows PowerShell runspaces

Wednesday, February 07, 2007 2:09 AM by Lex

Very cool nice and simple example, I've been looking at the hosting of runspaces and it seems relatively straight forward - with the possible exception of ensuring you grant the correct privileges.

Do you mention the MMC 3.0 runs on top of a powershell runspace but I see no evidence of that (in fact the only change I could see is the action panel).

I know that there was the original intention to be able to do actions in the GUI and save out scripts - did that actually happen I haven't spotted any documentation on MSDN.

# re: A first introduction to Windows PowerShell runspaces

Sunday, February 11, 2007 5:46 AM by bart

Hi Lex,

Basically what I meant is that MMC 3.0 snap-ins can act as a layer on top of PowerShell cmdlets and providers, which is what happens in Exchange 2007. However, MMC 3.0 itself isn't dependent on PowerShell - it's just that you *can* do it (which is the vision towards the future to have more and more products and technologies follow this approach to increase scriptability).


# Invoking PowerShell scripts from MSBuild

Saturday, February 16, 2008 2:22 AM by B# .NET Blog

I&#39;m a firm believer of the &quot;innovation through integration&quot; theme. As a fan of MSBuild