【问题标题】:Process stdout different in console vs when redirected在控制台中处理标准输出与重定向时不同
【发布时间】:2018-06-28 15:18:56
【问题描述】:

我们要从 Windows C# 应用程序运行 cipher.exe 以从未使用的磁盘空间(在本例中为 D: 驱动器)中删除数据:

cipher.exe /w:D:\

从 Windows 命令行完成时,输出将是:

To remove as much data as possible, please close all other applications while
running CIPHER /W.
Writing 0x00
...................................................................................................
Writing 0xFF
...................................................................................................
Writing Random Numbers
...................................................................................................

那些带点的行在密码过程中逐渐填满。我们认为我们会从 C# 应用程序中读取这些点来跟踪进度并将其显示在进度条上。但是我们注意到,当标准输出被捕获或重定向时,顺序是不同的:

cipher.exe /w:D:\ > out.txt

生成具有以下内容的文件:

To remove as much data as possible, please close all other applications while
running CIPHER /W.
Writing 0x00
Writing 0xFF
Writing Random Numbers
...................................................................................................
...................................................................................................
...................................................................................................

因此,当我们尝试从 C# 应用程序中捕获它时,我们直到过程结束时才读取这些点。例如使用以下代码时:

private void RunCipher()
{
    using (Process cipherProcess = new Process())
    {
        try
        {
            // Cipher does three phases, one with 00s, one with FFs and one with random bits
            // We count dots in the output for each phase to track the progress
            // The amount of dots per phase is always 99 (independent of the volume size)
            // See the end of this file to find the expected output

            cipherProcess.StartInfo.FileName = "cipher.exe";
            cipherProcess.StartInfo.Arguments = @"/w:D:\";

            cipherProcess.StartInfo.RedirectStandardOutput = true;
            cipherProcess.StartInfo.RedirectStandardError = true;
            cipherProcess.StartInfo.RedirectStandardInput = true;
            cipherProcess.StartInfo.UseShellExecute = false;
            cipherProcess.StartInfo.CreateNoWindow = true;

            cipherProcess.OutputDataReceived += CipherProcess_OutputDataReceived;
            cipherProcess.ErrorDataReceived += CipherProcess_ErrorDataReceived;
            cipherProcess.Exited += CipherProcess_Exited;

            cipherProcess.Start();

            cipherProcess.BeginOutputReadLine();
            cipherProcess.BeginErrorReadLine();
            cipherProcess.WaitForExit();
        }
        catch
        {
            Console.WriteLine("Exception occured");
        }
    }
    Console.WriteLine("Fininshed");
}

private void CipherProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine("OutputDataReceived: " + e.Data);
}

private void CipherProcess_Exited(object sender, EventArgs e)
{
    Console.WriteLine("Exited");
}

private void CipherProcess_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    Console.WriteLine("ErrorDataReceived: " + e.Data);
}

输出是:

OutputDataReceived: To remove as much data as possible, please close all other applications while
OutputDataReceived: running CIPHER /W.
OutputDataReceived: Writing 0x00
The thread 0x41ac has exited with code 0 (0x0).
The thread 0x408c has exited with code 0 (0x0).
OutputDataReceived: Writing 0xFF
The thread 0x39e4 has exited with code 0 (0x0).
The thread 0x3e30 has exited with code 0 (0x0).
OutputDataReceived: Writing Random Numbers
The thread 0x34ac has exited with code 0 (0x0).
The thread 0x3960 has exited with code 0 (0x0).
OutputDataReceived: ...................................................................................................
OutputDataReceived: ...................................................................................................
OutputDataReceived: ...................................................................................................
ErrorDataReceived: 
OutputDataReceived: 
Fininshed

我们也尝试不使用 OutputDataReceived+BeginOutputReadLine 而是使用 process.StandardOutput.Read() 但它有同样的问题:它首先读取所有三个 'Writing (..)' 输出:

private void RunCipher2()
{
    using (Process cipherProcess = new Process())
    {
        try
        {
            cipherProcess.StartInfo.FileName = "cipher.exe";
            cipherProcess.StartInfo.Arguments = @"/w:D:\";

            cipherProcess.StartInfo.RedirectStandardOutput = true;
            cipherProcess.StartInfo.RedirectStandardError = true;
            cipherProcess.StartInfo.RedirectStandardInput = true;
            cipherProcess.StartInfo.UseShellExecute = false;
            cipherProcess.StartInfo.CreateNoWindow = true;

            cipherProcess.Start();

            while (!cipherProcess.StandardOutput.EndOfStream)
            {
                char nextChar = (char)cipherProcess.StandardOutput.Read();
                Console.Write(nextChar);
            }

            cipherProcess.WaitForExit();
        }
        catch
        {
            Console.WriteLine("Exception occured");
        }
    }
    Console.WriteLine("Fininshed");
}

而且输出仍然是我们意想不到的:

To remove as much data as possible, please close all other applications while
running CIPHER /W.
Writing 0x00
Writing 0xFF
Writing Random Numbers
...................................................................................................
...................................................................................................
...................................................................................................
Fininshed

我们已经找到了这个线程,但是该解决方案需要重定向的输出才能首先工作:Read Process StandardOutput before New Line Received

这里发生了什么?有没有办法使这项工作?或者以其他方式跟踪进度?当然,我们可以检测到“Writing”消息并以三分之二报告进度……但这似乎应该是可能的:)

【问题讨论】:

    标签: c# windows asynchronous process stdout


    【解决方案1】:

    假设“...”随着进度的进行而逐渐出现,您需要做的不是使用 DataRecieved 事件,而是捕获 StandardOutput 流并实际一次读取一个字符。这样,您将看到每个单独的句号,因为它被写出来。然后,您可以计算您拥有的数量并以此来推断进度。

    【讨论】:

    • 我们试过了,但是在阅读“Writing 0x00”之后需要很长时间,然后再阅读“Writing 0xFF”。 IE。再次似乎是乱序读取虚线。
    • 我们发现即使将标准输出重定向到文件(没有 C# 应用程序),直到最后才会写入点。更新了原始问题并提供了更多详细信息...
    猜你喜欢
    • 2019-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    相关资源
    最近更新 更多