Appel de COM composante gérée visible à partir du code géré par L'intermédiaire de COM wrapper

j'ai un composant tiers, disons FIPreviewHandler pour gérer preview, qui implémente IPreviewHandler. FIPreviewHandler est mis en œuvre en tant que composante gérée, et utilise L'interface IPreviewHandler et les interfaces connexes au moyen d'un interop. FIPreviewHandler est enregistré en utilisant regasm.exe as COM.

j'ai une application client qui est également Géré. Je veux créer une instance de FIPreviewHandler comme composant COM dans mon application.

je avoir un assemblage interop qui définit IPreviewHandler et les interfaces connexes.

quand je crée une instance de FIPreviewHandler, en utilisant Activator.CreateInstance(), sur un type retourné par GetTypeByCLSID (), qui utilise le CLSID correct pour FIPreviewHandler, il me renvoie une instance gérée, comme il a l'Assemblée réelle disponible, et saute COM. Quand j'essaie de lancer cette instance comme n'importe laquelle des interfaces, IPreviewHandler par exemple, il renvoie null parce que, il est chargé comme un managed object, et bien que L'interface IPreviewHandler implémentée par FIPreviewHandler soit la même que celle que j'ai dans mon interop, mais c'est dans un espace de nom de différence/assembly, donc null. S'il devait me rendre une instance COM/RCW (System.__ComObject), il ne tiendrait pas compte de namespace, et rejetterait fine, et retournerait une instance valide.

FIPreviewHandler est un composant 32 bits, et sur une machine 64bit Win7, si je compile mon application client comme "N'importe quel CPU", Activateur.CreateInstance() renvoie une instance COM / RCW (System.__ComObject), comme il cudnt trouver une implémentation 64bit de FIPreviewHandler, renvoie donc un proxy. Dans ce scénario, mon application fonctionne très bien. Mais quand je le compile pour x86, il obtient l'implémentation 32bit, et renvoie une instance gérée de la classe managed actuelle, et non une instance COM, donc il échoue.

Je ne peux pas utiliser les interfaces définies dans L'assemblage de FIPreviewHandler, car je dois écrire un client générique pour IPreviewHandler, et mon application fonctionnera avec n'importe quel composant implémentant IPreviewHandler, ce qui fonctionnerait très bien pour les clients basés sur C++ accédant à FIPreviewHandler en tant qu'objet COM, mais est un échec pour les clients gérés.

j'espère que je sens, et je serais vraiment reconnaissant pour toute aide.

16
demandé sur shaibee 2010-06-25 20:09:16

7 réponses

il est donc clair que C'est un échec de la part de .NET car je trouve qu'il n'y a aucun moyen d'utiliser un enveloppeur COM autour d'un objet COM géré.

la "solution" (et j'emploie ce terme très vaguement) est l'utilisation d'une PIA ou "Primary Interop Assembly". La PIA fournit un seul assemblage robuste importé avec TlbImp.exe qui est enregistré au GAC. Fondamentalement, je suppose que nous devons alors compter sur les politiques des éditeurs GAC pour forcer les clients à la bonne interface d'assemblage.

voir aussi

http://social.msdn.microsoft.com/Forums/en/csharpgeneral/thread/b11a0f90-fcc5-487a-b057-632f5415bfc2

http://www.codeproject.com/KB/COM/BuildCOMServersInDotNet.aspx

1
répondu csharptest.net 2011-04-19 19:32:09

l'Homme, si j'étais vous, je voudrais juste faire le wrapper moi, seulement lorsque le type est NOT a COM type. Pour savoir si le type créé est un type COM, utilisez le IsCOMObjectType de l'objet:

myObject.GetType().IsCOMObject

Si c'est FALSE créer un wrapper, qui utilise la réflexion pour appeler le type géré. La réflexion est lente, mais vous pouvez cacher le MethodInfo objets que vous obtenez... sinon, vous pouvez générer du code IL, et créer un wrapper qui n'aura pas réflexion à tous.

bien sûr, il y a d'autres méthodes pour le faire... c'est à vous de voir.

comme je suis amoureux de la génération il dynamique, je peux vous fournir un code qui fait cela... si vous êtes intéressés!

1
répondu Miguel Angelo 2011-04-21 15:28:00

D'après mon expérience, Microsoft l'a fait de cette façon. Ils ne veulent pas que vos deux assemblages de code gérés communiquent via COM. Si vous essayez d'ajouter une assemblée qui soutient COM comme référence COM dans votre projet, l'erreur en dit autant.

si les interfaces COM sont le seul moyen d'obtenir la fonctionnalité dont vous avez besoin, un wrapper non géré devrait faire l'affaire. Ecrire un nouveau serveur COM en C++ ou VB6 (ou tout autre langage non géré qui est convivial) qui vous enveloppe serveur COM géré par un tiers. Puis ajoutez cette nouvelle DLL d'enrubannage à votre projet de code géré en tant que serveur COM.

1
répondu Richard Brightwell 2011-04-24 12:12:42

j'ai essayé de faire la même chose sans succès. (Dans mon cas, l'interface COM définie existante avait un bug qui signifiait qu'elle ne pouvait pas être implémentée dans .Net, donc j'ai redéfini L'interface COM dans un espace de noms séparé moi-même et j'ai implémenté cette interface à la place. L'objet résultant a implémenté les bonnes interfaces COM, mais je n'ai pas réussi à mettre la main sur un wrapper COM pour le Caster vers l'interface d'origine cassée).

