访问冲突异常/崩溃从C ++回调到C#功能(Access Violation Exception/C

2019-07-17 18:19发布

所以我有一个本土的第三方C ++代码库,我与我用来建立在C#中最终使用在C ++ / CLI的包装(.LIB和.HPP文件)工作。

我从调试切换到发布模式时,碰到一个特殊的问题,因为我得到一个访问冲突异常时一个回调的代码返回。

从回调函数的格式原来的HPP文件的代码:

typedef int (*CallbackFunction) (void *inst, const void *data);

从C ++ / CLI包装代码以便回调函数格式:(我会解释为什么我在某一时刻宣布二)

public delegate int ManagedCallbackFunction (IntPtr oInst, const IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);

--Quickly,原因我声明一个第二“UnManagedCallbackFunction”是我试图创建在包装的“中介”回调,所以链条从本机C ++> C#改变为版本的本机C ++> C ++ / CLI包装> C# ......充分披露,问题仍然生活,它只是被推迟到C ++ / CLI包装现在在同一行(返回)。

最后,从C#的崩溃代码:

public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData)
    {
        Console.WriteLine("in hReceiveLogEvent...");
        Console.WriteLine("pInstance: {0}", pInstance);
        Console.WriteLine("pData: {0}", pData);

        // provide object context for static member function
        helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target;
        if (hw == null || pData == null)
        {
            Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n");
            return 0;
        }

        // typecast data to DataLogger object ptr
        IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
        DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

        //Do Logging Stuff

        Console.WriteLine("exiting hReceiveLogEvent...");
        Console.WriteLine("pInstance: {0}", pInstance);
        Console.WriteLine("pData: {0}", pData);
        Console.WriteLine("Setting pData to zero...");
        pData = IntPtr.Zero;
        pInstance = IntPtr.Zero;
        Console.WriteLine("pData: {0}", pData);
        Console.WriteLine("pInstance: {0}", pInstance);

        return 1;
    }

所有控制台写操作完成,然后我们看到了回报可怕的崩溃:

在0x04d1004c HelloWorld.exe的中未处理的异常:0000005:访问冲突读取位置0x04d1004c。

如果我踏入从这里调试器,我看到的是,在调用堆栈中的最后一项是:>“04d1004c()”的计算结果为十进制值:80805964

这仅仅是有趣的,如果你看一下这表明控制台:

entering registerDataLogger
pointer to callback handle: 790848
fp for callback: 2631370
pointer to inst: 790844
in hReceiveLogEvent...
pInstance: 790844
pData: 80805964
exiting hReceiveLogEvent...
pInstance: 790844
pData: 80805964
Setting pData to zero...
pData: 0
pInstance: 0

现在,我知道,调试之间并释放出一些东西都在微软的世界完全不同。 我当然担心字节填充和变量的初始化,所以如果有什么我这里不提供,只是让我知道,我会加入到(已经长)职务。 我也认为,托管代码可能不释放所有所有权,然后在本地C ++的东西(我不具备的代码)可能试图删除或杀死的对象pData中,从而崩溃的应用程序。

更充分披露,这一切工作正常(貌似)在调试模式下!

一个真正的头划伤的问题是希望得到任何帮助!

Answer 1:

我想是因为不匹配的调用约定的堆栈被压碎:尝试把属性

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

在回调的委托声明。



Answer 2:

这并不直接回答你的问题 ,但它可能就导致你在正确的方向调试模式好与释放模式不行:

因为调试增添了不少的记录保持信息到堆栈,一般填充了我的计划的规模和布局在内存中,我是“让幸运”在调试模式下通过涂鸦超过912字节的内存不是很重要。 如果没有调试器,不过,我是涂鸦上的,而重要的事情上面,最终走我自己的内存空间之外,造成互操作删除记忆它没有自己。

什么是DataLoggerWrap的定义是什么? 一个char场可能是你收到的数据太小。



Answer 3:

我不知道你的要实现的目标。

有几个要点:

1)垃圾收集器是在释放模式更具侵略性所以不好拥有你所描述的现象并不少见。

2)我不明白什么下面的代码是意欲何为?

IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

您可以使用GCHandle.Alloc在内存中锁定DataLoggerWrap的实例,但你永远不会把它传递出非托管 - 那么你为什么锁定? 你也永远不会释放它?

然后第二行抓住后面的参考 - 为什么圆形路径? 为什么参考 - 你永远不使用它?

3)您可以设置IntPtrs为空 - 为什么? - 这将具有的功能范围之外没有任何影响。

4)你需要知道回调的合同是什么。 谁拥有的pData回调或调用函数?



Answer 4:

我与@jdehaan,除了CallingConvetion.StdCall可能是答案,尤其是当第三方的lib是写在BC ++,例如。



文章来源: Access Violation Exception/Crash from C++ callback to C# function