格式化托管 PowerShell 的输出

发布于 2024-10-09 01:47:46 字数 3523 浏览 0 评论 0原文

我找到了一个 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

厌倦 2024-10-16 01:47:46

如果你在 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文