C#コンパイラはどのようにしてCOMタイプを検出しますか?


168

編集:私は結果をブログ投稿として書きました。


C#コンパイラは、COMタイプを魔法のように扱います。たとえば、このステートメントは正常に見えます...

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

...それがApplicationインターフェースであることを理解するまで。インターフェイスのコンストラクターを呼び出しますか?ヨイクス!これは実際にはへの呼び出しとへの呼び出しに変換さType.GetTypeFromCLSID()Activator.CreateInstanceます。

さらに、C#4では、refパラメーターに非ref引数を使用でき、コンパイラーはローカル変数を追加して参照渡しし、結果を破棄します。

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

(うん、たくさんの引数が足りません。オプションのパラメーターはいいのではないですか?:)

私はコンパイラの動作を調査しようとしています、そして私は最初の部分を偽造することに失敗しています。私は問題なく2番目の部分を行うことができます。

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

私は書くことができるようにしたいと思います:

Dummy dummy = new Dummy();

でも。もちろん、実行時に実行されますが、それで問題ありません。実験中です。

リンクされたCOM PIA(CompilerGeneratedおよびTypeIdentifier)のコンパイラーによって追加された他の属性は、トリックを実行していないようです...魔法のソースは何ですか?


11
オプションのパラメータはいいですか?IMO、いや、彼らは良くない。マイクロソフトは、C#に膨張を追加することにより、Office COMインターフェイスの欠陥を修正しようとしています。
Mehrdad Afshari、

18
@Mehrdad:オプションのパラメーターは、もちろんCOM以外にも役立ちます。デフォルト値には注意する必要がありますが、それらと名前付き引数の間では、使用可能な不変タイプを作成する方がはるかに簡単です。
ジョンスキート、

1
そうだね。具体的には、名前付きパラメーターは、いくつかの動的環境との相互運用のために実際に必要になる場合があります。確かに、間違いなく、これは便利な機能ですが、それが無料で提供されるという意味ではありません。シンプルさが必要です(明示的に述べた設計目標)。個人的には、C#はチームが中断した機能に優れていると思います(そうでなければ、C ++クローンであった可能性もあります)。C#チームは素晴らしいですが、企業環境が政治の自由になることはほとんどありません。PDC'08の講演で彼が述べたように、アンダース自身はこれにあまり満足していなかったと思います。
Mehrdad Afshari、

7
チームは複雑さに注意を払う必要があることに同意します。動的なものは、ほとんどの開発者にとってはほとんど価値のない複雑さをもたらしますが、一部の開発者にとっては高い価値があります。
Jon Skeet、

1
フレームワーク開発者がその使用法について多くの場所で議論し始めているのを見てきました。IMOは、次の用途が見つかるまでの時間dynamicです。COM以外で重要である理由を確認するために静的/強力な型付けに慣れすぎています。
チャクリット

回答:


145

私は決してこれに精通しているわけではありませんが、私はあなたが望むと思うもの、CoClass属性クラスに最近つまずきました。

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

コクラスは、1つ以上のインターフェースの具体的な実装を提供します。COMでは、そのような具体的な実装は、Delphi、C ++、Visual BasicなどのCOMコンポーネント開発をサポートする任意のプログラミング言語で記述できます。

Microsoft Speech APIに関する同様の質問への私の回答を参照しください。ここでは、インターフェイスを「インスタンス化」できますSpVoice(ただし、インスタンス化していますSPVoiceClass)。

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

2
非常に興味深い-後で試してみる。ただし、リンクされたPIAタイプにはCoClassがありません。多分それはリンクプロセスと関係がある-私は元のPIAを見ていきます...
ジョンスキート

64
エリックリッペルトとジョンスキートも答えたとき、受け入れられた答えを書いて素晴らしいことに対する+1;)いいえ、実際には、CoClassについて言及するための+1。
OregonGhost 2009

61

あなたとマイケルの間で、あなたはほとんど作品をまとめました。これがその仕組みだと思います。(私はコードを記述しなかったので、少し誤解しているかもしれませんが、これがどのように行われるかはかなり確信しています。)

次の場合:

  • インターフェイスタイプを「新規」にしている。
  • インターフェイスタイプには既知のコクラスがあり、
  • このインターフェースに「ピアなし」機能を使用している

次に、コードは(IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))として生成されます

次の場合:

  • インターフェイスタイプを「新規」にしている。
  • インターフェイスタイプには既知のコクラスがあり、
  • このインターフェースに「ピアなし」機能を使用していない

次に、「新しいCOCLASSTYPE()」と言ったかのようにコードが生成されます。

ジョン、このことについて質問がある場合は、私またはサムに直接バグを報告してください。参考までに、サムはこの機能の専門家です。


36

さて、これはマイケルの答えにもう少し肉付けをするためのものです(彼が望むならそれを追加することを歓迎します。その場合、私はこれを削除します)。

Word.Applicationの元のPIAを見ると、3つのタイプが関係しています(イベントは無視されます)。

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

Eric Lippertが別の答えで話している理由には、2つのインターフェースがあります。そして、あなたが言ったようCoClassに、クラス自体とApplicationインターフェイスの属性の両方に関して-があります。

ここで、C#4でPIAリンクを使用すると、結果のバイナリに一部が埋め込まれますが、すべてではありません。のインスタンスを作成するだけのアプリケーションは、Application次のタイプになります。

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

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

いいえApplicationClass-おそらくそれは実行時に実際の COMタイプから動的にロードされるためです。

もう1つの興味深い点は、リンクされたバージョンとリンクされていないバージョンのコードの違いです。行を逆コンパイルすると

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

参照されたバージョンではと終わります。

Application application = new ApplicationClass();

一方、リンクされたバージョンでは、次のようになります

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

「本当の」PIAが必要のように見えるそれはそうCoClass属性は、そこので、リンクされたバージョンにはないではありませんCoClassコンパイラが実際に参照することができます。動的に行う必要があります。

この情報を使用してCOMインターフェイスを偽装して、コンパイラーにリンクさせることができるかどうか試してみるかもしれません...


27

マイケルの答えに少し確認を追加するだけです:

次のコードはコンパイルして実行します。

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

それが機能するには、ComImportAttributeとの両方が必要GuidAttributeです。

また、マウスをに合わせると、情報に注意してくださいnew IFoo()。Intellisenseは適切に情報を取得します。


おかげで、試してみましたが、ComImport属性がありませでしたが、F12を使用して作業していたソースコードに移動すると、CoClassGuidしか表示されませんが、なぜですか?
Ehsan Sajjad、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.