Что такое Reg-Free COM — механизм использования COM без регистрации

· · COM, Reg-Free COM, Registration-Free COM, Разработка Windows, Устаревшие технологии

В проектах с COM / ActiveX / OCX при каждом развёртывании и обновлении всплывает одна и та же грязь.

  • нужен regsvr32
  • почти всегда требуются права администратора
  • возникает конфликт с другой версией, установленной другим приложением
  • удаление приложения задевает и другие продукты
  • работает на машине разработчика, но не работает в чистом окружении

Значительно уменьшить эту трясину помогает Reg-Free COM. Правда, вопреки названию, это не «волшебство, которое полностью избавляет от всех хлопот с COM». Оно избавляет в первую очередь от хлопот, связанных с глобальной регистрацией. Сложности с разрядностью (bitness), зависимыми DLL, типовыми библиотеками и моделью потоков никуда не исчезают.

В этой статье Reg-Free COM рассматривается прежде всего в контексте использования COM DLL / OCX в Windows-десктоп-приложениях с их хранением локально для приложения.

1. Сначала — вывод (коротко)

Начнём с грубой, но полезной формулировки.

  • Reg-Free COM — это способ хранить регистрационную информацию COM не в реестре, а в манифесте
  • Во время выполнения при разрешении CoCreateInstance или CLSIDFromProgID сначала проверяется контекст активации (activation context)
  • Благодаря этому COM DLL / OCX можно хранить private для каждого приложения
  • Основные преимущества — это удобство XCOPY-распространения, более лёгкое предотвращение конфликтов версий и меньший риск поломки при удалении
  • Однако проблема 32-бит / 64-бит никуда не исчезает. Здесь одним усердием её не преодолеть
  • Кроме того, отдельно нужно продумывать зависимые DLL, типовые библиотеки, проектные ссылки и зависимость от нестандартной регистрации
  • На практике это отлично подходит, когда нужно разместить рядом с приложением COM-компоненты, предназначенные только для него

Иными словами, Reg-Free COM — это механизм, который возвращает активацию COM на уровень отдельного приложения.

2. Что этот текст понимает под Reg-Free COM

Reg-Free COM — сокращение от Registration-Free COM. По-японски это иногда описывают как «COM без регистрации».

«Без регистрации» здесь означает, что для использования COM не требуется полностью полагаться на глобальную регистрацию в реестре — HKCR / CLSID / InprocServer32 и подобное. Это не значит, что сам COM исчезает, и не значит, что GUID становится не нужен.

Основные предметы рассмотрения в этой статье — вот такие:

  • нативные COM DLL
  • COM-серверы на базе ATL
  • ActiveX / OCX
  • COM-взаимодействие на базе .NET Framework
  • публикация через COM host в .NET 5+ / .NET 8

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

  1. Reg-Free COM — это разговор об «активации»
  2. Распространение типовой информации и настройка проектных ссылок могут оставаться отдельным вопросом

Если смешать эти темы, разговор становится куда более мутным.

3. Сначала — общая картина на одной странице

Быстрее всего сначала взглянуть на общую картину на одной странице.

MyApp.exeApplication ManifestdependentAssemblyComponent / Assembly Manifestfile / comClass / typelibVendorControl.dll / .ocxActivation ContextCLSIDFromProgID / CoCreateInstance

В обычном COM при вызове CoCreateInstance для определения того, какую DLL загрузить, происходит обход реестра. В Reg-Free COM перед этим сначала проверяется текущий активный контекст активации, и разрешение выполняется по манифестной информации, записанной в нём.

Из-за этого приложениям A и B на одной машине становится проще работать, используя разные версии COM-компонентов одного семейства. Это немного возвращает культуру совместного использования COM в сторону локальности для конкретного приложения.

4. Почему обычное распространение COM обычно оказывается тяжёлым

Обычное распространение COM тяжело не столько из-за самого COM, сколько из-за допущения о глобальной регистрации.

Чтобы использовать класс COM, требуется примерно такая информация.

