Пользовательский PSHostUserInterface игнорируется Runspace

Фон

Я пишу приложение, которое программно выполняет сценарии PowerShell. Это приложение имеет пользовательскую реализацию PSHost, позволяющую сценариям выводить операторы регистрации. В настоящее время я наблюдаю следующее: некоторые запросы правильно перенаправляются на мой пользовательский PSHost, а другие просто игнорируются.

Все становится еще более странным, когда я начал проверять переменную $Host в своих сценариях, которые, кажется, предполагают, что моя пользовательская PSHost даже не используется.

Код

У меня есть код, выполняющий PowerShell в приложении .NET:

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?


person Mike Bailey    schedule 03.10.2013    source источник
comment
Вы реализуете свойство RawUI? У меня есть PSCustomHost, который я реализовал для утилиты Make-PS1ExeWrapper, которая работает. Вы можете проверить это здесь: skydrive.live.com/   -  person Keith Hill    schedule 04.10.2013
comment
Согласно документации Microsoft на RawUI, мне это не нужно: If you are also implementing a PSHostRawUserInterface class, return an instance of the class.   -  person Mike Bailey    schedule 04.10.2013


Ответы (2)


Вам необходимо предоставить реализацию для PSHostRawUserInterface.

Write-Host в конечном итоге вызывает вашу версию Write(ConsoleColor, ConsoleColor, string). PowerShell полагается на необработанную реализацию пользовательского интерфейса для цветов переднего плана и фона.

Я проверил это с помощью примера кода. Вместо обращения к файлу ps1 я напрямую вызвал Write-Host:

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

Запустив сценарий, PowerShell обрабатывал исключения за вас. Вызывая команду напрямую, вы можете легко увидеть исключения. Если бы вы проверили $error в исходном примере, вы бы увидели полезную ошибку.

Обратите внимание, что значение $host никогда не является фактической реализацией. PowerShell скрывает реальную реализацию, упаковывая ее. Я забыл точные детали того, почему он завернут.

person Jason Shirk    schedule 13.10.2013

Для тех, кто все еще испытывает затруднения после реализации PSHostUserInterface и PSHostRawUserInterface и обнаруживает, что WriteErrorLine() полностью игнорируется при вызове Write-Error, даже несмотря на то, что 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

Это объединит поток ошибок с выводом вашей консоли, иначе он, по-видимому, туда не пойдет. У меня нет подробного понимания того, почему (поэтому, возможно, мне не следует с самого начала реализовывать собственный 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 -Ошибка вывода. (Не давайте ему 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 и получите свойство ItemAdded. в аргументах события. Гораздо менее неуправляемый в работе, если вы делаете что-то кроме простого построчного вывода в своем графическом интерфейсе.

person user1454265    schedule 12.03.2015