У меня была очень похожая (возможно, точная) проблема, как вы описываете:
- Мне нужно, чтобы обновления консоли доставлялись мне асинхронно.
- Мне нужно, чтобы обновления были обнаружены независимо от того, был ли введен символ новой строки.
То, что я закончил, выглядит так:
- Запустить «бесконечный» цикл вызова
StandardOutput.BaseStream.BeginRead
.
- В обратном вызове для
BeginRead
проверьте, является ли возвращаемое значение EndRead
равным 0
; это означает, что консольный процесс закрыл свой выходной поток (то есть никогда больше не будет записывать что-либо в стандартный вывод).
- Поскольку
BeginRead
заставляет вас использовать буфер постоянной длины, проверьте, равно ли возвращаемое значение EndRead
размеру буфера. Это означает, что может быть больше выходных данных, ожидающих чтения, и может быть желательно (или даже необходимо), чтобы эти выходные данные обрабатывались целиком. Что я сделал, так это оставил StringBuilder
и добавил результат, прочитанный до сих пор. Всякий раз, когда вывод читается, но его длина <длина буфера, сообщите себе (я делаю это с событием), что есть вывод, отправьте содержимое <code>StringBuilder подписчику, а затем очистите его.
Однако , в моем случае я просто записывал больше материала в стандартный вывод консоли. Я не уверен, что означает «обновление» вывода в вашем случае.
Обновление: Я только что понял (разве вы не объясняете, что вы делаете с большим опытом в обучении?), Что в приведенной выше логике есть ошибка "один за другим": если длина выходных данных читается если BeginRead
равно в точности , то равно длине вашего буфера, то эта логика будет хранить выходные данные в StringBuilder
и блоке, пытаясь увидеть, есть ли еще выходные данные для добавления. «Текущие» выходные данные будут отправлены вам обратно только тогда, когда / если будет доступно больше выходных данных, как часть большей строки.
Очевидно, что для того, чтобы сделать это на 100% правильно, нужен какой-то метод защиты от этого (или большой буфер плюс вера в силу удачи).
Обновление 2 (код):
ПРЕДУПРЕЖДЕНИЕ:
Этот код не готов к производству. Это результат того, что я быстро взломал проверенное концептуальное решение, чтобы сделать то, что нужно сделать. Пожалуйста, не используйте его в своем производственном приложении. Если этот код вызывает у вас ужасные вещи, я буду притворяться, что кто-то другой написал его.
public class ConsoleInputReadEventArgs : EventArgs
{
public ConsoleInputReadEventArgs(string input)
{
this.Input = input;
}
public string Input { get; private set; }
}
public interface IConsoleAutomator
{
StreamWriter StandardInput { get; }
event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;
}
public abstract class ConsoleAutomatorBase : IConsoleAutomator
{
protected readonly StringBuilder inputAccumulator = new StringBuilder();
protected readonly byte[] buffer = new byte[256];
protected volatile bool stopAutomation;
public StreamWriter StandardInput { get; protected set; }
protected StreamReader StandardOutput { get; set; }
protected StreamReader StandardError { get; set; }
public event EventHandler<ConsoleInputReadEventArgs> StandardInputRead;
protected void BeginReadAsync()
{
if (!this.stopAutomation) {
this.StandardOutput.BaseStream.BeginRead(this.buffer, 0, this.buffer.Length, this.ReadHappened, null);
}
}
protected virtual void OnAutomationStopped()
{
this.stopAutomation = true;
this.StandardOutput.DiscardBufferedData();
}
private void ReadHappened(IAsyncResult asyncResult)
{
var bytesRead = this.StandardOutput.BaseStream.EndRead(asyncResult);
if (bytesRead == 0) {
this.OnAutomationStopped();
return;
}
var input = this.StandardOutput.CurrentEncoding.GetString(this.buffer, 0, bytesRead);
this.inputAccumulator.Append(input);
if (bytesRead < this.buffer.Length) {
this.OnInputRead(this.inputAccumulator.ToString());
}
this.BeginReadAsync();
}
private void OnInputRead(string input)
{
var handler = this.StandardInputRead;
if (handler == null) {
return;
}
handler(this, new ConsoleInputReadEventArgs(input));
this.inputAccumulator.Clear();
}
}
public class ConsoleAutomator : ConsoleAutomatorBase, IConsoleAutomator
{
public ConsoleAutomator(StreamWriter standardInput, StreamReader standardOutput)
{
this.StandardInput = standardInput;
this.StandardOutput = standardOutput;
}
public void StartAutomate()
{
this.stopAutomation = false;
this.BeginReadAsync();
}
public void StopAutomation()
{
this.OnAutomationStopped();
}
}
Используется так:
var processStartInfo = new ProcessStartInfo
{
FileName = "myprocess.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
};
var process = Process.Start(processStartInfo);
var automator = new ConsoleAutomator(process.StandardInput, process.StandardOutput);
// AutomatorStandardInputRead is your event handler
automator.StandardInputRead += AutomatorStandardInputRead;
automator.StartAutomate();
// do whatever you want while that process is running
process.WaitForExit();
automator.StandardInputRead -= AutomatorStandardInputRead;
process.Close();