【问题标题】:Order of IsHandleCreated and InvokeRequiredIsHandleCreated 和 InvokeRequired 的顺序
【发布时间】:2013-04-24 09:51:36
【问题描述】:

我有以下代码,我看到它以两种不同的方式编写。我只是好奇这两种方法中哪一种更好:

if (this.IsDisposed) return;

if (this.IsHandleCreated)
{
    if (this.InvokeRequired)
    {
        this.Invoke(action);
    }
    else
    {
        action();
    }
}

log.Error("Control handle was not created, therefore associated action was not executed.");

对比

if (this.InvokeRequired)
{
    this.Invoke(action);
}
else    
{
    if (this.IsDisposed) return;

    if (!this.IsHandleCreated)
    {
         log.Error("Control handle was not created, therefore associated action was not executed.");
         return;
    } 

    action();
}

我最关心的问题源于需要控件具有句柄的操作,而那些不是明确需要的。如果我要做这样的事情,它似乎通过确保控件在执行操作之前有句柄来解决我的问题。想法?

if (control.InvokeRequired)
{
     control.Invoke(action);
}
else
{
    if (control.IsDisposed) return;

    if (!control.IsHandleCreated)
    {
        // Force a handle to be created to prevent any issues.
        log.Debug("Forcing a new handle to be created before invoking action.");
        var handle = control.Handle;
    }

    action();
}

【问题讨论】:

  • 第一个会阻塞直到动作完成,第二个不会。此外,使用 BeginInvoke 而不调用 EndInvoke 是不好的做法,因为可能的异常将被收集并且永远不会被释放。这是关于如何在没有 Begin / EndInvoke 的情况下在 winforms 中同步 gui 线程的链接:weblogs.asp.net/psteele/archive/2008/12/03/…
  • 哎呀,那是 C&P 人工制品。它应该更像第一个块,这是我们的代码。第二个是我在这篇文章中找到的代码:aaronlerch.com/blog/2006/12/15/…
  • 我最关心的是在未创建句柄时发生的异常,但我们尝试对这些 GUI 元素执行操作。通过将 control.Handle 分配给变量来强制创建句柄是不好的做法吗?
  • 我不确定您将控制句柄分配给变量是什么意思。无论如何,如果您遇到此类问题,那么抑制错误将无法解决。如果您在未创建句柄时只是“返回”,那么您的程序几乎肯定会处于无效状态。
  • 不确定手柄部分 tbh。我会选择 Control.CreateControl 因为你的方法看起来有点笨拙:) 见msdn.microsoft.com/en-us/library/…

标签: c# .net multithreading winforms


【解决方案1】:

在检查InvokeRequired 之前,您应该始终检查IsDisposedIsHandleCreated。这是一个令人抓狂的场景,我花了很多时间来掌握。

以下是控件可以处于的状态:

  • 新建:控件存在,但尚未创建其句柄。在这种情况下,IsDisposed == falseIsHandleCreated == false,但是InvokeRequired == false不管你用什么线程来调用它。如果您信任InvokeRequired 的结果而不测试(或知道其他方式)是否已创建句柄以及是否已释放控件,您可能会意外导致创建的句柄与错误的线程相关联,它会使您的应用崩溃。 (更新)这种状态实际上只适用于控件是(或者是其子)其句柄尚未创建的表单。
  • Live:控件存在,它的句柄被创建。这是一个简单的场景,没什么奇怪的。
  • Disposed:这类似于上面的“新建”状态,但IsDisposed == true。同样,InvokeRequired 会骗你,给你带来痛苦。

正确的做法是:

if(control.IsDisposed || (!control.IsHandleCreated && !control.FindForm().IsHandleCreated))
{
    // some exceptional condition:
    // handle in whatever way is appropriate for your app
    return;
}

if(control.InvokeRequired)
{
    control.Invoke(action);
}
else
{
    action();
}

附加说明

在 .Net 2.0(和 3.x)中,这更糟糕。 InvokeRequired 现在将遍历控件层次结构以确定是否已创建任何祖先控件的句柄,并验证它是在哪个线程上创建的。但是,如果控件位于从未显示过的表单上,则会出现同样的危险。以前(在 2.0 - 3.5 中),InvokeRequired 没有走控制层次结构,导致更多的灾难机会。

【讨论】:

  • 所以在这种情况下,强制创建句柄可能会导致我在错误运行中出现更多问题?或者考虑到它在创建句柄时已经被调用,理论上会在正确的线程上创建吗?
  • 如果没有句柄(即 Win32 窗口句柄,顺便说一句),该控件将无法显示。但是,该句柄与线程具有关联性。它应该与 UI 线程相关联。如果您在IsHandleCreated == false 时检查InvokeRequired,它将立即创建句柄,无论您在哪个线程上。如果您在后台线程上,它会将控件的句柄与该后台线程相关联,这将使您的应用程序崩溃。所以,再次检查IsHandleCreatedIsDisposed,然后再检查InvokeRequired
  • 好吧,我想我明白了。谢谢你的解释!在这种情况下,如果我检查 Disposed 1st,IsHandleCreated 2nd(如果没有创建句柄,我创建一个),然后继续进行 InvokeRequired 等的典型检查。我应该是金色的。
  • 不!不要强制创建句柄!请勿拨打Handle!如果您在后台线程上,它会在错误的线程上创建句柄,并且会使您的应用崩溃!请参阅上面更新的代码示例。
  • 来自文章:“如果控件的句柄尚不存在,InvokeRequired 会向上搜索控件的父链,直到找到具有窗口句柄的控件或窗体。如果找不到合适的句柄,InvokeRequired 方法返回 false。”他们显然在 4.0 中加强了这一点;但是,如果它是尚未显示的形式,它仍然会咬你。更新了代码示例。
【解决方案2】:

没有真正的区别。第二个可能稍微高效一些,因为它仅在InvokeRequired == false 时调用IsHandleCreatedInvokeRequired在创建句柄时只能是true,所以这种情况下不需要调用IsHandleCreated

【讨论】:

  • 不正确,错误,会造成痛苦和痛苦。当IsHandleCreated == false 时调用InvokeRequired 可能会导致坏事(tm)。
  • @FMM 需要解释一下吗?当未创建句柄时,InvokeRequired 在所有线程上都是 false。所以,如果它是真的,我们现在就创建了句柄。
  • 是的,但是如果您从后台线程调用InvokeRequired,它会将句柄与后台线程而不是 UI 线程相关联,并使应用程序崩溃。在检查 InvokeRequired 之前,请务必检查 IsHandleCreatedIsDisposed。总是。
  • @FMM 不正确。 InvokeRequired 不创建句柄。 msdn.microsoft.com/de-de/library/…
猜你喜欢
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多