Domanda

Ho una situazione in cui ho racchiuso una DLL C ++ nativa con C ++ / CLI per un eventuale utilizzo in C #.

Esistono alcune funzioni di callback che causano alcuni problemi in fase di esecuzione. In particolare, ottengo la seguente eccezione:

  

Un'eccezione di tipo non gestita   'System.Runtime.InteropServices.InvalidOleVariantTypeException'   si è verificato in ToadWrapTest.dll

     

Ulteriori informazioni: OLE specificato   la variante non è valida.

Su questa riga di codice (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;
    };

Il motivo per cui ho effettuato questo "callback intermediario" è stato un tentativo di eludere l'eccezione variante non valida generata quando ho provato a mappare direttamente il delegato da C # al codice C ++ nativo. Come tentativo di aggirare, dichiaro un delegato sul lato C # e trasmetto tale funzione al wrapper C ++ / CLI. Quindi passo il funcptr intermedio al C ++ nativo e faccio semplicemente daisy chain le chiamate.

Quello che so è che tutto funziona nel mondo C ++ nativo. Il problema è mappare il vuoto * sul mondo gestito. Il codice seguente mostra la versione C ++ nativa del callback:

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

Se qualcuno può aiutarti qui, lo apprezzerei molto.

È stato utile?

Soluzione

pInstance e pData sono davvero VARIANTI? Se lo sono, mi aspetterei che la tua funzione di callback sia tipizzata più fortemente:

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

In tal caso, nel tuo codice dovresti essere in grado di esaminare l'attuale VARIANT per controllarlo manualmente. Se non si ottengono realmente VARIANTI (ovvero si ottengono solo puntatori * vuoti), non si dovrebbe provare a trasformarli in oggetti C # poiché non hanno un significato intrinseco per essi. Dovrebbero essere passati come IntPtr. Se sai che dovrebbero avere qualche altro tipo di significato intrinseco, devi metterli in Marshall come tipi appropriati.

Altri suggerimenti

Grazie mille per il plinto su questo! Sto pubblicando la soluzione finale di seguito a chiunque abbia a che fare con divertimento di terze parti come questo! Non esitate a criticare, poiché non ho finito di ottimizzare il codice. Questo potrebbe essere ancora per la rotonda di una soluzione.

Innanzitutto, le funzioni di callback sono diventate:

public delegate int ManagedCallbackFunction (IntPtr oInst, IntPtr oData);
public delegate int UnManagedCallbackFunction (void* inst, const void* data);
ManagedCallbackFunction^ m_callbackFn;

Grandi oggetti di scena su questo. Semplicemente non funzionerà se provi a trasmettere da void * direttamente a Object ^. Utilizzando IntPtr e il mio callback intermediario:

int intermidiaryCallback(void * pInstance, const void * pData)
{   
    void* temp = (void*)pData;
    return m_callbackFn->Invoke(IntPtr(pInstance), IntPtr(temp));
};

Finalmente otteniamo un modello funzionante sul lato C # con qualche massaggio degli oggetti:

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;
}

Cito " massaggiando " gli oggetti perché il delegato non è specifico di questa funzione di callback e non so quale sarà pData oggetto fino al runtime (dal POV dei delegati). A causa di questo problema, devo fare qualche lavoro extra con l'oggetto pData. Fondamentalmente ho dovuto sovraccaricare il costruttore nel mio wrapper per accettare un IntPtr. Il codice viene fornito per la massima "chiarezza":

DataPacketWrap (IntPtr dp)
{ 
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top