拍摄过程中由于输出和错误的正确顺序 [英] capture process stdout and stderr in the correct ordering

查看:183
本文介绍了拍摄过程中由于输出和错误的正确顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我启动从C#的过程如下:

I launch a process from C# as follows:

public bool Execute()
{
    ProcessStartInfo startInfo = new ProcessStartInfo();

    startInfo.Arguments =  "the command";
    startInfo.FileName = "C:\\MyApp.exe";

    startInfo.UseShellExecute = false;
    startInfo.RedirectStandardOutput = true;
    startInfo.RedirectStandardError = true;

    Log.LogMessage("{0} {1}", startInfo.FileName, startInfo.Arguments);

    using (Process myProcess = Process.Start(startInfo))
    {
        StringBuilder output = new StringBuilder();
        myProcess.OutputDataReceived += delegate(object sender, DataReceivedEventArgs e)
        {
            Log.LogMessage(Thread.CurrentThread.ManagedThreadId.ToString() + e.Data);
        };
        myProcess.ErrorDataReceived += delegate(object sender, DataReceivedEventArgs e)
        {
            Log.LogError(Thread.CurrentThread.ManagedThreadId.ToString() +  " " + e.Data);            
        };

        myProcess.BeginErrorReadLine();
        myProcess.BeginOutputReadLine();

        myProcess.WaitForExit();

    }

    return false;
}



但是,这有一个问题......如果有问题的应用程序写入到std并按此顺序STD错误:

But this has a problem... if the app in question writes to std out and std err in this order:

std out: msg 1
std err: msg 2
std out: msg 3

然后我从日志中看到的输出是:

Then the output I see from the logs is:

msg 2
msg 1
msg 3

这似乎是因为事件处理程序在另一个线程中执行。所以我的问题是如何能在写作过程中为std犯错和性病出的顺序进行维护?

This seems to be because the event handlers are executed in another thread. So my question is how can the order of the process writing to std err and std out be maintained?

我想用一个时间戳,但我不认为这会由于工作线程的预见性。

I thought of using a time stamp but I don't think this will work due to the preemptive nature of threads..

更新:证实,使用上的数据的时间戳是没有用的。

Update: Confirmed that using a time stamp on the data is no use.

最后更新:接受的答案解决了这个问题 - 但它确实有一个缺点,当流被合并没有办法知道哪个流被写入。因此,如果你需要写的逻辑来STDERR ==失败,而不是你可能仍然被拧紧应用程序退出代码。

Final update: The accepted answer solves this problem - however it does have one drawback, when the streams are merged there is no way to know which stream was written to. Hence if you require the logic of write to stderr == failure rather than the app exit code you might still be screwed.

推荐答案

据我了解,你想保留标准输出/标准错误消息的顺序。我没有看到任何体面的方式用C#管理过程要做到这一点(反思 - 是的,讨厌的子类黑客攻击 - 是的)。看来,这几乎是硬编码。

As far I understand, you want to preserve the order of stdout/stderr messages. I don't see any DECENT way to do this with C# managed Process(reflection - yes, nasty subclassing hacking - yes). It seems that it's pretty much hardcoded.

这功能不依赖于线程自己。如果你想保持顺序, STDOUT STDERROR 必须使用同一个句柄(缓冲)。如果他们使用相同的缓冲,这将同步

This functionality does not depend on threads themselves. If you want to keep the order, STDOUT and STDERROR have to use same handle(buffer). If they use the same buffer, it's going to be synchronized.

下面是Process.cs一个片段:

Here is a snippet from Process.cs:

 if (startInfo.RedirectStandardOutput) {
    CreatePipe(out standardOutputReadPipeHandle, 
               out startupInfo.hStdOutput, 
               false);
    } else {
    startupInfo.hStdOutput = new SafeFileHandle(
         NativeMethods.GetStdHandle(
                         NativeMethods.STD_OUTPUT_HANDLE), 
                         false);
}

if (startInfo.RedirectStandardError) {
    CreatePipe(out standardErrorReadPipeHandle, 
               out startupInfo.hStdError, 
               false);
    } else {
    startupInfo.hStdError = new SafeFileHandle(
         NativeMethods.GetStdHandle(
                         NativeMethods.STD_ERROR_HANDLE),
                         false);
}



你可以看到,有会是两个缓冲区,如果我们有两个缓冲区,我们已经失去了订单信息。

as you can see, there are gonna be two buffers, and if we have two buffers, we have already lost the order information.

基本上,你需要创建自己的过程()类,它可以处理这种情况。伤心?是。
中的好消息是,它并不难,它似乎很简单。下面是计算器,而不是C#,但有足够的了解该算法采取了代码:

Basically, you need to create your own Process() class that can handle this case. Sad? Yes. The good news is that it's not hard, it seems pretty simple. Here is a code taken from StackOverflow, not C# but enough to understand the algorithm:

function StartProcessWithRedirectedOutput(const ACommandLine: string; const AOutputFile: string;
  AShowWindow: boolean = True; AWaitForFinish: boolean = False): Integer;
var
  CommandLine: string;
  StartupInfo: TStartupInfo;
  ProcessInformation: TProcessInformation;
  StdOutFileHandle: THandle;
begin
  Result := 0;

  StdOutFileHandle := CreateFile(PChar(AOutputFile), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, 0);
  Win32Check(StdOutFileHandle <> INVALID_HANDLE_VALUE);
  try
    Win32Check(SetHandleInformation(StdOutFileHandle, HANDLE_FLAG_INHERIT, 1));
    FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
    FillChar(ProcessInformation, SizeOf(TProcessInformation), 0);

    StartupInfo.cb := SizeOf(TStartupInfo);
    StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESTDHANDLES;
    StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
    StartupInfo.hStdOutput := StdOutFileHandle;
    StartupInfo.hStdError := StdOutFileHandle;

    if not(AShowWindow) then
    begin
      StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESHOWWINDOW;
      StartupInfo.wShowWindow := SW_HIDE;
    end;

    CommandLine := ACommandLine;
    UniqueString(CommandLine);

    Win32Check(CreateProcess(nil, PChar(CommandLine), nil, nil, True,
      CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInformation));

    try
      Result := ProcessInformation.dwProcessId;

      if AWaitForFinish then
        WaitForSingleObject(ProcessInformation.hProcess, INFINITE);

    finally
      CloseHandle(ProcessInformation.hProcess);
      CloseHandle(ProcessInformation.hThread);
    end;

  finally
    CloseHandle(StdOutFileHandle);
  end;
end;

来源:的如何大量输出重定向从CreateProcess的执行的命令?

相反的文件,要使用CreatePipe。从管道,可以异步读像这样:

Instead of file, you want to use CreatePipe. From pipe, you can read asynchronously like so:

standardOutput = new StreamReader(new FileStream(
                       standardOutputReadPipeHandle, 
                       FileAccess.Read, 
                       4096, 
                       false),
                 enc, 
                 true, 
                 4096);

和BeginReadOutput()

and BeginReadOutput()

  if (output == null) {
        Stream s = standardOutput.BaseStream;
        output = new AsyncStreamReader(this, s, 
          new UserCallBack(this.OutputReadNotifyUser), 
             standardOutput.CurrentEncoding);
    }
    output.BeginReadLine();

这篇关于拍摄过程中由于输出和错误的正确顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