I found a C # example here for asynchronously calling a PowerShell script from the main application (in the Chapter 6 folder - Reading with events) and I am trying to use it in a Windows Forms application.
I have a button (button1) to run a PowerShell script, textBox1 should enter a script, and textBox2 displays the output of the script. Here is my current code:
using System; using System.Management.Automation; using System.Management.Automation.Runspaces; using System.Windows.Forms; namespace PSTestApp { delegate void SetTextDelegate(string text); public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { textBox2.Text = ""; Runspace runspace = RunspaceFactory.CreateRunspace(); runspace.Open(); Pipeline pipeline = runspace.CreatePipeline(textBox1.Text); pipeline.Output.DataReady += new EventHandler(HandleDataReady); pipeline.Error.DataReady += new EventHandler(HandleDataReady); pipeline.InvokeAsync(); pipeline.Input.Close(); } private void HandleDataReady(object sender, EventArgs e) { PipelineReader<PSObject> output = sender as PipelineReader<PSObject>; if (output != null) { while (output.Count > 0) { SetText(output.Read().ToString()); } return; } PipelineReader<object> error = sender as PipelineReader<object>; if (error != null) { while (error.Count > 0) { SetText(error.Read().ToString()); } return; } } private void SetText(string text) { if (textBox2.InvokeRequired) { SetTextDelegate d = new SetTextDelegate(SetText); this.Invoke(d, new Object[] { text }); } else { textBox2.Text += (text + Environment.NewLine); } } } }
The code works, but I have a problem processing the output. Pipeline.Output.Read () returns an instance of the PSObject, so ToString () returns different things for different objects. For example, if I use this PowerShell command:
Get-ChildItem
output:
PSTestApp.exe PSTestApp.pdb PSTestApp.vshost.exe PSTestApp.vshost.exe.manifest
and if I use:
Get-Process
output:
... System.Diagnostics.Process (csrss) System.Diagnostics.Process (ctfmon) System.Diagnostics.Process (devenv) System.Diagnostics.Process (devenv) ...
I could use returned PSObject instances to build the output, but it would be nice. If I could use the existing PowerShell formatting and get the same result as in the console. When I launch the application and check Runspace.RunspaceConfiguration.Formats, the number is 9 and DotNetTypes.format.ps1xml is present, but I do not know how to apply the format.
I noticed that if I add an Out-String to the end of the script:
... Pipeline pipeline = runspace.CreatePipeline(textBox1.Text); pipeline.Commands.Add("Out-String"); ...
output is formatted in the same way as in the standard PowerShell console. This works, but if I run the script with a long output, it takes a while to execute:
gci d:\ -recurse
The Pipeline.Output.DataReady event is generated only once (after the Out-String is completed), and only after that the output is added to the text field.
Is there a way to use standard PowerShell output formatting in a hosted PowerShell instance?