2009-12-22 9 views
2

J'utilise un objet NativeWindow pour sous-classer une pompe de message d'une fenêtre non gérée, dans le but d'intercepter ses messages.C#: Qu'est-ce qui détruit mon objet NativeWindow et pourquoi?

structure code

ressemble à ceci (son psuedo C#, s'il vous plaît excuser des problèmes mineurs de syntaxe):

class AppSubclass : Control { 

    class SpecialAppWndProc : NativeWindow { 

     protected override void WndProc(ref Message m) { 

      switch (m.msg) { 

       // do stuff 
       if (SpecialEvent != null) SpecialEvent(x); 
      } 

      base.WndProc(ref m); 

     } 

     public delegate void SpecialEventHandler(int Xstart); 
     public event SpecialEventHandler SpecialEvent; 

     ~SpecialAppWndProc() { 

      DebugTrace("Help! Save me!"); 

     } 

    } 

    private SpecialAppWndProc specialAppWndProc = new SpecialAppWndProc(); 

    private void StartMonitoring() { 

     // do stuff 


     specialAppWndProc.AssignHandle(hWndUnmanagedWindow); 
     specialAppWndProc.SpecialEvent += new SpecialAppWndProc.SpecialEventHandler(specialAppWndProc_SpecialEvent); 

    } 

    /* ... event handler ... */ 

    public AppSubClass() { 

     StartMonitoring(); 

    } 

} 

Maintenant, je pensais que la fixation d'un écouteur d'événement serait suffisante pour maintenir le Garbage Collector à la baie, si mon objet est en train de mourir à cause du GC. Si ce n'est pas le cas, est-il possible de tracer comment et pourquoi? Je n'ai jamais connu .Net pour tuer des objets en raison de bogues de code (les exceptions et l'échec silencieux occasionnel semblent être l'essentiel) et je ne sais pas comment ou pourquoi l'application hôte (mon application est un serveur COM pour code non géré) aurait assez de connaissances pour tuer mes objets non plus.

Étant donné que l'objet meurt apparemment au hasard (je ne l'ai pas été en mesure d'identifier un certain ensemble d'événements, seulement qu'il meurt partout de moins d'une seconde à quelques minutes après StartMonitoring() est appelée.

Il semblerait que HandleRef pourrait résoudre mes problèmes, mais je ne comprends pas comment utiliser cela dans ce contexte et je ne peux pas penser à comment l'insérer dans mon code (à part peut-être en déclarer un au niveau AppSubclass puis en assignant Alors, comment puis-je empêcher mon objet de mourir avant que je ne sois prêt à mourir?

Répondre

2

Vous devez stocker une référence à votre objet.

L'événement fonctionne dans l'autre sens, en gardant l'objet que l'événement va allumer en vie, pas la source de l'événement.

Si vous ajoutez quelques appels à GC.Collect et GC.WaitForPendingFinalizers, je suis sûr que vous pouvez provoquer le problème assez rapidement.

Permettez-moi d'étoffer un peu plus ma réponse.

Un événement est essentiellement un délégué déguisé. Le déguisement supprime simplement certaines des capacités associées aux délégués, de sorte que le code externe ne peut pas faire ce qu'il veut avec le délégué sous-jacent, mais au fond, c'est un délégué normal.

Alors qu'est-ce qu'un délégué? Un délégué qui fait référence à une méthode unique se compose de deux éléments:

  1. Une référence de la méthode
  2. une référence d'objet (la cible)

Lorsque l'événement est invoqué par l'objet qui la définit, comme un événement "Button.Click" étant déclenché, une méthode spécifique (par exemple, bt_Click) est déclenchée sur un objet spécifique (comme une instance de Form1).

L'événement contient donc une référence vers l'extérieur, vers l'objet sur lequel la méthode est définie. L'événement ne fait rien à cet autre objet, de sorte que l'autre objet, comme Form1 dans mon exemple ci-dessus, n'a aucun rapport avec cet événement et contient une référence à l'objet.

Donc dans votre cas, disons que vous avez ce code:

AppSubclass app = new AppSubclass(); // this starts monitoring 

Si vous laissez maintenant que la chute de variable hors de portée, cet objet est admissible à la collecte puisque rien ne détient une référence. Qu'il existe des références internes entre AppSubclass et SpecialAppWndProc n'a pas d'importance, il peut y avoir des références dans les deux sens, mais si aucun code externe ne contient de référence, ces objets peuvent être collectés.

Vous avez donc besoin de stocker une référence à votre objet, quelque part, pour éviter d'être collecté. Pour répondre à votre question initiale, qui était "C#: Qu'est-ce qui détruit mon objet NativeWindow et pourquoi?", La réponse est que c'est le garbage collector qui détruit votre objet NativeWindow, et la raison en est qu'il n'y a pas de référence enracinée à elle (par référence enracinée, je veux dire une référence stockée dans une variable statique, une variable membre d'autres références enracinées, ou comme une variable locale dans une méthode active.)

+0

Je ne suis pas entièrement sûr de comprendre votre réponse , cependant, j'ai pris l'habitude de créer un élément 'HandleRef' dans' AppSubclass' puis de le construire 'StartMonitoring()' et le problème semble s'être dissipé. Je vous remercie! –

+0

Un événement est essentiellement un délégué et consiste en une référence à la méthode que vous lui avez assignée et à l'objet sur lequel la méthode sera déclenchée. Prenez ceci: bt.Click + = bt_click; L'événement click fera maintenant référence à bt_Click sur "this", cependant, ce code n'ajoute aucune référence supplémentaire à l'objet référencé par bt, donc si la variable bt est modifiée plus tard (ou sort de la portée) et que c'était le seule référence à cet objet, cet objet est désormais éligible pour la collecte. –

+0

@Tom: Vous devez prendre le temps de comprendre la réponse de Lasse; ** Si vous ne maintenez pas de référence à votre objet quelque part dans votre code, votre objet peut être collecté **. Cela ne veut pas dire que ça le sera, mais ça PEUT l'être. –