Информация Роль
CLSID GUID, однозначно идентифицирующий класс
ProgID Удобное для человека имя
InprocServer32 Какую DLL загружать
ThreadingModel Допущения вроде Apartment / Both
TypeLib Информация о типах

Когда всё это попадает в реестр, это удобно для машины в целом, потому что легко использовать совместно из нескольких приложений.

Однако на практике это совместное использование даёт обратный эффект.

  • установка одного продукта перезаписывает COM-регистрацию другого продукта
  • деинсталлятор «думает, что удаляет только своё», а на деле ломает общий COM
  • регистрация, которая случайно присутствует на машине разработчика, отсутствует на боевой машине
  • регистрации для 32-бит и 64-бит не согласуются друг с другом, и только симптомы жутковато расходятся

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

5. Как работает Reg-Free COM

5.1 Указываем зависимости в манифесте приложения

Сначала приложение указывает в манифесте приложения, от каких side-by-side сборок оно зависит.

Этот манифест можно оформить любым из двух способов:

  • разместить рядом с EXE-файлом, например как MyApp.exe.manifest
  • встроить в EXE как ресурс

На практике часто выбирают так: внешний файл — если важно, чтобы распространение и замена были наглядными, встраивание — если в приоритете надёжность и простота распространения.

Отметим, что если существуют одновременно и внешняя, и встроенная версия, приоритет имеет манифест на файловой системе.

5.2 Описываем информацию о COM в манифесте компонента

Далее COM-сторона переносит информацию, которая раньше находилась в реестре, в манифест компонента.

Сюда входит, например, такая информация:

  • comClass
  • clsid
  • progid
  • threadingModel
  • typelib
  • при необходимости — proxy / stub, классы окон и т. п.

То есть идея в том, чтобы описывать «облик» COM на XML вместо реестра.

Этот манифест также можно оформить любым из двух способов:

  • разместить отдельным файлом рядом с DLL
  • встроить в DLL как ресурс

На практике встраивание в DLL в качестве private assembly обычно приводит к меньшему числу проблем. Работа с отдельным файлом нагляднее, но легче споткнуться о соответствие между именем файла и assemblyIdentity, о место размещения и о забытые копирования.

5.3 Во время выполнения сначала проверяется контекст активации

Здесь заключена суть Reg-Free COM.

Когда приложение вызывает CLSIDFromProgID или CoCreateInstance, среда выполнения COM смотрит на активный контекст активации. Если там есть нужная информация ProgID → CLSID и CLSID → DLL, разрешение выполняется без обращения к реестру.

И наоборот, если манифесту не хватает нужной информации, происходит откат к обычному разрешению на основе регистрации. Из-за такого поведения возникает ловушка: на машине разработчика приложение случайно работает. Кажется, что переход на Reg-Free состоялся, а на самом деле дело в том, что помогает локальная регистрация.

Это самая коварная ловушка Reg-Free COM.

6. В чём польза

На практике преимущества Reg-Free COM видны вполне отчётливо.

6.1 Удобное XCOPY-распространение

Все нужные файлы можно собрать вместе в папке приложения, что облегчает установщик и процедуру регистрации. Разумеется, если запись идёт в Program Files, вопрос прав доступа остаётся отдельной темой, но по крайней мере административную работу, связанную с регистрацией COM, обычно удаётся сократить.

6.2 Легче сократить конфликты версий

Даже если на одной машине присутствует несколько версий COM-компонента, становится проще развести используемую версию по приложениям. Заметно легче избежать неприятности вроде «поведение внезапно изменилось из-за установки другого продукта».

6.3 Часто не требует крупных изменений в существующем коде

Reg-Free COM — это механизм, который меняет способ разрешения, а не в корне переделывает то, как существующий код делает вызовы. Поэтому при удачном сценарии его можно внедрить, почти не трогая код на стороне CoCreateInstance.

6.4 Удаление и откат становятся проще

Поскольку всё замкнуто на уровне приложения, обновление и откат становятся заметно более прямолинейными. Утрируя, можно сказать, что легче принять подход «просто заменить всю папку целиком».

