在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke [英] Invoke or BeginInvoke cannot be called on a control until the window handle has been created

查看:66
本文介绍了在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类似于 Greg D 在这里讨论(减去 IsHandleCreated 检查).

I have a SafeInvoke Control extension method similar to the one Greg D discusses here (minus the IsHandleCreated check).

我从 System.Windows.Forms.Form 调用它,如下所示:

I am calling it from a System.Windows.Forms.Form as follows:

public void Show(string text) {
    label.SafeInvoke(()=>label.Text = text);
    this.Show();
    this.Refresh();
}

有时(此调用可以来自多个线程)这会导致以下错误:

Sometimes (this call can come from a variety of threads) this results in the following error:

System.InvalidOperationException 发生

Message= "在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke."

Message= "Invoke or BeginInvoke cannot be called on a control until the window handle has been created."

Source="System.Windows.Forms"

Source= "System.Windows.Forms"

StackTrace:
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 DriverInterface2.UI.WinForms.Dialogs.FormExtensions.SafeInvoke[T](T control, Action`1 action) 
in C:codeDriverInterface2DriverInterface2.UI.WinFormsDialogsFormExtensions.cs:line 16

这是怎么回事,我该如何解决?我知道这不是表单创建的问题,因为有时它会工作一次而下一次失败,那么问题是什么?

What is going on and how do I fix it? I know as much as it is not a problem of form creation, since sometimes it will work once and fail the next time so what could the problem be?

附注.我真的很擅长 WinForms,有没有人知道解释整个模型以及如何使用它的好系列文章?

PS. I really really am awful at WinForms, does anyone know a good series of articles that explains the whole model and how to work with it?

推荐答案

您可能在错误的线程上创建控件.考虑以下 来自 MSDN 的文档:

It's possible that you're creating your controls on the wrong thread. Consider the following documentation from MSDN:

这意味着 InvokeRequired 可以如果不需要调用,则返回 false(调用发生在同一线程上),或 如果控件是在不同的线程,但控件的句柄尚未创建.

This means that InvokeRequired can return false if Invoke is not required (the call occurs on the same thread), or if the control was created on a different thread but the control's handle has not yet been created.

如果控件的句柄尚未创建,您应该不是简单地调用属性、方法,或控件上的事件.这有可能导致控件的句柄为在后台线程上创建,隔离线程上的控制没有消息泵并制作应用不稳定.

In the case where the control's handle has not yet been created, you should not simply call properties, methods, or events on the control. This might cause the control's handle to be created on the background thread, isolating the control on a thread without a message pump and making the application unstable.

您可以通过以下方式防范这种情况还检查的值IsHandleCreated 当 InvokeRequired在后台线程上返回 false.如果控制手柄尚未创建,你必须等到它有在调用 Invoke 或开始调用.通常,会发生这种情况仅当创建了后台线程时在主要形式的构造函数中对于应用程序(如Application.Run(new MainForm()),在表格显示之前或已调用 Application.Run.

You can protect against this case by also checking the value of IsHandleCreated when InvokeRequired returns false on a background thread. If the control handle has not yet been created, you must wait until it has been created before calling Invoke or BeginInvoke. Typically, this happens only if a background thread is created in the constructor of the primary form for the application (as in Application.Run(new MainForm()), before the form has been shown or Application.Run has been called.

让我们看看这对您意味着什么.(如果我们也看到您对 SafeInvoke 的实现,这将更容易推理)

Let's see what this means for you. (This would be easier to reason about if we saw your implementation of SafeInvoke also)

假设您的实现与引用的实现相同,除了针对 IsHandleCreated,我们按照逻辑:

Assuming your implementation is identical to the referenced one with the exception of the check against IsHandleCreated, let's follow the logic:

public static void SafeInvoke(this Control uiElement, Action updater, bool forceSynchronous)
{
    if (uiElement == null)
    {
        throw new ArgumentNullException("uiElement");
    }

    if (uiElement.InvokeRequired)
    {
        if (forceSynchronous)
        {
            uiElement.Invoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
        else
        {
            uiElement.BeginInvoke((Action)delegate { SafeInvoke(uiElement, updater, forceSynchronous); });
        }
    }
    else
    {    
        if (uiElement.IsDisposed)
        {
            throw new ObjectDisposedException("Control is already disposed.");
        }

        updater();
    }
}

考虑我们从非 gui 线程调用 SafeInvoke 以获取句柄尚未创建的控件的情况.

Consider the case where we're calling SafeInvoke from the non-gui thread for a control whose handle has not been created.

uiElement 不为空,所以我们检查 uiElement.InvokeRequired.根据 MSDN 文档(粗体)InvokeRequired 将返回 false 因为,即使它是在不同的线程上创建的,句柄还没有被创建!这会将我们带到 else 条件,在那里我们检查 IsDisposed 或立即继续调用提交的操作...从后台线程

uiElement is not null, so we check uiElement.InvokeRequired. Per the MSDN docs (bolded) InvokeRequired will return false because, even though it was created on a different thread, the handle hasn't been created! This sends us to the else condition where we check IsDisposed or immediately proceed to call the submitted action... from the background thread!

在这一点上,所有赌注都关闭了:该控制,因为它的句柄是在没有消息泵的线程上创建的,如第二段所述.也许这就是您遇到的情况?

At this point, all bets are off re: that control because its handle has been created on a thread that doesn't have a message pump for it, as mentioned in the second paragraph. Perhaps this is the case you're encountering?

这篇关于在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