2013-03-18 13 views
1

System.Runtime.InteropServices.ClassInterfaceType Перечисление в .NET имеет значение AutoDispatch и значение AutoDual, но значение AutoUnknown. Почему это не так, и у кого-нибудь уже есть довольно автоматизированный способ его выполнения, так что мне не нужно изобретать велосипед?Почему нет значения AutoUnknown в System.Runtime.InteropServices.ClassInterfaceType Enumeration?

Для более битного фона, вот три значения в перечислении в настоящее время и что они делают:

  • ClassInterfaceType.None не создает интерфейс класса для вас. Это рекомендуемое значение Microsoft. Без интерфейса класса вам необходимо создать свой собственный интерфейс с любыми членами, которые вы намереваетесь, и сделать свой класс реализованным. Затем вы можете использовать атрибут System.Runtime.InteropServices.ClassInterfaceAttribute на своем интерфейсе, чтобы сделать его дисперсионным (поддерживающим поздние клиенты), неизвестным интерфейсом (поддерживающим клиентов с ранней задержкой) или двойным (поддерживающим как ранние, так и поздние клиенты).
  • ClassInterfaceType.AutoDispatch создает интерфейс класса, полученный от IDispatch без явных членов. Без явных членов он может поддерживать только опоздавших абонентов.
  • ClassInterfaceType.AutoDual создает интерфейс класса, полученный из IDispatch, что имеет, имеет явные члены для всех нестатических членов этого класса, а также всех его базового класса и любых реализованных интерфейсов. Microsoft решительно отказывается от использования этого значения «из-за ограничений версий» в результате того, что если какой-либо из членов класса изменится, интерфейс класса также изменится. (Так что абоненты, которые не связываются повторно после того, как класс изменился может закончиться вызовом неправильные методы или передавать неправильные параметры.)

Так что я интересно, почему это не значение называется ClassInterfaceType.AutoUnknown который создавал бы интерфейс класса, полученный от IUnknown, который явно объявляет членов вашего класса (а не базовый класс или другие интерфейсы, которые он реализует).

Хотя мы и находимся в этом, я ожидаю, что там будет что-то вроде ClassInterfaceType.AutoDualDirect, которое было бы похоже на AutoDual, за исключением того, что вместо того, чтобы подвергать всех членов базового класса и всех реализованных интерфейсов, он будет раскрывать только прямые члены класса, поскольку другие члены могут быть извлечены через другие интерфейсы COM.

Итак, что это за история, что мне не хватает? Я знаю, что Microsoft заявляет, что рекомендует не использовать AutoDual из-за «проблем с версиями» и что эти проблемы будут в некоторой степени применяться к AutoUnknown; но, несмотря на эту рекомендацию, они все еще имеют AutoDual. Почему бы и нет AutoUnknown?

Слово о тех «проблемах с управлением версиями»: наиболее опасные аспекты этих проблем относятся только к опоздавшим абонентам. AutoUnknown будет генерировать IUnknown -уложенный интерфейс, а не интерфейс IDispatch, поэтому нам нужно только заботиться о проблемах с версиями, которые могут иметь ранние клиенты. И так как IID любого автоматически генерируемого интерфейса класса будет меняться в любое время, когда изменяются публичные нестатические члены класса, клиент с ранней привязкой не сможет сделать «плохой вызов» существующим членам, он просто не сможет выполнить QueryInterface () для интерфейса класса. Еще неудача, но управляемая, а не крушение или что-то еще. Это даже не нарушает правило COM, которое никогда не позволяет изменять интерфейс; он не меняется, он становится новым интерфейсом (новый IID). Действительно, он практически ничем не отличается от половины механизма AutoDual (минус производные члены).Фактически, он страдает меньше «проблемы с управлением версиями», чем AutoDual, потому что AutoDual также имеет все проблемы с поздними ограничениями.

Так что снова я должен спросить: почему AutoDual существует, если AutoUnknown не делает!

Чтобы дать некоторые реальные примеры, рассмотрим следующую C# код:

using System.Runtime.InteropServices; 

[ComVisible(true)] 
[ClassInterface(ClassInterfaceType.AutoDual)] 
public class F 
{ 
    public int foo() 
    { 
     return 5; 
    } 
} 

генерирует следующий IDL:

[ 
    uuid(1F3A7DE1-99A1-37D4-943E-1BF5CFDF7DFA), 
    version(1.0), 
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F") 
] 
coclass F { 
    [default] interface _F; 
    interface _Object; 
}; 

[ 
    odl, 
    uuid(3D0A1144-1C0B-3877-BE45-AD8318898790), 
    hidden, 
    dual, 
    nonextensible, 
    oleautomation, 
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F")  

] 
interface _F : IDispatch { 
    [id(00000000), propget, 
     custom(54FC8F55-38DE-4703-9C4E-250351302B1C, 1)] 
    HRESULT ToString([out, retval] BSTR* pRetVal); 
    [id(0x60020001)] 
    HRESULT Equals(
        [in] VARIANT obj, 
        [out, retval] VARIANT_BOOL* pRetVal); 
    [id(0x60020002)] 
    HRESULT GetHashCode([out, retval] long* pRetVal); 
    [id(0x60020003)] 
    HRESULT GetType([out, retval] _Type** pRetVal); 
    [id(0x60020004)] 
    HRESULT foo([out, retval] long* pRetVal); 
}; 

