Пример моста COM для вызова 64-битной DLL из 32-битного приложения
· Го Комура · COM, Разработка Windows, 32-бит, 64-бит
Желание вызвать 64-битную DLL из 32-битного приложения — довольно типичное требование в Windows. Особенно когда нужно сохранить существующие активы как есть и использовать только функциональность 64-битной стороны, архитектура на основе моста COM часто оказывается практичным решением.
Оглавление
- Сценарий
- Решение
- Последовательность обработки (диаграмма последовательности)
- Пример кода (концептуально)
- Полный пример кода
- Источники
1. Сценарий
Это случай, когда нужно оставить существующее 32-битное приложение без изменений, но использовать обработку, находящуюся в 64-битной DLL. Проблема в том, что 32-битный процесс не может загрузить 64-битную DLL. Это ограничение на уровне ОС — его невозможно обойти хитрыми приёмами.
Типичная ситуация выглядит так:
- существующее 32-битное приложение — крупный актив, который нельзя быстро мигрировать;
- в 64-битной DLL есть новая функциональность, либо её зависимости доступны только в 64-битной версии;
- нужно вызывать её с 32-битной стороны «типобезопасно».
При таком сочетании факторов путь внутри одного процесса закрыт изначально.
2. Решение
Базовое решение — разделение через Out-of-proc COM (EXE-сервер). 64-битная DLL вызывается из 64-битного COM-сервера (EXE), а 32-битное приложение использует её через COM.
Последовательность такая:
- Создать 64-битный COM LocalServer (EXE), который внутри себя вызывает 64-битную DLL.
- Опубликовать общий интерфейс COM (IDL/TypeLib), чтобы раскрыть типы.
- 32-битное приложение вызывает COM «типобезопасно» (взаимодействие через прокси/маршалинг).
Однако есть и нюансы.
- Регистрация для 32-бит и 64-бит выполняется отдельно (включая WOW6432Node);
- пользовательские структуры требуют отдельного проектирования маршалинга;
- есть накладные расходы на IPC, поэтому при высокочастотных вызовах нужна осторожность.
Иными словами, проверенный подход — «вынести 64-битную обработку в отдельный процесс и связать её мостом через COM».
3. Последовательность обработки (диаграмма последовательности)
Ниже показана последовательность действий, когда 32-битное приложение вызывает обработку из 64-битной DLL.
sequenceDiagram
participant App as 32-битное клиентское приложение
box rgba(100,100,255,0.1) Обрабатывается зарегистрированной подсистемой маршалинга COM
participant Proxy as COM Proxy<br/>(32-битная сторона)
participant RPC as RPC/IPC<br/>(межпроцессное взаимодействие)
participant Stub as COM Stub<br/>(64-битная сторона)
end
participant Server as 64-битный COM-сервер<br/>(EXE)
participant DLL as 64-битная DLL
App->>Proxy: ICalcService.Add(1, 2)
rect rgba(100,100,255,0.1)
Note over Proxy: Маршалинг параметров
Proxy->>RPC: Сериализованные данные
RPC->>Stub: Передача через границу процессов
Note over Stub: Демаршалинг параметров
end
Stub->>Server: Add(1, 2)
Server->>DLL: Вызов нативной функции
DLL-->>Server: Результат: 3
Server-->>Stub: Результат: 3
rect rgba(100,100,255,0.1)
Note over Stub: Маршалинг возвращаемого значения
Stub-->>RPC: Сериализованный результат
RPC-->>Proxy: Передача через границу процессов
Note over Proxy: Демаршалинг возвращаемого значения
end
Proxy-->>App: Результат: 3
Ключевые моменты:
- 32-битное приложение может делать типобезопасные вызовы через интерфейс
ICalcService; - COM-подсистема пересекает границу процессов, используя зарегистрированную DLL прокси/заглушки, маршалер библиотеки типов, стандартный маршалер и так далее;
- из-за накладных расходов на межпроцессное взаимодействие предпочтительнее группировать обработку, а не делать множество мелких вызовов.
4. Пример кода (концептуально)
Ниже приведён концептуальный набросок. На практике потребуются ещё регистрация, генерация TypeLib и тому подобное.
// Общий интерфейс (эквивалент IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
int Add(int a, int b);
}
// 64-битный COM LocalServer (сторона EXE)
[ComVisible(true)]
[Guid("1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11")]
[ClassInterface(ClassInterfaceType.None)]
public class CalcService : ICalcService
{
public int Add(int a, int b)
{
// Здесь вызывается 64-битная DLL
return a + b;
}
}
// Сторона 32-битного приложения (клиент)
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService");
var calc = (ICalcService)Activator.CreateInstance(t);
int result = calc.Add(1, 2);
При таком подходе 32-битная сторона может работать «типобезопасно». COM внутри использует прокси/заглушки и выполняет вызов через IPC самостоятельно.
5. Полный пример кода
Рабочая реализация описанной выше концепции опубликована на GitHub.
Call64bitDLLFrom32bitProc - GitHub
Репозиторий содержит следующее:
- Call64bitDLLFrom32bitProc/ — 64-битный COM LocalServer (EXE)
- X64DLL/ — 64-битная DLL (реальная обработка)
- X86App/ — 32-битный клиент (WinForms)
- scripts/ — скрипты регистрации и снятия с регистрации COM-сервера
Если собрать и зарегистрировать проект по инструкции из README, можно увидеть, как 32-битный процесс фактически вызывает 64-битную DLL.
6. Источники
- Обзор Component Object Model (COM) https://learn.microsoft.com/en-us/windows/win32/com/component-object-model–com–portal
- Регистрация COM LocalServer32 https://learn.microsoft.com/en-us/windows/win32/com/localserver32
- Основы интерфейсов COM https://learn.microsoft.com/en-us/windows/win32/com/the-component-object-model
- COM Interop (использование из .NET) https://learn.microsoft.com/en-us/dotnet/standard/native-interop/cominterop
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Ловушки регистрации и bitness при разработке COM/OCX/ActiveX
Разбираем с практической точки зрения типичные ловушки разработки COM, OCX и ActiveX: 32-бит/64-бит, Visual Studio 2022, regsvr32/Regasm,...
Office 2024/Microsoft 365: почему не работает ActiveX и как это диагностировать
Разбираем порядок диагностики, когда ActiveX не работает в Office 2024/Microsoft 365: отключение по умолчанию, несовпадение 32-бит/64-бит...
Работают ли бизнес-приложения на Windows на Arm — реальность x64-эмуляции (Prism) и нативных DLL/COM
Отвечаем разработчикам и ИТ-специалистам на вопрос «заработает ли наше бизнес-приложение на Windows на Arm». Разбираем принцип работы x64...
Аутсорсинг и контрактная разработка Windows-приложения: что стоит прояснить перед заказом
Перед тем как заказать аутсорсинг или контрактную разработку Windows-приложения, разберём, что нужно прояснить: доработка существующего П...
Странная любовь разработчика, или Как я перестал беспокоиться и полюбил Windows
Windows — это хлопотно. Но эти хлопоты — от того, что данная ОС десятилетиями несла на себе реальный бизнес.
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Миграция ActiveX
Решения о сохранении, обёртке или замене компонентов COM / ActiveX / OCX.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Использование и перенос существующих активов
Речь идёт о построении моста к 64-битной стороне при сохранении 32-битных активов, поэтому тема напрямую связана с нашей поддержкой по повторному использованию и миграции существующих активов.
Технические консультации и ревью дизайна
Если вы прежде всего хотите проработать архитектуру моста COM или определить, где провести границы процессов, мы можем сравнить варианты вместе в рамках технической консультации и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Может ли 32-битное приложение загрузить 64-битную DLL?
- Нет. 32-битный процесс не может загрузить 64-битную DLL — это ограничение на уровне ОС Windows, а не то, что можно обойти хитрыми приёмами. Путь внутри одного процесса закрыт изначально. Если нужна функциональность, находящаяся в 64-битной DLL, обработку приходится выполнять в отдельном 64-битном процессе, и мост COM — проверенный способ связать их между собой.
- Как мост COM связывает 32-битное приложение с 64-битной DLL?
- Вы создаёте 64-битный COM LocalServer (EXE-файл), который вызывает 64-битную DLL внутри себя, публикуете общий интерфейс COM через IDL или библиотеку типов, чтобы раскрыть типы, и заставляете 32-битное приложение вызывать его через COM. COM-подсистема пересекает границу процессов с помощью прокси, заглушек и маршалинга, поэтому 32-битная сторона может делать типобезопасные вызовы через общий интерфейс.
- Какие основные подводные камни у моста COM между процессами?
- Их три. Во-первых, регистрация COM для 32-битной и 64-битной версий выполняется раздельно, включая раздел реестра WOW6432Node. Во-вторых, пользовательские структуры требуют отдельного проектирования маршалинга, а не работают автоматически. В-третьих, межпроцессное взаимодействие добавляет накладные расходы на каждый вызов, поэтому в сценариях с высокой частотой обращений предпочтительнее группировать работу, а не делать множество мелких вызовов.
- Есть ли рабочий пример моста COM из 32-бит в 64-бит?
- Да. Полная рабочая реализация опубликована на GitHub в репозитории Call64bitDLLFrom32bitProc. В нём есть 64-битный EXE-файл COM LocalServer, 64-битная DLL с реальной обработкой, 32-битный клиент на WinForms и скрипты регистрации. Если собрать и зарегистрировать всё по инструкции из README, можно увидеть, как 32-битный процесс фактически вызывает 64-битную DLL.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.
Публичные ссылки