167 votes

Comment le compilateur C # détecte-t-il les types COM?

EDIT: j'ai écrit les résultats comme un billet de blog.


Le compilateur C# traite les types de COM un peu comme par magie. Par exemple, cette déclaration semble normal...

Word.Application app = new Word.Application();

... jusqu'à ce que vous vous rendez compte que Application est une interface. Appel d'un constructeur sur une interface? Yoiks! Ce fait se traduit dans un appel à l' Type.GetTypeFromCLSID() et un autre pour Activator.CreateInstance.

En outre, en C# 4, vous pouvez utiliser la non-réf arguments pour ref paramètres, et le compilateur ajoute simplement une variable locale à passer par référence, en écartant les résultats:

// FileName parameter is *really* a ref parameter
app.ActiveDocument.SaveAs(FileName: "test.doc");

(Oui, il y a un tas d'arguments manquants. Ne sont pas des paramètres optionnels de nice? :)

Je suis en train d'étudier le comportement du compilateur, et je ne suis pas pour de faux la première partie. Je peux faire la deuxième partie avec pas de problème:

using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;

[ComImport, GuidAttribute("00012345-0000-0000-0000-000000000011")]
public interface Dummy
{
    void Foo(ref int x);
}

class Test
{
    static void Main()
    {
        Dummy dummy = null;
        dummy.Foo(10);
    }
}

J'aimerais être capable d'écrire:

Dummy dummy = new Dummy();

cependant. Évidemment, ça va aller bang au moment de l'exécution, mais c'est correct. Je suis juste à expérimenter.

Les autres attributs ajouté par le compilateur pour lié COM Pia (CompilerGenerated et TypeIdentifier) ne semblent pas faire l'affaire... ce qui est la magie de la sauce?

144voto

Michael Petrotta Points 35647

En aucun cas je suis un expert dans ce domaine, mais je suis tombé récemment sur ce que je pense que vous voulez: la Coclasse attribut de la classe.

[System.Runtime.InteropServices.CoClass(typeof(Test))]
public interface Dummy { }

Une coclasse fournitures de béton la mise en œuvre(s) d'un ou de plusieurs les interfaces. Dans COM, tels le béton les implémentations peuvent être écrits dans n'importe quel langage de programmation qui prend en charge la COM développement d'un composant, par exemple, Delphi, C++, Visual Basic, etc.

Voir ma réponse à une question similaire à propos de Microsoft Speech API, où vous êtes en mesure de "instancier" l'interface SpVoice (mais vraiment, vous êtes l'instanciation SPVoiceClass).

[CoClass(typeof(SpVoiceClass))]
public interface SpVoice : ISpeechVoice, _ISpeechVoiceEvents_Event { }

61voto

Eric Lippert Points 300275

Entre vous et Michael vous avez presque eu les morceaux ensemble. Je pense que c'est la façon dont il fonctionne. (Je n'ai pas écrit le code, donc je pourrais être un peu mal déclarant que celle-ci, mais je suis sûr que cela est de savoir comment il va.)

Si:

  • vous êtes "nouveau"ing un type d'interface, et
  • le type d'interface a connu une coclasse, et
  • vous ÊTES à l'aide de la "non pia" pour cette interface

ensuite, le code est généré en tant que (IPIAINTERFACE)Activateur.CreateInstance(De Type.GetTypeFromClsid(GUID DE COCLASSTYPE))

Si:

  • vous êtes "nouveau"ing un type d'interface, et
  • le type d'interface a connu une coclasse, et
  • vous n'ÊTES PAS à l'aide de la "non pia" pour cette interface

ensuite, le code est généré que si vous aviez dit "nouveau COCLASSTYPE()".

Jon, n'hésitez pas à me gonfler ou Sam directement si vous avez des questions à propos de ce genre de choses. Pour info, Sam est l'expert sur cette fonctionnalité.

36voto

Jon Skeet Points 692016

Bon d'accord, c'est juste pour mettre un peu plus de chair sur Michael réponse (il est bienvenu de l'ajouter dans le si il le veut, dans ce cas, je vais le supprimer).

En regardant l'original PIA pour Mot.De l'Application, il existe trois types impliqués (en ignorant les événements):

[ComImport, TypeLibType(...), Guid("..."), DefaultMember("Name")]
public interface _Application
{
     ...
}

[ComImport, Guid("..."), CoClass(typeof(ApplicationClass))]
public interface Application : _Application
{
}

[ComImport, ClassInterface(...), ComSourceInterfaces("..."), Guid("..."), 
 TypeLibType((short) 2), DefaultMember("Name")]
public class ApplicationClass : _Application, Application
{
}

Il y a deux interfaces pour des raisons que Eric Lippert parle, dans une autre réponse. Et là, comme vous l'avez dit, est l' CoClass - à la fois dans la classe elle-même et l'attribut sur l' Application interface.

Maintenant, si nous utilisons PIA liaison en C# 4, certains de ce qui est incorporé dans le binaire résultant... mais pas à tous. Une application qui viens de crée une instance de Application se termine avec ces types:

[ComImport, TypeIdentifier, Guid("..."), CompilerGenerated]
public interface _Application

[ComImport, Guid("..."), CompilerGenerated, TypeIdentifier]
public interface Application : _Application

Pas de ApplicationClass - sans doute parce que cela va être chargé dynamiquement à partir de la real COM type au moment de l'exécution.

Une autre chose intéressante est la différence dans le code entre la version liée et non liée version. Si vous décompiler la ligne

Word.Application application = new Word.Application();

dans le référencés version, il se termine ainsi:

Application application = new ApplicationClass();

alors que dans le lié version, il finit

Application application = (Application) 
    Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid("...")));

Donc il semble que le "réel" PIA a besoin de l' CoClass d'attribut, mais la version liée n'est pas parce qu'il n'est pas un CoClass le compilateur peut en fait référence. Il doit le faire de façon dynamique.

Je pourrais essayer de simuler une interface COM à l'aide de cette information et voir si je peux obtenir le compilateur pour le lien...

27voto

Rasmus Faber Points 24195

Juste pour ajouter un peu de confirmation à la réponse de Michael:

Le code suivant compile et s'exécute:

 public class Program
{
    public class Foo : IFoo
    {
    }

    [Guid("00000000-0000-0000-0000-000000000000")]
    [CoClass(typeof(Foo))]
    [ComImport]
    public interface IFoo
    {
    }

    static void Main(string[] args)
    {
        IFoo foo = new IFoo();
    }
}
 

Vous avez besoin à la fois du ComImportAttribute et du GuidAttribute pour que cela fonctionne.

Notez également les informations lorsque vous passez la souris sur le new IFoo() : Intellisense détecte correctement les informations: Nice!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X