UnmanagedFunctionPointer在使用.NET 4.0时导致堆栈溢出,3.5工作

时间:2016-10-17 01:41:26

标签: c# c++ exception interop clr

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

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

什么是unamanged DLL调用,这可能会破坏我的程序异常处理?

如果我在调试模式下运行程序,它会捕获异常,即使对于所有异常都没有“中断异常”。应用程序不会崩溃并按预期运行。

如果我将程序运行为“start without debugging”并在崩溃时点击调试我得到以下错误“堆栈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中运行时也发生了同样的崩溃。

更新#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崩溃了。 / p>

我更愿意使用4.0,任何想法如何调试此问题?

编辑:.NET 4.6.1也崩溃了

更新#3 http://codenition.blogspot.com.au/2010/05/pinvokestackimbalance-in-net-40i-beg.html

显然,在.NET 3.5中基本上忽略了pinvokestackimbalance,所以问题仍然存在,它只是不会使我的应用程序崩溃。

将以下代码添加到App.Config会导致.NET在转换回托管代码时修复堆栈。虽然性能很小,但它可以解决问题。

虽然这确实解决了这个问题,但我想知道我的UnmanagedFunctionPointer有什么问题导致问题出现。

<configuration> 
  <runtime> 
    <NetFx40_PInvokeStackResilience enabled="1"/>

编辑:此帖子不重复,另一个被删除...

1 个答案:

答案 0 :(得分:2)

好的问题是调用约定应该是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允许错误的调用约定,并且将修复&#34;堆栈。从4.0开始,情况不再如此,并引发了PinvokeStackImbalance异常。

您可以使用以下代码添加到App.Config

来强制4.0修复堆栈
<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);