Это хорошо, но я хотел бы предложить, что AutoUnknown бы услужливо генерировать этот IDL вместо :

[ 
    uuid(1F3A7DE1-99A1-37D4-943E-1BF5CFDF7DFA), 
    version(1.0), 
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "F") 
] 
coclass F { 
    [default] interface _F; 
    interface _Object; 
}; 

[ 
    odl, 
    uuid(BC84F393-DACC-353F-8DBE-F27CB2FB4757), 
    version(1.0), 
    oleautomation, 
    custom(0F21F359-AB84-41E8-9A78-36D110E6D2F9, "_F")  

] 
interface _F : IUnknown { 
    HRESULT _stdcall foo([out, retval] long* pRetVal); 
}; 

Если нет возможности сделать это автоматически n ow, я напишу что-то, что использует отражение, чтобы сделать это, поэтому я был бы признателен за любые рекомендации по этому поводу. Например, есть ли какие-либо атрибуты, которые мне нужно проверить на методах? По параметрам? Я выкопал в IL System.Runtime.InteropServices.TypeLibConverter, чтобы увидеть, как он это делает, но, к сожалению, все лакомства находятся в частной функции innerCall nConvertAssemblyToTypeLib(), к которой я не могу добраться.

Спасибо!

+1

Вы принципиально неправильно понимаете, как работает COM, начиная со значения [ClassInterfaceType]. Объясняя это, требуется книга, есть много доступных. –

+0

Я хорошо понимаю, как работает COM. Всегда больше узнать, конечно, спасибо за замечание, что книг много. В любом случае, чтобы повторить мой вопрос: мне понадобятся интерфейсы, основанные на IUnknown', полностью основанные на публичных нестационарных членах моих классов, поэтому я надеялся на некоторые идеи сообщества по их автогенерации, не нужно писать их при каждом изменении. Я открыт для любых конструктивных идей, включая альтернативные способы отказа от поздних клиентов без ручной обработки интерфейсов. Спасибо. – bob

+0

«Мне понадобятся интерфейсы, основанные на IUnknown', полностью основанные на публичных нестатических членах моих классов» - именно то, что предоставляет AutoDual. –

ответ

0

Реальный вопрос - что вы пытаетесь достичь?

От ClassInterface docs: "Указывает тип интерфейса класса, который должен быть сгенерирован для класса, открытого для COM, если создается какой-либо интерфейс."

Идея ClassInterface заключается в том, чтобы предоставить программный интерфейс класса COM-клиентам - вот почему вы видите полный интерфейс, включая методы базового класса. Цель состоит не в том, чтобы обеспечить представление подмножества функциональности класса. Это то, что интерфейсы для (и ClassInterfaceType.None)

Обеспечение интерфейс класса без позднего связывания способности (т.е. IDispatch) является то, что вы не должны реально сделать (и ClassInterface к счастью, не дает ему), как это непосредственно в зависимости от макет вашего класса для правильной работы. Это означает, что ни класс, ни его базовые классы не должны меняться. Это то, что трудно гарантировать и почему это вообще плохая идея, помимо того, что она идет вразрез с цельной философией COM. Клиенты с ранней привязкой тесно связаны с компоновкой интерфейса vtable - каждое изменение в макете может сломать его. Клиенты с ограниченной привязкой не обнаруживают эту проблему, так как методы разрешаются по имени, и они могут корректно обрабатывать отсутствующий метод.

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

+0

Я хочу поддерживать ТОЛЬКО ранних клиентов именно потому, что клиенты с поздними ограничениями подвержены серьезным рискам несовместимости версий, т. Е. Двоичные несоответствия, приводящие к сбою, в то время как ранние клиенты требуют QueryInterface(), чтобы получить необходимый им интерфейс, поэтому с изменением IID (потому что интерфейс изменился), они получат E_NOINTERFACE, которые они могут обрабатывать изящно. До тех пор, пока IID изменяется при изменении интерфейса, я не вижу причин, по которым интерфейс класса без поздней привязки не должен предоставляться. – bob

+0

Вы получили это назад. Клиенты с ранней привязкой подвергаются бинарным несоответствиям, поскольку они связаны с макетом vtable, а не с поздней задержкой - они просто возвращают 'DISP_E_MEMBERNOTFOUND' (или' DISP_E_BADPARAMCOUNT'/'DISP_E_BADVARTYPE') в случае несоответствия. Тем не менее, 'AutoDual' отлично подходит для ранних клиентов, поэтому просто используйте это, и вы готовы к работе. Вы пытаетесь использовать функцию, чтобы сделать что-то, что она не была предназначена. –

+0

Я соглашусь с тем, что правильно написанный клиент с поздними границами будет также безопасен из двоичных несоответствий, поскольку он не будет иметь информацию о кэшированном интерфейсе (например, сообщения и количество параметров). Я по-прежнему не вижу здесь никакой причины, по которой «AutoUnknown» не существует: я просто не хочу поддерживать клиентов с поздними ограничениями; Я не хочу, чтобы мой интерфейс получался из 'IDispatch', я не хочу раздуваться в моей библиотеке типов. Без ответа здесь я продолжаю свое собственное автогенерацию, но хотел бы услышать больше идей. Спасибо (вам и всем) за ваше понимание до сих пор! – bob

 Смежные вопросы

  • Нет связанных вопросов^_^