【问题标题】:InvokeRequired of Form == false and InvokeRequired of contained control == true表单的 InvokeRequired == false 和包含控件的 InvokeRequired == true
【发布时间】:2010-10-25 11:07:42
【问题描述】:

这怎么可能?我有 Windows 窗体控件,它派生自 System.Windows.Forms.Form,其中包含 WebBrowser 控件。 Webbrowser 对象实例在表单的构造函数中创建(在 InitializeComponent() 方法中)。然后在后台线程中我使用 WebBrowser 的内容进行操作,我发现在某些情况下 Form.InvokeRequired == false,而 WebBrowser.InvokeRequired == true。怎么可能?

【问题讨论】:

  • 是在启动或关闭表单时出现还是一直出现?
  • 当表单已经创建但未显示时发生(整个表单,不仅是浏览器)。我没有在创建后立即显示表单。

标签: .net winforms webbrowser-control invokerequired


【解决方案1】:

Form.InvokeRequired 在显示表单之前返回false

我做了一个简单的测试:

Form2 f2 = new Form2();
Thread t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();

f2.Show();

t = new Thread(new ThreadStart(() => PrintInvokeRequired(f2)));
t.Start();
t.Join();

借助助手

private void PrintInvokeRequired(Form form)
{
    Console.WriteLine("IsHandleCreated: " + form.IsHandleCreated + ", InvokeRequired: " + form.InvokeRequired);
}

输出是

IsHandleCreated:假,InvokeRequired:假
IsHandleCreated:真,InvokeRequired:真

另请注意,这在MSDN 上有一些记录:

如果控件的句柄还没有 存在,InvokeRequired 搜索 控件的父链,直到找到 具有 窗把手。如果没有合适的 可以找到句柄, InvokeRequired 方法返回 false。

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

在控件的句柄的情况下 尚未创建,您应该 不仅仅是调用属性、方法, 或控件上的事件。这有可能 导致控件的句柄是 在后台线程上创建, 隔离线程上的控件 没有消息泵并制作 应用不稳定。

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

您的解决方案是同时检查IsHandleCreated

编辑:
Handle 可以随时在 WebBrowser 控件内部或外部创建。这不会自动创建父窗体的句柄。

我创建了一个例子:

public Form2()
{
    InitializeComponent();

    Button button1 = new Button();
    this.Controls.Add(button1);

    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
    var tmp = button1.Handle; // Forces the Handle to be created.
    Console.WriteLine("button1: " + button1.IsHandleCreated + " this: " + this.IsHandleCreated);
}

输出:

button1: False this: False
button1: True this: False

【讨论】:

  • 但是我检查了包含的控件(WebBrowser)InvokeRequired 属性,它是 True 而 form.InvokeRequired 是 False。根据逻辑,如果子控件与父控件在同一线程中创建,则层次结构中所有子控件的 InvokeRequired 必须与父控件的 InvokeRequired 相同。在我的情况下,webbrowser 控件是在表单的构造函数中创建的,即它们是在一个线程中创建的。也许我们有一些特定的 WebBrowser 控件行为?
  • @Dmitry:不。控件的句柄可以独立于父窗体的句柄创建。我已经用一个例子更新了我的答案。
  • +1 非常感谢。我花了很长时间尝试模拟跨线程异常以获得失败的单元测试场景。这是随意创建它的关键。现在能够编写显示控件可以安全访问的测试:)
【解决方案2】:

这里是对应的更通用问题的详细调查:http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

【讨论】:

    【解决方案3】:

    我一直在调查同样的奇怪行为。 我需要从不同的线程操作一些控件(例如显示有关连接到主机的设备的信息或根据不同的设备状态触发操作)。

    这个链接给了我一个很好的提示: http://csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx

    我仍然不知道 MS 人打算如何使用他们自己的东西(并且在很多方面都不同意),但是,在一个应用程序中,我不得不做出以下肮脏和肮脏的解决方法:

    • 在主线程中创建控件/表单(确保它是主线程)。
    • 在相同的过程中,检查控制手柄。这个简单的检查将强制在正确的线程中创建它

    有多丑,不是吗? 我想知道是否有其他人有更好的方法。

    _my_control = new ControlClass( );
    _my_control.Owner = this;
    
    IntPtr hnd;
    
    // Force Handle creation by reading it.
    if ( !_my_control.IsHandleCreated || _my_control.Handle == IntPtr.Zero )
        hnd = _my_control.Handle;
    

    (很抱歉在这个有点旧的帖子中发帖,但我只是认为它可能对某人有用)。

    【讨论】:

    • 请注意,在您的示例中,句柄实际上将由检查 _my_control.Handle == IntPtr.Zero 创建。更短的方法可能是 _myControl = new ControlClass { Owner = this }; var temp = _myControl.Handle;
    猜你喜欢
    • 2012-09-07
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多