本文介绍了使用.NET 4.0、3.5时,UnmanagedFunctionPointer导致堆栈溢出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a simple function inside of a click handler that has a try catch block. If I throw an exception within this try catch block it catches the exception successfully.

如果在抛出异常之前对非托管DLL进行了调用,则未处理该异常,并且没被捕获。

If I put a call to an unmanaged DLL before I throw the exception the exception is unhandled and not caught.

什么是无用的DLL调用会破坏我的程序异常处理?

What is the unamanged DLL call doing that could be breaking my programs exception handling?

如果我在调试模式下运行该程序,即使未对所有异常都选中异常中断,它仍会捕获异常。该应用程序不会崩溃,并且可以按预期运行。

If I run the program in debug mode it catches the exception even with "break on exception" unticked for all exceptions. The application does not crash and runs as expected.

如果我以调试时启动的方式运行程序,并在崩溃时单击debug,则会收到以下错误消息: Stack cookie检测代码检测到基于堆栈的缓冲区溢出

If I run the program as "start without debugging" and hit debug when it crashes I get the following error "Stack cookie instrumentation code detected a stack-based buffer overrun"

编辑:
看来堆栈溢出中断了异常处理

edit:It appears the stack overflow breaks the exception handling

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

I've attached a simplified program that produces the crash.

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的调用,则不会发生崩溃

If I replace the call to the UnmanagedFunctionPointer PassThurConnect with the following the crash does NOT occur

[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时是否存在某些未执行的操作或执行不正确的操作会导致缺少

Is there something I am not performing or I am performing incorrectly when assigning the UnmanagedFunctionPointer that would cause the lack of a debugger to create a stackoverflow crash?

这个代码几周前似乎可以正常工作了,这甚至更奇怪了。主要变化是try catch在另一个线程中,而我没有使用lock(syncLock)。现在所有内容都在一个线程中,但是在BackgroundWorker中运行时也发生了相同的崩溃。

What is even stranger is this code appeared to work a few weeks ago. The main changes is the try catch was in another thread and I wasn't using lock(syncLock). Everything is now in one thread however the same crash occurred when run in a BackgroundWorker as well.

UPDATE#2问题半解决

好,所以我一步一步地回滚了我的提交,直到成功为止。改变的是我从.NET 3.5转到.NET 4.0

Ok so I rolled back through my commits one by one until it worked. What changed is I went from .NET 3.5 to .NET 4.0

.NET 3.5不会崩溃,无论是否连接调试器。如果未附加调试器,则.NET 4.0崩溃。为了排除代码中的错误,我只需删除日志的ConcurrentQueue(我使用的唯一4.0功能),然后将当前代码库转换回3.5,就不会收到此错误。

.NET 3.5 does not crash regardless of attaching a debugger or not. .NET 4.0 crashes if a debugger is not attached. To rule out a bug in my code I simply deleted the ConcurrentQueue for my log (the only 4.0 feature I was using) and converted my current code base back to 3.5 and I do not get this error.

要确保100%是4.0的问题,然后我将代码库从3.5转换回4.0,并保留了ConcurrentQueue(实际上只是更改了构建选项并进行了重建),并且StackOverflow崩溃了又回来了。

To be 100% sure it is an issue with 4.0 I then converted my code base back to 4.0 from 3.5 and left the ConcurrentQueue out (literally just changed the build options and did a rebuild) and the StackOverflow crash is back.

我想使用4.0,有什么想法可以调试此问题吗?

I would prefer to use 4.0, any ideas how to debug this issue?

编辑: .NET 4.6.1也崩溃

edit: .NET 4.6.1 also crashes

更新#3

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

Apparently pinvokestackimbalance is basically ignored in .NET 3.5, so the problem still exists, it just doesn't crash my application.

添加以下代码t o当过渡回托管代码时,App.Config导致.NET修复堆栈。性能稍有下降,但可以解决问题。

Adding the following code to App.Config causes .NET to repair the stack when transitioning back to managed code. A small performance hit but it will fix the problem.

虽然这确实可以解决问题,但我想知道UnmanagedFunctionPointer出了什么问题,从而导致了问题

Whilst this does fix the problem, I'd like to know what is wrong with my UnmanagedFunctionPointer to cause the problem in the first place.

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

编辑:此线程不是重复的,另一个已删除...

this thread isn't a duplicate, the other one is deleted...

推荐答案

好,所以问题在于调用约定应该是StdCall而不是Cdecl

Ok so the problem is the calling convention should be StdCall not Cdecl

这是有道理的,因为通用J2534 API文档指定了以下标头。尽管我提供的头文件没有制定此规范。

This makes sense as the generic J2534 API documentation specifies the following header. Although the header file I was supplied does not make this specification.

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

其中WINAPI也称为StdCall不像大多数C / C ++库通常使用的Cdecl。

Where WINAPI is also known as StdCall not Cdecl like most C/C++ libraries would typically use.

.NET 3.5允许使用错误的调用约定并修复堆栈。从4.0版本开始,情况不再如此,并且将引发PinvokeStackImbalance异常。

.NET 3.5 allows the wrong calling convention and will "fix" the stack. As of 4.0 this is no longer the case and a PinvokeStackImbalance exception is raised.

您可以强制4.0也通过将以下代码添加到App.Config来修复堆栈。

You can force 4.0 to also fix the stack with the following code added to your App.Config

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

或者您也可以通过将Cdecl更改为StdCall来简单地修改呼叫约定:

Or you can simply fix your calling convention by changing Cdecl to 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导致堆栈溢出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-21 12:30