Home > Net >  Why isn't the action in BeginInvoke executed when a control is disposed before the event loop s
Why isn't the action in BeginInvoke executed when a control is disposed before the event loop s

Time:01-27

The following code in WinForms (NET Core and Framework) doesn't attempt the action in BeginInvoke when Application.DoEvents is run:

namespace BeginInvokeTest
{
    internal static class Program
    {
        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            ApplicationConfiguration.Initialize();

            bool executed;

            var form = new Form();
            form.Show();

            form.BeginInvoke(() => { executed = true; });

            form.Dispose();
            Application.DoEvents();
        }
    }
}

However, if I move form.Dispose after Application.DoEvents the action is executed. So I know that messages queued by BeginInvoke are ignored if the owning control is disposed before starting the message loop.

Does anyone know where in WinForms this behaviour is enforced/implemented; does WinForms remove messages from the queue when a control is disposed?

(Have taken a look at the source in GitHub but can't workout where/how this happens)

Thanks!

CodePudding user response:

The code you are looking for is in Control.DestoryHandle, which is called by Control.Dispose

// If we're not recreating the handle, then any items in the thread callback list will
// be orphaned.  An orphaned item is bad, because it will cause the thread to never
// wake up.  So, we put exceptions into all these items and wake up all threads.
// If we are recreating the handle, then we're fine because recreation will re-post
// the thread callback message to the new handle for us.
//
if (!RecreatingHandle) {                
    if (threadCallbackList != null) {
        lock (threadCallbackList) {
            Exception ex = new System.ObjectDisposedException(GetType().Name);
 
            while (threadCallbackList.Count > 0) {
                ThreadMethodEntry entry = (ThreadMethodEntry)threadCallbackList.Dequeue();
                entry.exception = ex;
                entry.Complete();
            }
        }
    }
}

As you can see, if the handle is not being recreated, all remaining callbacks that have not yet been dispatched are set to having an exception. Because this is asynchronous, you don't see the exception unless you call EndInvoke.

You also do not see any exception if the callback has already completed. When you call DoEvents, any window messages in the queue are pumped. Invoked callbacks are dispatched via the window message queue, so all callbacks are completed at the point you call DoEvents. So when you Dispose afterwards, there is no exception even if you call EndInvoke, because the callback has already completed.

  •  Tags:  
  • Related