32 位应用调用 64 位 DLL 的 COM 桥接实例
· 小村 豪 · COM, Windows 开发, 32bit, 64bit
「想从 32 位应用调用 64 位 DLL」这一需求,在 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(通过 Proxy/Marshal 进行交互)
不过也有需要注意的地方。
- 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 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
要点:
- 32 位应用可以通过
ICalcService接口以类型安全的方式进行调用 - COM 运行时会使用已注册的 Proxy/Stub DLL、TypeLib 封送器、标准封送器等机制来跨越进程边界
- 由于存在进程间通信的开销,比起细粒度调用,更推荐采用批量处理
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
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
COM/OCX/ActiveX 开发中容易踩坑的注册与位数陷阱
本文从实务角度整理 COM、OCX、ActiveX 开发中容易踩坑的 32bit/64bit、Visual Studio 2022、regsvr32/Regasm、管理员权限、HKCR、STA/MTA 等问题。
Office 2024/Microsoft 365 中 ActiveX 无法运行的原因与排查步骤
梳理在 Office 2024/Microsoft 365 中 ActiveX 无法运行时,按默认禁用、32bit/64bit、COM 注册、依赖 DLL、IE 模式、Click-to-Run 日志的顺序进行排查的方法。
委托开发 Windows 应用程序前该梳理的事项
在委托外包开发 Windows 应用程序之前,梳理现有软件改造、设备联动、COM/ActiveX、发布与更新、维护等需要注意的要点。
Excel 报表输出的实现方法 - COM / Open XML / 模板
Excel 报表输出的设计会因为「是自动操作 Excel、还是直接生成 xlsx、还是保留现有 VBA」而大不相同。本文以 Windows 应用和业务系统中的报表输出为前提,整理方案选型的判断标准与推荐结构。
COM / ActiveX / OCX 是什么 - 区别与关系整理
从实务角度厘清 COM、ActiveX、OCX 三者的区别与关系:COM 是 Windows 组件互操作的二进制契约底层,ActiveX 是以 COM 为基础的可嵌入控件语境,OCX 则是 ActiveX 控件常见的扩展名。读完就能分清机制、组件、文件这三层边界。
常见问题
汇总了咨询这一主题时常见的问题。
- 32 位应用能直接调用 64 位 DLL 吗?
- 不能。32 位进程无法加载 64 位 DLL,这是操作系统层面的限制,并不是靠某种技巧就能规避的问题。由于在同一进程内调用的路径从一开始就被封死,因此需要把 64 位侧的处理分离到另一个进程中的架构设计。
- 32 位应用要使用 64 位 DLL 的功能,该怎么做?
- 王道做法是用 Out-of-proc COM(EXE 服务器)进行分离。64 位 DLL 由 64 位的 COM LocalServer(EXE)负责调用,32 位应用则通过 COM 接口以类型化的方式使用它。共享 COM 接口(IDL/TypeLib)以公开类型,COM 运行时会通过代理/存根与进程间通信为你跨越进程边界。
- COM 桥接架构有哪些注意事项?
- 主要有三点。第一,32 位与 64 位的注册是分开的(包括 WOW6432Node);第二,自定义结构体需要设计封送处理(marshaling);第三,进程间通信存在开销,因此需要留意高频率的细粒度调用。与频繁发出大量细小调用相比,更推荐把处理汇总为批量操作。
- 有实际可运行的示例代码吗?
- 有。GitHub 上的 Call64bitDLLFrom32bitProc 仓库公开了一整套示例,包含 64 位 COM LocalServer(EXE)、64 位 DLL、32 位客户端(WinForms),以及 COM 服务器注册・注销脚本。按照 README 中的步骤进行构建与注册,即可确认 32 位进程调用 64 位 DLL 的实际运行效果。
作者简介
本文作者的个人简介页面。
Go Komura
小村软件有限公司 代表
以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。