PaaP: Windows PowerShell as a Platform – Part 1

PaaP: Windows PowerShell as a Platform – Part 1

Rate This
  • Comments 10

In addition to being a scripting language, Windows PowerShell is also used as a platform in many applications. This is possible because the Windows PowerShell engine can be hosted inside an application. This blog post and the next will deal with the various APIs available for hosting Windows PowerShell in a C# application.

The most important type for hosting Windows PowerShell is the System.Management.Automation.PowerShell class. This class provides methods that create a pipeline of commands and then execute those commands in a runspace.

AddCommand

Let's take a very simple example. Suppose you want to get the list of running processes on the machine. The way to run this command is as follows.

Step 1 – Create a PowerShell object

PowerShell ps = PowerShell.Create();

Step 2 – Add the command that you want to execute

ps.AddCommand(“Get-Process”);

Step 3 – Invoke the command

ps.Invoke();

You can also do all of those in a single step like this:

PowerShell.Create().AddCommand(“Get-Process”).Invoke();

AddParameter

The previous example executes a single command without any parameters. Let’s say I want to get a list of all the PowerShell processes running on the machine. I can use the AddParameter method to add parameters to the command.

PowerShell.Create().AddCommand(“Get-Process”)
                   .AddParameter(“Name”, “PowerShell”)
                   .Invoke();

To add more parameters, you can call AddParameter again like this:

PowerShell.Create().AddCommand(“Get-Process”)
                   .AddParameter(“Name”, “PowerShell”)
                   .AddParameter(“Id”, “12768”)
                   .Invoke();

Or, if you have a dictionary of parameter names and values, you can use the AddParameters method to add all the parameters.

IDictionary parameters = new Dictionary<String, String>();
parameters.Add("Name", "PowerShell");

parameters.Add("Id", "12768");
PowerShell.Create().AddCommand(“Get-Process”)
		   .AddParameters(parameters)
   		   .Invoke()

AddStatement

Now that we have executed a single command and its parameters, let’s execute a bunch of commands. The PowerShell API provides a way for us to simulate batching with the use of AddStatement method, which adds an additional statement to the end of the pipeline. To get the list of running PowerShell processes, and then get the list of running services, do the following:

PowerShell ps = PowerShell.Create();
ps.AddCommand(“Get-Process”).AddParameter(“Name”, “PowerShell”);
ps.AddStatement().AddCommand(“Get-Service”);
ps.Invoke();

AddScript

The AddCommand method only run commands. If I want to run a script, I can use the AddScript method. Let's assume I have a simple script, a.ps1, that gets the number of running PowerShell processes on my machine.

------------D:\PshTemp\a.ps1----------
$a = Get-Process -Name PowerShell
$a.count
------------D:\PshTemp\a.ps1----------
PowerShell ps = PowerShell.Create();
ps.AddScript(“D:\PshTemp\a.ps1”).Invoke();

The AddScript API also provides an option to run the script in local scope. In this example, the script runs in local scope because we pass a value of true to the useLocalScope parameter.

PowerShell ps = PowerShell.Create();
ps.AddScript(@“D:\PshTemp\a.ps1”, true).Invoke();

Handling Errors, Verbose, Warning, Debug and Progress Messages

The PowerShell API also gives us access to the errors, verbose messages, etc. that are generated when the command runs. You can access the PowerShell.Streams.Error property to get the list of all errors and the PowerShell.Streams.Verbose property to get the verbose messages. There are properties for accessing the Progress, Debug and Warning messages as well.

PowerShell ps = PowerShell.Create()
                          .AddCommand("Get-Process")
                          .AddParameter("Name", "Non-ExistentProcess");
ps.Invoke();

ps.Streams.Error has the list of errors that were generated when running this command. In our case, we get one error for looking for a non-existent process.

Get-Process : Cannot find a process with the name "Non-ExistentProcess". 
Verify the process name and call the cmdlet again. 
+ CategoryInfo : ObjectNotFound: (Non-ExistentProcess:String) [Get-Process], 
ProcessCommandException 
+ FullyQualifiedErrorId : 
NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand

The PowerShell API is very powerful. It enables us to execute the commands in synchronous/asynchronous mode, to create a constrained runspace which exposes a limited set of commands, to execute commands in a nested pipeline, etc.

In all of the examples in this post, we created a default runspace that includes all of the built-in Windows PowerShell commands. This is not efficient in terms of the amount of memory consumed. In a lot of scenarios, people want to use a runspace with only a specific set of commands/language elements. In the next blog post, I'll explain how to create a more limited and efficient runspace.

Thanks,

Indhu Sivaramakrishnan [MSFT]
Windows PowerShell Developer
Microsoft Corporation

Leave a Comment
  • Please add 8 and 7 and type the answer here:
  • Post
  • Thanks for posting this.  I don't see much blogging about using PowerShell this way.  Lots of questions on StackOverflow, though.  :-)

  • Great stuff. I would love to see an example of a custom PSHostUserInterface and PromptForCredential; like a XAML application.

  • Very good post. About a year ago I saw a demo of a UI that allowed a user to selectively execute commands and then commands on the results.  Now I cannot find that sample but could really use something like that.

    Any idea of a demo like that floating around Microsoft?

  • This is very interesting and powerful. Can you detail some use cases as to why you may want to do this?

  • Very informative post, Indhu. Waiting for the next in the line....

  • Thanks, Indhu! This is so readable. I can't wait for Part 2.

  • Great post!

    Below is a link to the script [1] from Chapter 5 in my book "PowerShell for Developers" [2].

    It is a WPF music catalog application. It shows how to automate the WPF application by embedding PowerShell, expanding on the techniques used in this post.

    I can't wait for more posts like this

    Thanks

    Doug Finke - PowerShell MVP

    Use code DSUG to 50% off the ebook

    [1] github.com/.../chapter05

    [2] shop.oreilly.com/.../0636920024491.do

  • Hi,

    I saw lot of examples for Get-Process in the past. Honestly - every person writing about C# is showing it on Get-Process. I had troubles with something simple like Import-Module in the past and none of blog posts helped.

    Just a suggestion for future post - can you please show recommended way how to use PaaP in 32 vs. 64 bit environment?

    Thanks,

    Jirka

  • Dariel -- there are several open source implementations of PSHostUserInterface etc. Check out poshconsole.codeplex.com for one that's WPF

  • Hi PowerShell team! I really like this approach to have PowerShell being hosted inside different applications for easy extensibility and we are using this for several customers already with great success.

    Hopefully not too off-topic, but to really take PowerShell to the 'next level' as a generic 'platform' you might really want to consider to port this to non-Windows environments like Mono. With that you could leverage this superiour automation language/framework to address a much broader audience and many more use cases. Regards, Ronald

Page 1 of 1 (10 items)