Вопрос

У меня есть ситуация, когда я обернул Native C ++ DLL с C ++ / CLI для возможного использования в 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 ++. Проблема заключается в отображении пустоты * в управляемый мир. В следующем коде показана собственная версия обратного вызова для C ++:

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

Если кто-то может помочь здесь, я буду очень признателен.

Это было полезно?

Решение

pInstance и pData действительно ВАРИАНТЫ? Если это так, я бы ожидал, что ваша функция обратного вызова будет более типизированной:

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

Если это так, то в своем коде вы сможете посмотреть на реальный VARIANT , чтобы проверить его. Если вы на самом деле не получаете VARIANT (т.е. вы на самом деле просто получаете указатели void *), вам не следует пытаться превратить их в объекты 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