0

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

谢谢!

4

2 回答 2

1

您正在使用的事件是行缓冲的...您将必须实现自己的阅读器,该阅读器在流阅读器上调用 Read() / ReadAsync() 以获取每个字符...

于 2013-03-31T03:01:15.017 回答
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));
        }
    }

}

我现在唯一的问题是当我发送用户名或密码(通过标准输入)时,它只发送前 5 个字符。如果需要,我将对此问题进行一些研究并将其作为单独的问题发布。

谢谢!

于 2013-03-31T03:07:37.217 回答