Получить объект .NET Process для непрерывного потока потока ввода?

73
5

Я пытаюсь изменить свою библиотеку классов, которая говорит с клиентом командной строки Mercurial, а новая в клиенте 1.9 - это возможность развернуть сервер и поговорить с ним по стандартным каналам ввода-вывода. Это действительно многообещающе, так как одна из главных головных проблем использования клиента командной строки Mercurial заключается в том, что, поскольку он написан на Python, есть немного накладных расходов, зависящих от клиента, даже для простого запроса его версии.


Однако есть одна проблема. До сих пор я получил результат от клиента командной строки, читая стандартный вывод/ошибку, и когда процесс завершается, потоки сливаются, и я могу прочитать конец потока.


Однако в этом новом режиме процесс будет выгружать много текста в стандартную ошибку/вывод, а затем сидеть в ожидании следующей команды. Отлично подходит для уменьшения накладных расходов, но паршивая, так как .NET буферизует эти потоки.


Другими словами, поскольку процесс не выходит, я не получаю последнюю часть этого вывода, пока:


    Я прошу процесс выйти
    Я выдаю еще одну команду

Есть ли способ для меня


    Попросите буфер сбросить флажок, что означает, что я получаю все, что находится в буфере, даже если этого недостаточно, чтобы сбросить буфер самостоятельно.
    Если нет, могу ли я установить размер буфера на 1 символ?
    Что-нибудь еще, что я могу сделать?

Я могу иметь дело с P/Invoke, если это то, что нужно.


Здесь LINQPad программа доказательств концепции, которая иллюстрирует:


Что он делает, это развернуть командную строку и передать ей команду DIR, однако обратите внимание, что только до тех пор, пока эти 10 секунд не пройдут внутри цикла, программа выведет приглашение "C: > ", чтобы командная строка выводилась сразу после создавая список каталогов.


Другими словами, это то, что делает командная строка:


    Составить список каталогов
    Попросите другую команду, предложив "C: > \"

Однако это то, что видит ниже программа:


    Составить список каталогов
    (подождите 10 секунд)
    Закройте входной поток

    См. приглашение


    void Main()
    {   string clientExecutablePath = "cmd.exe";


    var psi = new ProcessStartInfo();
    psi.FileName = clientExecutablePath;
    psi.RedirectStandardError = true;
    psi.RedirectStandardInput = true;
    psi.RedirectStandardOutput = true;
    psi.CreateNoWindow = true;
    psi.WorkingDirectory = @"C:\";
    psi.WindowStyle = ProcessWindowStyle.Hidden;
    psi.UseShellExecute = false;
    psi.ErrorDialog = false;
    psi.StandardErrorEncoding = Encoding.GetEncoding("Windows-1252");
    psi.StandardOutputEncoding = Encoding.GetEncoding("Windows-1252");

    var p = Process.Start(psi);

    var input = p.StandardInput;
    var output = p.StandardOutput;
    var error = p.StandardError;

    Action<StreamReader, string> reader = delegate(StreamReader streamReader, string prefix)
    {
    string line;
    while ((line = streamReader.ReadLine()) != null)
    {
    Debug.WriteLine(prefix + line);
    }
    };

    IAsyncResult outputReader = reader.BeginInvoke(output, "o: ", null, null);
    IAsyncResult errorReader = reader.BeginInvoke(error, "e: ", null, null);

    input.Write("dir\n");
    input.Flush();

    while (!p.HasExited)
    {
    Thread.Sleep(10000);
    input.Close();
    }

    reader.EndInvoke(outputReader);
    reader.EndInvoke(errorReader);


    }


спросил(а) 2011-07-06T14:34:00+04:00 9 лет, 6 месяцев назад
1
Решение
61

Я не думаю, что есть способ заставить флеш, однако у меня есть несколько альтернатив, которые могут вам помочь:


    Я замечаю, что вы используете ReadLine() для чтения потока. Это не будет возвращено до тех пор, пока не будет написана полная строка вывода (включая CRLF или, по меньшей мере, LF). Поэтому вы можете захотеть использовать Read() вместо этого, который будет читать только один символ. Вы можете обнаружить, что это дает вам последний фрагмент вывода, который вы ищете, если серверный процесс не пишет CRLF на последней строке, пока он не выйдет.
    Вы можете читать потоки асинхронно, добавляя обработчики к событиям OutputDataReceived и ErrorDataReceived в Process, а затем используйте BeginOutputReadLine и BeginErrorReadLine для начала приема данных. Я не знаю, как часто вызывают события: регулярные интервалы, если доступны какие-либо данные, всякий раз, когда получена полная строка данных или для каждого символа.

НТН,


Барта

ответил(а) 2011-07-06T14:48:00+04:00 9 лет, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема