Rappel effectué sur un délégué par le GC

cs_Zap Messages postés 78 Date d'inscription mardi 31 décembre 2002 Statut Membre Dernière intervention 14 août 2010 - 25 janv. 2007 à 15:13
railarmenien Messages postés 4 Date d'inscription mercredi 11 octobre 2006 Statut Membre Dernière intervention 17 décembre 2009 - 17 déc. 2009 à 14:40
Bonjour,

J'ai un projet C# qui fait appel à une DLL Win32 dans laquelle une fonction a besoin de de l'adresse mémoire d'une fonction callback, à laquelle elle passe des paramètres.

Le problème c'est que lors de l'activation du ramasse miettes, le GC modifie l'adresse de la callback pointée par le délégué, ce qui provoque un plantage.

Comment puis-je faire pour que l'adresse mémoire de la callback pointée par le délégué et récupérée une fois pour toute dans la DLL Win32, lors de l'initialisation, ne soit pas modifiée.

Ce qui me trouble, c'est que j'utilise exactement la même callback via le même type de délégué  dans un autre projet qui utilise la même DLL Win32, et que dans ce cas tout fonctionne (l'adresse mémoire reste stable). La seule différence c'est que dans le projet qui me pause problème le programme effectue de très nombreux calculs de type temps réel.

Avez-vous une piste ?

Patrice Terrier

6 réponses

Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
25 janv. 2007 à 18:04
Salut, es-tu sûr que ton délégué n'est pas ramassé par le GC ? Si il est local à une fonction, essaye la même chose en le déclarant en champ de la classe.
0
cs_Zap Messages postés 78 Date d'inscription mardi 31 décembre 2002 Statut Membre Dernière intervention 14 août 2010
25 janv. 2007 à 18:54
Mon problème est hélas bien connu : CallbackOnCollectedDelegate

Dixit MSDN :




Symptômes






Les violations d'accès se produisent lors des tentatives d'appel de code managé par les pointeurs fonction obtenus par des délégués managés. Ces échecs, qui ne sont pas des bogues du Common Language Runtime (CLR), peuvent pourtant y ressembler car la violation d'accès se produit dans le code CLR.



L'échec n'est pas cohérent ; tantôt l'appel sur le pointeur fonction aboutit, tantôt il échoue. Il se peut que l'échec se produise uniquement sous une charge élevée ou sur un nombre aléatoire de tentatives.






Cause






Le délégué à partir duquel le pointeur fonction a été créé et exposé au code non managé a été récupéré par le garbage collector. Lorsque le composant non managé tente d'appeler sur le pointeur fonction, il génère une violation d'accès.



L'échec semble aléatoire car il dépend du moment où l'opération garbage collection se produit. Si un délégué peut participer à l'opération garbage collection, celle-ci peut avoir lieu après le rappel et l'appel aboutit. Dans d'autres cas, l'opération garbage collection se produit avant le rappel, le rappel génère une violation d'accès et le programme s'arrête.



La probabilité de l'échec dépend du laps de temps entre le moment où le délégué est marshalé et le rappel sur le pointeur fonction ainsi que de la fréquence des opérations garbage collection. L'échec est sporadique si le laps de temps entre le moment où le délégué est marshalé et le rappel résultant est court. Cela est généralement le cas si la méthode non managée qui reçoit le pointeur fonction n'enregistre pas le pointeur fonction pour une utilisation ultérieure mais rappelle immédiatement le pointeur fonction pour terminer son opération avant d'être retournée. De la même façon, davantage d'opérations garbage collection se produisent lorsqu'un système est sous une charge élevée, ce qui rend plus probable l'exécution d'une opération garbage collection avant le rappel.






Résolution






Une fois qu'un délégué a été marshalé en tant que pointeur fonction non managé, le garbage collector ne peut pas effectuer le suivi de sa durée de vie. Votre code doit plutôt conserver une référence au délégué pour la durée de vie du pointeur fonction non managé. Pour ce faire, vous devez d'abord identifier le délégué qui a été collecté. Lorsque le MDA est activé, il fournit le nom de type du délégué. Utilisez ce nom afin de rechercher votre code pour l'appel de plate-forme ou les signatures COM qui transmettent ce délégué au code non managé. Le délégué incriminé est transmis par l'un de ces sites d'appel. Vous pouvez également permettre au MDA GcUnmanagedToManaged d'obliger une opération garbage collection avant chaque rappel dans le runtime. Cela permet de supprimer l'incertitude introduite par l'opération garbage collection en garantissant qu'une opération garbage collection se produit toujours avant le rappel. Une fois que vous savez quel délégué a été collecté, modifiez votre code pour conserver une référence à ce délégué sur le côté managé pour la durée de vie du pointeur fonction non managé marshalé.

Malheureusement quoique je fasse, rien ni fait, je ne peux pas faire confiance au pointeur retourné par le délégué et je suis obligé de le passer à nouveau à ma DLL Win32, chaque fois que le GC bouge les adresses mémoire. Quoiqu'en dise Microsoft au début de l'article cité, pour moi c'est un bogue du CLR.
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
25 janv. 2007 à 20:16
Oui c'est connu et ça correspond bien à ce que j'ai dit..

ton délégué a été ramassé :

"Le délégué à partir duquel le pointeur fonction a été créé et exposé au code non managé a été récupéré par le garbage collector"

et tu dois garder une référence :

"Une fois que vous savez quel délégué a été collecté, modifiez votre code pour conserver une référence à ce délégué sur le côté managé pour la durée de vie du pointeur fonction non managé marshalé".

Avec le délégué en champ de la classe tu as le même problème !??  Regardes la classe GCHandle peut être..
0
Lutinore Messages postés 3246 Date d'inscription lundi 25 avril 2005 Statut Membre Dernière intervention 27 octobre 2012 41
25 janv. 2007 à 20:18
Ah non pas GCHandle pour une délégué en fait. :/
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
cs_Zap Messages postés 78 Date d'inscription mardi 31 décembre 2002 Statut Membre Dernière intervention 14 août 2010
25 janv. 2007 à 20:41
Lutinore,



"Une fois que vous savez quel délégué a été collecté, modifiez votre code pour conserver une référence à ce délégué sur le côté managé pour la durée de vie du pointeur fonction non managé marshalé".

Oui je sais exactement quel délégué a été collecté.
Mais je ne sais pas comment faire pour éviter qu'il ne soit déplacé en mémoire.



Mon code se présente de la façon suivante :




namespace Carousel
{
   
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate bool CallBack(IntPtr hWnd, int wMsg, int wParam, int lParam);






    public partial class MAIN_Form : Form
    {...

        // Very important the call back must be static
        // to prevent from access violation during garbage collection.
        // Search for <<Callback Functions>>
        static CallBack UseCallBack = null;






        public MAIN_Form()
        {




            // We use a callback to handle the GDImage events        
            // Very important the call back must be static
            // to prevent from access violation during garbage collection.
            // Search for <<Callback Functions>>
           


UseCallBack = new CallBack(GDImageCallBack);




        // ****************************************
        // Callback that handles the GDImage events
        // Search for <<Callback Functions>>
   




     public static bool GDImageCallBack(IntPtr hWnd, int wMsg, int wParam, int lParam)
        {







Je ne vois pas ce que je peux faire de plus "pour conserver une référence à ce délégué sur le côté managé pour la durée de vie du pointeur" ?

Patrice Terrier
www.zapsolution.com
0
railarmenien Messages postés 4 Date d'inscription mercredi 11 octobre 2006 Statut Membre Dernière intervention 17 décembre 2009
17 déc. 2009 à 14:40
UP!!

j'ai le meme probleme!
0
Rejoignez-nous