Skip to content

Instantly share code, notes, and snippets.

@retran
Last active January 5, 2020 08:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save retran/b57e4db1a173048c2cee49ac6d523fc2 to your computer and use it in GitHub Desktop.
Save retran/b57e4db1a173048c2cee49ac6d523fc2 to your computer and use it in GitHub Desktop.
WM_PAINT reentrancy
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WutExample
{
static class Program
{
class MyVeryCustomException : Exception { }
private static readonly object sync = new object();
static void Throw()
{
throw new MyVeryCustomException();
}
static void Lock()
{
lock (sync) { }
}
[STAThread]
static void Main()
{
var mainThreadId = Thread.CurrentThread.ManagedThreadId;
bool throwOnPaint = false;
var form = new Form();
form.Paint += (sender, args) =>
{
Debug.Assert(Thread.CurrentThread.ManagedThreadId == mainThreadId);
if (throwOnPaint)
{
Throw();
}
};
var button = new Button { Text = "Click Me" };
button.Click += (sender, args) =>
{
Task.Run(() =>
{
Debug.Assert(Thread.CurrentThread.ManagedThreadId != mainThreadId);
lock (sync)
{
Thread.CurrentThread.Join(500);
// Will send WM_PAINT if called from a non-main thread.
form.Invalidate();
}
});
try
{
Debug.Assert(Thread.CurrentThread.ManagedThreadId == mainThreadId);
throwOnPaint = true;
Thread.CurrentThread.Join(100);
Lock();
}
finally
{
throwOnPaint = false;
}
};
form.Controls.Add(button);
Application.Run(form);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment