控制台中的处理标准输出与重定向时不同

时间:2018-06-28 15:18:57

标签: c# windows asynchronous process stdout

从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”消息,并以三分之二的速度报告进度...但是看来这应该是可能的:)

1 个答案:

答案 0 :(得分:0)

假定随着进展的进行,“ ...”以递增方式出现,您需要执行的操作是捕获StandardOutput流并实际上一次读取一个字符,而不是使用DataRecieved事件。这样,您将看到每个句号被写出来。然后,您可以计算自己拥有的数量,并以此来推断进度。