【问题标题】:Invoke or BeginInvoke cannot be called Error不能调用 Invoke 或 BeginInvoke 错误
【发布时间】:2013-04-09 00:28:17
【问题描述】:

我有一个全世界客户都在使用的程序。我检查了我的错误日志,似乎有很多异常(如下所列)引发了我无法真正弄清楚或追踪的异常。

我有一些调用,但它们都受 InvokeRequired 保护。 现在我在想,如果我应该使用 if (HandleCreated) 来代替。

我什至不确定异常是在何时何地引发的。

在启动时,在 InitializeComponent(); 之后,我有一些任务需要访问某些控件,例如 datagridview。但是,就像我说的,我尝试使用 InvokeRequired 来保护它们。我不确定这是否是导致问题的地方。

我可以执行哪些建议以便尝试跟踪此问题?

无论如何,这是我的例外:

    System.InvalidOperationException: Invoke or BeginInvoke cannot be called on a 

control until the window handle has been created.
   at System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)
   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 System.Windows.Forms.Control.Invoke(Delegate method)
   at ..()
   at ..()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, 

ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

    标签: c# multithreading visual-studio exception exception-handling


    【解决方案1】:

    不,如果您使用 InvokeRequired,则不会发生这种情况。仅当句柄有效时才为真。非常简单的避免,只是在 Load 事件触发之前不要订阅事件或启动线程。

    当表单关闭时会发生这种崩溃。您在堆栈跟踪中看不到的东西,因为这发生在另一个线程上。 InvokeRequired + Begin/Invoke() 中有一个竞争条件。 InvokeRequired 可能会返回 true,并在微秒后关闭表单。您的 Begin/Invoke 调用将因此异常而失败。

    这不是你能解决的比赛。您必须确保在允许表单关闭之前线程不能再调用 BeginInvoke()。这总是意味着您必须防止表单关闭。背景信息在this answer

    【讨论】:

      【解决方案2】:

      我不确定 trace 是否会完全帮助您。在创建句柄之前,您在代码中的某处调用invoke/BeginInvoke 很明显。现在,我的建议可能需要一些工作,但您将确定过早调用未创建句柄的调用者。在尝试跟踪生产中的一些旧 C++ 遗留代码中的锁定/解锁线程问题时,我使用了这种技术。效果很好,我就让它保持原样。

      这是我的技术。创建一个扩展类,该类接受支持 Invoke/BeginInvoke 和 EndInvoke 的对象。它可能看起来像这样:

      public static class MyInvokeExtension
      {
         public static void TempInvoke(this objectthatsupportsinvoke, ...)
         {
              try
              {
                 objectthatsupportsinvoke.Invoke(...);
              }
              catch(Exception ex)
              {
                 Console.WriteLine();  // put a break-point here
              }
         }
      
         // add other BeginInvoke and EndInvoke methods and do the same as above.
      }
      
      • 现在在整个代码中进行搜索和替换,并将所有类似 Invoke 的调用替换为类似 TempInvoke 的调用。
      • 在调试中运行您的应用程序。
      • 在某些时候你会遇到一个断点。
      • 使用调用堆栈窗口找出谁比他们应该更早地调用了您的调用方法。

      我知道这是一项艰巨的工作,但从长远来看,这是值得的,相信我。事实上,您甚至可以使用此代码来验证对象是否可以处理给定时间点的调用。

      告诉我进展如何。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-09-11
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        • 2023-03-23
        相关资源
        最近更新 更多