Пример COM-моста: вызов 64-битной DLL из 32-битного приложения
· Обновлено: · Го Комура · COM, Разработка Windows, 32-bit, 64-bit
История изменений (3 обновлений, последнее 30 Aug 2026)
Журнал изменений этой статьи. Там, где версия до правки была заархивирована, она остаётся доступной для чтения по постоянной ссылке с DOI.
- После слияния с main уточнён абзац про DllSurrogate. Утверждения статьи не менялись.
- Русский текст переписан как полноценный технический перевод, а не калька с японского. Утверждения статьи не менялись.
- Перед разделом 3 добавлен абзац. Если то, к чему нужно обратиться с 64-битной стороны, само является in-proc COM-сервером (DLL, зарегистрированной через InprocServer32), то достаточно добавить AppID к её CLSID и пустой DllSurrogate под этим AppID, чтобы она загрузилась в штатный dllhost.exe Windows — и писать EXE-сервер может не понадобиться. Там же отмечено, что разрядность запускаемого суррогата определяется библиотекой, а не клиентом, и что при зарегистрированном LocalServer32 суррогат не используется. Однако если вызвать нужно обычную нативную DLL, как в этой статье, то размещать в суррогате попросту нечего, и описанный далее подход с EXE-сервером остаётся необходимым. Процедура регистрации и граница между тем, что покрывает суррогат, и тем, где нужен собственный EXE, оставлены ссылками на две другие статьи. Открыть версию до этого обновления (DOI: 10.5281/zenodo.21619626)
- Первая публикация
Цитирование статьи(DOI (зарегистрированный архив): 10.5281/zenodo.21619625)
Приведённые ниже DOI относятся к ранее зарегистрированным архивным версиям, которые могут отличаться от текущего текста. Для ссылки на текущий текст используйте URL этой страницы.
Го Комура (2026). Пример COM-моста: вызов 64-битной DLL из 32-битного приложения. KomuraSoft LLC. https://comcomponent.com/ru/blog/2026/01/25/002-com-case-study-32bit-to-64bit/
- DOI (зарегистрированный архив)
- 10.5281/zenodo.21619625
- DOI (последняя зарегистрированная версия)
- 10.5281/zenodo.22170265
Вызов 64-битной DLL из 32-битного приложения — довольно типичное требование в Windows. Особенно когда нужно оставить существующие наработки и взять только 64-битные функции, схема COM-моста часто оказывается рабочим решением.
Для кого: те, кто сопровождает существующее 32-битное приложение Windows и хочет пользоваться 64-битной DLL или библиотекой. Текст рассчитан на уровень «про COM слышал, но сам не собирал».
Среда: 64-битная Windows (x64) и среда, в которой пишут C# (Visual Studio и аналоги). Чтобы зарегистрировать COM-сервер на всю машину (под HKEY_LOCAL_MACHINE), нужны права администратора. Базовое устройство COM разобрано в статье «Что такое COM — почему устройство Windows COM до сих пор красиво».
Оглавление
- Исходная ситуация
- Как решать
- Ход вызова (диаграмма последовательности)
- Пример кода (набросок)
- Полный пример
- Итог
- Источники
На схеме сплошная линия обозначает отношение, которое выполняется всегда, а пунктирная — условное отношение (условия указаны в пояснении к каждому отношению на странице сведений). Полный список отношений (всего 19, с доказательствами и степенью уверенности) и определения основных понятий собраны на странице сведений карты знаний (на японском). Данные: JSON-LD / Turtle
1. Исходная ситуация
Существующее 32-битное приложение оставляют как есть, но нужна обработка из 64-битной DLL. Проблема в том, что 32-битный процесс не загружает 64-битную DLL. Это ограничение на уровне ОС — его не обходят трюком.
Типичная картина такая:
- существующее 32-битное приложение — крупная наработка, быстро не мигрировать;
- в 64-битной DLL есть новая функциональность, либо зависимость существует только в 64-битном виде;
- со стороны 32-bit вызов нужен типизированный.
При таком сочетании путь внутри одного процесса закрыт с самого начала.
flowchart TB
accTitle: Схема исходной ситуации
accDescr: Существующему 32-битному приложению нужна обработка из 64-битной DLL, но 32-битный процесс не может загрузить 64-битную DLL. Это ограничение ОС, поэтому путь внутри одного процесса закрыт.
app["существующее 32-битное приложение"] --> want["нужна обработка из 64-битной DLL"]
want --> deny["в том же процессе загрузить нельзя"]
deny -.-> os["ограничение ОС, обойти нельзя"]
Рис. 1: 32-битный процесс не загружает 64-битную DLL, поэтому пути внутри одного процесса изначально нет.
2. Как решать
С этой главы пойдут термины, поэтому сначала минимальный словарь.
| Термин | Смысл |
|---|---|
| In-proc COM (DLL-сервер) | COM-компонент загружают в тот же процесс, что и вызывающая сторона. Быстро, но разрядность должна совпадать, иначе загрузить нельзя |
| Out-of-proc COM (EXE-сервер) | COM-компонент запускают как отдельный процесс. Связать можно и при разной разрядности |
| LocalServer | COM-сервер, который работает отдельным процессом на той же машине. Путь к EXE регистрируют в ключе реестра LocalServer32 |
| IDL / TypeLib | Описание формы интерфейса (имена методов, типы аргументов) — IDL — и его двоичный вид (TypeLib). Нужны, чтобы обе стороны смотрели на один и тот же контракт |
| Маршалинг | Упаковка аргументов и возвращаемых значений в форму, которую можно передать через границу процесса. Обратная операция — демаршалинг |
| Proxy / Stub | Код-посредник, который реально делает маршалинг. Со стороны вызова стоит Proxy, со стороны сервера — Stub |
| WOW6432Node | Место на 64-bit Windows, где физически лежит содержимое реестра для 32-битных приложений. Одинаковое имя ключа на стороне 32-bit и 64-bit означает разное содержимое |
Базовое решение — разнести стороны через Out-of-proc COM (EXE-сервер). 64-битную DLL вызывает 64-битный COM-сервер (EXE), а 32-битное приложение пользуется ею через COM.
flowchart TB
accTitle: Базовая схема COM-моста
accDescr: 32-битное приложение через COM вызывает EXE 64-битного COM-сервера, а этот сервер внутри вызывает 64-битную DLL — разнесение по разным процессам.
a32["32-битное приложение"] -->|"вызывает через COM"| srv["64-битный COM-сервер (EXE)"]
srv -->|"вызывает внутри"| dll["64-битная DLL"]
Рис. 2: 64-битную DLL держат в 64-битном EXE-сервере, а 32-битное приложение пользуется этим сервером через COM.
Порядок такой:
- Подготовить 64-битный COM LocalServer (EXE) и внутри него вызывать 64-битную DLL.
- Опубликовать типы общим COM-интерфейсом (IDL/TypeLib).
- 32-битное приложение вызывает COM типизированно (обмен через Proxy/Marshal).
Есть и оговорки.
- Регистрация 32-bit и 64-bit раздельная (включая WOW6432Node)
- Для собственных структур нужно отдельно проектировать маршалинг
- Есть накладные расходы IPC, поэтому с частыми вызовами осторожнее
Иными словами, стандартный путь — вынести 64-битную обработку в отдельный процесс и связать стороны мостом через COM.
flowchart TB
accTitle: Три шага сборки моста
accDescr: Готовят 64-битный COM LocalServer и внутри вызывают 64-битную DLL, публикуют типы интерфейса через IDL и TypeLib, после чего 32-битное приложение вызывает их типизированно.
s1["подготовить 64-битный COM LocalServer"] --> s2["опубликовать типы в IDL / TypeLib"]
s2 --> s3["32-битное приложение вызывает типизированно"]
s3 -.-> ps["обмен через Proxy / Marshal"]
Рис. 3: Мост собирается в три шага: LocalServer, публикация типов, типизированный вызов.
Если на 64-битной стороне нужно использовать уже готовый in-proc COM-сервер (DLL, которую регистрируют через InprocServer32), EXE-сервер иногда можно не писать. К CLSID добавляют AppID, а в ключ этого AppID записывают пустую строку DllSurrogate — тогда DLL загружается в штатный суррогатный процесс Windows (для 64-битной DLL это System32\dllhost.exe), и 32-битному клиенту она видна как out-of-proc COM-сервер в отдельном процессе. Путь к EXE в LocalServer32 при этом не регистрируют: наоборот, если LocalServer32 есть, суррогат не используется. В обратную сторону (64-битное приложение вызывает 32-битную COM DLL) механизм тот же: разрядность запускаемого суррогата задаёт сторона DLL, а не клиент. Но если, как в этой статье, вызвать нужно обычную нативную DLL, размещать в суррогате нечего — COM-сервера нет, — поэтому дальше нужен способ с EXE-сервером. Порядок регистрации — в пункте 3.5 статьи Ловушки регистрации и bitness при разработке COM/OCX/ActiveX (там исходят из 32-битной DLL, поэтому меняется только view, в который пишут значение AppID); где суррогата достаточно, а где нужен свой EXE — в разделе 5.2 статьи Как сегодня поступать с ActiveX / OCX - таблица решений: оставить, обернуть или заменить.
3. Ход вызова (диаграмма последовательности)
Ниже — ход вызова, когда 32-битное приложение обращается к обработке в 64-битной DLL.
sequenceDiagram
participant App as 32-битный клиент
box rgba(100,100,255,0.1) Обрабатывает зарегистрированная подсистема маршалинга COM
participant Proxy as COM Proxy<br/>(сторона 32-bit)
participant RPC as RPC/IPC<br/>(межпроцессное взаимодействие)
participant Stub as COM Stub<br/>(сторона 64-bit)
end
participant Server as 64-bit COM Server<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
Рис. 4: Вызов 32-битного приложения доходит до 64-битного сервера и 64-битной DLL через Proxy, межпроцессное взаимодействие и Stub; результат возвращается тем же путём.
Важно:
- 32-битное приложение вызывает типизированно через интерфейс
ICalcService - среда выполнения COM пересекает границу процессов через зарегистрированную Proxy/Stub DLL, маршалер библиотеки типов, стандартный маршалер и т. п.
- из-за накладных расходов межпроцессного взаимодействия лучше собирать работу в более крупные порции, чем гонять мелкие вызовы
flowchart TB
accTitle: Как думать о гранулярности вызовов
accDescr: У межпроцессного взаимодействия есть накладные расходы, поэтому частые мелкие вызовы накапливаются, а при укрупнении порций влияние меньше.
ipc["накладные расходы межпроцессного взаимодействия"] --> fine["на мелких повторных вызовах накапливаются"]
ipc --> batch["при укрупнении порций влияние меньше"]
batch -.-> rec["предпочитать этот вариант"]
Рис. 5: Каждый переход через границу процессов имеет свою цену, поэтому вызовы лучше укрупнять.
4. Пример кода (набросок)
4.1. Общий интерфейс, сервер и клиент
Ниже — набросок идеи. Чтобы это заработало, нужна регистрация из пункта 4.2.
// Общий интерфейс (аналог IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
int Add(int a, int b);
}
// 64-bit COM LocalServer (сторона EXE)
[ComVisible(true)]
[Guid("1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("KomuraSoft.CalcService")]
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-bit работает типизированно. COM внутри использует Proxy/Stub и сам делает вызов через IPC.
Атрибут [ProgId("KomuraSoft.CalcService")] стоит затем, чтобы клиент мог найти сервер через Type.GetTypeFromProgID("KomuraSoft.CalcService"). ProgID — всего лишь «человекочитаемый псевдоним»; сервер на самом деле находят по регистрации CLSID, о которой ниже.
flowchart LR
accTitle: От ProgID до сервера
accDescr: ProgID, который указывает клиент, — лишь человекочитаемый псевдоним; по нему берут CLSID, а реальный сервер находят по регистрации CLSID.
progid["ProgID (человекочитаемый псевдоним)"] --> clsid["регистрация CLSID"]
clsid --> srv["сервер найден"]
Рис. 6: ProgID — входной псевдоним; на сервер указывает регистрация CLSID.
4.2. Минимальная процедура регистрации
COM устроен так: среда выполнения COM берёт зарегистрированный в реестре CLSID и запускает сервер. Незарегистрированный код не заработает никогда (Type.GetTypeFromProgID вернёт null или CreateInstance даст REGDB_E_CLASSNOTREG). Для EXE-сервера (LocalServer) нужны, по сути, только три ключа.
| Что регистрируют | Ключ | Значение |
|---|---|---|
| Соответствие ProgID → CLSID | HKEY_CLASSES_ROOT\KomuraSoft.CalcService\CLSID |
{1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11} |
| CLSID → путь к EXE | HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\LocalServer32 |
Полный путь к EXE 64-битного COM-сервера |
| Обратный поиск CLSID → ProgID | HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\ProgID |
KomuraSoft.CalcService |
И здесь же ловушка, вокруг которой построена эта статья. В документации Microsoft прямо сказано: HKEY_LOCAL_MACHINE\SOFTWARE\Classes общий для 32-битных и 64-битных приложений, а подраздел CLSID (и Interface и т. п.) на стороне 32-bit и 64-bit разный (физически 32-битная копия лежит в WOW6432Node). Ключ ProgID достаточно записать один раз — его видят обе стороны. Регистрацию CLSID нужно писать и в 32-bit view, и в 64-bit view, иначе 32-битный клиент сервер не найдёт.
flowchart TB
accTitle: Что в реестре общее, а что раздельное
accDescr: Ключ ProgID прямо под Classes достаточно записать один раз — его видят и 32-bit, и 64-bit. Регистрацию под CLSID нужно писать и в 32-bit view, и в 64-bit view; если нет стороны 32-bit, 32-битный клиент сервер не увидит.
progk["ключ ProgID (прямо под Classes)"] --> shared["виден с обеих сторон"]
clsk["регистрация под CLSID"] --> v64["писать в 64-bit view"]
clsk --> v32["писать в 32-bit view"]
v32 -.-> wow["физически это WOW6432Node"]
v32 -.-> warn["без этого 32-bit не видит"]
Рис. 7: Ключ ProgID общий, а ветка CLSID своя для каждого view 32-bit/64-bit, поэтому регистрируют в обоих.
Надёжный способ — командная строка с правами администратора и /reg:32 /reg:64 у команды reg (сам прописывать Wow6432Node в путь Microsoft не рекомендует).
:: Запускать в командной строке с правами администратора
set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService
set SERVER=C:\Program Files\KomuraSoft\CalcServer.exe
:: 1) ProgID → CLSID (прямо под HKLM\SOFTWARE\Classes общее для 32/64)
reg add "HKLM\SOFTWARE\Classes\%PROGID%\CLSID" /ve /d "%CLSID%" /f
:: 2) CLSID → LocalServer32 и ProgID (под CLSID 32/64 раздельные, писать в оба)
:: В значение LocalServer32 путь к исполняемому файлу кладут вместе с кавычками.
:: Если написать /d "%SERVER%", кавычки съест разбор аргументов reg.exe,
:: и в значении останется C:\Program Files\... Как командную строку COM
:: режет это по пробелу и сначала ищет C:\Program.exe
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID" /ve /d "%PROGID%" /f /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /d "\"%SERVER%\"" /f /reg:32
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\ProgID" /ve /d "%PROGID%" /f /reg:32
:: Проверить сохранённое значение. Правильно, если видно "C:\Program Files\..." с кавычками
reg query "HKLM\SOFTWARE\Classes\CLSID\%CLSID%\LocalServer32" /ve /reg:64
Кавычки в LocalServer32 — не вопрос хорошего тона. Без кавычек C:\Program Files\... можно прочитать как «запустить C:\Program, а Files\... передать аргументом». Если у кого-то есть право положить C:\Program.exe, запустится оно. Для пути с пробелами кавычки обязательны.
flowchart TB
accTitle: Как кавычки в LocalServer32 меняют разбор
accDescr: Если путь в LocalServer32 сохранить без кавычек, его можно разрезать по пробелу, и в среде, где можно положить C:\Program.exe, запустится оно. Если сохранить вместе с кавычками, стартует задуманный EXE.
noq["сохранить без кавычек"] --> cut["можно разрезать по пробелу"]
cut --> evil["может сначала стартовать C:\Program.exe"]
q["сохранить вместе с кавычками"] --> safe["стартует задуманный EXE"]
Рис. 8: Если путь с пробелами зарегистрировать без кавычек, появляется возможность запустить другой исполняемый файл.
Снятие с регистрации — просто удалить те же ключи.
set CLSID={1C9B6F4D-1E9A-4E61-9A4F-6A0F1D2D9A11}
set PROGID=KomuraSoft.CalcService
reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:64
reg delete "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /f /reg:32
reg delete "HKLM\SOFTWARE\Classes\%PROGID%" /f
Плюс сам EXE при старте должен представиться COM: «этот CLSID — мой». В C/C++ это CoRegisterClassObject, в .NET Framework — RegistrationServices.RegisterTypeForComClients. Реестр доводит COM только до запуска EXE. Если представления нет, EXE стартует, а объект создать нельзя — сбой, который трудно сразу понять.
flowchart TB
accTitle: Регистрация в реестре и представление EXE
accDescr: Реестр доводит COM только до запуска EXE. Объект можно создать лишь после того, как стартовавший EXE представится как ответственный за этот CLSID. Без представления EXE стартует, а объект не создаётся.
regd["регистрация в реестре"] --> boot["COM запускает EXE"]
boot --> ann["EXE представляется как ответственный за CLSID"]
ann --> ok["объект можно создать"]
boot -.->|"без представления"| ng["стартует, но создать нельзя"]
Рис. 9: Реестр отвечает за запуск EXE; дальше без представления самого EXE объект не создать.
Если нужно только попробовать при разработке, ту же структуру можно записать в HKCU\SOFTWARE\Classes вместо HKLM — права администратора не нужны (HKEY_CLASSES_ROOT — составной вид HKLM и HKCU). Но HKCU\SOFTWARE\Classes\CLSID тоже раздельный для 32-bit и 64-bit, так что писать в оба view всё равно нужно.
4.3. В .NET Framework и в .NET (5 и новее) это делается по-разному
Код выше на C#, но процедура сильно зависит от того, какое .NET вы берёте. Если смешать — застрянете.
| .NET Framework | .NET (Core 3.0 / 5 и новее) | |
|---|---|---|
| Инструмент регистрации | Есть RegAsm.exe (но он создаёт регистрацию InprocServer32 для in-proc, так что LocalServer32 всё равно пишут сами) |
Инструмента уровня RegAsm нет |
| Обычная публикация в COM | Атрибуты на сборке и RegAsm |
<EnableComHosting>true</EnableComHosting> даёт *.comhost.dll, регистрируют через regsvr32 (только in-proc) |
| Генерация TypeLib (.tlb) | Можно через TlbExp / RegAsm /tlb |
Не поддерживается. IDL пишут руками и компилируют MIDL (начиная с .NET 6 готовый .tlb можно встроить в comhost) |
| Указание CLSID | Можно опустить | Для класса, который создаёт COM, CLSID нужно указать явно |
| Поведение AnyCPU | Доступен клиентам и 32-bit, и 64-bit | Сопровождающий *.comhost.dll по умолчанию 64-bit, поэтому доступен только 64-битному клиенту |
Схема этой статьи (EXE-сервер) в .NET (5 и новее) лежит вне стандартного EnableComHosting, поэтому регистрацию пишут сами. Официальный образец Microsoft — OutOfProcCOM; если собирать на стороне .NET, с него и начинают.
flowchart TB
accTitle: Как собирать EXE-сервер на .NET (5 и новее)
accDescr: Схема EXE-сервера этой статьи в .NET 5 и новее лежит вне стандартного EnableComHosting, поэтому регистрацию пишут сами; отправная точка — официальный образец OutOfProcCOM.
exe["EXE-сервер на .NET (5 и новее)"] --> range["вне стандартного EnableComHosting"]
range --> self["регистрацию пишут сами"]
self -.-> smp["отправная точка — официальный образец OutOfProcCOM"]
Рис. 10: EXE-сервер на .NET (5 и новее) лежит вне стандартных средств, поэтому регистрацию пишут самостоятельно.
5. Полный пример
Концепцию выше в реально запускаемом виде мы выложили на GitHub.
Call64bitDLLFrom32bitProc - GitHub
В репозитории есть:
- Call64bitDLLFrom32bitProc/ — 64-bit COM LocalServer (EXE)
- X64DLL/ — 64-битная DLL (сама обработка)
- X86App/ — 32-битный клиент (WinForms)
- scripts/ — скрипты регистрации и снятия с регистрации COM-сервера
Если собрать и зарегистрировать по README, можно убедиться, что 32-битный процесс действительно вызывает 64-битную DLL.
6. Итог
COM-мост не универсален: задачи, которым он подходит и которым нет, различаются ясно. Прежде чем принимать решение, примерьте свой случай к таблице.
| Подходит | Не подходит |
|---|---|
| Само 32-битное приложение переписать нельзя (стоимость переделки не окупается) | Сторону 32-bit можно пересобрать в 64-bit (это самый короткий путь) |
| Вызовы крупной гранулярности (один вызов — одно изображение, один файл и т. п.) | Мелкие вызовы десятки тысяч раз, часто и по одному элементу (накладные расходы IPC начинают доминировать) |
| Обмениваются числами, строками, массивами — типами, которые удобно маршалировать | Гоняют туда-сюда сырые указатели или сложные собственные структуры |
| Хочется, чтобы при падении обработки на стороне 64-bit само приложение осталось живым (разделение процессов здесь плюс) | Не хочется писать восстановление после сбоя или перезапуска сервера |
| Нужен типизированный вызов (IntelliSense и проверка на этапе компиляции) | Хватает разовой пакетной обработки, достаточно передать данные через стандартный ввод-вывод или файлы |
Обратная сторона последней строки: всегда стоит рассмотреть простой вариант «сделать 64-битную обработку обычным консольным EXE и обмениваться аргументами и файлами». COM-мост имеет смысл, когда нужно сохранить типизированные вызовы и когда сервер с состоянием вызывают многократно.
flowchart TB
accTitle: Развилка между COM-мостом и простым вариантом
accDescr: Если нужны типизированные вызовы или сервер с состоянием, который вызывают многократно, работает COM-мост. Если хватает разовой пакетной обработки, стоит рассмотреть простой вариант: консольный EXE и обмен аргументами и файлами.
q{"Нужны типизированные вызовы или состояние?"} -->|"да"| br["COM-мост"]
q -->|"нет"| alt["консольный EXE, аргументы и файлы"]
Рис. 11: Нужны ли типизированные вызовы и сохранение состояния — развилка между мостом и простым вариантом.
Дальше удобно идти в таком порядке.
- Сначала клонировать образец из главы 5, собрать и зарегистрировать по README и получить у себя один работающий пример.
- Взять одну функцию своей 64-битной DLL, добавить один метод в интерфейс вроде
ICalcServiceиз образца и прогнать этот вызов. - Когда пройдёт — измерить число вызовов и объём данных за один вызов. Решение «укрупнить гранулярность» (несколько вызовов свернуть в один) лучше принять здесь: меньше придётся возвращаться.
7. Источники
- Обзор 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
- Перенаправитель реестра WOW64 (
HKLM\SOFTWARE\Classesобщий, веткаCLSIDраздельная для 32/64) https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys - Публикация компонентов .NET (Core / 5 и новее) в COM https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
Похожие статьи
Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.
Ловушки регистрации и bitness при разработке COM/OCX/ActiveX
Практический разбор типичных ловушек COM, OCX и ActiveX: разрядность 32/64-бит, Visual Studio 2022, regsvr32 и Regasm, права администрато...
Что такое Reg-Free COM: механизм использования COM без регистрации
Разбираем основы Reg-Free COM: роль контекста активации и манифестов, преимущества, ограничения и критерии выбора в реальных проектах.
Как построить вывод отчётов Excel: COM / Open XML / шаблоны
Проектирование вывода отчётов Excel сильно меняется в зависимости от того, автоматизируете ли вы сам Excel, собираете ли xlsx напрямую ил...
Введение в Media Foundation: как понять API через COM
Разбираем, что такое Media Foundation, вместе с базовой терминологией Windows-медиа-API — COM, HRESULT, IMFSourceReader, MFT — в том поря...
STA и MTA в COM: модель потоков и как не получить зависание
STA и MTA в COM — модель апартаментов, которая задаёт, с какого потока можно вызывать компонент. Разбираем UI-поток, цикл сообщений, марш...
Связанные темы
Эти страницы показывают тему статьи в более широком контексте услуг и решений.
Технические темы Windows
Раздел о разработке Windows, расследовании сбоев и использовании существующих активов.
Миграция ActiveX
Решения о сохранении, обёртке или замене компонентов COM / ActiveX / OCX.
Совместимость 32 и 64 бит
Совместимость 32/64 бит, нативные границы и решения по проектированию Windows.
Услуги по этой теме
Статья напрямую связана со следующими услугами.
Использование и перенос существующих активов
Речь о том, как оставить 32-битные наработки и перекинуть мост на 64-битную сторону, поэтому тема напрямую связана с повторным использованием существующего кода и поддержкой миграции.
Технические консультации и ревью дизайна
Если сначала нужно продумать COM-мост и то, где провести границы процессов, это можно разобрать как техническую консультацию и ревью архитектуры.
Частые вопросы
Вопросы, которые часто возникают при консультациях по теме статьи.
- Можно ли напрямую вызвать 64-битную DLL из 32-битного приложения?
- Нет. 32-битный процесс не может загрузить 64-битную DLL. Это ограничение на уровне ОС, а не то, что обходится трюком. Путь внутри одного процесса закрыт с самого начала, поэтому 64-битную обработку нужно вынести в отдельный процесс.
- Как пользоваться функциями 64-битной DLL из 32-битного приложения?
- Стандартный путь — разнести стороны через Out-of-proc COM (EXE-сервер). 64-битную DLL вызывает 64-битный COM LocalServer (EXE), а 32-битное приложение пользуется ею типизированно через COM-интерфейс. Типы публикуют общим COM-интерфейсом (IDL/TypeLib), а среда выполнения COM пересекает границу через Proxy/Stub и межпроцессное взаимодействие.
- На что смотреть в схеме COM-моста?
- На три вещи. Регистрация 32-bit и 64-bit раздельная (включая WOW6432Node). Для собственных структур нужно отдельно проектировать маршалинг. Из-за накладных расходов IPC осторожнее с частыми мелкими вызовами: лучше собирать работу в более крупные порции, чем гонять поток мелких обращений.
- Есть ли пример, который реально запускается?
- Да. В репозитории Call64bitDLLFrom32bitProc на GitHub выложен полный набор: 64-битный COM LocalServer (EXE), 64-битная DLL, 32-битный клиент (WinForms) и скрипты регистрации и снятия с регистрации COM-сервера. Если собрать и зарегистрировать по README, можно убедиться, что 32-битный процесс действительно вызывает 64-битную DLL.
Об авторе
Страница с профилем автора статьи.
Го Комура
Представитель KomuraSoft LLC
Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.