Cela peut se produire pour plusieurs raisons, les principales que je connais sont les suivantes.
Gestionnaires d'événements sans références fortes au délégué
Un appelant s'abonne à un événement sur l'objet com sans garder une référence forte au délégué de callback. Voici un exemple de la manière correcte de procéder et de la manière incorrecte de le faire : La raison en est qu'une référence forte doit être conservée au délégué, si elle sort de la portée, le wrapper libérera le compte de référence pour l'interface et de mauvaises choses se produiront.
public class SomeClass
{
private Interop.ComObjectWrapper comObject;
private event ComEventHandler comEventHandler;
public SomeClass()
{
comObject = new Interop.ComObjectWrapper();
// NO - BAD!
comObject.SomeEvent += new ComEventHandler(EventCallback);
// YES - GOOD!
comEventHandler = new ComEventHandler(EventCallback);
comObject.SomeEvent += comEventHandler
}
public void EventCallback()
{
// DO WORK
}
}
Appels à un wrapper d'appel du Runtime disposé
Le wrapper a été éliminé et des appels sont effectués après son élimination. Cela peut se produire si un contrôle utilise un contrôle Activex ou un objet COM et que le contrôle Dispose() est appelé dans le désordre.
- Un formulaire est appelé Close().
- System.Windows.Forms.Close() appellera Dispose()
- La fonction Dispose() virtuelle de votre formulaire sera appelée, ce qui, espérons-le, appellera base.Dispose() quelque part. Systems.Windows.Forms.Dispose() libérera tous les objets COM et les synchronisations d'événements sur le formulaire, même ceux des contrôles enfants.
- Si le contrôle qui possède un objet COM est explicitement éliminé après base.Dispose() et s'il appelle des méthodes sur son objet COM, celles-ci échoueront et vous obtiendrez l'erreur "L'objet COM qui a été séparé de son RCW sous-jacent ne peut être utilisé".
Étapes de débogage
Une bonne façon de déboguer ce problème est de faire ce qui suit :
- Écrivez une classe qui hérite de la classe Interop (également connue sous le nom de Runtime Callable Wrapper ou RCW).
- Remplacer DetachEventSink
- Annulation Dispose
- Appelez votre nouvelle classe au lieu d'appeler directement la classe d'interopérabilité.
- Ajouter un point d'arrêt à DetachEventSink et Dispose
- Voir qui appelle ces méthodes dans le désordre
Une autre chose
Cela n'a rien à voir avec ce problème, mais tant que nous sommes sur le sujet, à moins que vous ne sachiez autre chose, pensez toujours à vérifier que le thread à partir duquel vos objets COM sont utilisés est marqué STA. Vous pouvez le faire en ouvrant le débogueur et en vérifiant la valeur renvoyée par :
Thread.CurrentThread.GetApartmentState();