2012-05-08 77 views
1

首先,我的英語很抱歉,因爲它不是我的母語。Win32Exception:創建窗口句柄時出錯(大量嵌套控件)

問題發生在我的.NET Windows窗體應用程序中,出於某些原因僅在Virtaul PC Windows 7(x64)上覆制。

我在應用程序的一個地方遇到了Win32Exception,當調整包含大量自定義控件的主要應用程序表單時,包括兩個彼此堆疊的tabcontrols等等......異常的消息是「創建窗口句柄時出錯」。花一些時間,我在微軟支持網站上發現了這個問題的解決方案。這與我的情況非常相似。來自網站的Qoute:

在.NET應用程序中,如果父對象嵌套深度較大時,它們的父對象的調整大小有時不會調整。在64位和32位平臺上都會發生此問題,但在32位系統上需要更深的嵌套級別。

會發生什麼情況是,當頂層控件佈局時,SetBoundsCore調用SetWindowPos來調整子控件的大小。每個子控件都會收到一個WM_WINDOWPOSCHANGED通知,該通知觸發一個佈局事件,該事件調用SetBoundsCore,該事件調用SetWindowPos。每次調用SetWindowPos進入內核模式,然後退出以傳遞WM_WINDOWPOSCHANGED通知。最終線程將耗盡內核堆棧空間,並且SetWindowPos會自動失敗。

建議的解決方案是重寫爲容器控件OnSizeChanged方法(如面板或TabControl的(在我的情況)):

protected override void OnSizeChanged(EventArgs e) 
    { 
     if (this.Handle != null) 
     { 
      BeginInvoke((MethodInvoker)(() => base.OnSizeChanged(e);)); 
     } 
    } 

我成功地應用於該溶液到我的情況下:創建的自定義的TabControl(繼承自純winForms TabControl類)和改變TabControl實例與我的自定義控件實例。問題就消失了!但...

...當我在CCnet構建機器上啓動Build時,我在不同的單元測試中出現了很多Win32Exception -s「Error creation window handle」,但最有趣的不是我的問題控件(包含自定義TabControl對象)單元測試!錯誤情況有所不同,但它們與我的自定義控件無關。當我恢復更改時,一切正常,構建已創建(單元測試成功執行)。

它使我完全困惑,應用更改修復了用戶應用程序的可用性,但導致創建構建時單元測試失敗。

順便說一句,在本地機器上,所有的單元測試在任何情況下都可以正常執行(有或沒有修復)。

我花了很多時間研究這個問題,現在我只有一個假設:ccnet構建機器在一個進程中執行所有單元測試,並且測試不會正確地處理測試數據(gdi對象)(在拆卸時),因爲在達到允許的gdi對象句柄的限制(10000)時,通常會發生錯誤「創建窗口句柄錯誤」。

請您分享一下您的專業意見嗎?提前致謝。

回答

0

與您的錯誤沒有直接關係,但訪問Handle屬性將強制創建窗口句柄,這有時是不可能的。辦理支票不應該這樣進行:

if (this.Handle != null) 

代替,檢查IsHandleCreated屬性:

if (this.IsHandleCreated) 

我已經提到了這對the blog post這個問題而回,但它似乎不具備被注意到。

+0

謝謝!我會做出適當的改變。我也安裝了resharper,並且它總是標記爲「if(this.Handle!= null)」,表明它是多餘的「條件總是如此......」。老實說,我不明白是否有可能調整大小將在沒有創建句柄的控制上完成?僅僅是這種可能的處置控制? –

+0

是的,size屬性可以在創建句柄之前更改(通常在顯示控件時)。例如當Size通過設計器設置時,它在控件的構造函數中設置。 – roken

0

如果作爲CCNET機器上的單元測試運行,它可能運行在非交互式服務上下文中,這可能會阻止創建窗口,因爲沒有人可以查看它們。

+0

我沒有涉及到單元測試實現的細節,但我認爲他們創建表單實例但不渲染它們。我當然知道的是訪問器對象用於訪問私有字段。但是,無論是否顯示(呈現),每個gdi對象實例都會創建句柄。 –

0

我對深度嵌套控件有同樣的問題。然而,對於一個適當的解決方案,我不得不覆蓋並延遲SetBoundsCore

protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) 
    { 
     if (this.IsHandleCreated) 
     { 
      this.BeginInvoke(
          (MethodInvoker)delegate 
          { 
           base.SetBoundsCore(x, y, width, height, specified); 
          }); 
     } 
    }