我有一个Windows服务,它将处理plink.exe(Putty / SSH事件)的结果。我目前正成功捕获标准输出并通过标准输入向过程发送命令。
我的问题是,在我从控制台应用程序进程收到换行符之前,OutputDataReceived
事件似乎没有被提升。该过程提示输入密码,直到输入密码后才会出现换行符。
所以我的问题是,有没有办法逐个字符地处理标准输出,而不是从System.Diagnostics.Process
类逐行处理?
这是我的代码:
_processInfoTest = new ProcessStartInfo();
_processInfoTest.FileName = serviceSettings.PlinkExecutable;
_processInfoTest.Arguments = GetPlinkArguments(serviceSettings);
_processInfoTest.RedirectStandardOutput = true;
_processInfoTest.RedirectStandardError = true;
_processInfoTest.RedirectStandardInput = true;
_processInfoTest.UseShellExecute = false;
_processInfoTest.CreateNoWindow = true;
_processTest = new Process();
_processTest.StartInfo = _processInfoTest;
_processTest.OutputDataReceived += processTest_OutputDataReceived;
_processTest.ErrorDataReceived += processTest_OutputDataReceived;
_processTest.Start();
_processTest.BeginOutputReadLine();
_processTest.BeginErrorReadLine();
处理传入文本行的事件处理程序:
private static void processTest_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string line = e.Data;
if (line != null)
{
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(line, DateTime.Now));
if (line.Contains("If you do not trust this host, press Return to abandon the"))
{
_processTest.StandardInput.Write("y");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
// This code never gets called because the event doesn't get raised until a line-break occurs
if (line.Contains("'s password:"))
{
_processTest.StandardInput.Write("mypassword");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("Access granted"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.Success));
}
else if (line.Contains("Access denied") || line.Contains("Password authentication failed"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidUserOrPass));
}
else if (line.Contains("Host does not exist"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidHostname));
}
else if (line.Contains("Connection timed out"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.TimedOut));
}
}
}
谢谢!
答案 0 :(得分:1)
您正在使用的事件是行缓冲...您必须实现自己的读取器,在流读取器上调用Read()/ ReadAsync()以获取每个字符...
答案 1 :(得分:1)
我一直在寻找答案,在这里提出问题之后,我找到了解决办法。
不过,谢谢DarkSquirrel,你的头部钉了一针。这是我的解决方案:
_processInfoTest = new ProcessStartInfo();
_processInfoTest.FileName = serviceSettings.PlinkExecutable;
_processInfoTest.Arguments = GetPlinkArguments(serviceSettings);
_processInfoTest.RedirectStandardOutput = true;
_processInfoTest.RedirectStandardError = true;
_processInfoTest.RedirectStandardInput = true;
_processInfoTest.UseShellExecute = false;
_processInfoTest.CreateNoWindow = true;
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(_processInfoTest.Arguments, DateTime.Now));
_processTest = new Process();
_processTest.StartInfo = _processInfoTest;
_processTest.Start();
Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardError);
});
Task.Factory.StartNew(() =>
{
ProcessOutputCharacters(_processTest.StandardOutput);
});
我的方法:
private static void ProcessOutputCharacters(StreamReader streamReader)
{
int outputCharInt;
char outputChar;
string line = string.Empty;
while (-1 != (outputCharInt = streamReader.Read()))
{
outputChar = (char)outputCharInt;
if (outputChar == '\n' || outputChar == '\r')
{
if (line != string.Empty)
{
ProcessLine("Output: " + line);
}
line = string.Empty;
}
else
{
line += outputChar;
if (line.Contains("login as:"))
{
_processTest.StandardInput.Write("myusername");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("'s password:"))
{
_processTest.StandardInput.Write("mypassword");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
}
}
}
private static void ProcessLine(string line)
{
if (line != null)
{
WcfServerHelper.BroadcastRemoteCallback(x => x.PlinkTextOutput(line, DateTime.Now));
if (line.Contains("If you do not trust this host, press Return to abandon the"))
{
_processTest.StandardInput.Write("y");
_processTest.StandardInput.Write("\n");
_processTest.StandardInput.Flush();
}
if (line.Contains("Access granted"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.Success));
}
else if (line.Contains("Access denied") || line.Contains("Password authentication failed"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidUserOrPass));
}
else if (line.Contains("Host does not exist"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.InvalidHostname));
}
else if (line.Contains("Connection timed out"))
{
if (!_processTest.HasExited)
_processTest.Kill();
WcfServerHelper.BroadcastRemoteCallback(x => x.TestConnectionCallback(PlinkStatus.TimedOut));
}
}
}
我现在唯一的问题是当我发送用户名或密码(通过StandardInput)时,它只发送前5个字符。我会对这个问题进行一些研究,如果需要的话,将其作为一个单独的问题发布。
谢谢!