我们有一个庞大且非常复杂的C#应用程序,有时(很少)从包含以下内容的用户发回崩溃报告:
The handle is invalid
<stacktrace>
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.Threading.EventWaitHandle.Set()
at System.Windows.Forms.Control.ThreadMethodEntry.Complete()
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
</stacktrace>
使用IlSpy我检查了框架代码,似乎异常必须来自此处(在Control.InvokeMarshaledCallback中):
try
{
if (NativeWindow.WndProcShouldBeDebuggable && !threadMethodEntry.synchronous)
{
this.InvokeMarshaledCallback(threadMethodEntry);
}
else
{
try
{
this.InvokeMarshaledCallback(threadMethodEntry);
}
catch (Exception ex)
{
threadMethodEntry.exception = ex.GetBaseException();
}
}
}
finally
{
threadMethodEntry.Complete(); // <-- here! This calls Set() on the wait handle
if (!NativeWindow.WndProcShouldBeDebuggable && threadMethodEntry.exception != null && !threadMethodEntry.synchronous)
{
Application.OnThreadException(threadMethodEntry.exception);
}
}
}
然后我创建了一个测试程序,看看我是否可以复制这种情况,果然,这个测试可以做到:
using System;
using System.Windows.Forms;
namespace Test
{
static class Program
{
[STAThread]
static void Main()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var ex = (Exception)e.ExceptionObject;
MessageBox.Show(ex.Message);
MessageBox.Show(ex.StackTrace);
}
}
}
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Test
{
public partial class Form1 : Form
{
[DllImport("kernel32.dll", SetLastError = true, PreserveSig = true)]
static extern bool CloseHandle(
[In] IntPtr handle);
public Form1()
{
InitializeComponent();
}
void Post()
{
Text = "Some async method";
}
private void button1_Click(object sender, EventArgs e)
{
IAsyncResult wait = this.BeginInvoke((Action)Post);
// Close the wait handle. When Post() returns the framework
// tries to call .Set() on the wait event, but we closed it!
IntPtr nativehandle = wait.AsyncWaitHandle.SafeWaitHandle.DangerousGetHandle();
CloseHandle(nativehandle);
}
}
}
奖励信息:我只能得到句柄无效&#39;通过使用DangerousGetHandle()和p / invoke关闭本机句柄。调用AsyncWaitHandle.Close()生成&#39;安全句柄已关闭。
现在,问题是搜索我们的代码库,我找不到AsyncWaitHandle的单个实例。有大量的BeginInvoke,但没有等待,可能会意外关闭本机句柄。除非有其他办法,否则我错过了。
所以问题是:
什么可能导致这个原生句柄被关闭?
我应该注意一些框架竞争条件吗?似乎只有在多个线程同时发生许多事情时才会发生。