Як компілятор C # виявляє типи COM?


168

РЕДАКТУВАННЯ: Результати я записав як повідомлення в блозі .


Компілятор 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), схоже, не роблять фокусу ... що таке чарівний соус?


11
Не приємні параметри? ІМО, Ні, вони не приємні. Microsoft намагається виправити недолік інтерфейсів Office COM, додавши розширення до C #.
Мехрдад Афшарі

18
@Mehrdad: Додаткові параметри, звичайно, корисні поза COM, звичайно. Вам потрібно бути обережними зі значеннями за замовчуванням, але між ними та названими аргументами набагато простіше побудувати придатний для зміни тип.
Джон Скіт

1
Правда. Зокрема, названі параметри можуть бути практично необхідні для взаємодії з деякими динамічними середовищами. Звичайно, без сумніву, це корисна функція, але це не означає, що вона приходить безкоштовно. Це коштує простоти (чітко визначена мета дизайну). Особисто я вважаю, що C # є дивовижним для особливостей, які команда припинила (інакше це міг бути клон C ++). Команда C # чудова, але корпоративне середовище навряд чи може бути вільним від політики. Я думаю, що сам Андерс не дуже зрадів цьому, як він заявив у своїй розмові про PDC'08: "Нам знадобилося десять років, щоб повернутися туди, де ми були".
Мехрдад Афшарі

7
Я згоден, що команді потрібно буде пильно стежити за складністю. Динамічний матеріал додає багато складності для малої вартості для більшості розробників, але високої цінності для деяких розробників.
Джон Скіт

1
Я бачив розробників рамок, які починають обговорювати її використання в багатьох місцях. IMO, саме час, поки ми не знайдемо корисного для dynamic... ми просто занадто звикли до статичного / сильного набору тексту, щоб зрозуміти, чому це має значення поза COM.
чакрит

Відповіді:


145

Я ні в якому разі не є експертом у цьому, але нещодавно натрапив на те, що, на вашу думку, ви хочете: клас атрибутів CoClass .

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

2
Дуже цікаво - спробую пізніше. Однак пов'язані типи PIA не мають CoClass. Можливо, це пов’язане з процесом зв’язування - я загляну в оригінальну програму PIA ...
Джон Скіт

64
+1 за те, що він був дивовижним, написавши прийняту відповідь, коли Ерік Ліпперт та Джон Скіт також відповіли;) Ні, справді, +1 за згадку про CoClass.
OregonGhost

61

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

Якщо:

  • ви "новий" тип інтерфейсу, і
  • тип інтерфейсу має відомий клас, і
  • ви використовуєте для цього інтерфейсу функцію "no pia"

тоді код створюється як (IPIAINTERFACE) Activator.CreateInstance (Type.GetTypeFromClsid (GUID OF COCLASSTYPE))

Якщо:

  • ви "новий" тип інтерфейсу, і
  • тип інтерфейсу має відомий клас, і
  • ви НЕ використовуєте для цього інтерфейсу функцію "no pia"

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

Джон, сміливо клопіть мене або Сем безпосередньо, якщо у вас є запитання щодо цього матеріалу. FYI, Сем є експертом у цій функції.


36

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

Дивлячись на оригінальну програму 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, використовуючи цю інформацію, і побачити, чи зможу я компілятор зв’язати його ...


27

Просто, щоб додати трохи підтвердження відповіді Майкла:

Наступний код збирається та працює:

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


дякую, я намагався, але мені не вистачало атрибуту ComImport , але коли я переходжу до вихідного коду, я працював за допомогою F12, показує лише CoClass і Guid , чому це так?
Ehsan Sajjad
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.