我有一种情况,我用C ++ / CLI包装了一个Native C ++ DLL,以便最终在C#中使用。
有一些回调函数在运行时导致一些问题。特别是,我得到以下例外:
未处理的类型异常 'System.Runtime.InteropServices.InvalidOleVariantTypeException' 发生在ToadWrapTest.dll
其他信息:指定的OLE 变体无效。
在这行代码(C ++ / CLI)上:
public delegate int ManagedCallbackFunction (Object^ inst, const Object^ data);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
System::IntPtr ip1 = IntPtr(pInstance);
System::IntPtr ip2 = IntPtr(temp);
Object^ oInst = Marshal::GetObjectForNativeVariant(ip1);
Object^ oData = Marshal::GetObjectForNativeVariant(ip2);
//invoke the callback to c#
//return m_callbackFn::Invoke(oInst, oData);
return 0;
};
我做这个“中间回调”的原因是试图绕过当我试图将委托从C#直接映射到本机C ++代码时抛出的无效变体异常。作为尝试的解决方法,我在C#端声明了一个委托,并将该funcptr传递给C ++ / CLI包装器。然后我将中间函数funcptr传递给本机C ++,并将调用菊花链接在一起。
我所知道的是它都适用于原生C ++世界。问题是将void *映射到托管世界。以下代码显示了回调的本机C ++版本:
int (*CallbackFunction) (void *inst, const void *data);
如果有人可以在这里提供帮助,我真的很感激。
答案 0 :(得分:2)
pInstance和pData真的是VARIANT吗?如果是,我希望你的回调函数更强类型:
int (*CallbackFunction)(VARIANT *inst, VARIANT *data);
如果是这种情况,在您的代码中,您应该能够查看实际的VARIANT来进行检查。如果你没有真正获得VARIANT(即,你真的只是得到了无效*指针),你不应该试图把它们变成C#对象,因为它们没有固有的含义。它们应该作为IntPtr传递。如果您知道它们应该具有其他类型的固有含义,则需要将它们作为适当的类型进行编组。
答案 1 :(得分:2)
非常感谢这个基座的基座!我将以下最终解决方案发布给其他任何必须处理第三方乐趣的人!请随意批评,因为我没有优化代码。这可能仍然是一个解决方案。
首先,回调函数变为:
public delegate int ManagedCallbackFunction (IntPtr oInst, IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;
这个上面的大道具。如果您尝试从void *直接转换为Object ^,那么它将无法正常工作。使用IntPtr和我的中间回调:
int intermidiaryCallback(void * pInstance, const void * pData)
{
void* temp = (void*)pData;
return m_callbackFn->Invoke(IntPtr(pInstance), IntPtr(temp));
};
我们终于在C#端获得了一个工作模型,并对对象进行了一些按摩:
public static int hReceiveTestMessage(IntPtr pInstance, IntPtr pData)
{
// provide object context for static member function
helloworld2 hw = (helloworld2)GCHandle.FromIntPtr(pInstance).Target;
if (hw == null || pData == null)
{
Console.WriteLine("hReceiveTestMessage received NULL data or instance pointer\n");
return 0;
}
// populate message with received data
IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataPacketWrap(pData)));
DataPacketWrap dpw = (DataPacketWrap)GCHandle.FromIntPtr(ip2).Target;
uint retval = hw.m_testData.load_dataSets(ref dpw);
// display message contents
hw.displayTestData();
return 1;
}
我提到“按摩”对象,因为委托不是特定于此回调函数,我不知道pData将在运行时(从代理POV)到什么对象。由于这个问题,我必须对pData对象做一些额外的工作。我基本上不得不重载我的包装器中的构造函数来接受IntPtr。代码提供完整的“清晰度”:
DataPacketWrap (IntPtr dp)
{
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};