所以我有一个我正在使用的本地第三方C ++代码库(.lib和.hpp文件),我曾经在C ++ / CLI中构建一个包装器,以便最终在C#中使用。

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

原始hpp文件中的代码用于回调函数格式:

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

来自C ++ / CLI Wrapper的代码,用于回调函数格式: (我会解释为什么我马上宣布两个)

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

- 很快,我宣布了第二个“UnManagedCallbackFunction”的原因。是我试图创建一个“中介”包装器中的回调,因此链从Native C ++> C#到Native C ++版本> C ++ / CLI Wrapper> C#...完全公开,问题仍然存在,它刚刚被推送到C ++ / CLI Wrapper现在在同一行(返回)。

最后,来自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:0xC0000005:访问   违规阅读地点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对象,从而导致应用程序崩溃。

更全面的披露,它在调试模式下运行良好(貌似)!

一个真正的头部划痕问题,非常感谢任何帮助!

有帮助吗?

解决方案

我认为由于不匹配的调用约定而导致堆栈崩溃: 试着把属性

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

关于回调委托声明。

其他提示

这并没有直接回答你的问题,但它可能会引导你朝着正确的方向调试模式好对比释放模式不正常:

  

由于调试器将大量记录保存信息添加到堆栈中,通常在内存中填充我的程序的大小和布局,我很幸运“在调试模式下,通过涂写超过912字节的内存并不是非常重要的。但是,如果没有调试器,我会在相当重要的事情上乱涂乱画,最终走出我自己的记忆空间,导致Interop删除它没有拥有的记忆。

DataLoggerWrap的定义是什么? char字段可能对于您收到的数据而言太小。

我不确定你想要达到的目标。

几点:

1)垃圾收集器在发布模式下更具攻击性,因此如果拥有不良,您描述的行为并不罕见。

2)我不明白以下代码试图做什么?

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

您使用GCHandle.Alloc在内存中锁定DataLoggerWrap的实例,但是您永远不会将其传递给非托管 - 那么为什么要将其锁定? 你也永远不会释放它吗?

然后第二行抓住一个参考 - 为什么是圆形路径?为什么参考 - 你从不使用它?

3)你将IntPtrs设置为null - 为什么? - 这不会影响功能范围之外的效果。

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

我在@jdehaan,除了CallingConvetion.StdCall可能是答案,特别是当第三方lib是用BC ++编写的时候。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top