Process.Start捕获标准输出字符

时间:2013-03-31 02:29:21

标签: c# .net system.diagnostics

我有一个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));
        }
    }
}

谢谢!

2 个答案:

答案 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个字符。我会对这个问题进行一些研究,如果需要的话,将其作为一个单独的问题发布。

谢谢!