web-dev-qa-db-ja.com

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

EDIT:結果を ブログ投稿 として書きました。


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)は、トリックを実行していないようです...魔法のソースは何ですか?

166
Jon Skeet

私は決してこれに関する専門家ではありませんが、最近、私があなたが望むと思うものにつまずきました: CoClass 属性クラス。

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

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

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

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

あなたとマイケルの間で、あなたはほとんどピースを組み立てました。これがその仕組みだと思います。 (私はコードを書いていないので、少し誤解しているかもしれませんが、これがどうなるか確信しています。)

次の場合:

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

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

次の場合:

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

「new COCLASSTYPE()」と言ったようにコードが生成されます。

ジョン、この件について質問がある場合は、私やサムに直接バグを送ってください。参考までに、サムはこの機能のエキスパートです。

60
Eric Lippert

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

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が another answer で説明している理由から、2つのインターフェイスがあります。そして、あなたが言ったように、クラス自体とCoClassインターフェースの属性の両方に関して、Applicationがあります。

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

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

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

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

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

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

referencedバージョンでは、次のようになります。

Application application = new ApplicationClass();

一方、linkedバージョンでは、

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

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

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

35
Jon Skeet

マイケルの答えにちょっとした確認を追加するだけです:

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

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

ComImportAttributeGuidAttributeの両方が必要です。

マウスをnew IFoo()の上に置いたときに情報にも注意してください:Intellisenseは情報を適切に取得します。

27
Rasmus Faber