处理Process后触发Process.ErrorDataReceived?

时间:2014-07-03 00:45:07

标签: process dispose

我的代码如下,有时会在" errorWaitHandle.Set();"当我的流程实例被处理时,怎么会发生这种情况?

" System.ObjectDisposedException:安全句柄已关闭"

public static int Execute(string filename, string arguments, out string output, out string error, int timeoutInMilliSeconds = Timeout.Infinite)
    {
        using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false), errorWaitHandle = new AutoResetEvent(false))
        {
            //separate using for process to ensure this is disposed before handles above.
            using (System.Diagnostics.Process process = new System.Diagnostics.Process())
            {
                process.StartInfo.FileName = filename;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;

                StringBuilder outputSB = new StringBuilder();
                StringBuilder errorSB = new StringBuilder();

                process.OutputDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        outputWaitHandle.Set();
                    }
                    else
                    {
                        outputSB.AppendLine(e.Data);
                    }
                };
                process.ErrorDataReceived += (sender, e) =>
                {
                    if (e.Data == null)
                    {
                        errorWaitHandle.Set();
                    }
                    else
                    {
                        errorSB.AppendLine(e.Data);
                    }
                };

                process.Start();

                //See http://stackoverflow.com/questions/139593/processstartinfo-hanging-on-waitforexit-why
                // for why we need to read output and error asynch
                process.BeginOutputReadLine();
                process.BeginErrorReadLine();

                if (!process.WaitForExit(timeoutInMilliSeconds) ||
                    !outputWaitHandle.WaitOne(timeoutInMilliSeconds) ||
                    !errorWaitHandle.WaitOne(timeoutInMilliSeconds))
                {
                    throw new TimeoutException(
                        string.Format("Executing [{0}] with argument [{1}] didn't finish within timeout {2} milliseconds", filename, arguments, timeoutInMilliSeconds));
                }

                output = outputSB.ToString();
                error = errorSB.ToString();

                return process.ExitCode;
            }
        }
    }

2 个答案:

答案 0 :(得分:2)

解决方案是将OutputDataReceived和ErrorDataReceived事件订阅到实际方法而不是匿名方法。这样您就可以在Dispose方法中取消订阅。请在此处查看完整代码:

https://github.com/borismod/OsTestFramework/blob/master/OsTestFramework/ProcessExecutor.cs

答案 1 :(得分:2)

我发现Process事件可能会因意外命令而触发,因为它们具有异步性质(即“Exited”在“ErrorDataReceived”之前被触发)。

您也不知道这些事件是如何在Process类的封面下进行连接的,因此您不了解各种对象的生命周期。当您的处理程序被调用时,Process对象可能已经(并且显然是)被处理掉了。

我试图像你一样处理这个问题;通过使用AutoResetEvent并在各自的事件处理程序中构建错误/数据字符串。

我最终解决此问题的方法是拨打Process.WaitForExit()两次:

System.Diagnostics.Process process = new System.Diagnostics.Process()

// Process setup code
if(process.WaitForExit(timeout)){

    process.WaitForExit(); // Note the lack of a timeout parameter

    // By now all your events should have fired and your strings built
    string errorString = errorSB.ToString();
}

MSDN的摘录指出:

  

标准输出重定向到异步事件时   处理程序,输出处理可能没有   此方法返回时完成。确保异步事件   处理完成后,调用WaitForExit()重载   从此过载中接收到true后不接受任何参数。帮助   确保在Windows窗体中正确处理Exited事件   应用程序,设置SynchronizingObject属性。

     

来源:https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110)