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

РЕДАКТИРОВАТЬ: я написал результаты в виде сообщения в блоге.


Компилятор 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");

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

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

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 Кажется, что не получается... что за волшебный соус?

4 ответа

Решение

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

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

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

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

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

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

Если:

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

затем код генерируется как (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))

Если:

  • вы "новый" тип интерфейса, и
  • тип интерфейса имеет известный Coclass, и
  • Вы НЕ используете функцию "без пиа" для этого интерфейса

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

Джон, не стесняйся задавать мне или Сэму напрямую, если у тебя есть вопросы по этому поводу. К вашему сведению, Сэм является экспертом по этой функции.

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

Глядя на оригинальную PIA для Word.Application, можно выделить три типа (игнорируя события):

[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 в 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 атрибут, но связанная версия не, потому что нет 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 правильно подхватывает информацию: приятно!

Другие вопросы по тегам