7

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

4

3 回答 3

9

Form.InvokeRequiredfalse在表格显示之前返回。

我做了一个简单的测试:

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。

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

在尚未创建控件句柄的情况下,不应简单地调用控件上的属性、方法或事件。这可能会导致在后台线程上创建控件的句柄,从而在没有消息泵的线程上隔离控件并​​使应用程序不稳定。

当 InvokeRequired 在后台线程上返回 false 时,您还可以通过检查 IsHandleCreated 的值来防止这种情况。如果尚未创建控制句柄,则必须等到它创建完成后,才能调用 Invoke 或 BeginInvoke。通常,仅当在应用程序的主窗体的构造函数中创建后台线程时(如在 Application.Run(new 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

于 2010-10-25T12:23:09.070 回答
1

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

于 2010-10-25T19:53:18.790 回答
0

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

这个链接给了我一个很好的提示: 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;

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

于 2011-02-17T10:28:15.747 回答