所以我有一个我正在使用的本地第三方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对象,从而导致应用程序崩溃。
更全面的披露,它在调试模式下运行良好(貌似)!
一个真正的头部划痕问题,非常感谢任何帮助!
答案 0 :(得分:3)
我认为由于不匹配的调用约定而导致堆栈崩溃: 试着把属性
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
关于回调委托声明。
答案 1 :(得分:0)
This doesn't directly answer your question,但就调试模式而言,它可能会引导您朝正确的方向发展,而发布模式则不正常:
由于调试器向堆栈中添加了大量的记录保存信息,通常在内存中填充我的程序的大小和布局,我在调试模式中“通过涂写超过912字节的内存”来“幸运”非常重要。但是,如果没有调试器,我会在相当重要的事情上涂鸦,最终走出我自己的内存空间,导致Interop删除它不拥有的内存。
DataLoggerWrap的定义是什么? char字段可能对于您收到的数据而言太小。
答案 2 :(得分:0)
我不确定你想要达到的目标。
几点:
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回调或调用函数?
答案 3 :(得分:0)
我在@jdehaan,除了CallingConvetion.StdCall可能是答案,特别是当第三方lib是用BC ++编写的时候。