Component Object Model ( COM ) — это технология двоичного интерфейса для программных компонентов от Microsoft , которая позволяет использовать объекты нейтральным по отношению к языку способом между различными языками программирования , контекстами программирования, процессами и машинами .
COM является основой для других компонентных технологий Microsoft, специфичных для домена, включая OLE , OLE Automation , ActiveX , COM+ и DCOM, а также для таких реализаций , как DirectX , Windows shell , UMDF , Windows Runtime и Browser Helper Object .
COM позволяет использовать объект, зная только его интерфейс; не его внутреннюю реализацию. Реализатор компонента определяет интерфейсы , которые отделены от реализации.
Поддержка множественных контекстов программирования осуществляется путем опоры на объект для аспектов, которые было бы сложно реализовать как средство. Поддержка множественного использования объекта осуществляется путем требования, чтобы каждый объект уничтожал себя с помощью подсчета ссылок . Доступ к интерфейсам объекта (аналогично преобразованию типов ) также предоставляется каждым объектом.
COM доступен только в Microsoft Windows и Apple Core Foundation 1.3 и более поздних версиях подключаемого программного интерфейса приложений (API). [1] Последний реализует только подмножество всего интерфейса COM. [2]
Со временем COM заменяется другими технологиями, такими как Microsoft .NET и веб-сервисами (т.е. через WCF ). Однако объекты COM могут использоваться в языке .NET через COM Interop .
COM похож на другие компонентные технологии, такие как SOM , CORBA и Enterprise JavaBeans , хотя у каждой из них есть свои сильные и слабые стороны.
В отличие от C++ , COM предоставляет стабильный двоичный интерфейс приложения (ABI), на который не влияют различия компиляторов. [3] Это делает использование COM выгодным для объектно-ориентированных библиотек C++, которые должны использоваться клиентами, скомпилированными с помощью разных компиляторов.
Представленный в 1987 году динамический обмен данными (DDE) был одной из первых технологий межпроцессного взаимодействия в Windows . [4] [5] Он позволял отправлять и получать сообщения в так называемых диалогах между приложениями.
Энтони Уильямс, занимавшийся разработкой архитектуры COM, распространил в Microsoft две статьи, в которых рассматривалась концепция программных компонентов: «Архитектура объектов: работа с неизвестным или безопасность типов в динамически расширяемой библиотеке классов» в 1988 году и «О наследовании: что это значит и как его использовать» в 1990 году. Они легли в основу многих идей, лежащих в основе COM.
Object Linking and Embedding (OLE), первая объектно-ориентированная структура Microsoft, была построена на DDE и разработана специально для составных документов . Она была представлена в Word и Excel в 1991 году и позже была включена в Windows, начиная с версии 3.1 в 1992 году. Примером составного документа является электронная таблица, встроенная в документ Word. По мере внесения изменений в электронную таблицу в Excel они автоматически отображаются в документе Word.
В 1991 году Microsoft представила технологию Visual Basic Extension (VBX) с Visual Basic 1.0. VBX — это упакованное расширение в виде библиотеки динамической компоновки (DLL), которая позволяет графически размещать объекты в форме и управлять ими с помощью свойств и методов . Позднее они были адаптированы для использования другими языками, такими как Visual C++ .
В 1992 году с Windows 3.1 компания Microsoft выпустила OLE 2 с новой базовой объектной моделью COM. Двоичный интерфейс приложений COM (ABI) был таким же, как MAPI ABI (выпущенный в 1992 году), и, как и он, был основан на MSRPC и, в конечном счете, на DCE/RPC от Open Group . COM был создан для замены DDE, поскольку его текстовый диалог и дизайн обмена сообщениями Windows не были достаточно гибкими, чтобы обеспечить совместное использование функций приложений надежным и расширяемым способом.
В 1994 году была представлена технология OLE custom control (OCX), основанная на COM, в качестве преемника VBX. В то же время Microsoft заявила, что OLE 2 будет называться просто «OLE».
В начале 1996 года Microsoft нашла новое применение OCX – расширение возможностей своего веб-браузера. Microsoft переименовала некоторые части OLE, относящиеся к Интернету , в ActiveX и постепенно переименовала все технологии OLE в ActiveX, за исключением технологии составных документов, которая использовалась в Microsoft Office .
Позже, в 1996 году, Microsoft расширила COM для работы в сети с помощью DCOM . [6]
COM IDL основан на богатом функциями DCE/RPC IDL с объектно-ориентированными расширениями. Реализация DCE/RPC от Microsoft, MSRPC , используется в качестве основного механизма межпроцессного взаимодействия для служб и внутренних компонентов Windows NT, что делает его очевидным выбором основы.
DCOM расширяет COM от простой поддержки одного пользователя с отдельными приложениями, взаимодействующими на рабочем столе Windows, до активации объектов, работающих в разных контекстах безопасности и на разных машинах по всей сети. С этим были добавлены необходимые функции для настройки того, какие пользователи имеют полномочия создавать, активировать и вызывать объекты, для идентификации вызывающего пользователя, а также для указания требуемого шифрования для безопасности вызовов.
Microsoft представила Microsoft Transaction Server (MTS) в Windows NT 4 с целью предоставить разработчикам поддержку распределенных транзакций , объединения ресурсов, отключенных приложений, публикации и подписки на события, лучшего управления памятью и процессором (потоком), а также позиционировать Windows как альтернативу другим операционным системам корпоративного уровня.
Переименованный в COM+ в Windows 2000, набор функций был включен в операционную систему в отличие от серии внешних инструментов, предоставляемых MTS. В то же время Microsoft снизила акцент на DCOM как на отдельной сущности. Компоненты, которые использовали COM+, обрабатывались более непосредственно добавленным слоем COM+; в частности, поддержкой операционной системы для перехвата. В первом выпуске MTS перехват был добавлен — установка компонента MTS изменяла реестр Windows для вызова программного обеспечения MTS, а не компонента напрямую.
В Windows 2000 включены обновления панели управления «Службы компонентов» для настройки компонентов COM+.
Преимуществом COM+ было то, что его можно было запускать в «фермах компонентов». Экземпляры компонента, если они были правильно закодированы, можно было объединять и повторно использовать новыми вызовами его инициализирующей процедуры без выгрузки его из памяти. Компоненты также можно было распределять (вызывать с другой машины). COM+ и Microsoft Visual Studio предоставляли инструменты, упрощающие создание клиентских прокси, поэтому, хотя для удаленного вызова использовался DCOM, разработчикам было легко это сделать. COM+ также представил механизм событий подписчика/издателя, называемый COM+ Events , и предоставил новый способ использования MSMQ (технологии, которая обеспечивает асинхронный обмен сообщениями между приложениями) с компонентами, называемыми Queued Components . События COM+ расширяют модель программирования COM+ для поддержки событий с поздним связыванием (см. Позднее связывание ) или вызовов методов между издателем или подписчиком и системой событий.
.NET — это компонентная технология Microsoft, которая заменяет COM. .NET скрывает многие детали создания компонентов и, следовательно, упрощает разработку.
.NET предоставляет оболочки для часто используемых элементов управления COM.
.NET может использовать COM+ через System.EnterpriseServices
пространство имен, и несколько служб, предоставляемых COM+, были продублированы в .NET. Например, System.Transactions
пространство имен предоставляет TransactionScope
класс, который обеспечивает управление транзакциями без обращения к COM+. Аналогично, компоненты в очереди могут быть заменены Windows Communication Foundation (WCF) с транспортом MSMQ .
Ограниченная поддержка обратной совместимости. Объект COM может использоваться в .NET путем реализации Runtime Callable Wrapper (RCW). [7] Объекты NET, соответствующие определенным ограничениям интерфейса, могут использоваться в объектах COM путем вызова COM callable wrapper (CCW). [8] Как со стороны COM, так и со стороны .NET объекты, использующие другую технологию, отображаются как собственные объекты. См. COM Interop .
WCF облегчает ряд проблем удаленного выполнения COM. Например, он позволяет прозрачно упорядочивать объекты по значению через границы процессов или машин.
Windows Runtime ( WinRT ) — это API на основе COM, хотя и улучшенный вариант COM. Благодаря своей COM-подобной основе WinRT поддерживает взаимодействие из нескольких программных контекстов, но это неуправляемый, собственный API. Определения API хранятся в файлах «.winmd», которые закодированы в формате метаданных ECMA 335; тот же формат метаданных CLI , который использует .NET с несколькими изменениями. Этот формат метаданных обеспечивает значительно меньшие накладные расходы, чем P/Invoke, когда WinRT вызывается из приложений .NET.
Nano-COM — это подмножество COM, ориентированное на аспекты двоичного интерфейса приложения (ABI) COM, которые позволяют вызывать функции и методы через независимо скомпилированные модули/компоненты. Nano-COM может быть выражен в переносимом заголовочном файле C++. Nano-COM расширяет собственный ABI базовой архитектуры инструкций и ОС для поддержки типизированных ссылок на объекты, тогда как типичный ABI фокусируется на атомарных типах, структурах, массивах и соглашениях о вызове функций.
Заголовочный файл Nano-COM определяет или называет как минимум три типа:
dynamic_cast<T>
получения новых типов интерфейсов в стиле IUnknown и подсчета ссылок a lashared_ptr<T>
Во многих случаях использования Nano-COM определяются две функции для обращения к буферам памяти, выделенным вызываемым объектом, в качестве результатов:
Некоторые реализации Nano-COM, такие как Direct3D, избегают функций распределения и ограничиваются использованием только буферов, выделенных вызывающей стороной.
Nano-COM не имеет понятия о классах, апартаментах, маршалинге, регистрации и т. д. Вместо этого ссылки на объекты просто передаются через границы функций и выделяются с помощью стандартных языковых конструкций (например, new
оператора C++).
Основа Nano-COM была использована Mozilla для начальной загрузки Firefox (называемого XPCOM ), и в настоящее время используется в качестве базовой технологии ABI для DirectX / Direct3D / DirectML .
Поскольку элемент управления ActiveX (любой компонент COM) работает как собственный код, без защиты в песочнице , существует мало ограничений на то, что он может делать. Использование компонентов ActiveX, поддерживаемых Internet Explorer , на веб-странице приводит к проблемам с заражением вредоносным ПО . Microsoft осознала эту проблему еще в 1996 году, когда Чарльз Фицджеральд сказал: «Мы никогда не заявляли заранее, что ActiveX по своей сути безопасен». [9] Более поздние версии Internet Explorer запрашивают у пользователя перед установкой элемента управления ActiveX, что позволяет им блокировать установку.
В качестве уровня защиты элемент управления ActiveX подписывается цифровой подписью, гарантирующей подлинность.
Также можно полностью отключить элементы управления ActiveX или разрешить только некоторые из них.
Прозрачная поддержка внепроцессных COM-серверов способствует безопасности программного обеспечения с точки зрения изоляции процессов . Это может быть полезно для разделения подсистем большого приложения на отдельные процессы. Изоляция процессов ограничивает повреждение состояния в одном процессе от негативного влияния на целостность других процессов, поскольку они взаимодействуют только через строго определенные интерфейсы. Таким образом, для восстановления допустимого состояния необходимо перезапустить только затронутую подсистему. Это не относится к подсистемам в пределах одного процесса, где неисправный указатель в одной подсистеме может случайным образом повредить другие подсистемы.
COM поддерживается через привязки в нескольких языках, таких как C , C++ , Visual Basic , Delphi , Python [10] [11] и нескольких контекстах сценариев Windows. Доступ к компонентам осуществляется через методы интерфейса . Это позволяет осуществлять прямой вызов в процессе и через доступ подсистемы COM/DCOM между процессами и компьютерами.
Coclass , класс COM, реализует один или несколько интерфейсов. Он идентифицируется идентификатором класса, называемым CLSID , который является GUID , и понятным человеку программным идентификатором, называемым ProgID . Coclass создается с помощью одного из этих идентификаторов.
Каждый интерфейс COM расширяет IUnknown
интерфейс, который предоставляет методы для подсчета ссылок и доступа к другим интерфейсам объекта — аналогично преобразованию типов , также известному как приведение типов.
Интерфейс идентифицируется идентификатором интерфейса (IID), GUID.
Пользовательский интерфейс , все, что получено из IUnknown
, обеспечивает ранний доступ с привязкой через указатель на таблицу виртуальных методов , содержащую список указателей на функции, реализующие функции, объявленные в интерфейсе, в том порядке, в котором они объявлены. Накладные расходы на внутрипроцессный вызов, таким образом, сопоставимы с вызовом виртуального метода C++.
Диспетчеризация, также известная как доступ с поздним связыванием , обеспечивается путем реализации IDispatch
. Диспетчеризация позволяет осуществлять доступ из более широкого спектра контекстов программирования, чем пользовательский интерфейс.
Как и многие объектно-ориентированные языки, COM обеспечивает разделение интерфейса и реализации. Это различие особенно сильно в COM, где объект не имеет интерфейса по умолчанию. Клиент должен запросить интерфейс, чтобы получить какой-либо доступ. COM поддерживает несколько реализаций одного и того же интерфейса, так что клиенты могут выбирать, какую реализацию интерфейса использовать.
Библиотека типов COM определяет метаданные COM, такие как соклассы и интерфейсы. Библиотеку можно определить как язык определения интерфейсов (IDL); независимый от языка программирования синтаксис. IDL похож на C++ с дополнительным синтаксисом для определения интерфейсов и соклассов. IDL также поддерживает атрибуты в квадратных скобках перед объявлениями для определения метаданных, таких как идентификаторы и отношения между параметрами.
Файл IDL компилируется с помощью компилятора MIDL. Для использования с C/C++ компилятор MIDL генерирует заголовочный файл с struct
определениями, соответствующими vtbl объявленных интерфейсов, и файл C, содержащий объявления GUID интерфейсов . Исходный код C++ для прокси-модуля также может быть сгенерирован компилятором MIDL. Этот прокси содержит заглушки методов для преобразования вызовов COM в удаленные вызовы процедур , чтобы включить DCOM для внепроцессной коммуникации.
MIDL может генерировать двоичную библиотеку типов (TLB), которую могут использовать другие инструменты для поддержки доступа из другого контекста.
Следующий код IDL объявляет кокласс с именем SomeClass
, который реализует интерфейс с именем ISomeInterface
.
coclass SomeClass { [по умолчанию] интерфейс ISomeInterface;};
Это концептуально эквивалентно следующему коду C++, где ISomeInterface — это чисто виртуальный класс , также известный как абстрактный базовый класс.
класс ISomeInterface {}; класс SomeClass : public ISomeInterface { };
В C++ объекты COM создаются с помощью CoCreateInstance
функции подсистемы COM, которая принимает CLSID и IID. SomeClass
могут быть созданы следующим образом:
ISomeInterface * interface_ptr = NULL ; HRESULT hr = CoCreateInstance ( CLSID_SomeClass , NULL , CLSCTX_ALL , IID_ISomeInterface , ( void ** ) & interface_ptr );
COM-объект использует подсчет ссылок для управления жизненным циклом объекта. Подсчет ссылок объекта контролируется клиентами с помощью методов IUnknown
AddRef
и Release
. COM-объекты отвечают за освобождение собственной памяти, когда счетчик ссылок падает до нуля. Некоторые контексты программирования (например, Visual Basic ) обеспечивают автоматический подсчет ссылок для упрощения использования объекта. В C++ интеллектуальный указатель может использоваться для автоматизации управления счетчиком ссылок.
Ниже приведены рекомендации по тому, когда следует вызывать AddRef и Release :
Для удаленных объектов не все вызовы счетчика ссылок отправляются по сети. Прокси-сервер сохраняет только одну ссылку на удаленный объект и поддерживает свой собственный локальный счетчик ссылок.
Чтобы упростить разработку COM для разработчиков C++, Microsoft представила ATL (Active Template Library) . ATL обеспечивает относительно высокоуровневую парадигму разработки COM. Она также защищает разработчиков клиентских приложений COM от необходимости напрямую поддерживать подсчет ссылок, предоставляя типы интеллектуальных указателей . Другие библиотеки и языки, которые поддерживают COM, включают Microsoft Foundation Classes , VC Compiler COM Support, [12] VBScript , Visual Basic , ECMAScript ( JavaScript ) и Borland Delphi .
COM — это независимый от языка двоичный стандарт, позволяющий использовать объекты в любом программном контексте, способном получить доступ к его двоичным интерфейсам.
Клиентское программное обеспечение COM отвечает за включение подсистемы COM, создание экземпляров и подсчет ссылок на объекты COM, а также за запрос объектов для поддерживаемых интерфейсов.
Компилятор Microsoft Visual C++ поддерживает расширения языка C++, называемые атрибутами C++ [13] , которые предназначены для упрощения разработки COM и минимизации шаблонного кода, необходимого для реализации серверов COM на C++. [14]
Первоначально метаданные библиотеки типов требовалось хранить в системном реестре. Клиент COM использовал информацию реестра для создания объектов.
COM без регистрации (RegFree) был представлен в Windows XP , чтобы позволить хранить метаданные библиотеки типов в качестве манифеста сборки либо как ресурс в исполняемом файле, либо в отдельном файле, установленном с компонентом. [15] Это позволяет устанавливать несколько версий одного и того же компонента на одном компьютере в разных каталогах. И это позволяет выполнять развертывание XCOPY . [16] Эта технология имеет ограниченную поддержку для серверов EXE COM [17] и не может использоваться для общесистемных компонентов, таких как MDAC , MSXML , DirectX или Internet Explorer .
Во время загрузки приложения загрузчик Windows ищет манифест. [18] Если он присутствует, загрузчик добавляет информацию из него в контекст активации. [16] Когда фабрика классов COM пытается создать экземпляр класса, сначала проверяется контекст активации, чтобы увидеть, можно ли найти реализацию для CLSID. Только если поиск не удался, сканируется реестр . [16]
Объект COM может быть создан без информации о библиотеке типов; только с путем к файлу DLL и CLSID. Клиент может использовать функцию COM DLL DllGetClassObject
с CLSID и IID_IClassFactory для создания экземпляра объекта фабрики . Затем клиент может использовать объекты фабрики CreateInstance
для создания экземпляра. [19] Это тот же процесс, который использует подсистема COM. [20] Если объект, созданный таким образом, создает другой объект, он сделает это обычным способом (используя реестр или манифест). Но он может создавать внутренние объекты (которые могут быть вообще не зарегистрированы) и выдавать ссылки на интерфейсы для них, используя свои собственные частные знания.
Объект COM может быть прозрачно создан и использован из того же процесса (in-process), через границы процесса (out-of-process) или удаленно по сети (DCOM). Внепроцессные и удаленные объекты используют маршалинг для сериализации вызовов методов и возврата значений через границы процесса или сети. Этот маршалинг невидим для клиента, который обращается к объекту, как если бы это был локальный внутрипроцессный объект.
В COM потоки рассматриваются с помощью концепции, известной как апартаменты . [21] Отдельный объект COM живет ровно в одном апартаменте, который может быть как однопоточным, так и многопоточным. В COM есть три типа апартаментов: однопоточный апартамент (STA) , многопоточный апартамент (MTA) и потоконейтральный апартамент (NA). Каждый апартамент представляет собой один механизм, посредством которого внутреннее состояние объекта может быть синхронизировано между несколькими потоками. Процесс может состоять из нескольких объектов COM, некоторые из которых могут использовать STA, а другие — MTA. Все потоки, получающие доступ к объектам COM, также живут в одном апартаменте. Выбор апартамента для объектов COM и потоков определяется во время выполнения и не может быть изменен.
Потоки и объекты, принадлежащие к одному апартаменту, следуют тем же правилам доступа к потокам. Вызовы методов, которые производятся внутри одного апартамента, поэтому выполняются напрямую без какой-либо помощи со стороны COM. Вызовы методов, производимые между апартаментами, достигаются посредством маршалинга. Это требует использования прокси и заглушек.
COM относительно сложен, особенно по сравнению с более современными компонентными технологиями, такими как .NET.
При инициализации STA создается скрытое окно, которое используется для маршрутизации сообщений между апартаментами и процессами. Это окно должно регулярно «прокачивать» свою очередь сообщений. Эта конструкция известна как « насос сообщений ». В более ранних версиях Windows невыполнение этого требования могло привести к общесистемным взаимоблокировкам. Эта проблема усложняется некоторыми API Windows, которые инициализируют COM как часть своей реализации, что приводит к «утечке» деталей реализации.
Подсчет ссылок в COM может вызвать проблемы, если два или более объектов имеют циклические ссылки . Проектирование приложения должно учитывать это, чтобы объекты не оставались бесхозными. Объекты также могут оставаться с активными счетчиками ссылок, если используется модель «приемника событий» COM. Поскольку объекту, который запускает событие, нужна ссылка на объект, реагирующий на событие, счетчик ссылок последнего никогда не достигнет нуля. Циклы ссылок обычно прерываются с помощью либо внеполосного завершения, либо разделенных идентификаторов. В технике внеполосного завершения объект предоставляет метод, который при вызове заставляет его сбрасывать свои ссылки на другие объекты, тем самым разрывая цикл. В технике разделенной идентификатора одна реализация предоставляет два отдельных объекта COM (также известных как идентификаторы). Это создает слабую ссылку между объектами COM, предотвращая цикл ссылок.
Поскольку внутрипроцессные компоненты COM реализованы в файлах DLL, а регистрация допускает только одну версию на CLSID, в некоторых ситуациях они могут подвергаться эффекту " DLL Hell ". Возможность COM без регистрации устраняет эту проблему для внутрипроцессных компонентов; COM без регистрации недоступен для внепроцессных серверов.
Если вызов функции CoGetClassObject находит объект класса, который должен быть загружен в DLL, CoGetClassObject использует экспортированную функцию DllGetClassObject из DLL.