表单的 InvokeRequired == false 和包含控件的 InvokeRequired == true

发布于 2024-09-29 11:15:59 字数 243 浏览 4 评论 0原文

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

how is it possible? I have windows Form control, derived from System.Windows.Forms.Form with WebBrowser control contained in this form. Webbrowser object instance is created in constructor of form (in InitializeComponent() method). Then in background thread I manipulate with content of WebBrowser, and I found that in some cases Form.InvokeRequired == false, while WebBrowser.InvokeRequired == true. How can it be?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

想挽留 2024-10-06 11:15:59

在显示表单之前,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:False,InvokeRequired:False
IsHandleCreated:True,InvokeRequired:True

另请注意,这在 MSDN

如果控件的句柄还没有
存在,InvokeRequired 搜索
控制的父链,直到找到
确实有一个控件或表单
窗户把手。如果没有合适的
可以找到句柄,
InvokeRequired 方法返回 false。

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

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

您可以通过以下方式防范这种情况
还检查的值
IsHandleCreated 时 InvokeRequired
在后台线程上返回 false。
如果控制手柄还没有被
创建后,您必须等到它完成
在调用 Invoke 或之前创建
开始调用。通常,这种情况会发生
仅当创建后台线程时
在主要形式的构造函数中
对于应用程序(如
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:假这个:假
按钮1:正确此:错误

Form.InvokeRequired returns false before the form is shown.

I did a simple test:

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();

with the helper

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

the output is

IsHandleCreated: False, InvokeRequired: False
IsHandleCreated: True, InvokeRequired: True

Also note that this is somewhat documented on MSDN:

If the control's handle does not yet
exist, InvokeRequired searches up the
control's parent chain until it finds
a control or form that does have a
window handle. If no appropriate
handle can be found, the
InvokeRequired method returns 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.

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.

Your solution is to also check for IsHandleCreated.

Edit:
The Handle can be created at any time internal in the WebBrowser control or externally. This does not automatically create the handle of the parent form.

I created an example:

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);
}

with the output:

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

初心未许 2024-10-06 11:15:59

以下是对相应且更普遍的问题的详细调查:http://www.ikriv .com/en/prog/info/dotnet/MysteriousHang.html

Here's detailed investigation of corresponding and more generic problem: http://www.ikriv.com/en/prog/info/dotnet/MysteriousHang.html

妳是的陽光 2024-10-06 11:15:59

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

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

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

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

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

_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;

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

I've been investigating this same weird behaviour.
I need to operate some controls from different threads (e.g. show information about a device that is connected to the host or trigger actions depending on different devices states).

This link gave me a good hint:
http://csharpfeeds.com/post/2898/Control.Trifecta_InvokeRequired_IsHandleCreated_and_IsDisposed.aspx

I still don't know how MS people intended to make use of their own stuff (and don't agree in many aspects), but, in one application I had to make the following dirty and filthy workaround:

  • Create the control/form in the main thread (make sure it is the main thread).
  • In the same procedure, check the control Handle. This simple check will force it to be created and in the right thread!

How ugly, isn't?
I'd like to know if anyone else has a better way to do it.

_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;

(Sorry for posting in this somewhat old post, but I just thought it could be usefull to someone).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文