繁体   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