Introduction
This article describes how to provide PowerShell support in windows applications using Microsoft's Automation Library.
After reading this article you would understand how to import PowerShell modules into runspace and how to execute CmdLets with the use of Microsoft's Automation library.
So If you are trying to execute powershell CmdLets/scripts using CreateProcess
/ShellExecute
APIs, stop doing that and proceed reading this article.
I have attached sample project MonadUtil (Monad is a codename for PowerShell) with this article. Before you proceed using code it is recommended to install PowerShell SDK.
You can download PowerShell SDK from here.
Compilation and references
If you see below errors during compilation of code, It means System.Management.Automation.dll is not referenced in your code.
You can resolve references browsing at : C:\Program Files\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0\System.Management.Automation.dll
The type or namespace name 'RunspaceInvoke' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'Runspace' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'InitialSessionState' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'Automation' does not exist in the namespace 'System.Management' (are you missing an assembly reference?)
The name 'PowerShell' does not exist in the current context
The name 'RunspaceFactory' does not exist in the current context
Using the code
We need below namespaces to use Automation/PowerShell functionality support in our code.
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Management.Automation.Host;
m_Monad
is an object of PowerShell class and using
PowerShell.Create()
method
we create an empty pipe to PowerShell. m_SessionState
is an object of InitialSessionState
class which provides default and loaded
module commands provided by powershell. m_SessionState
is initiated by
InintalSessionState.CreateDefault()
method.
m_Monad = PowerShell.Create();
m_SessionState = InitialSessionState.CreateDefault();
Once m_Monad
and m_SessionState
are initiated we'll load some modules in runspace, there are various modules supoorted by powershell.
Some of default modules are listed below:
- ActiveDirectory
- AppLocker
- BestPractices
- BitsTransfer
- FailoverClusters
- PSDiagnostics
- SeverManager
- TroubleshootingPack
In MonadUtil I've loaded ActiveDirectory and BitsTransfer modules.
A string array named modules
is populated with these module names.
As shown in below code snippet m_SessionState
imports list of modules that I've passed as a parameter. Then we create a single PowerShell runspace
that uses the default host and runspace configuration that has been passed.
Opening runspace will load all cmdlets supported by loaded modules.
Then I've initiated m_invoker
, which will be used to execute cmdlets loaded in runspace.
m_SessionState.ImportPSModule(modules);
m_rSpace = RunspaceFactory.CreateRunspace(m_SessionState);
m_rSpace.Open();
m_invoker = new RunspaceInvoke(m_rSpace);
After creation of invoker, it's a time to invoke a script. Now script is as shown below, which will list down all the running processes with name starting with either C,O,D or E.
get-process c*,o*,d*,e*
As shown in below code snippet m_invoker.Invoke()
method is used to execute PowerShell script. After successful execution of script m_invoker
will
return collection of PSObjects
, which we are storing in results
object to iterate the result set.
In case bad CmdLet is passed as a script, Exception would be generated and It can be caught in System.Management.Automation.RuntimeException
handler
and read exception message/text from ex.Message
.
In case bad parameters are passed to CmdLets, erroneous output can be read in ErrorRecord
collection using m_Monad.streams.Error.ReadAll()
method.
We'll iterate this collection and display error string error.FullyQualifiedErrorId
as a error message.
Collection<ErrorRecord> errors = null;
Collection<PSObject> results = m_invoker.Invoke(script);
errors = m_Monad.Streams.Error.ReadAll();
if (errors.Count != 0)
{
foreach (ErrorRecord error in errors)
{
Console.WriteLine("{0}", error.FullyQualifiedErrorId);
}
return -1;
}
As we know after successful execution of script output is stored in PsObject
collection, we'll interate this collection to get appropriate result.
foreach (PSObject result in results)
{
Console.WriteLine("{0}", result.Members["ProcessName"].Value);
}
Conclusion
I hope this might be useful to you and I'm sure, after reading this article and going through the code you would be able to load required modules and execute
CmdLets/Script with the use of Automation APIs.
Please comment If you have any suggestions in order to improve this article.