2009-07-07 13 views
160

EDIT: Ich habe die Ergebnisse als blog post geschrieben.Wie erkennt der C# -Compiler COM-Typen?


Der C# -Compiler behandelt COM-Typen etwas magisch. Zum Beispiel sieht diese Aussage normal ...

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

... bis Sie erkennen, dass Application eine Schnittstelle ist. Konstruktor auf einer Schnittstelle aufrufen? Yooks! Dies wird tatsächlich in einen Anruf zu Type.GetTypeFromCLSID() und anderen zu Activator.CreateInstance übersetzt.

Zusätzlich in C# 4 können Sie nicht-ref Argumente für ref Parameter verwenden, und der Compiler fügt nur eine lokale Variable durch Verweis übergeben, um die Ergebnisse zu verwerfen:

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

(Ja, es gibt eine Reihe von Argumenten fehlt. Sind nicht optionale Parameter schön? :)

Ich versuche, das Compiler-Verhalten zu untersuchen, und ich bin nicht in der Lage, den ersten Teil zu fälschen. Ich kann ohne Probleme den zweiten Teil tun:

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

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

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

Ich mag würde in der Lage sein zu schreiben:

Dummy dummy = new Dummy(); 

though. Offensichtlich wird es zur Ausführungszeit knallen, aber das ist okay. Ich experimentiere nur.

Die anderen Attribute, die vom Compiler für verknüpfte COM-PIAs (CompilerGenerated und TypeIdentifier) hinzugefügt wurden, scheinen nicht den Trick zu machen ... was ist die magische Soße?

+0

Sie sind mir bei diesem Kram weit voraus (die meisten Sachen), aber nur zur Klarstellung, es klingt nach dem, wonach Sie suchen, ist die Abhängigkeitsinjektionsfunktionalität mit einer normalisierten Syntax - wäre das genau? –

+11

Sind nicht optionale Parameter nett? IMO, Nein, sie sind nicht nett. Microsoft versucht, den Fehler in Office COM-Schnittstellen zu beheben, indem Sie Bloat zu C# hinzufügen. –

+0

Nette Frage (+1). Ich habe das Gleiche bemerkt, wenn ich einen Konstruktor auf einer Schnittstelle anrufe. Es sieht im Code entsetzlich aus, weil es jeden Entwickler verwirren wird, der nicht mit dem vertraut ist, was vor sich geht. Ich habe mich immer gefragt, was hinter den Kulissen vor sich ging, damit es funktioniert. Sieht so aus, als würde ich dein neues Buch kaufen ;-) –

Antwort

138

Auf keinen Fall bin ich ein Experte in diesem, aber ich stolperte kürzlich auf, was ich denke, Sie wollen: die CoClass Attributklasse.

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

A coclass liefert Beton Implementierung (en) von einem oder mehreren Schnittstellen. In COM können solche konkreten Implementierungen in eine beliebige Programmiersprache geschrieben werden, die die COM Komponentenentwicklung, z.B. Delphi, C++, Visual Basic, usw.

Siehe my answer to a similar question about the Microsoft Speech API, wo Sie in der Lage sind zu "instanziiert" die Schnittstelle SpVoice (aber wirklich, Sie Instanziieren SPVoiceClass).

+2

Sehr interessant - werde es später versuchen.Die verknüpften PIA-Typen haben jedoch keine CoClass. Vielleicht hat es etwas mit dem Verlinkungsprozess zu tun - ich werde einen Blick in die Original-PIA werfen ... –

+64

+1 dafür, fantastisch zu sein, indem ich die akzeptierte Antwort schreibe, als Eric Lippert und Jon Skeet ebenfalls antworteten;) Nein, wirklich, +1 für die Erwähnung von CoClass. – OregonGhost

60

Zwischen Ihnen und Michael haben Sie fast die Stücke zusammen. Ich denke, so funktioniert es. (Ich habe den Code nicht geschrieben, also könnte ich etwas falsch angeben, aber ich bin mir ziemlich sicher, dass es so ist.)

If:

  • Sie "neue" ing ein Interface-Typ, und
  • der Schnittstellentyp sind eine bekannte Co-Klasse, und
  • Sie die "no pia" -Funktion für diese verwenden
  • Schnittstelle

dann wird der Code erzeugt, wie (IPIAINTERFACE) Activator.CreateInstance (Type.GetTypeFromClsid (GUID COCLASSTYPE))

If:

  • Sie sind "neue" ing ein Interface-Typ, und
  • der Schnittstellentyp hat eine bekannte Co-Klasse, und
  • Sie nicht für diese Schnittstelle die "no pia" Funktion verwenden

dann wird der Code generiert, als ob Sie "new COCLASSTYPE()" gesagt hätten.

Jon, zögern Sie mich oder Sam direkt, wenn Sie Fragen zu diesem Zeug haben. Zu Ihrer Information, Sam ist der Experte für dieses Feature.

35

Okay, das ist nur ein bisschen mehr Fleisch auf Michaels Antwort (er ist willkommen, es hinzuzufügen, wenn er will, in diesem Fall werde ich dieses entfernen).

Mit Blick auf die ursprünglichen PIA für Word.Application, gibt es drei Arten beteiligt sind (die Ereignisse zu ignorieren):

[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 
{ 
} 

Es gibt zwei Schnittstellen für die Gründe dafür, dass Eric Lippert über in another answer spricht. Und da, wie Sie gesagt haben, ist die CoClass - sowohl in Bezug auf die Klasse selbst und das Attribut auf der Application Schnittstelle.

Jetzt, wenn wir PIA-Verknüpfung in C# 4 verwenden, einige davon ist in die resultierende Binärdatei eingebettet ... aber nicht alles davon. die eine Anwendung nur erstellt eine Instanz von Application endet mit diesen Typen:

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

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

Kein ApplicationClass - vermutlich, weil das wird dynamisch aus der zum Zeitpunkt der Ausführung echte COM-Typ geladen werden.

Eine andere interessante Sache ist der Unterschied im Code zwischen der verknüpften Version und der nicht verknüpften Version.Wenn Sie die Zeile decompile

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

in der verwiesen Version endet es nach oben wie:

Application application = new ApplicationClass(); 

während in der verknüpften Version endet es sich als

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

es also sieht aus wie der "echte" PIA braucht dasAttribut, aber die verknüpfte Version nicht weil dort ist nicht ein CoClass der Compiler kann tatsächlich verweisen. Es muss es dynamisch machen.

Ich könnte versuchen zu fälschen eine COM-Schnittstelle mit diesen Daten und sehen, ob ich die Compiler es zu verbinden bekommen ...

26

nur ein bisschen Bestätigung Michaels Antwort hinzuzufügen:

Der folgenden Code kompiliert und ausgeführt:

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(); 
    } 
} 

Sie müssen sowohl die ComImportAttribute und die GuidAttribute für sie zu arbeiten.

Beachten Sie auch die Informationen, wenn Sie die Maus über die new IFoo() bewegen: Intellisense greift die Informationen richtig auf: Schön!

+0

danke, ich habe versucht, aber ich fehlte ** ComImport ** Attribut, aber wenn ich gehe ich zum Quellcode ich arbeitete mit F12 zeigt nur ** CoClass ** und ** Guid **, warum ist das? –