Runspace忽略自定义PSHostUserInterface

时间:2013-10-03 16:08:38

标签: c# powershell runspace

背景

我正在编写一个以编程方式执行PowerShell脚本的应用程序。此应用程序具有自定义PSHost实现,以允许脚本输出日志记录语句。目前,我看到的行为是某些请求被正确转发到我的自定义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会出现奇怪的行为?

2 个答案:

答案 0 :(得分:6)

您需要为PSHostRawUserInterface提供实现。

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

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

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

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

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

答案 1 :(得分:1)

对于其他人在实现PSHostUserInterface和PSHostRawUserInterface后仍然苦苦挣扎,并且在调用Write-Error时发现WriteErrorLine()被完全忽略,即使Warning,Debug和Verbose进入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, PipelineResultTypes.Output);

powershell.Invoke() // you had this already

这会将Errors流合并到您的控制台输出中,否则它显然不会去那里。我没有详细了解原因(也许我不应该开始实施自定义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样式字符模式显示,那么您需要给它一个假的缓冲区大小,因为它需要在给出Write-Error输出之前请先咨询。 (不要给它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中执行除了简单的逐行输出之外的其他操作,那就不那么难以驾驭了。