Как компилятор C# определяет типы COM?
-
11-09-2019 - |
Вопрос
РЕДАКТИРОВАТЬ: Я записал результаты в виде Сообщение блога.
Компилятор C# обращается с типами COM как по волшебству.Например, это утверждение выглядит нормально...
Word.Application app = new Word.Application();
...пока ты не поймешь это Application
это интерфейс.Вызов конструктора интерфейса?Йойкс!Фактически это преобразуется в вызов 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 PIA (CompilerGenerated
и TypeIdentifier
) похоже, не помогает...что за волшебный соус?
Решение
Я ни в коем случае не являюсь экспертом в этом, но недавно я наткнулся на то, что, как мне кажется, вам нужно:тот 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 { }
Другие советы
Между вами и Майклом вы почти собрали все воедино.Я думаю, вот как это работает.(Я не писал код, поэтому, возможно, я немного неправильно его выразил, но я почти уверен, что именно так и происходит.)
Если:
- вы «новый» тип интерфейса, и
- тип интерфейса имеет известный компонентный класс и
- вы используете функцию «no pia» для этого интерфейса
тогда код генерируется как (IPIAINTERFACE)Activator.CreateInstance(Type.GetTypeFromClsid(GUID OF COCLASSTYPE))
Если:
- вы «новый» тип интерфейса, и
- тип интерфейса имеет известный компонентный класс и
- вы НЕ используете функцию «no pia» для этого интерфейса
тогда код генерируется так, как если бы вы сказали «новый 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 правильно воспринимает информацию:Хороший!