Получить объект .NET Process для непрерывной очистки потока ввода? - PullRequest
2 голосов
/ 06 июля 2011

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

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

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

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

  1. Я прошу процесс выйти
  2. Я ввожу другую команду

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

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

Я могу справиться с P / Invoke, если это то, что нужно.

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

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

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

  1. Создание списка каталогов
  2. Запросите другую команду, предложив "C:> \"

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

  1. Составить список каталогов
  2. (подождите 10 секунд)
  3. Закрыть входной поток
  4. См. Подсказку

    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);
    

    }

1 Ответ

1 голос
/ 06 июля 2011

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

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

НТН,

Bart

...