Geo*_*uer 76 multithreading winforms
我有一个类似于Greg D讨论的SafeInvoke Control扩展方法(减去IsHandleCreated检查).
我从System.Windows.Forms.Form以下地址调用它:
public void Show(string text) {
label.SafeInvoke(()=>label.Text = text);
this.Show();
this.Refresh();
}
Run Code Online (Sandbox Code Playgroud)
有时(此调用可能来自各种线程),这会导致以下错误:
System.InvalidOperationException发生了
Message="在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke."
Source="System.Windows.Forms"Run Code Online (Sandbox Code Playgroud)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:\code\DriverInterface2\DriverInterface2.UI.WinForms\Dialogs\FormExtensions.cs:line 16
发生了什么,我该如何解决?我知道这不是形式创建的问题,因为有时它会工作一次并且下次失败,那么问题是什么呢?
PS.我真的很擅长WinForms,有没有人知道一系列很好的文章解释整个模型以及如何使用它?
Gre*_*g D 71
这意味着如果不需要Invoke(调用发生在同一个线程上),或者控件是在另一个线程上创建但尚未创建控件的句柄,则InvokeRequired可以返回false .
如果尚未创建控件的句柄,则不应简单地在控件上调用属性,方法或事件.这可能导致在后台线程上创建控件的句柄,在没有消息泵的情况下隔离线程上的控件并使应用程序不稳定.
当InvokeRequired在后台线程上返回false时,还可以通过检查IsHandleCreated的值来防止这种情况.如果尚未创建控件句柄,则必须等到创建它之后再调用Invoke或BeginInvoke.通常,只有在应用程序的主要表单的构造函数中创建后台线程(如在Application.Run(new MainForm())中,在显示表单或调用Application.Run之前,才会发生这种情况.
让我们看看这对你意味着什么.(如果我们看到你的SafeInvoke实现,这将更容易推理)
假设您的实现与引用的实现相同,但对IsHandleCreated的检查除外,让我们遵循以下逻辑:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
考虑我们SafeInvoke从非gui线程调用尚未创建句柄的控件的情况.
uiElement不是空的,所以我们检查uiElement.InvokeRequired.根据MSDN文档(粗体)InvokeRequired将返回,false因为即使它是在不同的线程上创建的,手柄还没有创建!这将我们发送到else我们检查IsDisposed或立即从后台线程调用提交的操作的条件!
在这一点上,所有的赌注都是关闭的:那个控制因为它的句柄是在一个没有消息泵的线程上创建的,如第二段所述.也许这就是你遇到的情况?
Mat*_*ieu 35
我发现InvokeRequired不可靠,所以我只是使用
if (!this.IsHandleCreated)
{
this.CreateHandle();
}
Run Code Online (Sandbox Code Playgroud)
Ben*_*jol 20
我认为(尚未完全确定)这是因为如果尚未加载/显示控件,InvokeRequired将始终返回false.我做了一个似乎暂时工作的解决方法,即在其创建者中简单引用相关控件的句柄,如下所示:
Run Code Online (Sandbox Code Playgroud)var x = this.Handle;(见 http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html)
您链接到的帖子中的方法调用Invoke/BeginInvoke,然后检查是否已从未创建控件的线程调用控件的句柄.
因此,当您从创建控件的线程以外的线程调用方法时,您将获得异常.这可能发生在远程事件或排队的工作用户项目中......
编辑
如果在调用invoke之前检查InvokeRequired和HandleCreated,则不应该得到该异常.
| 归档时间: |
|
| 查看次数: |
133249 次 |
| 最近记录: |