從.NET1.1升級到.NET2.0時出現的PInvokeStackImbalance錯誤

.NET1.1升級到.NET2.0時出現的PInvokeStackImbalance錯誤

 

微軟官方的解釋 (http://msdn2.microsoft.com/zh-cn/library/0htdy0k3.aspx)

如果 CLR 檢測到平臺調用之後的堆棧深度與 DllImportAttribute 屬性指定的調用約定中以及託管簽名的參數聲明中提供的預期堆棧深度不匹配,則將激活 PInvokeStackImbalance 託管調試助手 (MDA)

 

下面將舉一個具體的例子

PCCamera(UserLib.Device.PCCamera攝像頭類)在從.NET1.1級到.NET2.0時出現的PInvokeStackImbalance錯誤:

檢測到 PInvokeStackImbalance

Message: PInvoke 函數“WindowsApplication1!UserLib.Device.PCCamera::SendMessage”的調用導致堆棧不對稱。原因可能是託管的 PInvoke 簽名與非託管的目標籤名不匹配。請檢查 PInvoke 簽名的調用約定和參數與非託管的目標籤名是否匹配。

錯誤首次發生在這一行代碼: SendMessage(hWndC, WM_CAP_SET_CALLBACK_VIDEOSTREAM, 0, 0);

經過分析發現所有調用SendMessage函數的地方都會出現以上錯誤

於是查看DLLImport:

[DllImport("User32.dll")]

private static extern bool SendMessage(IntPtr hWnd, int wMsg, int wParam, long lParam);

原來是因爲WinAPIlong類型是32位的,C#long64位的,這就導致堆棧不對稱,引發錯誤.

 

原因: (http://discuss.develop.com/archives/wa.exe?A2=ind0512c&L=dotnet-winforms&D=0&T=0&P=8094)

.NET2.0中加入了MDA(managed debugging assistant), 在平臺調用時後會檢查棧的指針, 如果發現不平衡, 就會拋出PInvokeStackImbalance異常; 而在.NET1.1中不會檢查, 所以不會捕獲到異常, 但在運行時會導致不穩定.

 

解決方法:

將最後一個long lParam改爲 int wParam”, 因爲C#int32位的. 並且將之後有涉及到SendMessage函數的參數適當地轉成int型就可以了.

但是, 之後查閱了資料http://www.pinvoke.net/default.aspx/user32/SendMessage.html

發現先前的解決方案還有不合適的地方, 應該將其中的 ”wParm” ”lParm” 參數的類型都轉成IntPtr類型,並且將後面涉及到的參數的 ”0”改爲 “IntPtr.Zero”. 因爲如果使用int類型,那麼這段代碼在64位的Windows上面將會無法正常運行.

 

總結:

       我們在調用WinAPI時要特別小心, 因爲WinAPIC#的數據類型不是完全一樣, 就好像在WinAPI中的long類型在C#中就是int類型, 如果沒有處理好類型問題, 就很可能會導致堆棧的不平衡,引發PInvokeStackImbalance錯誤, 但是這類錯誤在.NET1.1下不會被暴露出來, 所以在從.NET1.1級到.NET2.0時要特別注意此類問題.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章