J'ai un problème avec mon complément COM qui traîne depuis des mois, et je ne peux pas comprendre pourquoi.Nettoyage des boutons CommandBar
La mise en œuvre IDTExtensibility2
a été examiné par des pairs Carlos Quintero (le gars derrière MZ-Tools) déjà, et jugé correct.
public void OnBeginShutdown(ref Array custom)
{
_isBeginShutdownExecuted = true;
ShutdownAddIn();
}
Mon complément:
Par ses recommandations, la mise en œuvre OnBeginShutdown
définit un indicateur qui est vérifié dans OnDisconnection
, pour assurer ShutdownAddIn
fonctionne qu'une seule fois (certaines applications hôtes VBE n'appellent OnBeginShutdown
, c'est pourquoi) utilise Ninject pour DI/IoC, et ma méthode ShutdownAddIn
se résume à appeler Dispose
sur le Ninject IKernel
instance, puis libérer tous les objets COM avec Marshal.ReleaseComObject
:
private void ShutdownAddIn()
{
if (_kernel != null)
{
_kernel.Dispose();
_kernel = null;
}
_ide.Release();
_isInitialized = false;
}
Je ne peux pas penser à un temps plus tôt pour exécuter ce code. Pourtant, quand Dispose
fonctionne sur mes emballages commandbar et le menu, je reçois un InvalidCastException
en StopEvents
lorsque le CommandBar/menus essayer de démonter leurs contrôles:
public void HandleEvents()
{
// register the unmanaged click events
((Microsoft.Office.Core.CommandBarButton)Target).Click += Target_Click;
}
public void StopEvents()
{
// unregister the unmanaged click events
((Microsoft.Office.Core.CommandBarButton)Target).Click -= Target_Click;
}
public event EventHandler<CommandBarButtonClickEventArgs> Click;
private void Target_Click(Microsoft.Office.Core.CommandBarButton ctrl, ref bool cancelDefault)
{
// handle the unmanaged click events and fire a managed event for managed code to handle
var handler = Click;
if (handler == null)
{
return;
}
var args = new CommandBarButtonClickEventArgs(new CommandBarButton(ctrl));
handler.Invoke(this, args);
cancelDefault = args.Cancel;
}
Le InvalidCastException
dit qu'il ne peut pas jeter à IConnectionPoint
- et ce que j'ai trouvé est que la raison en est que lorsque ce code est exécuté, mon Target
(l'enveloppé __ComObject
) est déjà parti, et je suis parti avec un pointeur non valide et une référence persistante à un objet COM qui ne existe. Si j'attrape toutes les exceptions lancées pendant mon processus de démontage (j'ai plus d'exceptions provenant du même problème racine, quand j'essaie de Delete
les boutons et les menus), l'application hôte se ferme mais le processus hôte reste - et alors je avoir à le tuer de Gestionnaire des tâches. Ce comportement est cohérent avec une fuite de mémoire causée par les gestionnaires de clics non supprimés, je pense.
est-il un moyen plus robuste que je peux faire face à l'ajout/suppression des gestionnaires d'événements pour un emballage Microsoft.Office.Core.CommandBarButton
? Pourquoi mes objets COM enveloppés sont-ils déjà "partis" lorsque OnBeginShutdown
s'exécute, si je ne les ai pas encore libérés?
Des assemblages chargés à partir d'emplacements différents ... pourrait 'Rubberduck.VBEditor.dll' être vissés les choses ... edge case 1 semble être une réelle possibilité ici. –
Cela semble bizarre de le dire, mais cela pourrait-il provenir de notre utilisation des bibliothèques interopérables de PIA de Microsoft au lieu de générer notre propre @ Mat'sMug? Une balle dans l'obscurité. https://www.mztools.com/articles/2012/MZ2012011.aspx – RubberDuck
Je ne l'ai pas vu la nuit dernière, mais maintenant je vois qu'après avoir appelé handler.Invoke vous n'appelez pas Marshal.ReleaseComObject sur le ctrl .NET- côté RCW que vous recevez du côté COM dans le gestionnaire d'événements Target_Click. C'est une fuite. Ou si le wrapper CommandBarButton qui le reçoit en tant que paramètre l'appelle dans une méthode Dispose, vous devez appeler sa méthode Dispose dans le gestionnaire d'événements plutôt que de le laisser flottant jusqu'à la prochaine garbage collection. –