使用.NET 4.0、3.5時,UnmanagedFunctionPointer導致堆棧溢出

本文介紹了使用.NET 4.0、3.5時,UnmanagedFunctionPointer導致堆棧溢出的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨着小編來一起學習吧!

問題描述

我在帶有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屋!

參考鏈接:https://www.it1352.com/1792610.html

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