diff --git a/src/System.Windows.Forms/System/Windows/Forms/Control.ThreadMethodEntry.cs b/src/System.Windows.Forms/System/Windows/Forms/Control.ThreadMethodEntry.cs index 9d7acffbc59..1c90650afd5 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Control.ThreadMethodEntry.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Control.ThreadMethodEntry.cs @@ -76,7 +76,14 @@ internal void Complete() lock (_invokeSyncObject) { IsCompleted = true; - _resetEvent?.Set(); + try + { + _resetEvent?.Set(); + } + catch (ObjectDisposedException) + { + // AsyncWaitHandle exposes the event and allows callers to dispose it before completion. + } } } } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.Methods.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.Methods.cs index 428fd19325b..6fe81b34790 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.Methods.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ControlTests.Methods.cs @@ -4276,6 +4276,21 @@ await Task.Run(() => Assert.Equal(0, createdCallCount); } + [WinFormsFact] + public void Control_BeginInvoke_DisposedAsyncWaitHandle_CompletesCallback() + { + using Control control = new(); + Assert.NotEqual(IntPtr.Zero, control.Handle); + bool callbackInvoked = false; + IAsyncResult asyncResult = control.BeginInvoke(() => callbackInvoked = true); + asyncResult.AsyncWaitHandle.Dispose(); + + control.TestAccessor.Dynamic.InvokeMarshaledCallbacks(); + + Assert.True(callbackInvoked); + Assert.True(asyncResult.IsCompleted); + } + [WinFormsFact] public void Control_Invoke_Action_calls_correct_method() {