A few days ago I posted up my first procrastination backlog post. My procrastination backlog is a list of posts that I want to write but for which I haven’t yet had the motivation.
One of the ones that comments seemed to be most interested in was how to host the PowerShell runtime in your own applications so I’ve decided now is the time to write this post.
What is PowerShell?
For those unfamiliar with PowerShell, it is a new interactive command shell and scripting tool from Microsoft that is integrated into Windows Server 2008 but also available as a separate download for Windows XP, Windows Vista and Window Server 2003.
Starting up PowerShell yields a fairly familiar command prompt which you can use to interactively issue commands, in fact it responds to some of your old favourites that you might be familiar with with cmd.exe or the various UNIX-based shells.
Scratching beneath the surface is things get really interesting, for instance, the “dir” command that we know and love is actually just an alias for a command called “get-childitem”, just looking at that command means we can deduce a lot about PowerShell.
- The command uses a consistent verb-noun syntax.
- The command implies a generic hierarchical structure.
- The command looks internal.
PowerShell is actually implemented in .NET and commands are actually classes which derive from a common base class. Even the mechanism for accessing the file system and other information stores is abstracted away into a set of .NET base classes within the System.Management.Automation.dll assembly. Perhaps the most telling sign of how pervasive .NET is within PowerShell is what you get when you expect the output from the “dir” or “get-childitem” command more closely.
Notice how the output of the “get-childitem” command (actually they are called cmdlets) is piped to the “get-member” cmdlet. The “get-member” cmdlet takes the output from the preceeding cmdlet and uses .NET reflection to figure out what the capabilities of the objects being return are. Re-read that last sentence – that’s right, PowerShell understands .NET objects, and in fact uses them to pass data from cmdlet to cmdlet. In the screenshot above we can see that the “get-childitem” is returning System.IO.DirectoryInfo objects which are part of the .NET Framework.
If this is the first time that you have seen PowerShell, hopefully I have wet your appetite and you will take a closer look. If you have already been using PowerShell for a while then you are possibly interested in how you can exploit it in your own applications – if so, read on.
The Hosting Architecture
Fortunately for us PowerShell was designed from the ground up to be hosted in different execution environments such as the interactive shell, Microsoft Management Console, Microsoft System Centre Virtual Machine Manager, and now your own applications!
In order to host the PowerShell runtime in your own application you need some way for your application to pass data in and out of the PowerShell environment and tell it what underlying PowerShell commands to execute. Many of the classes that you need from a developers perspective are defined within the System.Management.Automation assembly and two classes that you need to know about are PSHostUserInterface and PSHost. By sub-classing these two interfaces you can interact with PowerShell from your own application logic.
Next up, lets look at a simple working example of how to get some data into PowerShell and back out again.
A Simple Working Example
Unfortunately I don’t have the PowerShell SDK installed on my machine, if you find yourself in this situation it can sometimes be a bit tricky to find a reference to the System.Management.Automation assembly as it isn’t included in the pick list when you go to “Add References”. Instead, drop out to a command prompt and navigate to C:\Windows\assembly\GAC_MSIL\System.Management.Automation\126.96.36.199__31bf3856ad364e35″, and copy the System.Management.Automation.dll file contained within that directory to another location where you can select it from – its a bit messy.
As a general rule I like to keep track of my dependencies so I will often create a solution folder and plonk various DLLs that I depend on in there – normally I wouldn’t worry about the GAC ones in this case but not every build server has PowerShell installed. In my case I end up with a solution with a single console application and the automation DLL.
Once you have your basic environment set up you can go ahead and implement your own PSHost. It is too much code to list here completely, but you can download my sample from the MSDN Code Gallery.
Once you have a basic host implemented you need to instantiate a Runspace (associated with the host) and build a pipeline of commands that you want to execute. In the sample below I am creating a pipeline with the built-in command “get-childitem”, invoking it then enumerating the results to the console output.
It is interesting to note that you are actually getting a stream of objects as output and you need to decide how you process them whereas the default PowerShell command-line host will format them out as a table or list as is appropriate. In this however the output is a simple listing of files.
This works because the ToString method on PSObject calls the ToString method on the object it contains which happens to be a FileInfo object instance.
The new extension methods feature has always fascinated me. One of the cute things you can do with extension methods and PowerShell hosting is create a really easy way to script out to PowerShell from within anywhere in your application.