我下面有我的代码,有时在ObjectDisposedException
处获得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;
}
}
}
[我发现Process
事件由于其异步特性(例如,在“ ErrorDataReceived”之前触发了“退出”)。
您还不知道这些事件如何在Process类的掩盖下进行连接,因此您对各个对象的生存期并不了解。到您的处理程序被调用时,Process对象可能已经(并且显然已经被处置)。
我试图与您几乎完全一样地解决这个问题;通过使用AutoResetEvent
并从它们各自的事件处理程序中构建Error / Data字符串。
我最终解决此问题的方法是两次调用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)
解决方案是将OutputDataReceived
和ErrorDataReceived
事件订阅给实际方法,而不是匿名方法。这样,您可以在Dispose()
方法中退订。