简体   繁体   English

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

[英]Process stdout different in console vs when redirected

From a Windows C# application we want to run cipher.exe to remove data from unused disk space, in this case the D: drive: 在Windows C#应用程序中,我们要运行cipher.exe来从未使用的磁盘空间(在本例中为D:驱动器)中删除数据:

cipher.exe /w:D:\

When done from a Windows command line, the output would be: 从Windows命令行完成后,输出为:

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

Those lines with dots fill up incrementally over the duration of the cipher procedure. 这些带有点的线在加密过程的持续时间内逐渐填充。 We thought we'd read those dots from a C# application to track the progress and display it on a progress bar. 我们认为应该从C#应用程序中读取这些点以跟踪进度并将其显示在进度条上。 However we noticed that when the stdout is captured or redirected then the order is different: 但是,我们注意到,当捕获或重定向标准输出时,顺序是不同的:

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

Results in a file with the following contents: 结果文件包含以下内容:

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

And so when we try to capture that from a C# application, we don't read the dots until the very end of the process. 因此,当我们尝试从C#应用程序中捕获这些内容时,直到过程结束时才读取点。 For example when using the following code: 例如,当使用以下代码时:

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);
}

The output of that is: 输出为:

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

We also tried without OutputDataReceived+BeginOutputReadLine and instead using process.StandardOutput.Read() but it has the same problem: it first reads all three of the 'Writing (..)' output: 我们还尝试了不使用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");
}

And still the output is in order we wouldn't expect: 而且输出仍然是我们无法期望的:

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

We already found this thread, but that solution requires the redirected output to first work: Read Process StandardOutput before New Line Received 我们已经找到了该线程,但是该解决方案需要将重定向的输出进行首次工作: 在收到新行之前读取Process StandardOutput

What's happening here? 这里发生了什么事? Is there a way to make this work? 有没有办法使这项工作? Or to track the progress in another way? 还是以其他方式跟踪进度? Of course we could detect on the 'Writing' messages and report progress in thirds... but it seems like this should be possible :) 当然,我们可以检测到“ Writing”消息,并以三分之二的速度报告进度……但是看来这应该是可能的:)

Assuming that the "..." comes out incrementally as progress is made, what you need to do is instead of using the DataRecieved event, is capture the StandardOutput stream and actually read it one character at a time. 假设随着进展的进行,“ ...”以递增方式出现,那么您需要执行的操作是捕获StandardOutput流并一次读取一个字符,而不是使用DataRecieved事件。 This way you'll see each individual full stop as it's written out. 这样,您将看到每个句号被写出来。 You can then count how many you've had and use that to infer the progress. 然后,您可以计算自己拥有的数量,并以此来推断进度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM