Как компилятор C# обнаруживает типы COM?

EDIT: я записал результаты как блоге.


компилятор C# обрабатывает типы COM несколько волшебным образом. Например, это утверждение выглядит нормально...

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

... пока ты не поймешь, что Application - это интерфейс. Вызов конструктора на интерфейсе? Yoiks! Это фактически переводится в вызов Type.GetTypeFromCLSID() и другое Activator.CreateInstance.

кроме того, в C# 4 Вы можете использовать аргументы без ссылки для ref параметры, и компилятор просто добавляет локальную переменную для передачи по ссылке, отбрасывая результаты:

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

(Да, есть куча аргументов не хватает. Разве необязательные параметры не хороши? :)

Я пытаюсь исследовать поведение компилятора, и мне не удается подделать первую часть. Я могу сделать вторую часть с проблема:

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 PIAs (CompilerGenerated и TypeIdentifier), похоже, не делают трюк... что за волшебный соус?

4 ответов


ни в коем случае я не эксперт в этом, но я недавно наткнулся на то, что, как я думаю, вы хотите:компонентный класс атрибут class.

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

кокласс поставляет бетон осуществление(осуществление) одного или нескольких межфазные границы. В COM, такой бетон реализации могут быть написаны в любом язык программирования, поддерживающий COM разработка компонент, например, Delphi, C++, Visual Basic и т. д.

посмотреть мой ответ на подобный вопрос о Microsoft Speech API, где вы можете "создать экземпляр" интерфейса SpVoice (но на самом деле, вы создаете экземпляр SPVoiceClass).

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

между вами и Майклом вы почти собрали кусочки вместе. Я думаю, вот как это работает. (Я не писал код, поэтому я могу немного ошибаться, но я уверен, что это так.)

Если:

  • вы "новый" ing тип интерфейса, и
  • тип интерфейса имеет известный coclass, и
  • вы используете функцию "no pia" для этого интерфейса

затем код генерируется как (IPIAINTERFACE)активатор.CreateInstance(Тип.GetTypeFromClsid (GUID COCLASSTYPE))

Если:

  • вы "новый" ing тип интерфейса, и
  • тип интерфейса имеет известный coclass, и
  • вы не используете функцию "no pia" для этого интерфейса

затем код генерируется так, как если бы вы сказали "new COCLASSTYPE()".

Джон, не стесняйтесь беспокоить меня или Сэма напрямую, если у вас есть вопросы об этом материале. К ВАШЕМУ СВЕДЕНИЮ, Сэм является экспертом по этой функции.


хорошо, это просто, чтобы положить немного больше плоти на ответ Майкла (он может добавить его, если захочет, и в этом случае я удалю этот).

глядя на оригинальный PIA для Word.Применение, Есть три типа, участвующих (игнорируя события):

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

есть два интерфейса по причинам, о которых говорит Эрик Липперт в еще один ответ. И там, как вы сказали, находится CoClass - как с точки зрения самого класса и атрибут на Application интерфейс.

теперь, если мы используем PIA linking в C# 4,какой-то это заложено в результирующий двоичный файл... но не все. Приложение, которое просто создает экземпляр Application заканчивается этими типами:

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

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

нет ApplicationClass - предположительно, потому что это будет загружаться динамически из реальная тип COM во время выполнения.

еще одна интересная вещь-это разница в коде между связанной и несвязанной версиями. Если вы декомпилируете строку

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

на ссылка версия он заканчивается как:

Application application = new ApplicationClass();

а в связан версия он заканчивается как

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

таким образом, похоже, что "настоящая" PIA нуждается в CoClass атрибут, но связанная версия не делает, потому что там не a CoClass компилятор может фактически ссылаться. Он должен это сделать. динамично.

я мог бы попытаться подделать COM-интерфейс, используя эту информацию, и посмотреть, смогу ли я заставить компилятор связать его...


просто чтобы добавить немного подтверждения к ответу Майкла:

следующий код компилируется и работает:

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 правильно подбирает информацию: приятно!