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(), к которой я не могу добраться.
Спасибо!
Вы принципиально неправильно понимаете, как работает COM, начиная со значения [ClassInterfaceType]. Объясняя это, требуется книга, есть много доступных. –
Я хорошо понимаю, как работает COM. Всегда больше узнать, конечно, спасибо за замечание, что книг много. В любом случае, чтобы повторить мой вопрос: мне понадобятся интерфейсы, основанные на IUnknown', полностью основанные на публичных нестационарных членах моих классов, поэтому я надеялся на некоторые идеи сообщества по их автогенерации, не нужно писать их при каждом изменении. Я открыт для любых конструктивных идей, включая альтернативные способы отказа от поздних клиентов без ручной обработки интерфейсов. Спасибо. – bob
«Мне понадобятся интерфейсы, основанные на IUnknown', полностью основанные на публичных нестатических членах моих классов» - именно то, что предоставляет AutoDual. –