【问题标题】:Custom PSHostUserInterface is ignored by Runspace自定义 PSHostUserInterface 被 Runspace 忽略
【发布时间】:2013-10-10 10:25:06
【问题描述】:

背景

我正在编写一个以编程方式执行 PowerShell 脚本的应用程序。此应用程序有一个自定义的PSHost 实现,以允许脚本输出日志记录语句。目前,我看到的行为是 some 请求被正确转发到我的自定义PSHost,而其他请求则被完全忽略。

当我开始检查脚本中的 $Host 变量时,事情变得更加奇怪,这似乎表明我的自定义 PSHost 甚至没有被使用。

代码

我有一些代码在 .NET 应用程序中执行 PowerShell:

var state = InitialSessionState.CreateDefault();
state.AuthorizationManager = new AuthorizationManager("dummy"); // Disable execution policy

var host = new CustomPsHost(new CustomPsHostUI());

using (var runspace = RunspaceFactory.CreateRunspace(host, state))
{
    runspace.Open();

    using (var powershell = PowerShell.Create())
    {
        powershell.Runspace = runspace;

        var command = new Command(filepath);

        powershell.Invoke(command);
    }
}

CustomPsHost 的实现非常简单,只包含转发PSHostUserInterface 所需的内容:

public class CustomPsHost : PSHost
{
    private readonly PSHostUserInterface _hostUserInterface;

    public CustomPsHost(PSHostUserInterface hostUserInterface)
    {
        _hostUserInterface = hostUserInterface;
    }

    public override PSHostUserInterface UI
    {
        get { return _hostUserInterface; }
    }

    // Methods omitted for brevity
}

CustomPsHostUI 用作日志记录的包装器:

public class CustomPsHostUI : PSHostUserInterface
{
    public override void Write(string value) { Debug.WriteLine(value); }
    public override void Write(ConsoleColor foregroundColor, ConsoleColor backgroundColor, string value){ Debug.WriteLine(value); }
    public override void WriteLine(string value) { Debug.WriteLine(value); }
    public override void WriteErrorLine(string value) { Debug.WriteLinevalue); }
    public override void WriteDebugLine(string message) { Debug.WriteLine(message); }
    public override void WriteProgress(long sourceId, ProgressRecord record) {}
    public override void WriteVerboseLine(string message) { Debug.WriteLine(message); }

    // Other methods omitted for brevity
}

在我的 PowerShell 脚本中,我正在尝试将信息写入主机:

Write-Warning "This gets outputted to my CustomPSHostUI"
Write-Host "This does not get outputted to the CustomPSHostUI"

Write-Warning $Host.GetType().FullName # Says System.Management.Automation.Internal.Host.InternalHost
Write-Warning $Host.UI.GetType().FullName # Says System.Management.Automation.Internal.Host.InternalHostUserInterface

为什么我的CustomPSHostUI 会出现奇怪的行为?

【问题讨论】:

  • 你在实现 RawUI 属性吗?我有一个 PSCustomHost 我为一个有效的 Make-PS1ExeWrapper 实用程序实现。你可以在这里查看:skydrive.live.com/…
  • 对于RawUI 上的Microsoft's documentation,我不需要它:If you are also implementing a PSHostRawUserInterface class, return an instance of the class.

标签: c# powershell runspace


【解决方案1】:

您需要为 PSHostRawUserInterface 提供一个实现。

Write-Host 最终会调用您的 Write(ConsoleColor, ConsoleColor, string) 版本。 PowerShell 依赖于前景色和背景色的原始 ui 实现。

我已经用示例代码验证了这一点。我没有调用 ps1 文件,而是直接调用了 Write-Host:

powershell.AddCommand("Write-Host").AddParameter("Testing...")

通过运行脚本,PowerShell 为您处理异常。通过直接调用命令,您可以更轻松地查看异常。如果您在原始示例中检查了 $error,您会看到一个有用的错误。

请注意,$host 的值绝不是实际的实现。 PowerShell 通过包装来隐藏实际的实现。我忘记了为什么要包装它的确切细节。

【讨论】:

    【解决方案2】:

    对于在实现 PSHostUserInterface 和 PSHostRawUserInterface 并发现在调用 Write-Error 时 WriteErrorLine() 被完全忽略(即使警告、调试和详细信息进入 PSHostUserInterface)后仍在苦苦挣扎的其他人,以下是如何获取错误:

    密切关注https://msdn.microsoft.com/en-us/library/ee706570%28v=vs.85%29.aspx 并在您的 .Invoke() 调用之前添加这两行,如下所示:

    powershell.AddCommand("out-default");
    powershell.Commands.Commands[0].MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output);
    
    powershell.Invoke() // you had this already
    

    这会将错误流合并到您的控制台输出中,否则它显然不会去那里。我没有详细了解原因(所以也许我不应该从一开始就实施自定义 PSHost),但还有一些进一步的解释:

    http://mshforfun.blogspot.com/2006/07/why-there-is-out-default-cmdlet.html

    https://msdn.microsoft.com/en-us/library/system.management.automation.runspaces.command.mergemyresults%28v=vs.85%29.aspx

    另外,假设您的主机不是控制台应用程序,并且您没有实现自己的 cmd 样式字符模式显示,您需要给它一个假缓冲区大小,因为它需要在给出之前咨询这个你写错误输出。 (不要给它 0,0,否则你会得到一个永无止境的空白行,因为它很难将输出放入一个没有大小的缓冲区。)我正在使用:

    class Whatever : PSHostRawUserInterface
    {
        public override Size BufferSize
        {
            get { return new Size(300, 5000); }
            set { }
        }
        
        ...
    }
    

    如果您是控制台应用,只需使用 Console.BufferWidth 和 Console.BufferHeight。

    更新:如果您希望在 ErrorRecord 对象中获取错误,而不是将预格式化的错误文本行转到 WriteErrorLine 覆盖,请挂钩 PowerShell.Streams.Error.DataAdding 事件并获取事件 args 上的 ItemAdded 属性。如果您在 GUI 中执行的操作不是简单的逐行输出,那么使用起来就不会那么不守规矩了。

    【讨论】:

      猜你喜欢
      • 2010-09-10
      • 2014-10-09
      • 2021-05-13
      • 2010-10-04
      • 2017-05-12
      • 1970-01-01
      • 2020-08-21
      • 2017-04-05
      • 1970-01-01
      相关资源
      最近更新 更多