处理WinForm多线程程序时的陷阱,出现窗口句柄为创建就被访问
来源:互联网 发布:transactsql和sql区别 编辑:程序博客网 时间:2024/06/03 20:26
与所有的UI开发平台一样,.NET下线程开发图形界面同样要遵循一个基本原则:就是对UI对象的操作一定要在产生该UI对象的线程里进行(该线程称作UI线程),因为大部分UI对象都不是线程安全的。
在.NET中,把调用调用放在UI线程里执行是通过Form类及其子类的Invoke()方法实现的(具体的过程请参考其他资料),可以这样做是因为Form对象保存了创建它的线程的信息,而且Form类有一个bool类型的属性InvokeRequired,可以通过它查看当前线程是否为创建该Form对象的线程(UI线程)——如果为true,则表示当前线程不是UI线程,反之则是。下面提供一个例子:
using System.Threading;
using System.Windows.Forms;
namespace csharpTest
{
public class TestForm : Form
{
private Form form1;
private Form form2;
public static void Main()
{
TestForm tf = new TestForm();
tf.Show();
tf.UIThread();
Application.Run();
}
public void UIThread()
{
form1 = new Form();
form2 = new Form();
form2.Show();//这里是关键
form1.Show();
Thread thread = new Thread(new ThreadStart(WorkerThread));
thread.Start();
}
public void WorkerThread()
{
if (form2.InvokeRequired)
form2.Invoke(new MethodInvoker(WorkerThread));
else
{
form1.Text = "This is from WorkerThread.";
}
}
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
base.OnClosing (e);
Application.Exit();
}
}
}
TestForm里有两个需要注意的方法,UIThread——用来模拟UI线程,WorkerThread——用来模拟用户线程,UIThread中实例化了成员form1与form2,并调用了它们的Show方法,在WorkerThread中改变form1的Text属性。请注意WorkerThread里有个技巧, if (form2.InvokeRequired) 即如果当前线程不是创建该form2的线程,则将方法通通过过Invoke方法放到UI线程里去执行。但就是这里问题出现了。form1和form2都是在UIThread里建立的,所以它们保存的线程的信息应该是一样的。所以form1.InvokeRquired和form2.InvokeRquired的值在任何线程里都是一样的,即在WorkerThread中InvokeRquire的值都应该是true(因为在不同的线程里)。但是如果注释掉form2.Show()的话form2.InvokeRquired在WorkerThread中的值却是false(在vs.net中调试看到),怎么会这样呢?而且如果不经过判断直接在WorkerThread里调用form2对象的Invoke的话…………居然会抛出异常——“在创建窗口句柄之前,不能在控件上调用 Invoke 或 InvokeAsync”
分析一下该异常的信息,在win32里每一个窗体都有一个窗体句柄,是该窗体在建立时系统分配的,但我们确实在UI线程里建立了form2对象的。这里有个误区.Net里的Form对象并不是和win32的窗体对象完全对应的。本人窃以为,产生一个Form类的实例时,只是产生了一个内存中的普通的对象,并不产生系统窗体(好像叫做User对象吧),只有它第一次呈现在屏幕上(或称作创建)时,才产生系统里表示窗体的User对象且分配句柄,对应的WIN32 API的CreateWindow()方法大概也在这个时候执行(先声明:本人对WIN32 AP 并不熟悉,所以这里如果有什么不妥的话请大家指正)
只有.NET里的form对象调用某种方法使系统产生真正的窗体时,form才会有创建它的线程的信息,且InvokeRquired才有效,即才能调用form的Invoke方法。不过我还没弄清楚哪几个方法可以做到。据我所知Show, CreateGraphics可以产生系统真正的系统窗体。
在Window窗体程序开发的时候,如果使用多线程编程,在子线程中访问主线程窗体内的控件,就需要使用控件的Control.Invoke方法或者BeginInvoke方法。但是有时候因为Window执行速度太快,尤其是你写代码的时候在InitializeComponent();完成之前起了一个线程去执行某些操作,涉及到窗体控件的,当你在调用
Control.Invoke的时候,就可能出现 “在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke”错误。
解决的办法就是让线程等待,直到窗口句柄创建完毕:
//防止在窗口句柄初始化之前就走到下面的代码
while (!this.IsHandleCreated)
{
;
}
this.Invoke(new MethodWithoutParameter(LoadContactTemplate));
- 处理WinForm多线程程序时的陷阱,出现窗口句柄为创建就被访问
- 处理WinForm多线程程序时的陷阱
- 处理WinForm多线程程序时的陷阱
- 创建窗口句柄时出错。
- C#中出现了一个问题,就是程序主窗体已经打开,在调用子窗体时候出现了创建窗口句柄时出错。
- winform 创建新注册表时出现“不允许所请求的注册表访问权”,解决办法
- [ 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。]错误的处理
- 创建窗口句柄时出错。(未处理 Win32Exception)
- Win32Exception 创建窗口句柄时出错
- C#程序调用Form.Show报“创建窗口句柄出错”
- 通过创建窗口进程的ID来查找窗口句柄
- winform程序,使用dotnetbar后,偶尔出现的打开窗口异常的解决
- 安装SQL Server出现在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的错误解决办法
- 安装SQL Server出现在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的错误解决办法
- 安装SQL Server出现在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的错误解决办法
- 安装SQL Server出现在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke的错误解决办法
- 不知道程序的窗口句柄时结束该程序的进程
- Winform中多线程的控件访问引发的“线程间操作无效,从不是创建控件的线程访问它”
- 浅谈VB编程中网格控件的选用及使用方法
- php 观察者设计模式
- 网络安全系列连载(5)利用数字证书进行代码签名
- android-日记本
- 浅议非Access数据库在VB中的编程及应用
- 处理WinForm多线程程序时的陷阱,出现窗口句柄为创建就被访问
- VC++ 创建管道
- SQL2005中的事务与锁定(八)
- C++大学教程(第二版)c++how to program Second Edition --电子工业出版社【美】harvey M.Deitel and paul James Deitel(更新中)
- PHP简单计数器
- 研究VBA、Word和数据库的联合编程
- 田洪川的忠告
- DB2 精萃
- DWR捕捉异常信息的例子