7. Где это подходит, а где — нет

7.1 Ситуации, где это подходит

В следующих случаях Reg-Free COM оказывается весьма сильным решением.

Ситуация Насколько подходит
Нужно поставлять COM DLL / OCX, предназначенные только для конкретного приложения Очень хорошо
Нужно, чтобы на одном ПК сосуществовало несколько версий Очень хорошо
Нужно избежать проблем с регистрацией компонентов от поставщика Хорошо
Нужно использовать ActiveX / OCX приватно в существующем десктопном приложении Хорошо
Нужно облегчить распространение, не меняя сильно существующие вызовы Хорошо

Как правило, это хорошо сочетается с бизнес-десктоп-приложениями, инструментами интеграции с оборудованием и существующими наработками на VB6 / MFC / WinForms.

7.2 Ситуации, где это не подходит или требует осторожности

С другой стороны, есть случаи, которые стоит рассматривать осторожнее.

Ситуация Комментарий
Нужно, чтобы COM был общим для всей машины Польза Reg-Free невелика
Разрядность (bitness) не совпадает Reg-Free это не решает
Сильная зависимость от нестандартной регистрационной информации или собственного установщика Трудно выразить в манифесте
Не продумано распространение зависимых DLL или среды выполнения VC++ В итоге споткнётесь в другом месте
Проектные инструменты или настройка ссылок в IDE предполагают наличие реестра Нужен отдельный порядок эксплуатации

Особенно важен последний пункт. Reg-Free COM помогает с активацией во время выполнения, но не меняет одним махом то, на что рассчитан UI настройки ссылок на этапе проектирования.

8. Распространённые заблуждения

8.1 «С Reg-Free COM проблема разрядности исчезает»

Не исчезает. 32-битный процесс может загружать только 32-битные in-proc COM DLL, а в 64-битный попадают только 64-битные DLL. В этом отношении Reg-Free ничего не меняет по сравнению с прежним подходом.

8.2 «С Reg-Free COM реестр вообще не используется»

Это тоже неверно. Если манифесту не хватает нужной информации, происходит откат к обычному разрешению на основе регистрации. Поэтому успешный запуск на машине разработчика ещё не означает, что конфигурация Reg-Free корректна.

8.3 «С Reg-Free COM вопрос типовых библиотек тоже решается автоматически»

Здесь верно лишь наполовину. В манифесте действительно можно указать информацию typelib, но работа с типовой информацией — настройка ссылок в VBA, #import в C++, генерация проектных ссылок на стороне .NET — обычно требует отдельного продумывания.

Reg-Free COM в первую очередь про то, чтобы приложение вообще запускалось. Как вести типизированную разработку — это следующий, отдельный вопрос.

8.4 «С Reg-Free COM подойдёт любой ActiveX / OCX как есть»

Это тоже опасное заблуждение. Если компонент опирается на стандартную регистрационную информацию COM, продвигаться легко, но если он сильно зависит от собственных настроек реестра, дополнительной установки, обработки лицензий или набора других модулей, переход на Reg-Free резко усложняется.

8.5 «Reg-Free COM в .NET Framework и .NET 8 — примерно одно и то же»

Сходство есть, но набор инструментов заметно различается. Контекст .NET Framework + RegAsm и контекст .NET 5+ / .NET 8 + comhost — это разные площадки, даже если речь об одном и том же COM.

9. Различия между нативным COM, .NET Framework, .NET 5+ и .NET 8

Здесь легко всё перепутать, поэтому разберём отдельно.

Направление Краткое описание
Нативные COM DLL / OCX В основе — связка манифеста приложения и манифеста компонента
COM-взаимодействие на базе .NET Framework Помимо манифеста приложения в стиле Win32, нужен ещё и манифест на стороне управляемого компонента
Публикация COM в .NET 5+ / .NET 8 EnableComHosting создаёт COM host, а EnableRegFreeCom позволяет сгенерировать манифест для Reg-Free

9.1 COM на базе .NET Framework

В COM на базе .NET Framework образуется двухуровневая структура: манифест приложения в стиле Win32 на стороне COM-приложения и манифест компонента на стороне управляемого компонента.

Это чуть сложнее, чем в случае нативного COM. Разговор снова слегка «увязает» в тот момент, когда «Reg-Free COM вроде понятен, но как только в дело вступает managed component, внезапно появляется ещё один манифест».

9.2 Публикация COM в .NET 5+ / .NET 8

В .NET 5+ / .NET 8 точкой входа для публикации COM становится *.comhost.dll. Если дополнительно указать EnableRegFreeCom=true, будет сгенерирован side-by-side манифест для Reg-Free COM.

Но и здесь важно, что Reg-Free COM и стратегия работы с TLB — разные вопросы. .NET Core / .NET 5+ — это не тот мир, где TLB естественным образом появляется из сборки, как было во времена .NET Framework. Если требуется типизированное использование, безопаснее отдельно продумать генерацию, встраивание и регистрацию TLB.

10. Набросок минимальной конфигурации

Здесь приведён минимальный набросок того, как MyApp.exe использует Vendor.CameraControl.dll через Reg-Free COM.

10.1 Набросок структуры файлов

MyApp.exe
MyApp.exe.manifest
Vendor.CameraControl.dll
Vendor.Helper.dll

В примере выше предполагается, что манифест компонента встроен в саму DLL. Можно оформить его и отдельным файлом, но для начала со встраиванием проще навести порядок.

10.2 Набросок манифеста приложения

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="KomuraSoft.MyApp"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Vendor.CameraControl.Asm"
        version="1.0.0.0"
        processorArchitecture="amd64" />
    </dependentAssembly>
  </dependency>
</assembly>

10.3 Набросок манифеста компонента

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Vendor.CameraControl.Asm"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <file name="Vendor.CameraControl.dll">
    <comClass
      clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
      progid="Vendor.CameraControl.1"
      threadingModel="Apartment"
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}" />

    <typelib
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}"
      version="1.0"
      helpdir="" />
  </file>
</assembly>

В этом примере по-настоящему важны не детали XML, а то, что dependentAssembly на стороне приложения и assemblyIdentity на стороне компонента совпадают. Если здесь возникает рассогласование, мучиться приходится довольно тихо и незаметно.

Отметим, что GUID и имена выше — лишь пример для иллюстрации. На практике их нужно правильно указывать в соответствии с CLSID / TLBID / ProgID и моделью потоков, которые реально предоставляет компонент.

11. Типичные ловушки

11.1 Работает на машине разработчика, но не работает на целевом компьютере

В первую очередь стоит заподозрить сценарий, при котором на самом деле помогала регистрация в реестре. Проверку Reg-Free COM по возможности безопаснее проводить в чистом окружении.

11.2 Приложение не запускается с ошибкой «side-by-side configuration is incorrect»

Эта группа ошибок возникает из-за несогласованности манифеста, нехватки зависимых DLL, отсутствия среды выполнения VC++, несовпадения архитектуры и тому подобного. Одного лишь текста ошибки на поверхности недостаточно, поэтому стандартный способ разобраться — журнал событий и sxstrace.

11.3 Рассогласование между манифестом компонента и манифестом приложения

  • отличается name
  • отличается version
  • отличается processorArchitecture
  • манифест, который вы думали, что скопировали, на самом деле устарел

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

11.4 Забытые зависимые DLL

Если ограничиться только Vendor.CameraControl.dll и на этом успокоиться, легко упустить Vendor.Helper.dll, загружаемый следом, среду выполнения VC++ и proxy / stub DLL. Reg-Free COM сокращает проблемы регистрации COM, но не устраняет заодно и проблемы разрешения нативных зависимостей.

11.5 Откладывание вопросов типовой библиотеки и настройки ссылок

Даже если активация во время выполнения проходит успешно, как только появляется потребность

  • в раннем связывании из VBA,
  • в #import из C++,
  • в создании проектного interop на стороне .NET,

требуется способ распространения типовой информации. Reg-Free COM не настраивает всё это автоматически, поэтому важно рассматривать runtime и design-time отдельно друг от друга.

12. Итоги

Если сформулировать Reg-Free COM одной фразой, это механизм, который переносит регистрационную информацию COM с уровня всей машины на уровень отдельного приложения.

Благодаря этому появляются такие преимущества:

  • легче хранить COM DLL / OCX локально для приложения
  • легче сокращать конфликты версий
  • легче упрощать распространение и откат

В то же время по-прежнему важны:

  • разрядность 32-бит / 64-бит
  • зависимые DLL
  • TLB / настройка ссылок
  • зависимость от нестандартной регистрации
  • проверка в чистом окружении

Поэтому базовый подход при внедрении Reg-Free COM такой:

  1. Чётко принять, что это вопрос активации
  2. Разделять вопросы runtime и design-time
  3. Проверять в чистом окружении
  4. Сначала согласовать разрядность и зависимые DLL

Если смотреть на всё в таком порядке, риск проблем заметно снижается.

13. Похожие статьи

14. Источники

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Если вам досталась система без исходного кода и без документации — практический план, как сопровождать её, не останавливая работу

Разбираем практический план начала эксплуатации и сопровождения бизнес-системы, у которой нет ни исходного кода, ни спецификаций. Охватыв...

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

Технические консультации и ревью дизайна

Также подходит для того, чтобы разобраться, стоит ли переходить на Reg-Free COM или сохранить регистрационную модель эксплуатации, и как разделить работу с типовыми библиотеками и настройкой проектных ссылок.

Частые вопросы

Вопросы, которые часто возникают при консультациях по теме статьи.

Что такое Reg-Free COM?
Reg-Free COM (Registration-Free COM) — это механизм, при котором информация о регистрации COM хранится не в реестре, а в манифесте. Во время выполнения при разрешении CoCreateInstance или CLSIDFromProgID сначала проверяется контекст активации (activation context), и DLL разрешается по манифестной информации, записанной в нём. Благодаря этому COM DLL / OCX можно хранить private для каждого приложения, что даёт такие преимущества, как удобное XCOPY-распространение, более лёгкое предотвращение конфликтов версий и меньший риск поломки при удалении.
Решает ли переход на Reg-Free COM проблему 32-бит / 64-бит?
Нет. 32-битный процесс может загружать только 32-битные in-proc COM DLL, а 64-битный — только 64-битные DLL; в этом отношении Reg-Free COM ничего не меняет. Кроме того, отдельно нужно продумывать распространение зависимых DLL и среды выполнения VC++, работу с типовыми библиотеками, настройку проектных ссылок и зависимость от нестандартной регистрационной информации. Reg-Free COM в основном избавляет только от хлопот, связанных с глобальной регистрацией.
Почему конфигурация Reg-Free COM работает на машине разработчика, но не работает на целевом компьютере?
Прежде всего стоит заподозрить, что на самом деле дело было в помощи со стороны регистрации в реестре. Если манифесту не хватает нужной информации, среда выполнения COM откатывается к обычному разрешению на основе регистрации, поэтому на машине разработчика приложение может случайно работать благодаря локальной регистрации. Поэтому проверку Reg-Free COM безопаснее проводить в чистом окружении. Если приложение не запускается с ошибкой «side-by-side configuration is incorrect», причиной обычно являются несогласованность манифеста или нехватка зависимых DLL, и стандартный способ разобраться — журнал событий и sxstrace.
Можно ли сделать Reg-Free COM для COM-компонента, созданного на .NET 8?
Да. В .NET 5+/.NET 8 параметр EnableComHosting создаёт *.comhost.dll — точку входа для публикации COM, а добавление EnableRegFreeCom=true выводит side-by-side манифест для Reg-Free COM. Однако Reg-Free COM и стратегия работы с TLB — это отдельные вопросы. В .NET Core/.NET 5+ TLB не появляется из сборки естественным образом, как это было во времена .NET Framework, поэтому если требуется типизированное использование — например, раннее связывание из VBA, — генерацию, встраивание и регистрацию TLB безопаснее продумывать отдельно.

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог