I overlooked the comments section in your code, Indeed that fails when uncommenting the var f = new Form();
Reason is subtle, Control
class will automatically overwrite the synchronization context to WindowsFormsSynchronizationContext
if it sees that SynchronizationContext.Current
is null
or its is of type System.Threading.SynchronizationContext
.
As soon as Control class overwrite the SynchronizationContext.Current
with WindowsFormsSynchronizationContext
, all the calls to Send
and Post
expects the windows message loop to be running in order to work. That's not going to happen till you created the Handle
and you run a message loop.
Relevant part of the problematic code:
internal Control(bool autoInstallSyncContext)
{
...
if (autoInstallSyncContext)
{
//This overwrites your SynchronizationContext
WindowsFormsSynchronizationContext.InstallIfNeeded();
}
}
You can refer the source of WindowsFormsSynchronizationContext.InstallIfNeeded
here.
If you want to overwrite the SynchronizationContext
, you need your custom implementation of SynchronizationContext
to make it work.
Workaround:
internal class MyContext : SynchronizationContext
{
}
[TestMethod]
public void TestMethod1()
{
// Create new sync context for unit test
SynchronizationContext.SetSynchronizationContext(new MyContext());
var waitHandle = new ManualResetEvent(false);
var doer = new DoSomethinger();
var f = new Form();
doer.DoSomethingAsync(() => waitHandle.Set());
Assert.IsTrue(waitHandle.WaitOne(10000), "Wait timeout exceeded.");
}
Above code works as expected :)
Alternatively you could set WindowsFormsSynchronizationContext.AutoInstall
to false
, that will prevent automatic overwriting of the synchronization context mentioned above.(Thanks for OP @OffHeGoes for mentioning this in comments)
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…