首页 > 解决方案 > 从不同线程触发 UI 事件时的 ObjectDisposedException

问题描述

目前我有一个项目,其中包括一些 WinForms 绑定,以允许不仅使用鼠标操作界面,还可以通过外部硬件设备操作界面。

外部设备通过 DLL 连接,该 DLL 允许订阅硬件按钮推送事件,这些事件是从单独的线程发送的。

这是我为按钮制作的类的样子(省略细节):

public class PanelBoundButton : Button, IPanelBoundControl
    {
        /// <summary>
        /// Gets or sets what hardware button the control is bound to.
        /// </summary>
        [Description("Panel button to bind"), Category("Hardware Interface")]
        public HardwareButtonFlags Binding
        {
            get;
            set;
        }

        // For debouncing
        private DateTime pressTime = DateTime.MinValue;
        private const int DEBOUNCE_MS = 100;

        public void Bind()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) return;
            ButtonReader.ButtonsChanged += new EventHandler<HardwareButtonEvent>(HardwareHandleButtonChange);
        }

        public void Unbind()
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) return;
            ButtonReader.ButtonsChanged -= HardwareHandleButtonChange;
        }

        // External hardware button event handler
        void HardwareHandleButtonChange(object sender, HardwareButtonEvent e)
        {
            if (this.Binding != 0 && e.Released.HasFlag(this.Binding) && this.Enabled)
            {
                if(DateTime.Now.Subtract(pressTime).TotalMilliseconds < DEBOUNCE_MS)
                {
                    Logger.Log("Button event seems like noise, ignoring for debouncing");
                    return;
                }
                pressTime = DateTime.Now;
                Action clk = delegate()
                {
                    this.PerformClick();
                };
                if (this.InvokeRequired)
                    this.Invoke(clk);
                else clk();
            }
        }
    }

但是,有时在操作按钮时,尤其是关闭表单的按钮时,用户会因以下堆栈跟踪而崩溃。最初我认为这可能是因为按钮按下被识别两次,因此上面代码的去抖动部分,但它没有帮助(并且去抖动行也从未被记录)。

Cannot access a disposed object.
Object name: 'AppOperationSelectorTabPages'.
Happened in object: System.Windows.Forms
Happened in method: System.Object MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)
     at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   at projectname.GUI.PanelBoundButton.HardwareHandleButtonChange(Object sender, HardwareButtonEvent e)
   at System.EventHandler`1.Invoke(Object sender, TEventArgs e)
   at projectIoLib.FrontPanel.ButtonReader.OnButtonEvent(Object sender, HardwareButtonEvent e)
   at projectIoLib.FrontPanel.ButtonEventEmitter._ButtonCheckingThread()
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

但是,从这个堆栈跟踪中,我无法真正确定在访问时处理了哪个对象?是我的按钮吗?还是那个 TabPages 对象?

此外,当前取消绑定按钮由窗体关闭时处理。然而,我的猜测是,这发生得太晚了,以至于崩溃发生了。我是否错过了Dispose按钮类中某些方法(如)的覆盖,以便在处理它们变得致命之前取消订阅硬件事件?

标签: c#multithreadingwinformsinvoke

解决方案


推荐阅读