簡體   English   中英

Process.Start捕獲標准輸出字符

[英]Process.Start capturing standard output characters

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

謝謝!

您正在使用的事件是行緩沖...您必須實現自己的讀取器,在流讀取器上調用Read()/ ReadAsync()以獲取每個char ...

我一直在尋找答案,在問這里問題之后,我找到了解決辦法。

順便說一句,感謝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個字符。 我將對這個問題進行一些研究,如果需要的話,將其作為一個單獨的問題發布。

謝謝!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM