IsHandleCreated和InvokeRequired的顺序

3

我有以下代码,我见过两种不同的写法。我只是好奇哪一种写法更好:

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是一种不好的做法,因为可能的异常将被收集并且永远不会释放。这里有一个链接,介绍如何在WinForms中同步GUI线程而不使用Begin/EndInvoke:http://weblogs.asp.net/psteele/archive/2008/12/03/synchronization-in-winforms-with-lambdas.aspx - Mecaveli
糟糕,那是一个复制和粘贴的错误。它应该更像第一个块,也就是我们的代码。第二个是我在这篇文章中找到的代码:http://www.aaronlerch.com/blog/2006/12/15/controltrifecta-invokerequired-ishandlecreated-and-isdisposed/。 - DTI-Matt
我最关心的是当我们尝试对那些GUI元素执行操作时,如果没有创建句柄会发生异常。通过将control.Handle分配给变量来强制创建句柄是否是不良实践? - DTI-Matt
我不确定你所说的将控件句柄分配给变量是什么意思。无论如何,如果你遇到这种问题,那么抑制错误并不能解决它。如果在句柄未创建时简单地“返回”,那么你的程序几乎肯定会处于无效状态。 - Mecaveli
@Mecaveli看看我的第三个代码示例。通过这种方式,我确保在处理操作之前创建了句柄。这可以防止仅仅返回导致的无效状态。你有什么想法? - DTI-Matt
1
对于 Handle 部分我不太确定。我会选择 Control.CreateControl,因为你的方法似乎有点 hacky :) 参见 http://msdn.microsoft.com/en-us/library/system.windows.forms.control.createcontrol.aspx - Mecaveli
2个回答

14

在检查InvokeRequired之前,您应始终检查IsDisposedIsHandleCreated。这是一个令人沮丧的情况,我花了很多时间来掌握它。

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

  • 新建:控件存在,但其句柄尚未创建。在这种情况下,IsDisposed == falseIsHandleCreated == false,但无论使用哪个线程调用,InvokeRequired == false如果您信任InvokeRequired的结果而没有测试(或知道其他方法)句柄是否已创建以及控件是否已释放,则可能会意外地导致句柄与错误的线程关联,并导致应用程序崩溃。(更新)只有当控件是(或是窗体的子级),并且该窗体的句柄尚未创建时,此状态才适用。
  • 已创建:控件存在,其句柄已创建。这是一种简单的情况,没有奇怪的地方。
  • 已释放:类似于上面的“新建”状态,但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 没有遍历控制层次结构,导致发生灾难的机会更大。


因此,在这种情况下,强制创建句柄可能会在错误运行时引起更多问题,而不是带来任何好处?或者从理论上讲,它是否会在正确的线程上创建,考虑到它在创建句柄时已经被调用过了? - DTI-Matt
控件没有句柄(Win32窗口句柄)时无法显示。但是,该句柄与线程有关联,应该与UI线程相关联。如果在IsHandleCreated == false时检查InvokeRequired,它会立即创建句柄,而不管您在哪个线程上。如果您在后台线程上,它将将控件的句柄与该后台线程关联,这将导致应用程序崩溃。因此,请先检查IsHandleCreatedIsDisposed再检查InvokeRequired - FMM
好的,我想我明白了。感谢您的解释!在这种情况下,如果我首先检查Disposed,其次是IsHandleCreated(如果句柄未被创建,则创建一个),然后继续进行InvokeRequired等典型检查,那么我应该没问题了。 - DTI-Matt
不要强制创建句柄!不要调用 Handle!如果你在后台线程上,它会在错误的线程上创建句柄并且会崩溃你的应用程序!请参见上面更新的代码示例。 - FMM
但是根据我在MSDN上的了解,链接在这里:http://msdn.microsoft.com/en-ca/library/system.windows.forms.control.invokerequired.aspx 我应该检查InvokeRequired,如果它为false,则检查IsHandleCreated,如果需要,调用Handle。这与您所说的直接相反,因此我希望能得到一个明确的答案。 :) - DTI-Matt
1
从文章中可以得知:“如果控件的句柄尚不存在,则 InvokeRequired 方法会向上搜索控件的父级链,直到找到具有窗口句柄的控件或窗体。如果找不到适当的句柄,则 InvokeRequired 方法返回 false。” 他们显然在4.0中加强了这一点;但是,如果是尚未显示的窗体,它仍然可能会影响您。更新的代码示例。 - FMM

0

实际上没有什么区别。第二种方法可能会稍微更有效率,因为它只在InvokeRequired == false时调用IsHandleCreated。 当句柄被创建时,InvokeRequired只能是true,所以在这种情况下不需要调用IsHandleCreated


不正确,错误的做法会带来痛苦和苦难。当 IsHandleCreated == false 时调用 InvokeRequired 可能会导致不良后果(商标)。 - FMM
@FMM 能否解释一下?当句柄未被创建时,所有线程上的 InvokeRequired 均为 false。因此,如果它为 true,我们现在知道该句柄已被创建。 - Henrik
是的,但如果你从后台线程调用InvokeRequired,它会将句柄与后台线程关联而不是UI线程,并导致应用程序崩溃。在检查InvokeRequired之前,始终检查IsHandleCreatedIsDisposed。一定要这样做。 - FMM
@FMM不正确。InvokeRequired不会创建句柄。http://msdn.microsoft.com/de-de/library/system.windows.forms.control.invokerequired.aspx - Henrik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接