autant que je sache il y a seulement 2 solutions:

  1. déclarer toutes les interfaces COM dans un Assemblage Interop Primaire, (soit à l'aide de TLBimp or by hand) de sorte que toutes les interfaces soient définies dans un espace de noms commun. Notez que cette assemblée ne contient normalement aucune implémentation, et ne devrait donc pas avoir besoin de faire référence à d'autres assemblées (sauf si ces autres assemblées sont ensembles interop qui déclarent des COM dépendants interface.)

  2. créer un wrapper (par exemple dans Managed C++) qui exécute les appels par COM "traditionnel", plutôt que par interop.ComNet.

Option 1. cela me semble certainement votre meilleure option-notez qu'il n'est pas nécessaire d'enregistrer cette assemblée ou de la placer dans le GAC, et la mise en œuvre peut se faire dans une assemblée entièrement séparée aussi longtemps que l'Assemblée commune interop est référencée.

Je ne peux pas penser de nombreuses situations légitimes dans lesquelles un assemblage Interop primaire n'est pas réalisable.

1
répondu Justin 2011-04-27 01:42:48

on dirait qu'il y a deux problèmes:

  1. accès aux Interfaces
  2. Bitness du composant.

tout d'abord, pourquoi le référencer comme un objet COM en premier lieu si vous savez qu'il est géré? Pourquoi ne pas faire une référence directe à la DLL plutôt que par interop et de cette façon, vous devriez avoir accès aux interfaces de la même manière que la bibliothèque a accès à eux. Ainsi, vous n'auriez pas besoin d'appeler Activator.CreateInstance. Au lieu de cela, vous auriez appelé var foo = new FIPreviewHandler();. Cependant, cela ne résout pas le problème de bitness donc le point est discutable. Plutôt...

pour en venir au deuxième problème, une solution est de mettre le composant COM dans une Application COM+ Server. Les applications COM+ déterminent leur bitness lorsqu'elles sont chargées pour la première fois. Si toutes les bibliothèques de L'application COM+ sont à 32 bits, lorsqu'elle est chargée, elle se chargera dans un WOW32 et vous pouvez l'appeler à partir d'une application à 64 bits. J'ai utilisé ce truc pour les bibliothèques COM qui n'existaient qu'en 32 bits. mais j'avais besoin de l'utiliser sur un serveur 64 bits. L'inconvénient est que vous chargez la bibliothèque dans un processus séparé et encourir les coûts de formation en raison de l'exécution étant hors processus pour votre application, mais une fois instancié, il devrait fonctionner assez bien.

0
répondu Thomas 2011-04-20 21:41:11

utilisez PInvoke pour appeler la fonction COM CoCreateInstance (Ex) et passer le CLSID défini dans votre assemblage interop et L'IID D'IPreviewHandler. Net n'a pas l'occasion de s'en mêler.

vous dites que vous avez une assemblée interop. C'est mieux si C'est une PIA distribuée par l'auteur de la troisième assemblée, mais c'est très bien si vous devez la construire vous-même. (Les ÉFVP n'ont pas à être enregistrées dans le GAC, bien qu'elles le soient souvent.) Vous pouvez obtenir le CLSID en chargeant le assembly dans le IL désassembleur et à la recherche sur le Système.Runtime.InteropServices.Attribut GuidAttribute sur la classe.

0
répondu Ciaran Keating 2011-04-27 01:12:46

si je lis ce droit, vous devez le forcer à utiliser COM. Deux idées:

  1. le comportement de Activator.CreateInstance() modifier, si vous obtenez le type à l'aide de GetTypeFromProgID() au lieu de GetTypeByCLSID()?

  2. Comment Microsoft.VisualBasic.Interaction.CreateObject() se comporter? Oui, je déteste amener VB là-dedans, mais je suis curieux que ça renvoie L'objet COM au lieu de la classe .NET.

0
répondu Lynn Crumbling 2012-07-12 09:53:55