Что такое COM — почему дизайн Windows COM до сих пор красив
· Обновлено: · Го Комура · COM, ActiveX, Разработка Windows
История изменений (1 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Первая публикация
Цитирование статьи(DOI: 10.5281/zenodo.21619623)
Статья заархивирована на Zenodo. Ниже приведены DOI, который всегда ведёт к последней версии, и DOI, закреплённый за версией, которую вы читаете.
Го Комура (2026). Что такое COM — почему дизайн Windows COM до сих пор красив. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21619623 https://comcomponent.com/ru/blog/2026/01/25/001-why-com-is-beautiful/
- DOI (последняя версия)
- 10.5281/zenodo.21619623
- DOI (эта версия)
- 10.5281/zenodo.21619624
Что такое COM?
COM (Component Object Model) — это бинарный контракт, по которому компоненты общаются друг с другом в Windows. Механизм, в котором компоненты общаются через интерфейсы как строгие контракты, поверх различий языка и компилятора. В основе лежит идея «писать код к контракту, а не к реализации».
Карта знаний этой статьи
COM — это двоичный контракт, по которому компоненты в Windows обмениваются друг с другом. IUnknown, который наследуют все интерфейсы, даёт проверку возможностей через QueryInterface и управление подсчётом ссылок через AddRef/Release. Уникальная идентификация компонента и интерфейса GUID-ами CLSID и IID избегает столкновений имён; проект, который добавляет новые интерфейсы, не меняя существующие, и проверяет их через QueryInterface, обеспечивает сосуществование версий. In-proc (DLL-сервер), загружаемый в тот же процесс, что и вызывающая сторона, требует совпадения разрядности и увлекает вызывающего при падении партнёра; Out-of-proc (LocalServer), работающий в отдельном процессе, свободен от этого ограничения, но требует готовности к ошибкам разрыва вроде RPC_E_DISCONNECTED, когда партнёр исчез. Выражение успеха и неудачи через возвращаемое значение HRESULT вместе с Proxy/Stub и предварительным определением контракта в IDL делают весь этот механизм независимым от границ языка и процесса.
flowchart LR
accTitle: Карта знаний: почему устройство COM изящно
accDescr: Схема, которая показывает, как COM на основе подсчёта ссылок через IUnknown и QueryInterface и идентификации по CLSID и IID реализует сильные стороны — двоичную совместимость, разделение интерфейсов, сосуществование версий и повторное использование через границу процесса — и как это связано с вариантами размещения In-proc и Out-of-proc, HRESULT и взаимодействием с .NET.
com["COM (Component Object Model)"]
iunknown["IUnknown"]
queryinterface["QueryInterface"]
com_reference_counting["подсчёт ссылок COM (AddRef/Release)"]
clsid["CLSID (Class ID)"]
iid["IID (идентификатор интерфейса)"]
hresult["HRESULT"]
com_binary_compatibility["бинарная совместимость COM"]
com_interface_versioning["версионирование интерфейсов COM"]
in_proc_com["In-proc COM (DLL-сервер)"]
com_localserver["COM LocalServer (внепроцессный COM-сервер)"]
bitness_match_requirement["требование совпадения разрядности"]
in_proc_crash_propagation["падение хоста вместе с In-proc crash"]
out_of_proc_com_disconnection_error["ошибка разрыва out-of-proc COM"]
proxy_stub["Proxy/Stub"]
idl["IDL (Interface Definition Language)"]
dotnet_com_interop["COM Interop в .NET"]
progid["ProgID (Programmatic Identifier)"]
activex["ActiveX"]
com -->|"использует"| iunknown
com -->|"использует"| queryinterface
com -->|"использует"| com_reference_counting
com -->|"использует"| clsid
com -->|"использует"| iid
com -->|"использует"| hresult
com -->|"реализует"| com_binary_compatibility
com_binary_compatibility -->|"требует"| iunknown
com -->|"реализует"| com_interface_versioning
com_interface_versioning -->|"требует"| queryinterface
com -.->|"использует"| in_proc_com
com -.->|"использует"| com_localserver
in_proc_com -->|"требует"| bitness_match_requirement
in_proc_com -->|"может вызвать"| in_proc_crash_propagation
com_localserver -->|"предотвращает"| in_proc_crash_propagation
com_localserver -.->|"может вызвать"| out_of_proc_com_disconnection_error
com -.->|"использует"| proxy_stub
com -->|"использует"| idl
dotnet_com_interop -.->|"использует"| progid
dotnet_com_interop -->|"использует"| queryinterface
dotnet_com_interop -->|"использует"| hresult
dotnet_com_interop -->|"использует"| com_reference_counting
activex -->|"требует"| com
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 23, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
Три ключевых элемента COM
В этой главе — составные части, из которых собран механизм COM. «Четыре сильные стороны COM» ниже — свойства, которые получаются, когда эти части складывают вместе; это не повтор одного и того же дважды. Соответствие сведём в таблицу в конце главы про сильные стороны.
1. Проектирование вокруг интерфейса
В COM «контракт раньше реализации». Объектом можно пользоваться, не зная внутренней реализации: достаточно знать опубликованные интерфейсы.
2. Идентификация по GUID (CLSID / IID)
Каждому компоненту и интерфейсу выдаётся глобально уникальный идентификатор (GUID), поэтому конфликта имён просто не возникает.
3. IUnknown
Базовый интерфейс, от которого наследуют все интерфейсы COM. Он даёт три операции.
| Метод | Роль |
|---|---|
QueryInterface |
Спрашивает, есть ли у объекта другой интерфейс |
AddRef |
Увеличивает счётчик ссылок |
Release |
Уменьшает счётчик ссылок (при нуле объект уничтожает себя) |
Когда эти три элемента складываются, между вызывающей стороной и реализацией остаётся только «интерфейс, опознанный по GUID, с фиксированным порядком методов». Ни язык, ни компилятор в этом контракте не фигурируют.
flowchart LR
accTitle: Бинарный контракт COM
accDescr: Вызывающая сторона на любом языке и реализация на любом языке сходятся в контракте, зафиксированном в бинарном виде. IID (GUID) однозначно задаёт контракт, порядок методов начинается с трёх методов IUnknown, у каждого метода свои типы аргументов и возврата и соглашение о вызовах.
subgraph CALLER["Вызывающая сторона — язык любой"]
A1["Приложение на C++"]
A2["Приложение на C#"]
A3["VBA / Python и др."]
end
CONTRACT["Контракт (то, что зафиксировано в бинарном виде)<br/>· IID (GUID) однозначно задаёт, какой это контракт<br/>· порядок методов начинается с трёх методов IUnknown<br/>· типы аргументов и возврата и соглашение о вызовах для каждого метода"]
subgraph IMPL["Реализация — язык любой"]
B1["Компонент, написанный на C++"]
B2["Компонент, написанный на C#"]
end
A1 --> CONTRACT
A2 --> CONTRACT
A3 --> CONTRACT
CONTRACT --> B1
CONTRACT --> B2
Рис. 1: Бинарный контракт COM. Ни язык вызывающей стороны, ни язык реализации в контракт не входят, поэтому любую сторону можно заменить без пересборки другой.
«Писать код к контракту» на примере
На словах это хуже видно, поэтому свернём к минимуму кода. Вызывающая сторона пользуется компонентом ICalcService, который только складывает числа, вообще не зная реализации.
Сначала C++ (сырой COM). Определение ICalcService здесь — сам контракт. Написана реализация на C++ или на C# — в коде вызывающей стороны этого нигде нет.
#include <objbase.h>
// Определение контракта. Обычно такой заголовок генерируется из IDL.
// Наследует IUnknown, поэтому QueryInterface / AddRef / Release есть всегда.
struct __declspec(uuid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001"))
ICalcService : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Add(int a, int b, int* result) = 0;
};
// Расширенный контракт, добавленный позже. GUID другой.
struct __declspec(uuid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B002"))
ICalcServiceEx : public ICalcService
{
virtual HRESULT STDMETHODCALLTYPE Multiply(int a, int b, int* result) = 0;
};
// CLSID компонента-реализации. Обычно определяется в заголовке, сгенерированном из IDL.
static const CLSID CLSID_CalcService =
{ 0x1C9B6F4D, 0x1E9A, 0x4E61, { 0x9A, 0x4F, 0x6A, 0x0F, 0x1D, 0x2D, 0x9A, 0x11 } };
// Вызывающая сторона
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) { return hr; }
ICalcService* calc = nullptr;
hr = CoCreateInstance(CLSID_CalcService, nullptr, CLSCTX_ALL,
__uuidof(ICalcService), reinterpret_cast<void**>(&calc));
// Вернувшийся указатель уже с AddRef (счётчик ссылок равен 1)
if (SUCCEEDED(hr))
{
int sum = 0;
hr = calc->Add(1, 2, &sum); // Можно вызвать, не зная реализации
// «Есть ли ещё и расширенный контракт?» — спрашиваем в runtime = сосуществование версий
ICalcServiceEx* calcEx = nullptr;
if (SUCCEEDED(calc->QueryInterface(__uuidof(ICalcServiceEx),
reinterpret_cast<void**>(&calcEx))))
{
// Сюда попадаем, только если это новая версия компонента
calcEx->Release(); // Кто получил указатель, тот и отдаёт
}
// Если интерфейса нет, возвращается E_NOINTERFACE — старая версия продолжает работать
calc->Release(); // Счётчик ссылок становится 0, объект уничтожается
}
CoUninitialize();
Три места, на которые стоит смотреть.
AddRefпочти никогда не пишут руками. ИCoCreateInstance, иQueryInterfaceуже сделалиAddRefна возвращаемый указатель. Правило такое: кто получил указатель, тот вызываетRelease. ЯвныйAddRefнужен только когда тот же указатель начинают держать ещё в одном месте.- Отказ
QueryInterface— не авария. «Этого контракта нет» (E_NOINTERFACE) — штатный ответ. Именно он позволяет добавлять новые возможности, не ломая старый компонент. - Все возвраты —
HRESULT. Успех и неудача передаются кодом возврата, а не исключением: так договариваются, чтобы пересечь границу языка. Как бросать исключения, у каждого языка своё; целое как код возврата умеет разобрать кто угодно.
flowchart TB
accTitle: Правило счётчика ссылок и Release
accDescr: CoCreateInstance и QueryInterface возвращают указатель, на который уже сделан AddRef, поэтому получившая сторона в конце вызывает Release, и когда счётчик ссылок становится 0, объект уничтожается.
api["CoCreateInstance или QueryInterface"] --> ret["Возвращается указатель с уже сделанным AddRef"]
ret --> use["Получившая сторона использует"]
use --> rel["Получившая сторона вызывает Release"]
rel --> zero["При нуле счётчика объект уничтожается"]
use -.->|"Явный AddRef"| add["Тот же указатель начинают держать ещё в одном месте"]
Рис. 2: Кто получил указатель, тот вызывает Release. Явный AddRef — только когда мест хранения становится больше.
Тот же контракт с C# выглядит так. Одинаковый GUID означает один и тот же контракт, поэтому реализация на C++ не мешает.
using System;
using System.Runtime.InteropServices;
// Пишем тот же GUID, что и на стороне C++. Это объявление «тот же контракт».
[ComImport]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
int Add(int a, int b);
}
// Вызывающая сторона
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService")
?? throw new InvalidOperationException("COM-сервер не зарегистрирован.");
object server = Activator.CreateInstance(t)!;
try
{
var calc = (ICalcService)server; // Это приведение соответствует QueryInterface
int sum = calc.Add(1, 2);
Console.WriteLine(sum); // 3
}
finally
{
Marshal.ReleaseComObject(server); // Соответствует Release
}
На стороне C# у Add возвращаемый тип int, потому что COM Interop в .NET делает фиксированное преобразование: последний аргумент [out, retval] становится возвращаемым значением, а неуспешный HRESULT превращается в исключение. AddRef/Release с вызывающей стороны тоже пропадают из виду — не потому что их нет, а потому что тонкая обёртка .NET (RCW) вызывает их за вас. Один и тот же контракт можно писать так, как естественно для языка — вот как бинарный контракт выглядит на практике.
flowchart TB
accTitle: Как запись на C# соответствует COM
accDescr: В C# приведение к интерфейсу соответствует QueryInterface, Marshal.ReleaseComObject — Release, неуспешный HRESULT превращается в исключение, а AddRef и Release за вызывающую сторону вызывает RCW.
cs["Код на C#"] --> cast["Приведение к типу"]
cs --> rco["ReleaseComObject"]
cs --> hrx["Неуспешный HRESULT"]
cast --> qi["Соответствует QueryInterface"]
rco --> rel["Соответствует Release"]
hrx --> ex["Преобразуется в исключение"]
cs -.-> rcw["RCW ведёт счётчик ссылок"]
Рис. 3: Даже при том же контракте на стороне C# запись становится естественной для языка; преобразование берут на себя RCW и слой взаимодействия.
Четыре сильные стороны COM
1. Бинарная совместимость
Собранный однажды компонент можно использовать повторно независимо от языка программирования и runtime. Вызвать из C# или Python COM-компонент, написанный на C++, — обычная практика.
2. Разделение интерфейса и реализации
Реализация полностью скрыта, наружу выходит только контракт, поэтому внутренности можно менять свободно: на вызывающую сторону это не влияет.
3. Сосуществование версий
Базовый приём добавить возможности, сохранив обратную совместимость, — добавлять новые интерфейсы. Новую функциональность дают, не меняя старые интерфейсы.
Если скомбинировать старую и новую вызывающую сторону со старым и новым компонентом, из четырёх сочетаний три работают как есть, а оставшееся просто отвечает «этого нет».
flowchart LR
accTitle: Сосуществование версий через QueryInterface
accDescr: Старая и новая вызывающая сторона в сочетании со старой и новой версией компонента. Три сочетания работают как есть, четвёртое отвечает E_NOINTERFACE и продолжает работу со старой функциональностью.
OLDC["Старая вызывающая сторона<br/>знает только ICalcService"]
NEWC["Новая вызывающая сторона<br/>спрашивает ICalcServiceEx"]
OLDS["Старая версия компонента<br/>реализует только ICalcService"]
NEWS["Новая версия компонента<br/>реализует оба"]
OLDC -->|"QueryInterface(IID_ICalcService)<br/>S_OK"| OLDS
OLDC -->|"QueryInterface(IID_ICalcService)<br/>S_OK — после обновления не ломается"| NEWS
NEWC -->|"QueryInterface(IID_ICalcServiceEx)<br/>S_OK — доступны новые возможности"| NEWS
NEWC -.->|"QueryInterface(IID_ICalcServiceEx)<br/>E_NOINTERFACE — продолжаем со старой функциональностью"| OLDS
Рис. 4: Если существующие интерфейсы не трогать и добавить новый IID, работают любые сочетания старого и нового. Пунктир — штатный ответ «этого контракта нет».
4. Повторное использование через границы процесса
У COM-компонента два места размещения. In-proc (DLL-сервер) загружается как DLL в тот же процесс, что и вызывающая сторона. Out-of-proc (EXE-сервер, LocalServer) запускается как EXE в отдельном процессе. Снаружи вызывающий код выглядит одинаково.
flowchart TB
accTitle: Два места размещения компонента
accDescr: От одного и того же снаружи вызывающего кода можно прийти и к In-proc, который загружается как DLL в тот же процесс, и к Out-of-proc, который запускается как EXE в отдельном процессе.
caller["Код вызывающей стороны (снаружи одинаковый)"] --> ip["In-proc (DLL-сервер)"]
caller --> op["Out-of-proc (EXE-сервер)"]
ip -.-> ipd["Загружается как DLL в тот же процесс"]
op -.-> opd["Запускается как EXE в отдельном процессе"]
Рис. 5: Мест размещения два — In-proc и Out-of-proc, но снаружи вызывающий код не меняется.
| In-proc (DLL-сервер) | Out-of-proc (EXE-сервер) | |
|---|---|---|
| Где выполняется | В том же процессе, что и вызывающая сторона | В отдельном процессе |
| Как на самом деле идёт вызов | Прямой вызов через указатель на функцию | Аргументы перепаковываются (маршалинг) и уходят через межпроцессное взаимодействие |
| Скорость | Быстро | Медленнее из-за IPC |
| Если вторая сторона упала | Вызывающая сторона падает вместе с ней | Вызывающая сторона остаётся жива (вызов возвращается как неудача) |
| Разрядность (32/64-bit) | Без совпадения не загрузится | Может отличаться |
Через Out-of-proc COM (EXE-сервер) можно безопасно вызывать функциональность другого процесса. Последняя строка — «разрядность может отличаться» — на практике бывает важна. Конкретную сборку, где 32-bit приложение вызывает 64-bit DLL, разбираем в статье «Пример COM-моста: вызов 64-битной DLL из 32-битного приложения».
Но отдельный процесс значит и то, что вторая сторона может исчезнуть в любой момент. Если процесс-сервер падает или завершается, вызывающая сторона получает неудачу вроде следующей. Это не «сломалось», а «второй стороны уже нет» — ошибки, характерные именно для Out-of-proc.
| Код ошибки | Смысл |
|---|---|
RPC_E_DISCONNECTED |
Объект, к которому обратились, отсоединён от клиента (объекта на той стороне уже нет) |
RPC_S_SERVER_UNAVAILABLE |
До сервера (процесса) на той стороне достучаться нельзя |
В In-proc «упал он — упал и я», и думать об этом не нужно. В Out-of-proc в проектирование входят восстановление: перезапуск и повторное подключение. Точнее думать так: взамен на безопасность появляется дополнительная работа.
flowchart TB
accTitle: Что происходит, когда процесс на той стороне исчезает
accDescr: В Out-of-proc, если процесс-сервер падает или завершается, вызов возвращается как неудача; вызывающая сторона остаётся жива, поэтому в проект нужно закладывать восстановление — перезапуск и повторное подключение.
gone["Крах или завершение процесса-сервера"] --> fail["Вызов возвращается как неудача"]
fail --> alive["Вызывающая сторона остаётся жива"]
alive --> rec["К восстановлению: перезапуск и повторное подключение"]
fail -.-> mean["Не «сломалось», а «второй стороны уже нет»"]
Рис. 6: В Out-of-proc падение той стороны не утягивает вызывающую, но взамен в проект входит восстановление.
Как три элемента связаны с четырьмя сильными сторонами
Как сказано в начале, «три элемента» — составные части, «четыре сильные стороны» — результат. Если выписать, какой элемент какую сторону держит, роли того, что казалось перекрытием, становятся ясны.
| Сильная сторона | Какие элементы в основном работают |
|---|---|
| 1. Бинарная совместимость | Проектирование вокруг интерфейса + IUnknown (порядок вызовов зафиксирован на бинарном уровне) |
| 2. Разделение интерфейса и реализации | Проектирование вокруг интерфейса (наружу выходит только контракт) |
| 3. Сосуществование версий | GUID + QueryInterface (другой контракт можно запросить в runtime) |
| 4. Повторное использование через границы процесса | Проектирование вокруг интерфейса (от контракта можно отвязать даже место реализации) |
COM до сих пор в строю
Его часто считают «старой технологией», но COM — механизм, который до сих пор используется в самой основе Windows.
Где встречается COM
- Расширения Проводника (контекстное меню, предпросмотр)
- Автоматизация Office (внешнее управление Excel и Word)
- Взаимодействие с .NET (COM Interop)
- Существующие системы, включая ActiveX
- DirectX, Windows Shell API и множество других Windows API
Даже если кажется, что «меня это не касается», пока вы разрабатываете под Windows, COM где-нибудь встретится.
Итог
В центре дизайна COM — независимость от языка, процесса и реализации. Нейтральные к языку интерфейсы, однозначная идентификация и версионирование через GUID, счётчик ссылок через IUnknown и механизм, который прозрачно закрывает межпроцессное взаимодействие.
flowchart TB
accTitle: Центр дизайна COM
accDescr: Нейтральное к языку устройство интерфейсов, однозначная идентификация и версионирование через GUID, счётчик ссылок IUnknown и прозрачное межпроцессное взаимодействие — четыре механизма, которые держат независимость от языка, процесса и реализации.
i1["Нейтральное к языку устройство"] --> core["Независимость от языка, процесса и реализации"]
i2["Однозначная идентификация через GUID"] --> core
i3["Счётчик ссылок IUnknown"] --> core
i4["Прозрачное межпроцессное взаимодействие"] --> core
Рис. 7: Четыре механизма вместе держат центр COM — независимость от языка, процесса и реализации.
Слово «красиво» субъективно, но основания для такой оценки конкретны. Если рядом поставить задачу, которую COM брался решить, и сам способ решения, получается так.
| Ограничение тогда (и сейчас) | Ответ COM |
|---|---|
| У C++ нет стандартного бинарного соглашения (ABI), разные компиляторы уже не дают повторно использовать код. Не совпадают ни манглинг имён, ни раскладка объекта в памяти | Соглашением сделали только «порядок в таблице виртуальных функций». Сузили до этой одной точки — и язык с компилятором перестали иметь значение |
| Замена библиотеки заставляет пересобирать всё, что ею пользуется | Пока контракт (интерфейс) не меняется, пересборка не нужна. Можно подменить бинарник как есть |
| Хочется добавить возможности, не ломая существующую вызывающую сторону | Интерфейсы добавляют, не меняя, и в runtime спрашивают через QueryInterface, «есть ли он» |
| Конфликт имён (под одним именем класса живут разные вещи) | Однозначная идентификация по GUID. Согласовывать имена стало незачем |
| Вызов функций другого процесса или другой машины полностью меняет то, как пишется вызов | Между сторонами ставят Proxy/Stub и сохраняют форму кода вызывающей стороны |
То есть дизайн COM начался не с вопроса «как сделать красиво». Нынешняя форма — результат того, что по одному снимали конкретные препятствия, которые мешали повторному использованию. Проектов, где соответствие ограничений и решений прослеживается так прямо, немного.
И этот способ решения без изменений остался в современной компонентной разработке.
| COM | Современный аналог |
|---|---|
| Контракт сначала фиксируют в IDL и из него генерируют код обеих сторон | Клиент и сервер генерируют из схемы OpenAPI или Protocol Buffers |
| Существующие интерфейсы не меняют, добавляют новые | В Protocol Buffers поля добавляют, не переиспользуя номера; API версионируют |
| Язык реализации не важен (бинарный контракт) | Язык реализации не важен (контракт сообщений через сеть) |
| Proxy/Stub прячет границу процесса | Клиентский stub RPC прячет границу сети |
Различается только тип границы (процессы на одной машине или сеть) и форма контракта (бинарный или текст/схема). Если понять COM, в проектировании интерфейсов между микросервисами вопросы «почему контракт фиксируют заранее» и «почему нельзя удалять существующие поля» укладываются не как мода, а как необходимость.
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Ловушки регистрации и bitness при разработке COM/OCX/ActiveX
Практический разбор типичных ловушек COM, OCX и ActiveX: разрядность 32/64-бит, Visual Studio 2022, regsvr32 и Regasm, права администрато...
Что продумать перед заказом разработки Windows-приложения
Перед заказом разработки Windows-приложения разберём, что стоит прояснить: доработка существующего ПО, интеграция с оборудованием, COM/Ac...
Странная любовь разработчика, или Как я перестал беспокоиться и полюбил Windows
Windows — штука хлопотная. Но эти хлопоты — как раз от того, что это ОС, которая тащила на себе реальную работу.
Office 2024/Microsoft 365: почему не работает ActiveX и как это диагностировать
Когда ActiveX не работает в Office 2024/Microsoft 365, разбираем порядок диагностики: отключение по умолчанию, 32-бит/64-бит, регистрация...
Что такое Reg-Free COM: механизм использования COM без регистрации
Разбираем основы Reg-Free COM: роль контекста активации и манифестов, преимущества, ограничения и критерии выбора в реальных проектах.
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Миграция ActiveX
Решения о сохранении, обёртке или замене компонентов COM / ActiveX / OCX.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Использование и перенос существующих активов
Разобраться в дизайне COM и в модели совместимости — естественная точка входа, если вы думаете, как использовать существующие Windows-активы.
Технические консультации и ревью дизайна
Если нужно выстроить направление с учётом IUnknown, GUID и границ компонентов, это ведёт к технической консультации и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Что такое COM?
- COM (Component Object Model) — бинарный контракт, по которому компоненты общаются друг с другом в Windows. Это механизм, в котором компоненты общаются через интерфейсы как строгие контракты, поверх различий языка и компилятора. В основе лежит идея «писать код к контракту, а не к реализации».
- Что такое IUnknown?
- Базовый интерфейс, от которого наследуют все интерфейсы COM. Он даёт три операции: QueryInterface спрашивает, есть ли у объекта другой интерфейс; AddRef увеличивает счётчик ссылок; Release уменьшает счётчик и, когда тот становится нулём, объект уничтожает себя. Временем жизни объектов COM управляет именно этот счётчик ссылок.
- COM ещё используют?
- Да. COM — механизм, который до сих пор используется в самой основе Windows. Он появляется в расширениях Проводника (контекстное меню и предпросмотр), в автоматизации Office (Excel и Word), во взаимодействии с .NET (COM Interop), в существующих системах, включая ActiveX, в DirectX, Windows Shell API и во многих других местах. Пока вы разрабатываете под Windows, COM где-нибудь встретится.
- В чём сильные стороны COM?
- Их четыре. Бинарная совместимость: собранный однажды компонент можно использовать повторно независимо от языка и runtime. Разделение интерфейса и реализации: реализация скрыта, наружу выходит только контракт. Сосуществование версий: новые интерфейсы добавляют, не ломая обратную совместимость. И безопасный вызов функциональности другого процесса через Out-of-proc COM (EXE-сервер).
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.