Question

J'ai parfois enveloppé une DLL C ++ native avec C ++ / CLI pour une utilisation éventuelle en C #.

Quelques fonctions de rappel entraînent des problèmes au moment de l'exécution. En particulier, j'obtiens l'exception suivante:

  

Une exception de type non gérée   'System.Runtime.InteropServices.InvalidOleVariantTypeException'   s'est produite dans ToadWrapTest.dll

     

Informations complémentaires: OLE spécifié   la variante est invalide.

Sur cette ligne de code (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;
    };

La raison pour laquelle j'ai effectué ce "rappel intermédiaire". Il s'agissait d'une tentative de contourner l'exception de variante non valide levée lorsque j'ai tenté de mapper directement le délégué de C # au code C ++ natif. En tant que tentative de solution de contournement, je déclare un délégué du côté C # et le transfère au gestionnaire C ++ / CLI. Je passe ensuite le funcptr intermédiaire au C ++ natif et enchaîne simplement les appels.

Ce que je sais, c'est que tout fonctionne dans le monde C ++ natif. Le problème est de mapper le vide * sur le monde géré. Le code suivant montre la version C ++ native du rappel:

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

Si quelqu'un peut aider ici, je l'apprécierais vraiment.

Était-ce utile?

La solution

pInstance et pData sont-ils vraiment VARIANT? S'ils le sont, je m'attendrais à ce que votre fonction de rappel soit plus fortement typée:

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

Si tel est le cas, dans votre code, vous devriez pouvoir consulter le fichier VARIANT à vérifier à la main. Si vous n'obtenez pas vraiment de VARIANT (c'est-à-dire que vous n'obtenez que des indicateurs vides *), vous ne devriez pas essayer de les transformer en objets C #, car ils n'ont aucune signification inhérente. Ils devraient être passés en tant que IntPtr. Si vous savez qu'ils doivent avoir un autre type de signification inhérente, vous devez les regrouper comme types appropriés.

Autres conseils

Merci beaucoup de vous appuyer sur celui-ci! Je publie la solution finale ci-dessous à quiconque est confronté à un jeu amusant comme celui-ci! N'hésitez pas à critiquer, je n'ai pas encore optimisé le code. C'est peut-être encore une solution.

Tout d’abord, les fonctions de rappel sont devenues:

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

Gros accessoires sur celui-ci. Cela ne fonctionnera tout simplement pas si vous essayez de lancer du vide * directement vers Object ^. Utilisation de IntPtr et de mon rappel intermédiaire:

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

Nous avons enfin un modèle de travail côté C # avec un massage des objets:

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

Je parle de "masser" les objets parce que le délégué n'est pas spécifique à cette fonction de rappel et je ne sais pas quel objet pData sera avant l'exécution (à partir du POV des délégués). En raison de ce problème, je dois effectuer un travail supplémentaire avec l'objet pData. En gros, je devais surcharger le constructeur de mon wrapper pour accepter un IntPtr. Le code est fourni pour plus de "clarté":

DataPacketWrap (IntPtr dp)
{ 
DataPacket* pdp = (DataPacket*)(dp.ToPointer());
m_NativeDataPacket = pdp;
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top