Недопустимый сбой варианта
Вопрос
У меня есть ситуация, когда я обернул 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;
};