UnmanagedFunctionPointer 在使用 .NET 4.0、3.5 时会导致 stackoverflow

UnmanagedFunctionPointer causes stackoverflow when using .NET 4.0, 3.5 works

我在具有 try catch 块的点击处理程序中有一个简单的函数。如果我在此 try catch 块中抛出异常,它会成功捕获异常。

如果我在抛出异常之前调用非托管 DLL,则异常未处理且未被捕获。

未管理的 DLL 调用在做什么,可能会破坏我的程序异常处理?

如果我 运行 程序处于调试模式,它会捕获异常,即使 "break on exception" 未勾选所有异常。应用程序未按预期崩溃和 运行s。

如果我 运行 程序作为 "start without debugging" 并在它崩溃时点击调试,我得到以下错误 "Stack cookie instrumentation code detected a stack-based buffer overrun"

编辑: 看起来堆栈溢出破坏了异常处理

我附上了导致崩溃的简化程序。

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 会导致缺少调试器导致 Whosebug 崩溃时,我是否没有执行或执行不正确?

更奇怪的是这段代码似乎在几周前就起作用了。主要变化是 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(实际上只是更改了构建选项并进行了重建),Whosebug 崩溃又回来了.

我更愿意使用 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 文档指定了以下内容 header。虽然我提供的 header 文件没有做这个规范。

extern "C" long WINAPI PassThruConnect
(
unsigned long ProtocolID;
unsigned long Flags
unsigned long *pChannelID
)

WINAPI 也被称为 StdCall,而不是大多数 C/C++ 库通常使用的 Cdecl。

.NET 3.5 允许错误的调用约定并将 "fix" 堆栈。从 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);