問題描述
我在帶有try catch塊的點擊處理程序中有一個簡單的函數。如果我在此try catch塊中拋出異常,則它會成功捕獲該異常。
如果在拋出異常之前對非託管DLL進行了調用,則未處理該異常,並且沒被捕獲。
什麼是無用的DLL調用會破壞我的程序異常處理?
如果我在調試模式下運行該程序,即使未對所有異常都選中異常中斷,它仍會捕獲異常。該應用程序不會崩潰,並且可以按預期運行。
如果我以調試時啓動的方式運行程序,並在崩潰時單擊debug,則會收到以下錯誤消息: Stack cookie檢測代碼檢測到基於堆棧的緩衝區溢出
編輯:
看來堆棧溢出中斷了異常處理
我附上了一個導致崩潰的簡化程序。
ISOConnection _comm; //This is instantiated at another time in the same thread //C# test function that crashes when run without a debugger attached bool DoMagic() { try { //if I uncomment this line the exception becomes unhandled and cannot be caught //_comm.ConnectISO15765(); throw new Exception(); } catch (Exception ex) { MessageBox.Show("Caught exception") } //Within ISOConnection class public void ConnectISO15765(){ ... lock(syncLock){ uint returnCode = J2534Interface.PassThruConnect((uint)DeviceId, (uint)ProtocolID.ISO15765, (uint)ConnectFlag.NONE, (uint)BaudRate.ISO15765, ref ChannelId); //C# UnmanagedFunctionPointer allocation code [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate uint PassThruConnect(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelId); public PassThruConnect Connect; [DllImport("kernel32.dll")] public static extern IntPtr LoadLibrary(string dllToLoad); m_pDll = NativeMethods.LoadLibrary(path); ... pAddressOfFunctionToCall = NativeMethods.GetProcAddress(m_pDll, "PassThruConnect"); if (pAddressOfFunctionToCall != IntPtr.Zero) Connect = (PassThruConnect)Marshal.GetDelegateForFunctionPointer( pAddressOfFunctionToCall, typeof(PassThruConnect)); //C++ function declaration long PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long Baudrate, unsigned long *pChannelID);
更新
如果我使用以下命令替換對UnmanagedFunctionPointer PassThurConnect的調用,則不會發生崩潰
[DllImport("op20pt32.dll", EntryPoint = "PassThruConnect", CallingConvention = CallingConvention.Cdecl)] public static extern uint PassThruConnect2(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelId);
分配UnmanagedFunctionPointer時是否存在某些未執行的操作或執行不正確的操作會導致缺少
這個代碼幾周前似乎可以正常工作了,這甚至更奇怪了。主要變化是try catch在另一個線程中,而我沒有使用lock(syncLock)。現在所有內容都在一個線程中,但是在BackgroundWorker中運行時也發生了相同的崩潰。
UPDATE#2問題半解決
好,所以我一步一步地回滾了我的提交,直到成功爲止。改變的是我從.NET 3.5轉到.NET 4.0
.NET 3.5不會崩潰,無論是否連接調試器。如果未附加調試器,則.NET 4.0崩潰。爲了排除代碼中的錯誤,我只需刪除日誌的ConcurrentQueue(我使用的唯一4.0功能),然後將當前代碼庫轉換回3.5,就不會收到此錯誤。
要確保100%是4.0的問題,然後我將代碼庫從3.5轉換回4.0,並保留了ConcurrentQueue(實際上只是更改了構建選項並進行了重建),並且StackOverflow崩潰了又回來了。
我想使用4.0,有什麼想法可以調試此問題嗎?
編輯: .NET 4.6.1也崩潰
更新#3
http://codenition.blogspot.com.au/2010/05/pinvokestackimbalance-in-net-40i-beg .html
在.NET 3.5中,顯然pinvokestackimbalance基本上被忽略了,所以問題仍然存在,只是不會使我的應用程序崩潰。
添加以下代碼t o當過渡回託管代碼時,App.Config導致.NET修復堆棧。性能稍有下降,但可以解決問題。
雖然這確實可以解決問題,但我想知道UnmanagedFunctionPointer出了什麼問題,從而導致了問題
<configuration> <runtime> <NetFx40_PInvokeStackResilience enabled="1"/>
編輯:此線程不是重複的,另一個已刪除...
推薦答案
好,所以問題在於調用約定應該是StdCall而不是Cdecl
這是有道理的,因爲通用J2534 API文檔指定了以下標頭。儘管我提供的頭文件沒有制定此規範。
extern "C" long WINAPI PassThruConnect ( unsigned long ProtocolID; unsigned long Flags unsigned long *pChannelID )
其中WINAPI也稱爲StdCall不像大多數C / C ++庫通常使用的Cdecl。
.NET 3.5允許使用錯誤的調用約定並修復堆棧。從4.0版本開始,情況不再如此,並且將引發PinvokeStackImbalance異常。
您可以強制4.0也通過將以下代碼添加到App.Config來修復堆棧。
<configuration> <runtime> <NetFx40_PInvokeStackResilience enabled="1"/>
或者您也可以通過將Cdecl更改爲StdCall來簡單地修改呼叫約定:
[UnmanagedFunctionPointer(CallingConvention.StdCall)] public delegate uint PassThruConnect(uint deviceId, uint protocolId, uint flags, uint baudRate, ref uint channelID);
這篇關於使用.NET 4.0、3.5時,UnmanagedFunctionPointer導致堆棧溢出的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持IT屋!