我有一种情况,我用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);

如果有人可以在这里提供帮助,我真的很感激。

有帮助吗?

解决方案

pInstance和pData真的是VARIANT吗?如果是,我希望你的回调函数更强类型:

int (*CallbackFunction)(VARIANT *inst, VARIANT *data);

如果是这种情况,在您的代码中,您应该能够查看实际的 VARIANT 亲自检查一下。如果你没有真正获得VARIANT(即,你真的只是获得无效*指针),你不应该试图将它们变成C#对象,因为它们没有固有的含义。它们应该作为IntPtr传递。如果您知道它们应该具有其他类型的固有含义,则需要将它们作为适当的类型进行编组。

其他提示

非常感谢这个基座的基座!我将以下最终解决方案发布给其他任何必须处理第三方乐趣的人!请随意批评,因为我没有优化代码。这可能仍然是一个解决方案。

首先,回调函数变为:

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;
};
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top