"在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke" 异常的解决方法
“在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke”,出现这种异常的原因大致有两种情况,一种是控件句柄还没有创建就调用控件的Invoke,而另一种就是在控件句柄已经释放后再次调用Invoke函数。针对上诉的两种情况,解决方案如下:
句柄创建前调用Invoke
这种情况,网上的普遍解决方案是利用控件的IsHandleCreated属性来进行判断,如果该属性为真,再执行Invoke操作。这种方法能解决大部分的问题,但是如果我要使用的控件或是窗口在调用Invoke操作之前根本就没有创建句柄那又怎么办,这时如果仍然用IsHandleCreated属性进行判断,则永远无法执行到Invoke函数。
调用控件的Invoke操作本质上是希望在UI线程中执行Invoke参数中的委托代码,而至于是UI类中哪个控件调用的则无关紧要,只要是通过Invoke操作,都能够将后面的委托参数带到UI线程中来执行。我编程时遇到的问题是,我希望在UI类中创建一个A窗口,而在其他的线程中使这个A窗口弹出。按照常规的思路是利用A窗口调用Invoke操作,但是此时就会出现上面的那种异常。经过上网查询,原因如下:
当一个窗口创建时,这个窗口并没有创建句柄,只有Application.Run(form)或者form.Show()之后才有句柄,即窗口只有显示或者启动消息循环后才有句柄!如果创建form之后Form form = new Form(),主线程中调用form.Handle,如果句柄尚未创建,引用该属性将强制创建句柄,对系统内的逻辑将产生致命的影响。也就是说如果我只是new了一个窗口,并不会创建句柄,因此会出现异常。
通过上面的分析我们能得到解决的方法:在UI类中利用this.Invoke(这里this代表主窗口)调用本想调用的委托代码,UI类的句柄在我调用时一定已将创建了,这样可以避免没有创建的句柄,上面已经说过,我们的目的只是想在UI线程中调用Invoke的委托,至于是哪个控件调用的Invoke操作应该都无所谓。
句柄释放后调用Invoke
这种往往发生在关闭窗口是发生,当窗口资源已经释放,句柄已经释放时,非UI线程并没有结束,仍然调用控件的Invoke操作,而此时句柄已经释放,则会产生上述异常,解决方法是在窗口的closing事件中,终止调用Invoke操作的线程,这样调用Invoke的线程终止后才释放控件句柄,就不会发生上诉的异常。
浙公网安备 33010602011771号