We have a large and pretty complicated C# application that sometimes (rarely) sends back crash reports from users containing the following:
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>
Using IlSpy i checked the framework code, and it seems the exception must come from exactly here (in 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);
}
}
}
I then created a test program to see if I could replicate the situation, and sure enough, this test does it:
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);
}
}
}
Bonus info: I can only get 'the handle is invalid' by closing the native handle, using DangerousGetHandle() and p/invoke. Calling AsyncWaitHandle.Close() generates 'the safe handle is closed', instead.
Now, the problem is that searching through our code base, I find not a single instance of AsyncWaitHandle. There are tons of BeginInvoke, but no waiting that could accidentally close the native handle. Unless there are other ways to do it, that I missed.
So the question:
What could cause this native handle to be closed?
Is there some framework race condition I should be aware of? It seems to only happen when lots of things are going on at the same time, from multiple threads.