Question

J'ai donc une base de code C ++ native avec laquelle je travaille (fichiers .lib et .hpp) et sur laquelle j'ai construit un wrapper en C ++ / CLI pour une utilisation éventuelle en C #.

J'ai rencontré un problème particulier lors du passage du mode Debug au mode Publication, en ce sens que je reçois une exception de violation d'accès lorsque le code d'un rappel est renvoyé.

Code des fichiers hpp d'origine pour le format de la fonction de rappel:

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

Code du wrapper C ++ / CLI pour le format de la fonction de rappel: (Je vais expliquer pourquoi j'ai déclaré deux dans un instant)

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

- Rapidement, la raison pour laquelle j'ai déclaré un deuxième " UnManagedCallbackFunction " c’est que j’ai essayé de créer un "intermédiaire" rappel dans le wrapper, la chaîne a donc changé de C ++ natif > C # vers une version de C ++ natif > Wrapper C ++ / CLI > C # ... Divulgation complète, le problème est toujours là, il vient juste d'être transféré dans l'encapsuleur C ++ / CLI maintenant sur la même ligne (le retour).

Et enfin, le code de crash de C #:

public static int hReceiveLogEvent(IntPtr pInstance, IntPtr pData)
    {
        Console.WriteLine("in hReceiveLogEvent...");
        Console.WriteLine("pInstance: {0}", pInstance);
        Console.WriteLine("pData: {0}", pData);

        // provide object context for static member function
        helloworld hw = (helloworld)GCHandle.FromIntPtr(pInstance).Target;
        if (hw == null || pData == null)
        {
            Console.WriteLine("hReceiveLogEvent: received null instance pointer or null data\n");
            return 0;
        }

        // typecast data to DataLogger object ptr
        IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
        DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

        //Do Logging Stuff

        Console.WriteLine("exiting hReceiveLogEvent...");
        Console.WriteLine("pInstance: {0}", pInstance);
        Console.WriteLine("pData: {0}", pData);
        Console.WriteLine("Setting pData to zero...");
        pData = IntPtr.Zero;
        pInstance = IntPtr.Zero;
        Console.WriteLine("pData: {0}", pData);
        Console.WriteLine("pInstance: {0}", pInstance);

        return 1;
    }

Toutes les écritures sur la console sont terminées, puis nous voyons le crash redouté au retour:

  

Exception non gérée à 0x04d1004c dans   helloworld.exe: 0xC0000005: accès   emplacement de lecture de violation 0x04d1004c.

Si j'entre dans le débogueur à partir d'ici, tout ce que je vois, c'est que la dernière entrée de la pile d'appels est: > "04d1004c ()" qui donne une valeur décimale de: 80805964

Ce qui n’est intéressant que si vous regardez la console qui affiche:

entering registerDataLogger
pointer to callback handle: 790848
fp for callback: 2631370
pointer to inst: 790844
in hReceiveLogEvent...
pInstance: 790844
pData: 80805964
exiting hReceiveLogEvent...
pInstance: 790844
pData: 80805964
Setting pData to zero...
pData: 0
pInstance: 0

Maintenant, je sais qu’entre le débogage et la publication certaines choses sont assez différentes dans le monde Microsoft. Bien sûr, je m'inquiète du remplissage des octets et de l'initialisation des variables. Par conséquent, s'il y a quelque chose que je ne fournis pas ici, faites le moi savoir et j'ajouterai à la publication (déjà longue). Je pense également que le code managé NE libère PAS tous les propriétaires, puis que les éléments C ++ natifs (pour lesquels je n’ai pas le code) peuvent essayer de supprimer ou de supprimer l’objet pData, provoquant ainsi le blocage de l’application.

Plus d'informations complètes, tout fonctionne bien (apparemment) en mode débogage!

Un vrai casse-tête qui mériterait toute aide!

Était-ce utile?

La solution

Je pense que la pile a été écrasée à cause des conventions d’appel incompatibles: essayez de mettre l'attribut

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

sur la déclaration du délégué de rappel.

Autres conseils

Cela ne répond pas directement à votre question , mais cela peut vous conduire dans la bonne direction en ce qui concerne le mode débogage correct ou le mode correctif non correct:

  

Étant donné que le débogueur ajoute de nombreuses informations sur la conservation des enregistrements à la pile, complétant généralement la taille et la présentation de mon programme en mémoire, j’ai eu de la chance en mode débogage en gribouillant plus de 912 octets de mémoire. t très important. Sans le débogueur, cependant, j’étais en train de gribouiller des choses assez importantes, pour finalement sortir de mon propre espace mémoire, ce qui a amené Interop à effacer la mémoire qui ne lui appartenait pas.

Quelle est la définition de DataLoggerWrap? Un champ de caractères est peut-être trop petit pour les données que vous recevez.

Je ne suis pas sûr de ce que vous essayez d'atteindre.

Quelques points:

1) Le ramasse-miettes est plus agressif en mode de publication. Par conséquent, le comportement que vous décrivez n'est pas rare.

2) Je ne comprends pas ce que le code ci-dessous tente de faire?

IntPtr ip2 = GCHandle.ToIntPtr(GCHandle.Alloc(new DataLoggerWrap(pData)));
DataLoggerWrap dlw = (DataLoggerWrap)GCHandle.FromIntPtr(ip2).Target;

Vous utilisez GCHandle.Alloc pour verrouiller une instance de DataLoggerWrap en mémoire, mais vous ne la transmettez jamais à une personne non gérée - alors pourquoi la verrouillez-vous? Vous aussi ne le libérez jamais?

La deuxième ligne récupère ensuite une référence: pourquoi le tracé circulaire? pourquoi la référence - vous ne l'utilisez jamais?

3) Vous définissez IntPtrs sur null - pourquoi? - cela n'aura aucun effet en dehors de la portée de la fonction.

4) Vous devez savoir quel est le contrat du rappel. À qui appartient pData le rappel ou la fonction appelante?

Je suis avec @jdehaan, sauf CallingConvetion.StdCall pourrait être la réponse, en particulier lorsque la lib de tierce partie est écrite en BC ++, par exemple.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top