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