因此,我有一个本机第三方C++代码库我正在使用(.lib和.hpp文件),我用它在C++/CLI中构建包装以供最终使用在C#中。访问冲突异常/从C++回调崩溃到C#函数
从调试模式切换到发布模式时遇到特定问题,因为回调代码返回时出现访问冲突异常。
从回调函数的格式原来的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: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对象,从而导致应用程序崩溃。
更全面的披露,这一切工作正常(看似)在调试模式!
一个真正的头刮伤问题,将不胜感激任何帮助!
对于支持,这是最正确的。联系第三方供应商后,我们发现他们使用cdecl规范进行编译,而不是托管代码合规性所需的stdcall:http://msdn.microsoft.com/en-us/library/367eeye0%28VS.80%29.aspx 。我在StackOverflow上添加了一个问题,询问为什么需要这样做?希望有人会比引用的MSDN文章给出更好的解释。 – TomO 2009-09-21 15:14:30
如果未使用__declspec()指定,则在项目设置中会使用调用约定(C/C++)的默认值。该调用约定在代码中不可见。不匹配约定会发生什么情况很明显:如果堆栈清理的责任不匹配,则会压碎堆栈(由于双重清理或太少而未重置为调用之前的状态)。这取决于栈上传递的参数的数量。 http://en.wikipedia.org/wiki/Calling_convention – jdehaan 2009-09-21 16:20:22