2008-11-12 14 views
1

J'écris du code C# qui doit se connecter aux événements COM. Je mis en œuvre l'utilisation de IConnectionPointContainer et IConnectionPoint ainsi:Connexion aux événements COM en C# - prise en charge des serveurs gérés et non gérés

 IConnectionPointContainer connectionPointContainer = internalGenerator as IConnectionPointContainer; 
     if (connectionPointContainer == null) 
     { 
     Debug.Fail("The script generator doesn't support the required interface - IConnectionPointContainer"); 
     throw new InvalidCastException("The script generator doesn't support the required interface - IConnectionPointContainer"); 
     } 
     Guid IID_IScriptGeneratorEvents = typeof(IScriptGeneratorCallback).GUID; 
     connectionPointContainer.FindConnectionPoint(ref IID_IScriptGeneratorEvents, out m_connectionPoint); 
     m_connectionPoint.Advise(this, out m_cookie); 

Le problème est que lorsque le serveur COM est effectivement mis en œuvre en .Net (par exemple, C#), après .Net crée, il le gère comme. Objet net, pas un objet COM. Puisque l'objet .Net n'implémente pas l'interface IConnectionPointContainer, je reçois la valeur null lorsque j'essaie de convertir l'objet en cette interface.

Une idée comment puis-je contourner ce problème? Je peux bien sûr implémenter IConnectionPointContainer par moi-même dans le serveur COM C#, mais je voudrais une solution plus simple, que je peux facilement expliquer aux autres développeurs qui ont besoin d'implémenter le serveur COM. P. Je dois utiliser IConnectionPointContainer car le serveur COM peut être implémenté dans non.Net (C++, Java).

Merci, Inbar

Répondre

0

Je n'ai pas trouvé un moyen de le faire. Finalement, je vais définir une autre interface dans .Net et j'écrirai 2 chemins de code, un pour les objets .Net et un pour les vrais objets COM.

1

IConnectionPointContainer est mis en œuvre sur la Convention (wrapper COM callable) que .NET génère automatiquement lors de l'exposition de votre objet .NET comme un objet COM externe.

Essayez d'appeler Marshal.GetComInterfaceForObject sur l'objet .NET pour obtenir une interface COM pour IConnectionPointContainer au lieu de simplement le convertir.

mise à jour ... et si cela ne fonctionne pas Marshal.GetIUnknownForObject doit retourner quelque chose, et peut-être que soutiendra alors un appel Marshal.QueryInterface.

0

Le problème est que faire un appel GetIUnknownForObject renvoie un pointeur que vous pouvez ensuite appeler avec succès pour atteindre IConnectionPointContainer pour l'objet à l'aide de son GUID. Mais, cet appel à QueryInterface renvoie simplement l'objet .NET d'origine, pas une interface IConnectionPointContainer.

Je suis également coincé avec cela, et si quelqu'un a d'autres idées, n'hésitez pas à partager. Mon scénario est que j'expose un contrôle .NET en tant qu'ActiveX en utilisant COM interop. J'ai une ComSourceInterface définie pour le récepteur d'événements, mais dans un hôte VB6, les événements ne sont pas connectés comme prévu. J'essaye donc d'obtenir l'interface IConnectionPointContainer pour mon contrôle .NET exposé afin de raccorder les événements manuellement, mais je ne peux pas accéder à cette interface, si elle est effectivement implémentée - ou peut-être que je regarde simplement le mauvais objet?

0

Je suis allé un peu plus loin, car j'ai eu le même problème en connectant manuellement les événements lors de l'exposition d'un contrôle .NET en tant qu'ActiveX en utilisant COM interop. Si vous creusez un peu dans la classe UserControl en utilisant Reflector (par exemple Redgate Reflector), vous verrez un membre de classe imbriqué 'ActiveXImpl', qui contient une autre classe statique imbriquée appelée 'AdviseHelper', qui a les membres ComConnectionPointContainer et ComConnectionPoint . Il a également des fonctions auxiliaires pour connecter les points selon vos besoins.

Il y a un problème. Lorsqu'un événement est connecté par COM interop depuis votre contrôle (la source de l'événement) vers le conteneur du point de connexion (qui contient le récepteur d'événements pour les événements de votre contrôle), l'IQuickActivate.La fonction QuickActivate est appelée, qui appelle la fonction AdviseConnectionPoint de la classe AdviseHelper. Un pointeur d'interface IUnknown vers le collecteur d'événements sur votre client (c'est-à-dire pas votre contrôle, l'objet qui le contient) est passé à cette fonction QuickActivate (paramètre 'pUnkEventSink') Dans le réflecteur, cette fonction ressemble à ceci. avons mis en évidence où il fait l'événement réel raccordement:

internal void QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl) 
{ 
    int num; 
    this.LookupAmbient(-701).Value = ColorTranslator.FromOle((int) pQaContainer.colorBack); 
    this.LookupAmbient(-704).Value = ColorTranslator.FromOle((int) pQaContainer.colorFore); 
    if (pQaContainer.pFont != null) 
    { 
     Control.AmbientProperty ambient = this.LookupAmbient(-703); 
     IntSecurity.UnmanagedCode.Assert(); 
     try 
     { 
      Font font2 = Font.FromHfont(((UnsafeNativeMethods.IFont) pQaContainer.pFont).GetHFont()); 
      ambient.Value = font2; 
     } 
     catch (Exception exception) 
     { 
      if (ClientUtils.IsSecurityOrCriticalException(exception)) 
      { 
       throw; 
      } 
      ambient.Value = null; 
     } 
     finally 
     { 
      CodeAccessPermission.RevertAssert(); 
     } 
    } 
    pQaControl.cbSize = UnsafeNativeMethods.SizeOf(typeof(UnsafeNativeMethods.tagQACONTROL)); 
    this.SetClientSite(pQaContainer.pClientSite); 
    if (pQaContainer.pAdviseSink != null) 
    { 
     this.SetAdvise(1, 0, (IAdviseSink) pQaContainer.pAdviseSink); 
    } 
    IntSecurity.UnmanagedCode.Assert(); 
    try 
    { 
     ((UnsafeNativeMethods.IOleObject) this.control).GetMiscStatus(1, out num); 
    } 
    finally 
    { 
     CodeAccessPermission.RevertAssert(); 
    } 
    pQaControl.dwMiscStatus = num; 
    if ((pQaContainer.pUnkEventSink != null) && (this.control is UserControl)) 
    { 
     Type defaultEventsInterface = GetDefaultEventsInterface(this.control.GetType()); 
     if (defaultEventsInterface != null) 
     { 
      IntSecurity.UnmanagedCode.Assert(); 
      try 
      { 
       **AdviseHelper.AdviseConnectionPoint(this.control, pQaContainer.pUnkEventSink, defaultEventsInterface, out pQaControl.dwEventCookie);** 
      } 
      catch (Exception exception2) 
      { 
       if (ClientUtils.IsSecurityOrCriticalException(exception2)) 
       { 
        throw; 
       } 
      } 
      finally 
      { 
       CodeAccessPermission.RevertAssert(); 
      } 
     } 
    } 
    if ((pQaContainer.pPropertyNotifySink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pPropertyNotifySink)) 
    { 
     UnsafeNativeMethods.ReleaseComObject(pQaContainer.pPropertyNotifySink); 
    } 
    if ((pQaContainer.pUnkEventSink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pUnkEventSink)) 
    { 
     UnsafeNativeMethods.ReleaseComObject(pQaContainer.pUnkEventSink); 
    } 
} 

la variable « pUnkEventSink » est passée à cette fonction via la structure de tagQACONTROL, mais comme vous pouvez le voir, à la différence du IAdviseSink, conteneur de contrôle, style de police, etc. , cette variable n'est définie sur aucun membre de la classe 'ActiveXImpl' et, par conséquent, vous ne pouvez pas y accéder après que cette fonction a été initialement appelée par le framework

Vous devez obtenir cette variable IUnknown pUnkEventSink pour appeler la fonction AdviseHelper.AdviseConnectionPoint(), qui effectuera le branchement d'événement manuel. Et c'est le problème que j'ai eu - malheureusement, vous ne pouvez tout simplement pas le saisir.

Quelqu'un d'autre a eu d'autres développements avec ce faire le moi savoir!

0

Je sais que je suis un peu en retard à cela, mais j'ai réussi à faire fonctionner les événements de puits en utilisant IConnectionPoint. Voir ma réponse here. Spécifiquement vérifier la classe MyAppDotNetWrapper et comment il est utilisé le test.

En fin de compte, je pense que votre m_connectionPoint.Advise(this, out m_cookie); échoue parce que this doit être [ComVisible(true)], [ClassInterface(ClassInterfaceType.None)] et public.