格式化托管 PowerShell 的输出
我找到了一个 C# 示例 此处 从主机应用程序异步调用 PowerShell 脚本(在文件夹第 6 章 - 读取事件中),并尝试在 Windows 窗体应用程序中使用它。
我有一个按钮(button1)来启动PowerShell脚本,textBox1用于输入脚本,textBox2显示脚本输出。这是我当前的代码:
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);
}
}
}
}
该代码可以工作,但我在处理输出时遇到问题。 Pipeline.Output.Read() 返回 PSObject 的实例,因此 ToString() 对于不同的对象返回不同的内容。例如,如果我使用此 PowerShell 命令:
Get-ChildItem
输出为:
PSTestApp.exe
PSTestApp.pdb
PSTestApp.vshost.exe
PSTestApp.vshost.exe.manifest
如果我使用:
Get-Process
输出为:
...
System.Diagnostics.Process (csrss)
System.Diagnostics.Process (ctfmon)
System.Diagnostics.Process (devenv)
System.Diagnostics.Process (devenv)
...
我可以使用返回的 PSObject 实例来构造输出,但如果我可以使用现有的 PowerShell 格式并获取,那就太好了与控制台中的输出相同。当我运行应用程序并检查 Runspace.RunspaceConfiguration.Formats 时,计数为 9,并且存在 DotNetTypes.format.ps1xml,但我不知道如何应用该格式。
我注意到,如果我在脚本末尾添加 Out-String:
...
Pipeline pipeline =
runspace.CreatePipeline(textBox1.Text);
pipeline.Commands.Add("Out-String");
...
输出的格式与标准 PowerShell 控制台中一样。这是可行的,但如果我运行一个具有长输出的脚本,需要一些时间才能执行:
gci d:\ -recurse
Pipeline.Output.DataReady 事件仅引发一次(在执行结束的 Out-String 之后),然后才将输出添加到文本中盒子。
有没有办法在托管 PowerShell 实例中使用标准 PowerShell 输出格式?
I found a C# example here of invoking a PowerShell script asynchronously from a host application (in folder Chapter 6 - Reading With Events) and am trying to use it in a Windows Forms application.
I have a button (button1) to start the PowerShell script, textBox1 is to enter the script and textBox2 displays the script output. 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 handling the output. Pipeline.Output.Read() returns an instance of PSObject so ToString() returns different things for different objects. For example, if I use this PowerShell command:
Get-ChildItem
the output is:
PSTestApp.exe
PSTestApp.pdb
PSTestApp.vshost.exe
PSTestApp.vshost.exe.manifest
and if I use:
Get-Process
the output is:
...
System.Diagnostics.Process (csrss)
System.Diagnostics.Process (ctfmon)
System.Diagnostics.Process (devenv)
System.Diagnostics.Process (devenv)
...
I could use the returned PSObject instances to construct the output, but it would be nice If I could use the existing PowerShell formatting and get the same output as in the console. When I run the application and check Runspace.RunspaceConfiguration.Formats, the count is 9, and DotNetTypes.format.ps1xml is present, but I don't know how to apply the format.
I have noticed that if I add Out-String at the end of the script:
...
Pipeline pipeline =
runspace.CreatePipeline(textBox1.Text);
pipeline.Commands.Add("Out-String");
...
the output is formatted just as in the standard PowerShell console. This works, but if I run a script with a long output that takes some time to execute:
gci d:\ -recurse
Pipeline.Output.DataReady event is raised only once (after the ending Out-String is executed) and only then is the output added to the text box.
Is there a way to use standard PowerShell output formatting in a hosted PowerShell instance?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如果你在 out-string 上使用 -stream 参数,我想你会发现它不会阻塞。
另外,如果您实际构建主机(实现主机接口,包括 UI 和可能的 rawui),您将实现处理“标准”主机输出的方法。
您也可以尝试使用 out-default 而不是 out-string。我知道在自托管环境中我通常使用它。
If you use the -stream parameter on out-string, I think you'll find that it doesn't block.
Also, if you actually build a host (implement the host interface, including the UI and possibly the rawui), you'll implement methods to handle the "standard" host output.
You might also try using out-default instead of out-string. I know in self-hosted environments I usually use that.