C#中有關資源、BeginInvoke, Invoke和事件的事情


 

事情是這么來的,我開發的一個程序報了一個錯誤 “在創建窗口句柄之前,不能在控件上調用 Invoke 或 BeginInvoke錯誤”。

然后我在網上查資料,發現一個有意思的問題,文章出處為“在創建窗口句柄之前,不能在控件上調用 Invoke 或 BeginInvoke”錯誤。

 

問題

程序是如下這樣的。

Form1有Button1、Button2和Button3兩個按鈕,Button2是動態的new Form2窗體,Form2中注冊了Form1中的事件.

Button1的作用是關閉所有new出來的Form2窗體,並且把其資源釋放掉(Dispose()).

Button3是讓Form2中注冊Form1了的事件都發生。

操作:

先點Button2  new出幾個Form2的窗體,然后點擊Button1釋放掉所有的資源,然后再單擊Button2 new出幾個Form2的窗體,再單擊Button3問題就出現了。老是提示"在創建窗口句柄之前,不能在控件上調用 Invoke 或 BeginInvoke"。

 

作者給出的解析

“在Window窗體程序開發的時候,如果使用多線程編程,在子線程中訪問主線程窗體內的控件,就需要使用控件的Control.Invoke方法或者BeginInvoke方法。但是有時候因為Window執行速度太快,尤其是你寫代碼的時候在InitializeComponent();完成之前起了一個線程去執行某些操作,涉及到窗體控件的,當你在調用Control.Invoke的時候,就可能出現 “在創建窗口句柄之前不能在控件上調用 Invoke 或 BeginInvoke”錯誤。

這個解釋我感覺十分有道理。

 

給出的解決方法

解決的辦法就是讓線程等待,直到窗口句柄創建完畢:
//防止在窗口句柄初始化之前就走到下面的代碼
while (!this.IsHandleCreated)
{
    ;
}
this.BeginInvoke(new ProListIndexChangedDelegate(GetProLyric));

//根據不同情況也可以:
if (this.IsHandleCreated)
BeginInvoke(new ProListIndexChangedDelegate(GetProLyric));

 

其它人(鑽葛格)給的解決方法:

一個更巧妙的方法,只要在BeginInvoke方法的調用語句前再加一句:IntPtr i = this.Handle;就OK了,這比死循環配合this.IsHandleCreated的判斷方法更簡潔,因為this.Handle這個屬性本身就對應一個方法,取不到句柄,程序就不會向下進行。

如果以上方式時,IntPtr IsHandleCreated = this.Handle;報錯:“從不是創建他的線程訪問”。則可把this換成你想提取的句柄所在的線程中的窗體類(或其中任一控件)的實例名。

 本人也就是通過該方法最終解決問題的。也很有可能是作者給出的解釋的那種原因。

 

結果

作者的解決的結果

再次新建窗體的窗口句柄在Show()之后都通過IsHandleCreated屬性檢測過了,再調用Invoke()之前都創建了,還是會出錯。那問題出在哪里呢?一開始還以為單擊Button1沒有真正的釋放資源,然后我就強制GC了,然后檢測不到以前的窗體了,但是還是不行,還是有問題。

然后就想到了“事件”上面來了。是不是事件的原因呀?以前創建的窗體沒有注銷該事件,把之前所有的窗體都注銷該事件,問題就解決了......

 

有留言認為作者的意思是:根本不是沒創建的原因,而是沒有清空事件。

我覺得確實應該是訪問了沒有創建的UI資源造成的!作者的解決方法是對的,但是說法是錯的。
也就是作者在Button1中釋放(Close)了Form2了。而卻沒有取消Form2中所訂閱的Form1的事件。
此時當點擊了button3的話,將激發Form1中事件的委托鏈,而已經釋放掉的Form2對象的事件的響應,由於找到不到該對象資源了,造成了老是提示"在創建窗口句柄之前,不能在控件上調用 Invoke 或 BeginInvoke"。
解決辦法缺失如作者所說,去除已經被釋放對象的訂閱的事件即可。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM