Как сегодня поступать с ActiveX / OCX: оставить, обернуть или заменить
· Обновлено: · Го Комура · COM, ActiveX, OCX, .NET, разработка под Windows, модернизация
История изменений (2 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- В 5.2 добавлен DllSurrogate — вариант, который стоит рассмотреть до того, как писать собственный вспомогательный EXE. Если компонент является in-proc COM-сервером, достаточно добавить AppID к его CLSID и пустой DllSurrogate под этим AppID, чтобы вынести его из процесса, не написав ни строки своего кода. При этом суррогат не устраняет разницу в разрядности: исчезает только требование находиться в одном процессе, а вызовы становятся межпроцессными, со всеми издержками маршалинга и межпроцессного взаимодействия. Новая таблица разграничивает случаи, которые покрывает суррогат, и случаи, где всё же нужен собственный вспомогательный EXE; добавлены также два источника. Сама процедура регистрации описана в другой статье, сюда вынесена только ссылка на неё. Открыть версию до этого обновления (DOI: 10.5281/zenodo.21619663)
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619662)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Как сегодня поступать с ActiveX / OCX: оставить, обернуть или заменить. KomuraSoft LLC. https://comcomponent.com/ru/blog/2026/03/12/001-activex-ocx-keep-wrap-replace-decision-table/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21619662
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.22170275
Проекты, где звучат слова ActiveX / OCX, обычно сразу даются тяжело.
- VB6 или старые приложения на C++ / MFC всё ещё в работе
- SDK промышленного оборудования или измерительных приборов поставляется только как OCX
- внутренний веб-портал завязан на ActiveX и не может выйти из режима IE
- хочется уйти с 32 бит на 64, но один OCX этому мешает
И «выбросить всё, потому что это старое», и «сохранить навечно, потому что оно работает» — одинаково грубые решения. Главное — понять, является ли этот ActiveX / OCX простым UI-компонентом или границей, в которую уже встроена бизнес-логика и спецификация оборудования.
В статье разбираем, в каком порядке проще решить — оставить, обернуть или заменить — когда вы находите ActiveX / OCX.
В качестве примеров рассматриваются такие случаи:
- существующие настольные приложения на VB6 / MFC / WinForms;
- поэтапный переход на C# / .NET;
- устаревшие экраны с WebBrowser / режимом IE;
- Windows-приложения с элементами ActiveX от сторонних вендоров.
Содержание
- Сначала вывод (в двух словах)
- Что в этой статье понимается под ActiveX / OCX
- Таблица решений, с которой стоит начать
- 3.1. Общая картина
- 3.2. Решение оставить
- 3.3. Решение обернуть
- 3.4. Решение заменить
- 3.5. Зависимость от браузера рассматриваем отдельно
- Моменты, которые легко сбивают с толку
- 4.1. UI-компонент или компонент со встроенной спецификацией
- 4.2. 32-бит / 64-бит и границы процессов
- 4.3. Регистрация, распространение, права, лицензии
- 4.4. STA / цикл сообщений / обратные вызовы
- 4.5. Есть ли тесты, можно ли наблюдать за поведением
- Рекомендации по типовым сценариям
- 5.1. Внутреннее настольное приложение, которое до сих пор стабильно работает
- 5.2. Хотите перенести 32-битный OCX на 64-битную сторону
- 5.3. Экраны, завязанные на IE / WebBrowser
- 5.4. ActiveX с управлением оборудованием или собственной спецификацией
- Частые антипаттерны
- Чек-лист для начала миграции
- Краткая шпаргалка по выбору
- Итог
- С какими вопросами к нам обращаться
- Источники
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 24, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
1. Сначала вывод (в двух словах)
- Увидев ActiveX / OCX, в первую очередь нужно судить не «старое ли это», а что именно взял на себя этот компонент
- Если это простой UI-компонент, заменить его сравнительно легко
- Если он несёт управление оборудованием, отчёты, собственный формат файлов или многолетние особенности эксплуатации, безопаснее сначала обернуть его, а не сразу переписывать заново
- Если он стабильно работает на настольном хосте и область изменений невелика, решение оставить как есть вполне обосновано
- У зависимости от ActiveX в браузере продлить жизнь можно, но на долгую перспективу рассчитывать не стоит, так что здесь стоит смотреть на замену как на приоритет
- 32-битный OCX нельзя напрямую загрузить в 64-битный процесс. Усилием воли эту границу не обойти
- Регистрация, зависимые DLL, права администратора, лицензии, STA / MTA — трение вне самой реализации часто оказывается самым сложным местом
- И «на всякий случай переписать всё», и «заморозить навсегда из страха» — варианты с высокой вероятностью сбоев
Иначе говоря, порядок принятия решения такой.
- Что несёт в себе этот OCX
- Обязательно ли использовать его в том же процессе
- Не застрянете ли вы на 32-бит / 64-бит, регистрации или зависимости от браузера
- Стоит ли сначала создать тестируемую границу, а потом уже заменять
При таком порядке разобраться получается заметно проще.
flowchart TB
accTitle: Порядок принятия решения
accDescr: Смотрите по порядку: что несёт этот OCX, нужен ли тот же процесс, не застрянете ли на разрядности, регистрации или браузере, и стоит ли сначала сделать тестируемую границу, а потом заменять.
s1["Что несёт этот OCX"] --> s2["Нужен ли тот же процесс"]
s2 --> s3["Застрянете ли на bitness, регистрации, браузере"]
s3 --> s4["Сначала граница, потом замена?"]
Рис. 1: Смотрите не на «старое ли это», а в этом порядке — тогда «оставить / обернуть / заменить» складывается проще.
2. Что в этой статье понимается под ActiveX / OCX
Сначала зафиксируем, как в этой статье используются термины.
| Термин | Значение в этой статье |
|---|---|
| COM | Бинарно-совместимая компонентная модель Windows. Основа публичных интерфейсов, регистрации, Apartment Model и так далее |
| ActiveX / OCX | На практике этими словами часто обозначают в совокупности COM-компоненты и связанные с ними активы. В частности, сюда часто относят UI-элементы управления .ocx и компоненты, встраиваемые в IE или контейнеры |
| Зависимость от WebBrowser / IE | Даже если это не сам ActiveX, сюда относятся встроенные браузеры и интеграции, построенные на модели IE. С точки зрения принятия решений это очень близкая проблема |
Строго говоря, ActiveX и COM — не одно и то же. Но на практике проблемные точки у них весьма похожи.
- Совпадают ли 32-бит / 64-бит
- Как распространять регистрацию и зависимые DLL
- В каком хосте / контейнере это работает
- Не застрянете ли вы на STA, цикле сообщений, обратных вызовах
- Не осталась ли зависимость от браузера
В этой статье мы рассматриваем такие практические точки принятия решений в комплексе.
flowchart TB
accTitle: Где на практике обычно застревают
accDescr: Строго говоря ActiveX и COM — не одно и то же, но точки, где обычно застревают, очень похожи: совпадение разрядности, регистрация и зависимые DLL, хост, предпосылки STA и callback, зависимость от браузера.
ax["Проект с ActiveX / OCX"] --> p1["Совпадает ли bitness"]
ax --> p2["Регистрация и распространение зависимых DLL"]
ax --> p3["В каком хосте это работает"]
ax --> p4["Предпосылки STA и callback"]
p4 -.-> p5["Не осталась ли зависимость от браузера"]
Рис. 2: ActiveX и COM строго говоря разные вещи, но на практике застревают примерно в одних и тех же местах.
Дальше по тексту постоянно встречаются сокращения, поэтому сведём их заранее. В главе 7 чек-лист говорит «выписать ProgID и CLSID» — без этой таблицы на этом шаге не сдвинуться.
| Термин | Как читать / официальное имя | Смысл |
|---|---|---|
| CLSID | Class ID | GUID, однозначно указывающий реализацию (класс) COM-компонента. Регистрация в реестре тоже крутится вокруг этого значения |
| ProgID | Programmatic Identifier | Человекочитаемое имя, привязанное к CLSID. Строка вроде Excel.Application |
| IID | Interface ID | GUID, однозначно указывающий COM-интерфейс. Это не CLSID |
| TLB | Type Library | Файл с двоичной информацией о типах: интерфейсы, методы, аргументы. Именно поэтому VB6 и .NET могут вызывать компонент «с типами» |
| RegAsm | Assembly Registration Tool | Инструмент из .NET Framework. Регистрирует сборку .NET в реестре так, чтобы её можно было вызывать из COM |
| AxHost | — | Базовый класс, чтобы хостить элемент ActiveX на Windows Forms |
| AxImp | ActiveX Control Importer | Инструмент, который из OCX генерирует обёрточную сборку для Windows Forms |
| in-proc / out-of-proc | внутри процесса / вне процесса | Работает в том же процессе, что вызывающий код (DLL или OCX), или в отдельном процессе (EXE-сервер) |
| LocalServer | — | Форма, когда COM-сервер работает как EXE в отдельном процессе. Позволяет обойти стену разрядности и изолировать падения |
| Reg-Free COM / side-by-side | COM без регистрации | Механизм, который решает COM без записи в реестр — по сведениям в манифесте приложения |
| design-time / runtime лицензия | на этапе разработки / на этапе выполнения | У вендорских элементов управление лицензией при размещении на форме в IDE и при запуске у заказчика может быть раздельным |
| adapter / facade | — | Проектный приём: мелкий существующий API заменить своим, более крупным и удобным |
| STA / MTA | Single / Multi Threaded Apartment | Потоковая модель COM. От неё зависит, с какого потока можно вызывать объект |
3. Таблица решений, с которой стоит начать
3.1. Общая картина
Если начать с этой таблицы, общее направление, как правило, определяется сразу.
| Ситуация | Первый выбор | Причина |
|---|---|---|
| Есть зависимость от ActiveX в браузере | Склоняемся к замене | Сам Edge ActiveX не поддерживает, а режим IE позиционируется как мера продления срока службы |
| OCX стабильно работает в настольном приложении, область изменений невелика | Склоняемся к тому, чтобы оставить | Стоимость слома сейчас часто выше |
| Хочется перевести на .NET только окружение, но поведение компонента непредсказуемо | Склоняемся к обёртке | Безопаснее сначала зафиксировать границу |
| Хотите поместить 32-битный OCX напрямую в 64-битный процесс | Обернуть / изменить конфигурацию | Это граница, которую нельзя пересечь in-proc |
| Используется только как UI-компонент, есть замена | Склоняемся к замене | Часто достаточно поверхностной замены |
| Вендор прекратил поддержку, постоянные проблемы с подписью, регистрацией, зависимыми DLL | Склоняемся к замене | Эксплуатационные издержки уже проявились как технический долг |
| Внутри — управление оборудованием, отчёты, собственный протокол | Склоняемся к обёртке | Пока поведение не зафиксировано, стоимость замены непредсказуема |
flowchart TD
start["Есть ActiveX / OCX"] --> q1{"Зависимость от браузера?"}
q1 -- "Да" --> p1["Приоритет — замена<br/>режим IE — мера продления"]
q1 -- "Нет" --> q2{"В основном UI-компонент?"}
q2 -- "Да" --> q3{"Есть равноценная замена?"}
q3 -- "Да" --> p2["Рассмотреть замену"]
q3 -- "Нет" --> p3["Сначала обернуть и зафиксировать границу"]
q2 -- "Нет" --> q4{"Есть управление оборудованием / своя спецификация / логика отчётов?"}
q4 -- "Да" --> p4["Сначала обернуть<br/>собрать тесты, затем заменять поэтапно"]
q4 -- "Нет" --> q5{"Больно с регистрацией / разрядностью / распространением?"}
q5 -- "Да" --> p5["Пересмотреть конфигурацию<br/>рассмотреть out-of-proc / связь через отдельный процесс / Reg-Free COM"]
q5 -- "Нет" --> p6["Решение оставить тоже реалистично"]
Рис. 3: Первое ветвление — есть ли зависимость от браузера; дальше направление задают «это UI-компонент?», «есть ли замена?» и «несёт ли он спецификацию?».
Далее рассмотрим каждый вариант по порядку.
3.2. Решение оставить
Сам по себе факт, что это ActiveX / OCX, ещё не делает его объектом замены. При соблюдении следующих условий оставить его как есть чаще всего действительно дешевле всего.
- Область использования замкнута, а условия эксплуатации зафиксированы — внутреннее распространение, поставка вместе с оборудованием и тому подобное
- Этот компонент до сих пор стабильно работает, и запросы на изменение невелики
- Вендор всё ещё активен, либо компания может обеспечить хотя бы минимальную поддержку своими силами
- Нет зависимости от браузера — всё замкнуто на существующем настольном хосте
- Предпосылку о 32-бит / 64-бит пока менять не нужно
Здесь важно понимать: оставить — не значит забросить. Если вы оставляете компонент, стоит сделать как минимум следующее.
- Задокументировать поддерживаемую ОС, разрядность, необходимые зависимые DLL и порядок регистрации
- Перевести установку, регистрацию и удаление со «человеческих» заметок на скрипты или инсталлятор
- Подготовить дымовой тест (smoke test) для чистого окружения
- По возможности собрать все обращения к компоненту в одном месте, а не разбрасывать по всему приложению
Хуже всего — 10 лет подряд следовать принципу «работает — не трогай», пока никто уже не сможет объяснить исходные предпосылки. Чем чаще вы выбираете оставить компонент, тем важнее становится сделать эти предпосылки явными.
flowchart TB
accTitle: Решение оставить и обязательная фиксация предпосылок
accDescr: Оставить — не значит забросить: в комплект входят документирование предпосылок, скрипты регистрации, дымовой тест на чистом окружении и сбор обращений в одном месте.
keep["Решение оставить"] --> d1["Зафиксировать предпосылки письменно"]
keep --> d2["Перевести регистрацию на скрипты"]
keep --> d3["Подготовить дымовой тест"]
keep --> d4["Собрать обращения в одном месте"]
Рис. 4: «Оставить» не равно «забросить». Чем чаще выбираете оставить, тем важнее сделать предпосылки явными.
3.3. Решение обернуть
На практике именно этот выбор отнимает больше всего работы.
«Обернуть» здесь означает изолировать ActiveX / OCX внутри узкой границы и представить его окружению как новый API или новый экранный компонент.
Это довольно эффективно. Причина в том, что если начать полную переработку на этапе, когда поведение старого компонента ещё не разгадано до конца, легко получить двойную нагрузку — раскопки спецификации и воспроизведение дефектов одновременно. Безопаснее сначала изолировать старый компонент и навести порядок только на границе.
flowchart TB
accTitle: Схема выбора «обернуть»
accDescr: ActiveX / OCX изолируют внутри узкой границы и снаружи показывают как новый API или экранный компонент, чтобы не начинать полную переработку, пока поведение ещё не разгадано, и не получить сразу раскопки спецификации и воспроизведение дефектов.
old["ActiveX / OCX"] --> wall["Изолировать внутри узкой границы"]
wall --> api["Показать как новый API"]
api --> app["Снаружи виден только новый фасад"]
wall -.-> safe["Избежать двойной нагрузки раскопок и воспроизведения"]
Рис. 5: «Обернуть» — это изоляция старого компонента и новый фасад снаружи; оно работает как этап перед полной переработкой.
У обёртки есть несколько устоявшихся форм.
| Способ обёртки | Подходит для | На что обратить внимание |
|---|---|---|
| Хост на WinForms + AxHost / Aximp | Встраивание в существующие настольные экраны, когда нужно оставить лишь несколько экранов | STA, события, зависимости времени дизайна, лицензирование |
| 32-битный вспомогательный EXE / COM LocalServer / связь через отдельный процесс | Переход на 64-битную сторону, изоляция сбоев | Межпроцессное взаимодействие, порядок запуска, мониторинг, развёртывание |
| COM-совместимый фасад на стороне .NET | Обновление внутренней реализации при сохранении существующих вызывающих COM-кода | IID / CLSID / TLB / способ регистрации / разрядность |
Когда направление выбрано, полезно знать и первый шаг.
| Способ обёртки | Что делать сначала | Подробная процедура |
|---|---|---|
| Хост на WinForms + AxHost | В Visual Studio щёлкнуть панель элементов правой кнопкой → «Выбрать элементы» → вкладка «COM-компоненты» и выбрать нужный. Из командной строки — aximp |
Ловушки регистрации и bitness при разработке COM/OCX/ActiveX |
| 32-битный вспомогательный EXE / LocalServer | Зарегистрировать 32-битный EXE как COM-сервер и вызывать его с 64-битной стороны out-of-proc | Пример моста COM для вызова 64-битной DLL из 32-битного приложения |
| Reg-Free COM | В манифесте приложения описать file и comClass и решать COM без записи в реестр |
Что такое Reg-Free COM — механизм использования COM без регистрации |
| COM-совместимый фасад на стороне .NET | Опубликовать сторону .NET как COM и при необходимости сгенерировать TLB через dscom | Как использовать DLL на .NET 8 из VBA с типизацией — публикация COM и TLB через dscom |
aximp запускают из Developer Command Prompt Visual Studio.
aximp C:\path\to\MyControl.ocx
На выходе получаются две сборки: runtime callable wrapper для COM-типов и обёртка для Windows Forms, производная от AxHost. Имена файлов берутся не из исходного имени файла, а из ProgID — это легко пропустить. В примере документации Microsoft из msdxm.ocx выходят MediaPlayer.dll и AxMediaPlayer.dll. В ссылки проекта добавляют вторую и кладут на форму AxMediaPlayer.
flowchart TB
accTitle: Что генерирует aximp
accDescr: Если передать aximp OCX, он генерирует runtime callable wrapper для COM-типов и обёртку Windows Forms, производную от AxHost; на форму кладут вторую. Имена файлов определяются ProgID, а не исходным именем файла.
ocx["Исходный OCX"] --> tool["Запуск aximp"]
tool --> rcw["Обёрточная DLL COM-типов"]
tool --> ax["Обёрточная DLL, производная от AxHost"]
ax --> form["Добавить в ссылки и положить на форму"]
tool -.-> name["Имена файлов берутся из ProgID"]
Рис. 6: aximp выдаёт две DLL; на форму кладут обёртку, производную от AxHost.
Для Reg-Free COM манифест приложения в минимальном виде выглядит так.
<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MyApp" version="1.0.0.0" />
<file name="MyControl.ocx">
<comClass
clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
threadingModel="Apartment"
progid="MyCompany.MyControl.1" />
</file>
</assembly>
В варианте LocalServer путь к EXE пишется в реестр в HKEY_CLASSES_ROOT\CLSID\{CLSID}\LocalServer32. EXE-серверы, собранные на ATL или MFC, часто умеют саморегистрироваться через MyServer.exe /regserver и /unregserver. Но представления реестра разделены по разрядности, поэтому 32-битный EXE регистрируется в 32-битном представлении реестра — это нужно держать в голове всегда.
flowchart TB
accTitle: Регистрация LocalServer и представления реестра
accDescr: В варианте LocalServer путь к EXE пишется в LocalServer32 под CLSID; саморегистрация через /regserver обычна, но представления реестра разделены по разрядности, и 32-битный EXE попадает в 32-битное представление.
exe["EXE-сервер"] --> reg["Записать путь в LocalServer32"]
reg --> view{"Разрядность EXE?"}
view -->|"32-бит"| v32["Регистрация в 32-битном представлении"]
view -->|"64-бит"| v64["Регистрация в 64-битном представлении"]
Рис. 7: Куда регистрируется LocalServer, задаёт представление реестра, разделённое по разрядности.
Особенно важно при обёртке не копировать старый API целиком, все 200 методов подряд. Если так сделать, вы просто перенесёте старые особенности прямо в новый код без изменений.
При обёртке заметно помогает учитывать следующее.
- Использовать методы крупной гранулярности
- Не давать экранному коду напрямую трогать OCX
- Фиксировать на границе журналы, необходимые при сбое
- Определить на границе ответственность за тайм-ауты, повторные попытки и преобразование исключений
- Сделать так, чтобы будущую замену можно было подставить через тот же интерфейс
Иногда на новой стороне .NET хочется сохранить только входную точку COM. В этом случае реалистична конфигурация «обновляем содержимое, но сохраняем только COM-контракт». Однако ощущений эпохи .NET Framework, когда достаточно было «на всякий случай сделать RegAsm», может не хватить. Работу с современным COM host в .NET, TLB, разрядностью и Registry-Free COM лучше спроектировать заранее — потом будет проще. Пошагово это разобрано в статьях Как использовать DLL на .NET 8 из VBA с типизацией — публикация COM и TLB через dscom и Что такое Reg-Free COM — механизм использования COM без регистрации.
flowchart TB
accTitle: Ответственность, которую фиксируют на границе
accDescr: При обёртке делают методы крупной гранулярности, не дают экранному коду трогать OCX напрямую, на границе фиксируют журналы, тайм-ауты и преобразование исключений и оставляют возможность подставить будущую замену через тот же интерфейс.
b["Граница обёртки"] --> r1["Методы крупной гранулярности"]
b --> r2["Журналы на границе"]
b --> r3["Тайм-ауты и преобразование исключений"]
b --> r4["Та же точка входа для будущей замены"]
r1 -.-> ng["Не копировать 200 старых методов"]
Рис. 8: Ценность обёртки — собрать ответственность на границе; копирование старого API эту ценность снимает.
3.4. Решение заменить
Замена подходит главным образом для случаев, когда проблема — это поверхностное устаревание.
В таких ситуациях лучше рассматривать замену как приоритет.
- Этот ActiveX используется только как UI-компонент
- Вендор выпустил преемника для .NET / WPF / WebView2
- Тормозит зависимость от браузера или предпосылка про IE
- Постоянные проблемы с регистрацией, подписью, правами администратора, настройками безопасности
- Есть тесты или бизнес-сценарии, позволяющие проверить альтернативную реализацию
И наоборот, если разом выбросить компонент, несущий управление оборудованием или логику отчётов, только потому, что он выглядит старым, проект как правило расползается.
Если вы заменяете, начинайте с UI.
- Сетки (grid)
- Календари
- Деревья
- Область отображения браузера
- Простые вспомогательные элементы ввода
Их сравнительно легко заменить. С другой стороны, встречаются и такие, что выглядят как UI, а внутри плотно набиты логикой.
- ActiveX от вендора для управления оборудованием
- Элементы управления, слитые с печатью или формированием отчётов
- Элементы управления, инкапсулирующие чтение и запись собственного формата файлов
- Элементы управления с COM-обратными вызовами или предпосылками о потоках
Ошибитесь в этом различии — и оценка трудозатрат рассыплется в одночасье.
flowchart TB
accTitle: Как отличить случай для замены
accDescr: Если компонент используется только как UI и есть замена, менять сравнительно легко; если за внешним видом стоят управление оборудованием, отчёты, свой формат или предпосылки о потоках, сразу выбрасывать его обычно значит увязнуть.
q{"Что стоит за внешним видом"}
q -->|"только поверхностное устаревание"| easy["Заменять сравнительно легко"]
q -->|"внутри сидит спецификация"| heavy["Сразу выбросить — увязнуть"]
heavy --> wrap["Сначала решение обернуть"]
Рис. 9: Годится ли замена, решает различие «поверхностное устаревание» и «плотная начинка».
3.5. Зависимость от браузера рассматриваем отдельно
Это действительно отдельная категория.
У ActiveX в браузере, в отличие от настольного OCX, довольно слабые основания для дальнейшего развития.
Причина проста: современные браузерные платформы больше не делают это своим главным полем боя. Сам Microsoft Edge не поддерживает ActiveX. Режим IE, в свою очередь, использует движок семейства IE для настроенных сайтов и может служить слоем совместимости для запуска части функций IE, включая ActiveX.
Иными словами:
- Продлить жизнь, чтобы оно работало сейчас, — можно
- Но как долгосрочное проектное решение на это рассчитывать не стоит
flowchart TB
accTitle: Место ActiveX в браузере
accDescr: Сам Microsoft Edge ActiveX не поддерживает; режим IE — слой совместимости, который для заданных сайтов использует движок семейства IE. Продлить жизнь сейчас можно, но как долгосрочное решение рассчитывать на это не стоит.
bax["ActiveX в браузере"] --> edge["В самом Edge не работает"]
bax --> iem["Режим IE может продлить жизнь"]
iem --> future["Долгосрочно рассчитывать не стоит"]
future --> rep["Смотреть на замену как на приоритет"]
Рис. 10: Для ActiveX в браузере разделяйте продление жизни и постоянное решение и смотрите на замену как на приоритет.
То же самое происходит и с элементом управления WebBrowser, встроенным в Windows-приложения. WebBrowser тянет модель IE, поэтому если нужно просто отображать HTML, для новой работы естественнее сделать первым кандидатом WebView2.
Однако здесь важно учитывать: WebView2 — не полная замена WebBrowser «один в один».
- Скрипты, рассчитанные на DOM IE
- Зависимости от ActiveX
- Допущения вокруг
window.external - Поведение, завязанное на зоны безопасности и интранет
Всё это напрямую не переносится. Если вы заменяете компонент, нужно заново спроектировать не только движок отрисовки, но и сам стык браузера с нативным кодом.
flowchart TB
accTitle: Что в WebView2 напрямую не переносится
accDescr: WebView2 — не полная замена элемента управления WebBrowser; скрипты, рассчитанные на DOM IE, зависимость от ActiveX, допущения вокруг window.external и поведение зон безопасности напрямую не переносятся.
wb["От WebBrowser к WebView2"] --> ok["Если нужно только HTML — первый кандидат"]
wb --> ng["Напрямую не переносится"]
ng --> n1["Скрипты, рассчитанные на DOM IE"]
ng --> n2["Зависимость от ActiveX"]
ng --> n3["Допущения window.external"]
ng --> n4["Поведение зон безопасности"]
Рис. 11: WebView2 может заменить движок отрисовки, но стык модели IE не наследует.
4. Моменты, которые легко сбивают с толку
4.1. UI-компонент или компонент со встроенной спецификацией
Это самое важное.
Для старой сетки или календаря достаточно проверить совместимость внешнего вида и событий — и разговор заметно продвигается. С другой стороны, у ActiveX с управлением оборудованием, отчётами или собственным форматом за внешним видом скрыта целая спецификация.
Хотя это выглядит как один и тот же «элемент управления на экране», на деле диапазон довольно широк.
- Простой компонент отображения списка
- Компонент, посылающий команды оборудованию по собственному протоколу
- Компонент, который внутри самостоятельно занимается тайм-аутами, переподключением, повторной отправкой и поглощением исключений
- Компонент, несущий на себе совместимость печати или форматов экспорта
Переписывать последний вариант заново с нуля — как правило, это превращается в проект по раскопке спецификации. Здесь безопаснее сначала обернуть компонент.
flowchart TB
accTitle: UI-компонент или компонент со встроенной спецификацией
accDescr: Один и тот же «элемент на экране» может быть простой отображающей деталью или компонентом, который сам шлёт команды оборудованию, переподключается и держит совместимость отчётов; сразу переписывать второй — обычно проект раскопок спецификации.
look["Элемент управления на экране"] --> ui["Просто отображающая деталь"]
look --> spec["Компонент со встроенной спецификацией"]
ui --> go["Совместимость внешнего вида уже двигает разговор"]
spec --> dig["Сразу переписывать — раскопки спецификации"]
dig --> wrap["Безопаснее сначала обернуть"]
Рис. 12: Самое важное различие. Внешний вид может быть одним и тем же, а ход работы зависит от того, зашита ли за ним спецификация.
4.2. 32-бит / 64-бит и границы процессов
Это часто упускают из виду, хотя по сути это очень важный момент.
In-proc OCX должен совпадать по разрядности с процессом, который его загружает. То есть 32-битный OCX нельзя напрямую загрузить в 64-битное приложение.
Реалистичные варианты здесь, как правило, сводятся к трём.
- Пока оставить хост-приложение тоже 32-битным
- Изолировать OCX в отдельном 32-битном процессе и связать его с 64-битной стороной через IPC или out-of-proc COM
- Заменять зависимость от этого OCX, начиная с тех мест, где это возможно
Расчёт «раз Any CPU, то как-нибудь само образуется» здесь обычно не работает. Даже если вы создаёте COM-совместимый фасад на новой стороне .NET, внешний вид managed-кода и фактическая разрядность COM host — это разные вопросы. Начните здесь небрежно — и получите неприятную ситуацию, когда сборка проходит, а на машине заказчика ничего не запускается.
flowchart TB
accTitle: Три варианта для 32-битного OCX и перехода на 64-бит
accDescr: 32-битный OCX нельзя загрузить in-proc в 64-битный процесс, поэтому остаются три варианта: держать хост 32-битным, изолировать в отдельном 32-битном процессе и связать через IPC или out-of-proc COM, либо заменять зависимость там, где уже можно.
wall["32-битный OCX in-proc нельзя"] --> o1["Держать хост 32-битным"]
wall --> o2["Изолировать в отдельном 32-битном процессе"]
wall --> o3["Заменять там, где уже можно снять зависимость"]
o2 -.-> ipc["Связь через IPC или out-of-proc COM"]
Рис. 13: Стену разрядности усилием воли не обойти; реалистичные варианты сходятся к этим трём.
4.3. Регистрация, распространение, права, лицензии
Технически компонент вызывается без проблем, а вот на этапе распространения всё умирает. Для ActiveX / OCX это довольно частая история.
Обычно проблемными местами становятся такие вещи.
- Предпосылки для
regsvr32держатся только в чьей-то голове - Размещение зависимых DLL нигде явно не зафиксировано
- Нужны права администратора, но это не отражено в порядке эксплуатации
- У вендорского компонента разделены лицензии design-time и runtime
- Работает на машине разработчика, но не работает в чистом окружении
Всё это способно остановить проект, даже если не тронуть ни строчки кода.
Конфигурации без регистрации или размещение side-by-side иногда облегчают ситуацию, но это не волшебный порошок — совместимость с контейнером и способом распространения всё равно нужно проверять.
Иными словами, миграция ActiveX / OCX — это не только реализация, но и проектирование распространения. Отложите этот момент на потом — и в конце вас ждёт эффектное падение.
flowchart TB
accTitle: Узкие места, на которых останавливает распространение
accDescr: Человеческая зависимость от regsvr32, неявное размещение зависимых DLL, права администратора вне эксплуатационной процедуры и лицензии, разделённые на design-time и runtime, останавливают проект, даже если не тронуть ни строчки кода.
dist["Проектирование распространения"] --> d1["regsvr32 держится на людях"]
dist --> d2["Зависимые DLL неявны"]
dist --> d3["Права администратора вне процедуры"]
dist --> d4["Лицензия разделена надвое"]
d1 -.-> stopx["Останавливает, даже не трогая код"]
Рис. 14: Компонент технически вызывается, а умирает на распространении — это узкое место проектируют отдельно от реализации.
4.4. STA / цикл сообщений / обратные вызовы
ActiveX / OCX — это не просто вызов DLL. Он может нести предпосылки о потоковой модели COM и о цикле сообщений.
Особенно стоит насторожиться в таких случаях:
- Стабильность обеспечивается только при работе в потоке UI
- Компонент рассчитан на STA, но его небрежно вызывают со стороны MTA
- Во время синхронного вызова возвращается обратный вызов (callback)
- Неясно, в каком потоке должны приниматься события
Поначалу это проявляется как «иногда зависает», «иногда не приходят события». Но по сути это почти всегда нарушение предпосылок.
Поэтому и при обёртке, и при замене лучше заранее зафиксировать, в каком потоке создавать объект, из какого потока вызывать и где принимать события.
flowchart TB
accTitle: Сначала зафиксировать предпосылки о потоках
accDescr: Если заранее не зафиксировать, в каком потоке создавать объект, из какого вызывать и где принимать события, получите «иногда зависает / иногда нет событий» — типичное лицо нарушения предпосылок.
fix["Три пункта, которые фиксируют заранее"] --> t1["В каком потоке создавать"]
fix --> t2["Из какого потока вызывать"]
fix --> t3["Где принимать события"]
t1 -.-> ghost["Если размыто — нарушение предпосылок под видом случайных сбоев"]
Рис. 15: За «иногда зависает» почти всегда стоит нарушение предпосылок; его предотвращают, заранее зафиксировав потоковые договорённости.
4.5. Есть ли тесты, можно ли наблюдать за поведением
Заменить компонент трудно не только потому, что код старый. Трудно потому, что нет критерия, по которому можно сказать «поведение осталось тем же».
Одно только наличие следующего сильно меняет дело.
- Дымовые тесты для каждого сценария работы
- Примеры входных и выходных данных
- Снимки экрана или образцы отчётов
- Шаблоны ошибок и ожидаемое поведение
- Журналы на случай тайм-аута или отсутствия подключённого оборудования
Особенно когда задействовано оборудование или отчёты, случается странная вещь: реальное поведение оказывается более правдивым источником, чем спецификация. Без средств наблюдения замена превращается в раскопки.
flowchart TB
accTitle: Средства наблюдения держат замену
accDescr: Без дымовых тестов, образцов ввода-вывода и отчётов, шаблонов ошибок и ожидаемого поведения нечем сказать «поведение осталось тем же», и замена превращается в раскопки.
q{"Есть ли средства наблюдения"}
q -->|"есть"| ok["Можно сказать, что поведение то же"]
q -->|"нет"| dig["Замена превращается в раскопки"]
ok -.-> ex["Дымовые тесты, образцы и аналоги"]
Рис. 16: Трудность замены задаёт не столько возраст кода, сколько наличие средств наблюдения, по которым можно сказать «поведение то же».
5. Рекомендации по типовым сценариям
5.1. Внутреннее настольное приложение, которое до сих пор стабильно работает
Рекомендация — склоняться к тому, чтобы оставить.
При таких условиях часто лучше не отдирать компонент насильно.
- Используется только внутри компании
- Целевые устройства и ОС в достаточной мере зафиксированы
- Этот OCX задействован лишь на нескольких экранах
- Запросы на доработку невелики, срок жизни предсказуем
Тем не менее лучше не оставлять компонент как есть, без обёртки, а хотя бы собрать места вызова в одном месте — это окупится позже.
Иначе говоря, стратегия такая:
- Сейчас — оставить
- Но навести порядок хотя бы на границе
- Сделать так, чтобы, когда потребуется замена, можно было начать именно отсюда
Такая трёхступенчатая структура выглядит естественно.
5.2. Хотите перенести 32-битный OCX на 64-битную сторону
Рекомендация — обернуть / изменить конфигурацию.
Идти здесь напрямую — тупик: 32-битный OCX нельзя загрузить in-proc в 64-битный процесс.
Реалистично управлять этим удобнее, изолировав компонент во вспомогательном 32-битном процессе или LocalServer и общаясь с 64-битным приложением через крупнозернистый API.
sequenceDiagram
participant App as 64-битное .NET-приложение
participant Bridge as 32-битный хелпер / LocalServer
participant Ocx as 32-битный OCX
App->>Bridge: Запрос через крупнозернистый API
Bridge->>Ocx: In-proc вызов
Ocx-->>Bridge: Результат / события
Bridge-->>App: Преобразованный результат
Рис. 17: 64-битное приложение вызывает OCX, изолированный в 32-битном хелпере, через крупнозернистый API.
Здесь важно не транслировать все мелкие методы напрямую один в один. Межпроцессная граница быстро становится болезненной, если прогонять через неё большой поток мелких вызовов.
- Стремитесь к гранулярности примерно «1 операция = 1 запрос»
- Приводите возвращаемые значения и ошибки к осмысленным единицам
- Фиксируйте журналы на границе
При такой форме позже, когда потребуется по-настоящему заменить внутреннюю реализацию, это тоже будет проще.
Здесь, прежде чем сразу писать собственный вспомогательный EXE, есть ступень, которую стоит проверить.
Если этот OCX / DLL — in-proc COM-сервер, то есть его можно зарегистрировать через InprocServer32, то его можно поместить в суррогатный процесс, поставляемый с Windows, и выставить наружу как out-of-proc Local Server.
Достаточно добавить к CLSID значение AppID и записать в соответствующий ключ AppID пустую строку DllSurrogate — своего кода при этом не нужно ни строки.
Порядок регистрации собран в разделе 3.5 статьи Ловушки регистрации и bitness при разработке COM/OCX/ActiveX.
Однако суррогат не устраняет разницу в разрядности. Исчезает только ограничение «это должно находиться в одном процессе»: вызовы становятся out-of-proc, и стоимость маршалинга и межпроцессного взаимодействия ложится сверху как есть.
Что из этого выбрать, как правило, определяется этой таблицей.
| Ситуация | Что выбрать | Причина |
|---|---|---|
| Объект автоматизации, работа с которым сводится к вызовам методов и событиям | Сначала суррогат | Потому что out-of-proc получается одной лишь регистрацией и писать код не нужно |
Типы, которыми вы обмениваетесь, поддаются маршалингу через IDispatch или зарегистрированный proxy / stub |
Сначала суррогат | Потому что средства для пересечения границы уже есть |
У этого CLSID уже есть регистрация EXE вроде LocalServer32 |
Суррогату здесь делать нечего | Потому что запуск EXE-сервера или службы всегда имеет приоритет |
| Нужен как визуальный элемент управления, который кладут на форму и заставляют рисовать | Продумать другую конфигурацию | Потому что окно предполагается находящимся внутри процесса хоста |
| Мелкие вызовы идут часто, и транслировать их напрямую тяжело | Собственный вспомогательный EXE | Потому что нужен свой слой, собирающий их в крупнозернистый API |
| Для собственного интерфейса нет маршалера | Собственный вспомогательный EXE | Потому что быстрее подготовить proxy / stub или самому определить типы на границе |
| Хочется самому держать в руках порядок инициализации, переподключение, тайм-ауты и журналы | Собственный вспомогательный EXE | Потому что временем жизни процесса суррогата распоряжается COM |
Иначе говоря, суррогат — это минимальный ход, который стоит попробовать первым, а собственный EXE — ход на случай, когда границу хочется спроектировать самому. Строка таблицы из 3.1 «Хотите поместить 32-битный OCX напрямую в 64-битный процесс → обернуть / изменить конфигурацию» не меняется. Просто читайте её так, что внутри этого «обернуть» есть две ступени.
5.3. Экраны, завязанные на IE / WebBrowser
Рекомендация — приоритет замены.
Это область, где «работает сейчас» и «легко поддерживать в дальнейшем» плохо совпадают. Режим IE сильно помогает с совместимостью, но предпосылка всё равно остаётся связанной с семейством IE.
Поэтому удобно разделить подход так:
- Продлевать жизнь через режим IE, чтобы не останавливать внутренние процессы
- При этом не путать продление жизни с постоянным решением
- Выбирать замену из WebView2, чистого веба, гибрида нативного UI и веба и подобных вариантов
Зафиксируем и конкретные меры на стороне продления. Режим IE не включается «сам, если поставить Edge»: сайт открывается движком семейства IE только после того, как его задали политикой. Входов в настройку три.
| Способ | Настройка | Дополнение |
|---|---|---|
| Перечислить сайты | В групповой политике Microsoft Edge 78 и новее «Configure the Enterprise Mode Site List» указать расположение XML со списком сайтов корпоративного режима | Базовая форма |
| Переиспользовать список со стороны старого IE | Политика Internet Explorer «Use the Enterprise Mode IE website list» | Если задана политика стороны Edge, она имеет приоритет |
| Отправить весь интранет | Включить групповую политику Microsoft Edge 77 и новее «Send all intranet sites to Internet Explorer» | Охват широкий, инвентаризацию это не заменяет |
Предпосылки: на Windows и Edge стоят свежие обновления, установлены административные шаблоны Microsoft Edge, в компонентах Windows включён Internet Explorer 11. Если чего-то из этого нет, режим IE не заработает.
В режиме IE работают элементы ActiveX и Browser Helper Object. То есть продление жизни действительно возможно. Именно поэтому, если пользоваться им без условия выхода, из него потом не выбраться. Как снимать зависимость, разобрано в статье Как избавиться от зависимости внутренних веб-систем от режима IE.
flowchart TB
accTitle: Как мыслить продление через режим IE
accDescr: Режим IE начинает работать только после того, как сайты заданы политикой, и ActiveX в нём действительно работает; именно поэтому его нужно использовать вместе с условием выхода, не путая продление жизни с постоянным решением.
policy["Задать целевые сайты политикой"] --> iem["Открывается в режиме IE"]
iem --> alive["Можно продлить жизнь, включая ActiveX"]
alive --> cond["Пользоваться вместе с условием выхода"]
cond -.-> exitw["Без условия потом не выбраться"]
Рис. 18: Режим IE продлевает жизнь именно потому, что «действительно работает» — поэтому его используют только вместе с условием выхода.
Особенно если элемент управления WebBrowser используется просто как HTML-вьюер, приоритет замены высокий.
С другой стороны, если ActiveX внутри браузера выполняет ещё и роли вроде работы с локальными файлами, оборудованием, подписью или собственными надстройками, это уже не замена движка отрисовки, а переработка нативной интеграции. Здесь разговор становится немного тяжелее.
5.4. ActiveX с управлением оборудованием или собственной спецификацией
Рекомендация — сначала обернуть.
Этот тип содержательнее, чем кажется по внешнему виду. Даже если документация SDK скудная, за годы работы в реальных условиях в компоненте могло неявно накопиться поведение вроде такого:
- Как он ждёт при неудачном подключении
- Повторные попытки после тайм-аута
- Порядок событий
- Обходные пути, поглощающие особенности реального оборудования
- Интерпретация исключений и кодов ошибок
Если переписывать такой компонент заново, руководствуясь принципом «он всё равно старый», с высокой вероятностью загорятся полевые испытания.
Поэтому безопаснее начинать именно отсюда.
- Изолировать существующий компонент внутри границы
- Добавить журналирование, чтобы было видно, что происходит
- Собрать тестовые сценарии и шаблоны поведения реального оборудования
- И только затем выделить те участки, которые можно заменить
Это не эффектно, но на практике именно это работает лучше всего.
flowchart TB
accTitle: Как идти с ActiveX, который несёт спецификацию
accDescr: Сначала изолировать существующий компонент внутри границы, добавить журналирование, чтобы было видно, что происходит, собрать тестовые сценарии и шаблоны реального оборудования и только потом выделять участки, которые можно заменить.
s1["Изолировать внутри границы"] --> s2["Добавить журналирование и сделать поведение видимым"]
s2 --> s3["Собрать сценарии и шаблоны реального оборудования"]
s3 --> s4["Выделить участки, которые можно заменить"]
Рис. 19: Для компонента с управлением оборудованием или собственной спецификацией этот порядок снижает риск, что полевые испытания вспыхнут.
6. Частые антипаттерны
| Антипаттерн | В чём боль | Первый шаг к исправлению |
|---|---|---|
| Полная переработка только потому, что есть ActiveX | Легко упустить часть спецификации и получить взрыв трудозатрат | Сначала инвентаризация и выделение границ |
| Попытка напрямую поместить 32-битный OCX в 64-битное приложение | Принципиально невозможно | Изолировать на 32-битной стороне или изменить конфигурацию |
| Прямые вызовы API компонента из экранного кода | Легко становится невозможно заменить | Свести к адаптеру / фасаду |
Ручная эксплуатация процедуры regsvr32 |
Различия окружений вызывают сбои каждый раз | Рассмотреть инсталлятор, скрипты, манифесты |
| Успокоенность из-за наличия режима IE | Легко перепутать продление жизни с постоянным решением | Определить план замены и условия завершения |
| Поведение не зафиксировано перед заменой | Невозможно судить о завершённости | Подготовить дымовые тесты, тестовые данные, журналы |
Из них на практике особенно часто встречаются три.
- Спешка с полной переработкой
- Недооценка барьера разрядности
- Разбрасывание API по всему приложению
Избегая этих трёх пунктов, вы уже заметно снижаете вероятность сбоев.
flowchart TB
accTitle: Три антипаттерна, которые видят чаще всего
accDescr: Вероятность сбоев уже заметно падает, если не спешить с полной переработкой, не недооценивать стену разрядности и не разбрасывать API компонента по всему приложению.
a1["Спешка с полной переработкой"] --> avoid["Избегать этих трёх"]
a2["Недооценка стены разрядности"] --> avoid
a3["Разбрасывание API по всему приложению"] --> avoid
avoid --> down["Вероятность сбоев заметно падает"]
Рис. 20: Среди антипаттернов эти три встречаются чаще всего; избегать их уже даёт большой эффект.
7. Чек-лист для начала миграции
Проекты с ActiveX / OCX идут лучше, если сначала провести инвентаризацию, а не сразу бросаться в реализацию. Порядок примерно такой.
- Составить перечень используемых OCX / DLL
- Имя файла, версия, ProgID, CLSID, вендор, наличие лицензии
- Выяснить, где они используются
- Экраны, функции, отчёты, оборудование, пакетные задания, интеграция с Office и так далее
- Проверить разрядность и условия хоста
- 32-бит / 64-бит, in-proc / out-of-proc, предпосылка STA, зависимость от браузера
- Проверить условия распространения
- Способ регистрации, зависимые DLL, права администратора, тихая установка, воспроизведение в чистом окружении
- Создать дымовые тесты
- Не только штатный сценарий, но и сбои, отсутствие подключения, тайм-ауты
- Построить границу
- Адаптер, сервис, фасад, связь через отдельный процесс и так далее
- Опробовать на малых единицах — один экран, одна функция, одно устройство
- Расширять от границ, которые сработали, постепенно применяя оставить / обернуть / заменить
Пропустите эти шаги — и потом будет сложно объяснить даже то, что именно оказалось трудным.
8. Краткая шпаргалка по выбору
| Ситуация | Первый выбор |
|---|---|
| Стабильно работает только внутри компании, изменения невелики | Оставить |
| Хочется перевести на .NET только окружение | Обернуть |
| Сталкиваются 32-бит / 64-бит | Обернуть / изменить конфигурацию |
| Зависимость от IE / WebBrowser / ActiveX в браузере | Заменить |
| Простой UI-компонент, есть замена | Заменить |
| Несёт управление оборудованием, отчёты, собственную спецификацию | Обернуть |
| Постоянные проблемы с регистрацией или распространением | Обернуть или заменить |
Если сомневаетесь, для начала определите, UI-компонент это или граница со встроенной спецификацией, — так вы вряд ли ошибётесь.
9. Итог
Как поступать с ActiveX / OCX — это не вопрос, который решается фразой «это легаси, поэтому плохо».
Есть четыре момента, на которые стоит смотреть в первую очередь.
- Это простой UI-компонент или граница со встроенной спецификацией
- Обязательно ли использовать его в том же процессе
- Не застрянете ли вы на 32-бит / 64-бит, регистрации, зависимости от браузера или лицензировании
- Можно ли наблюдать за поведением до замены
Когда эти четыре момента прояснены, картина обычно складывается так:
- Стабильно работает, срок жизни предсказуем — оставить
- Хочется модернизировать только окружение — обернуть
- UI-компонент или зависимость от браузера — заменить
- Компонент со встроенной спецификацией — сначала обернуть, затем заменять поэтапно
Устаревшая технология — не повод для насмешек, а действующий объект, в котором сжаты история и контракты. Но чтобы жить с этим объектом дальше, необходимо проектирование границ.
Когда вы научитесь совмещать в своём мышлении «оставить, обернуть, заменить», проекты с ActiveX / OCX внезапно превращаются в решаемую задачу.
flowchart TB
accTitle: Сводка итога
accDescr: Если компонент стабильно работает и срок жизни предсказуем — оставить; если модернизируете только окружение — обернуть; UI-компонент или зависимость от браузера — заменить; компонент со встроенной спецификацией — сначала обернуть, затем заменять поэтапно.
q["Как поступать с ActiveX / OCX"] --> k["Стабильная работа — оставить"]
q --> w["Модернизация окружения — обернуть"]
q --> r["UI или зависимость от браузера — заменить"]
q --> p["Встроенная спецификация — обернуть и заменять поэтапно"]
Рис. 21: Когда четыре контрольные точки ясны, «оставить / обернуть / заменить» складывается в эту форму.
10. С какими вопросами к нам обращаться
Эта тема часто приносит пользу уже на этапе прояснения направления, ещё до начала самой разработки.
Например, к нам хорошо подходят такие запросы:
- Хотите провести инвентаризацию и понять, какие OCX действительно нужно заменить
- Хотите заранее разобраться только с узкими местами 32-бит / 64-бит
- Хотите перейти на .NET, но сохранить только входную точку COM
- Хотите сравнить меры продления жизни и стратегию отступления для ActiveX, поддержку которого прекратил вендор
- Хотите увидеть, с какого места можно начать отделять зависимость от IE / WebBrowser
- Хотите для начала безопасно выделить только один экран или одну функцию
В проектах с ActiveX / OCX исход часто решает не реализация, а то, как провести границы. Даже начать с оценки текущего состояния, сравнения конфигураций и проектирования порядка миграции как предварительный этап перед полной переработкой — уже вполне осмысленно.
11. Источники
- Microsoft Learn: AxHost Class (System.Windows.Forms)
- https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.forms.axhost
- Microsoft Learn: Aximp.exe (Windows Forms ActiveX Control Importer)
- https://learn.microsoft.com/ja-jp/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer
- Microsoft Learn: How to: Add ActiveX Controls to Windows Forms
- https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-activex-controls-to-windows-forms
- Microsoft Learn: Expose .NET Core components to COM
- https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
- Microsoft Learn: Registration-Free COM Interop
- https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop
- Microsoft Learn: DllSurrogate
- https://learn.microsoft.com/en-us/windows/win32/com/dllsurrogate
- Microsoft Learn: Registering the DLL Server for Surrogate Activation
- https://learn.microsoft.com/en-us/windows/win32/com/registering-the-dll-server-for-surrogate-activation
- Microsoft Learn: Frequently Asked Questions about Microsoft Edge
- https://learn.microsoft.com/ja-jp/deployedge/microsoft-edge-frequently-asked-questions
- Microsoft Learn: What is Internet Explorer (IE) mode?
- https://learn.microsoft.com/en-us/deployedge/edge-ie-mode
- Microsoft Learn: WebBrowser Class (System.Windows.Forms)
- https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.webbrowser
- Microsoft Learn: Introduction to Microsoft Edge WebView2
- https://learn.microsoft.com/en-us/microsoft-edge/webview2/
- KomuraSoft Blog: Основы COM STA/MTA — модели потоков и как избежать зависаний
- https://comcomponent.com/ru/blog/2026/01/31/000-sta-mta-com-relationship/
- KomuraSoft Blog: Вызов нативных DLL из C#: обёртка на C++/CLI или P/Invoke
- https://comcomponent.com/ru/blog/2026/03/07/000-cpp-cli-wrapper-for-native-dlls/
- KomuraSoft Blog: Пример моста COM для вызова 64-битной DLL из 32-битного приложения
- https://comcomponent.com/ru/blog/2026/01/25/002-com-case-study-32bit-to-64bit/
- KomuraSoft Blog: Что такое COM / ActiveX / OCX - объясняем различия и связь между ними
- https://comcomponent.com/ru/blog/2026/03/13/000-what-is-com-activex-ocx/
- KomuraSoft Blog: Как использовать DLL на .NET 8 из VBA с типизацией — публикация COM и TLB через dscom
- https://comcomponent.com/ru/blog/2026/03/16/007-dotnet8-dll-typed-vba-com-dscom-tlb/
- KomuraSoft Blog: Что такое Reg-Free COM — механизм использования COM без регистрации
- https://comcomponent.com/ru/blog/2026/03/16/011-what-is-reg-free-com/
- KomuraSoft Blog: Ловушки регистрации и bitness при разработке COM/OCX/ActiveX
- https://comcomponent.com/ru/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- KomuraSoft Blog: Как избавиться от зависимости внутренних веб-систем от режима IE
- https://comcomponent.com/ru/blog/2026/04/25/003-ie-mode-internal-web-system-life-extension-and-exit/
- KomuraSoft Blog: После IE-режима — WebView2? Ограничение по ActiveX и реалистичный план миграции
- https://comcomponent.com/ru/blog/webview2-embed-web-ui-in-windows-apps/
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Что продумать перед заказом разработки Windows-приложения
Перед заказом разработки Windows-приложения разберём, что стоит прояснить: доработка существующего ПО, интеграция с оборудованием, COM/Ac...
Ловушки регистрации и bitness при разработке COM/OCX/ActiveX
Практический разбор типичных ловушек COM, OCX и ActiveX: разрядность 32/64-бит, Visual Studio 2022, regsvr32 и Regasm, права администрато...
Что такое COM / ActiveX / OCX — различия и связь
Что такое COM, ActiveX и OCX: чем они отличаются, как связаны с OLE, где встречаются на практике и как к ним относиться сегодня.
Обратная совместимость интерфейсов DLL и COM — таблица: какие изменения ломают вызывающий код
Какие изменения DLL или COM-компонента ломают вызывающий код. Разбираем три слоя совместимости — бинарную, исходного кода и поведенческую...
Запустятся ли бизнес-приложения на Windows on Arm — эмуляция x64 (Prism) и реальность нативных DLL и COM
Разработчикам и ИТ-службам: запустится ли бизнес-приложение на Windows on Arm. Разбираем, как устроена эмуляция x64 (Prism), какие слои о...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Миграция ActiveX
Решения о сохранении, обёртке или замене компонентов COM / ActiveX / OCX.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Использование и перенос существующих активов
Решение оставить, обернуть или заменить COM / ActiveX / OCX — как раз центральная тема нашей поддержки по использованию и переносу существующих активов.
Технические консультации и ревью дизайна
Если до реализации нужно провести границы и порядок замены, направление удобнее уточнять на технической консультации и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Нужно ли заменять ActiveX / OCX?
- Смотрите не на «старое это или нет», а на то, что компонент на себя взял. Простой UI-компонент при наличии замены — заменяйте; если внутри управление оборудованием, отчёты или свой формат файлов — сначала оберните и зафиксируйте границу; если он стабильно работает и область изменений невелика, оставить как есть тоже реалистично. И «на всякий случай переписать всё», и «заморозить навсегда из страха» — оба варианта с высокой вероятностью сбоев.
- Можно ли использовать 32-битный OCX из 64-битного приложения?
- In-proc — нет. OCX должен совпадать по разрядности с процессом, который его загружает, и это принципиальное ограничение. Реалистичные варианты три: пока оставить хост-приложение тоже 32-битным; изолировать OCX в отдельном 32-битном процессе (вспомогательный EXE или COM LocalServer) и связать с 64-битной стороной через IPC или out-of-proc COM; либо заменять зависимость от OCX там, где это уже можно.
- Что делать с зависимостью от ActiveX в браузере?
- Здесь разумнее смотреть на замену как на приоритет. Сам Microsoft Edge ActiveX не поддерживает, а режим IE позиционируется как мера продления срока службы. Элемент управления WebBrowser точно так же тянет модель IE, поэтому если нужно просто показывать HTML, первым кандидатом становится WebView2. Но WebView2 — не полная замена «один в один»: скрипты, рассчитанные на DOM IE, и допущения вокруг window.external напрямую не переносятся.
- Что конкретно значит «обернуть» ActiveX / OCX?
- Изолировать ActiveX / OCX внутри узкой границы и снаружи показать его как новый API или новый экранный компонент. Устоявшиеся формы: хост на WinForms + AxHost, связь через отдельный процесс с помощью 32-битного вспомогательного EXE или LocalServer, COM-совместимый фасад на стороне .NET. При обёртке не копируйте старый API целиком — делайте методы крупной гранулярности, на границе закрепите ответственность за журналирование, тайм-ауты и преобразование исключений и сделайте так, чтобы будущую замену можно было подставить через тот же интерфейс.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.