簡體   English   中英

UnmanagedFunctionPointer 在使用 .NET 4.0 時會導致 stackoverflow,3.5 有效

[英]UnmanagedFunctionPointer causes stackoverflow when using .NET 4.0, 3.5 works

我在點擊處理程序中有一個簡單的函數,它有一個 try catch 塊。 如果我在這個 try catch 塊中拋出異常,它會成功捕獲異常。

如果我在拋出異常之前調用非托管 DLL,則異常未處理且未被捕獲。

未管理的 DLL 調用在做什么可能會破壞我的程序異常處理?

如果我在調試模式下運行程序,它會捕獲異常,即使所有異常都未勾選“異常中斷”。 應用程序不會崩潰並按預期運行。

如果我以“不調試啟動”的方式運行程序並在崩潰時點擊調試,我會收到以下錯誤“堆棧 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 時是否有什么我沒有執行或我執行不正確會導致缺少調試器來創建 stackoverflow 崩潰?

更奇怪的是這段代碼在幾周前似乎可以工作。 主要的變化是 try catch 在另一個線程中,而我沒有使用 lock(syncLock)。 現在一切都在一個線程中,但是在 BackgroundWorker 中運行時也會發生同樣的崩潰。

更新 #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

顯然 pinvokestackimbalance 在 .NET 3.5 中基本上被忽略了,所以問題仍然存在,它只是不會使我的應用程序崩潰。

將以下代碼添加到 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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM