32 位应用调用 64 位 DLL 的 COM 桥接实例

· 更新日期: · · COM, Windows 开发, 32bit, 64bit

更新记录(3 条,最后更新 2026年09月03日)

本文的修改记录。已保存的更新前版本,可通过带有 DOI 的永久链接阅读。

本文此前是日文原文的节译,缺少大量章节、表格、Mermaid 图、图题、脚注与 FAQ。现已改写为日文原文的完整译文,技术主张与日文版一致,并补上了此前缺失的图与表,同时统一了全篇的术语译法。 查看更新前的版本 (DOI: 10.5281/zenodo.22276551)
补充了日文原文中已有的咨询引导(consultation_services)。正文内容没有改动。 查看更新前的版本 (DOI: 10.5281/zenodo.22170266)
在第 3 节之前补充了一段。如果你想从 64 位这一侧调用的东西本身就是 in-proc 的 COM 服务器(通过 InprocServer32 注册的 DLL),那么只要给它的 CLSID 加上 AppID,并在该 AppID 键下写入空字符串的 DllSurrogate,它就能被 Windows 自带的 dllhost.exe 承载,有时根本不必自己写 EXE 服务器。这一段还指出,启动的 surrogate 的位数由 DLL 决定而非客户端,并且只要注册了 LocalServer32,surrogate 就不会被使用。不过,如果要调用的只是一个普通的原生 DLL(本文正是这种情况),那就压根没有可供承载的 COM 服务器,后文介绍的 EXE 服务器方案依然是必要的。注册步骤,以及 surrogate 是否够用、何时该自己写 EXE 的界线,分别交给了指向另外两篇文章的链接。 查看更新前的版本 (DOI: 10.5281/zenodo.21615409)
首次发布
引用本文(DOI: 10.5281/zenodo.21615408)

本文保存于 Zenodo。以下同时提供始终指向最新版本的 DOI,以及固定于您正在阅读版本的 DOI。

小村 豪(2026)。《32 位应用调用 64 位 DLL 的 COM 桥接实例》。小村软件有限公司。https://doi.org/10.5281/zenodo.21615408 https://comcomponent.com/zh-CN/blog/2026/01/25/002-com-case-study-32bit-to-64bit/

DOI(最新版本)
10.5281/zenodo.21615408
DOI(此版本)
10.5281/zenodo.22281915

想从 32 位应用调用 64 位 DLL,这类需求在 Windows 上相当典型。 尤其是想在保留现有资产的前提下只使用 64 位一侧的功能时,COM 桥接往往能成为现实可行的解决方案。

目标读者: 正在维护现有 32 位 Windows 应用,同时又想使用 64 位一侧的 DLL 或库的人。本文按照“听说过 COM,但没有自己动手搭过”的前提来写。

前提环境: 64 位版本的 Windows(x64),以及能编写 C# 的开发环境(例如 Visual Studio)。把 COM 服务器注册到整台机器范围(HKEY_LOCAL_MACHINE 之下)的操作需要管理员权限。COM 的基本思路已在“什么是 COM - 为什么 Windows COM 的设计至今依然优美”中做了梳理。

目录

  1. 假设情境
  2. 解决方法
  3. 处理流程(序列图)
  4. 示例代码(概念示意)
  5. 完整示例代码
  6. 总结
  7. 参考资料

图中实线表示始终成立的关系,虚线表示带条件的关系(成立条件写在详情页各关系的说明中)。关系的完整列表(共 19 条,附依据与确信度)以及主要概念的定义,汇总在知识地图详情页(日文)。数据:JSON-LD / Turtle

1. 假设情境

这是一种希望保留现有的 32 位应用不变,同时使用 64 位 DLL 的处理逻辑的情形。 但是,32 位进程无法加载 64 位 DLL。这是操作系统层面的限制,并不是动动脑筋就能绕开的问题。

常见的情况大致如下。

  • 现有的 32 位应用作为资产体量较大,无法立刻迁移
  • 新功能位于 64 位 DLL 一侧,或者依赖库只提供 64 位版本
  • 希望从 32 位一侧以“类型化”的方式调用

在这种组合下,在同一进程内调用的路径从一开始就被封死了。

假设情境的构图该图表示:32 位的现有应用想使用 64 位 DLL 的处理逻辑,但 32 位进程无法加载 64 位 DLL,这一操作系统层面的限制封死了在同一进程内调用的路径。32 位的现有应用想使用 64 位 DLL 的处理逻辑同一进程内无法加载操作系统层面的限制,无法绕开

图1:由于 32 位进程无法加载 64 位 DLL,在同一进程内调用的路径从一开始就不存在。

2. 解决方法

从本节开始会连续出现专业术语,所以先把最低限度的词汇列出来。

术语 含义
In-proc COM(DLL 服务器) 把 COM 组件加载到与调用方同一个进程中使用的形式。速度快,但位数不一致就无法加载
Out-of-proc COM(EXE 服务器) 把 COM 组件作为独立进程启动后使用的形式。位数不同也能协同工作
LocalServer 指在同一台电脑上以独立进程运行的 COM 服务器。要把 EXE 的路径注册到注册表的 LocalServer32 键
IDL / TypeLib 描述接口形态(方法名、参数类型)的定义(IDL),以及把它编译成二进制的产物(TypeLib)。用途是让两侧看到同一份“契约”
封送处理(marshaling) 为了跨越进程边界,把参数和返回值重新打包成可传输的形式。反向操作称为解封送
Proxy / Stub 实际执行封送处理的代理代码。调用方一侧竖起 Proxy,服务器一侧竖起 Stub
WOW6432Node 在 64 位 Windows 上,面向 32 位应用的注册表内容实际存放的位置。即使键名相同,32 位一侧和 64 位一侧的内容也是分开的

解决问题的基本思路是用 Out-of-proc COM(EXE 服务器)进行分离。 64 位 DLL 由 64 位的 COM 服务器(EXE)负责调用,32 位应用则通过 COM 来使用它。

COM 桥接的基本构成该图表示 32 位应用经由 COM 调用 64 位 COM 服务器 EXE,而该服务器在内部调用 64 位 DLL 这样一种独立进程分离的构成。经由 COM 调用在内部调用32 位应用64 位 COM 服务器(EXE)64 位 DLL

图2:把 64 位 DLL 交给 64 位的 EXE 服务器持有,32 位应用经由 COM 使用该服务器。

流程如下。

  1. 准备一个 64 位的 COM LocalServer(EXE),在其内部调用 64 位 DLL
  2. 共享 COM 接口(IDL/TypeLib),公开类型
  3. 32 位应用以“类型化”的方式调用 COM(通过 Proxy/Marshal 进行交互)

不过也有需要注意的地方。

  • 32 位/64 位的注册是分开的(包括 WOW6432Node)
  • 自定义结构体需要设计封送处理
  • 存在 IPC 开销,因此高频率调用需要留意

也就是说,“把 64 位的处理转移到另一个进程,再用 COM 搭桥连接”才是主流做法。

搭建桥接的三个步骤该图表示三个步骤的流程:准备 64 位的 COM LocalServer 并在其内部调用 64 位 DLL,用 IDL 和 TypeLib 公开接口的类型,再由 32 位应用以类型化的方式调用。准备 64 位的 COM LocalServer用 IDL / TypeLib 公开类型32 位应用以类型化的方式调用通过 Proxy / Marshal 进行交互

图3:准备 LocalServer、公开类型、类型化调用这三个阶段共同构成了桥接。

另外,如果 64 位一侧想要使用的东西本来就是 in-proc 的 COM 服务器(用 InprocServer32 注册的 DLL),那么有时不必自己写 EXE 服务器也能解决。给 CLSID 加上 AppID,并往那个 AppID 键里写入空字符串的 DllSurrogate,该 DLL 就会被承载到 Windows 自带的 surrogate 进程(如果是 64 位 DLL,就是 System32\dllhost.exe)里,在 32 位客户端看来,它就是一个在独立进程中运行的 out-of-proc COM 服务器(这并不是把 EXE 的路径注册到 LocalServer32。恰恰相反,只要存在 LocalServer32,surrogate 就不会被使用)。反方向(从 64 位应用使用 32 位的 COM DLL)机制也一样,启动起来的 surrogate 的位数不是由客户端决定,而是由 DLL 一侧决定。不过,如果像本文这样想调用的只是一个普通的原生 DLL,那就根本不存在可以放进 surrogate 的 COM 服务器,因此仍然需要后文介绍的 EXE 服务器方案。注册步骤整理在 COM/OCX/ActiveX 开发中容易踩坑的注册与位数陷阱 的 3.5(那篇是以 32 位 DLL 为前提的,因此只需把写入 AppID 值的 view 换成对应的一侧来读即可);至于 surrogate 是否够用、还是要自己写 EXE,这条界线整理在 ActiveX / OCX 现在该如何处理 - 保留、封装、替换的判断表 的 5.2。

3. 处理流程(序列图)

以下是 32 位应用调用 64 位 DLL 处理逻辑时的流程。

由已注册的 COM 封送基础设施处理64 位 DLL64 位 COM Server(EXE)COM Stub(64 位侧)RPC/IPC(进程间通信)COM Proxy(32 位侧)32 位客户端应用64 位 DLL64 位 COM Server(EXE)COM Stub(64 位侧)RPC/IPC(进程间通信)COM Proxy(32 位侧)32 位客户端应用对参数进行封送对参数进行解封送对返回值进行封送对返回值进行解封送ICalcService.Add(1, 2)序列化后的数据跨越进程边界转发Add(1, 2)调用原生函数结果: 3结果: 3序列化后的结果跨越进程边界转发结果: 3

图4:32 位应用发出的调用经过 Proxy、进程间通信、Stub 到达 64 位服务器和 64 位 DLL,结果再沿同一条路径返回。

要点:

  • 32 位应用可以通过 ICalcService 接口以类型安全的方式进行调用
  • COM 运行时会使用已注册的 Proxy/Stub DLL、TypeLib 封送器、标准封送器等机制来跨越进程边界
  • 由于存在进程间通信的开销,比起细粒度调用,更推荐采用批量处理
调用粒度的思路该图表示这样一种思路:进程间通信存在开销,因此高频地进行细粒度调用会不断累加,把处理向批量方向靠拢更为可取。进程间通信的开销反复进行细粒度调用会不断累加向批量处理靠拢则影响较小更可取的是这一侧

图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 位 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 位一侧就能以“类型化”的方式处理。 COM 会在内部使用代理/存根,并通过 IPC 完成调用。

之所以加上 [ProgId("KomuraSoft.CalcService")],是为了让客户端能用 Type.GetTypeFromProgID("KomuraSoft.CalcService") 找到它。ProgID 只不过是“人类可读的别名”,真正定位到服务器的,是接下来要说明的 CLSID 注册。

从 ProgID 到达服务器的过程该图表示解析顺序:客户端指定的 ProgID 只是人类可读的别名,由它查出 CLSID,再通过 CLSID 的注册找到真正的服务器。ProgID(人类可读的别名)CLSID 的注册找到服务器

图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 64 位 COM 服务器 EXE 的完整路径
CLSID → ProgID 的反向查找 HKEY_CLASSES_ROOT\CLSID\{1C9B6F4D-…}\ProgID KomuraSoft.CalcService

这里就有本文主题本身的那个陷阱。Microsoft 的文档中明确写道:HKEY_LOCAL_MACHINE\SOFTWARE\Classes 由 32 位应用和 64 位应用共享,而它之下的 CLSID 子键(以及 Interface 等)在 32 位一侧和 64 位一侧是各自独立的(32 位一侧的实体就是 WOW6432Node)。也就是说,ProgID 的键写一次两边都能看到,但 CLSID 的注册必须同时写入 32 位视图和 64 位视图,否则 32 位客户端找不到服务器。

注册表中共享的部分与分开的部分该图表示:Classes 直下的 ProgID 键写一次就能被 32 位和 64 位两侧看到,而 CLSID 之下在 32 位视图和 64 位视图中是分开的,必须两边都写,缺了 32 位一侧,32 位客户端就找不到服务器。ProgID 的键(Classes 直下)两侧都能看到CLSID 之下的注册写入 64 位视图写入 32 位视图实体是 WOW6432Node缺失则 32 位一侧看不到

图7:ProgID 的键是共享的,而 CLSID 之下按 32 位/64 位视图各自独立,所以两边都要注册。

最稳妥的做法是在管理员权限的命令提示符中,使用 reg 命令的 /reg:32 /reg:64(Microsoft 不推荐自己在路径里写 Wow6432Node)。

:: 在管理员权限的命令提示符中执行
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\... 就可能被解释成“把 Files\... 作为参数传给 C:\Program”。结果是,在存在有权创建 C:\Program.exe 的对象的环境里,那个程序有可能被抢先启动。只要路径中包含空格,就一定要加上引号。

LocalServer32 有无引号导致的解释差异该图表示:如果 LocalServer32 的值中不带引号地写入路径,就会成立在空格前截断的解释,在可以放置 C:\Program.exe 的环境中那个程序可能被抢先启动;而连同引号一起存入,则会启动预期的 EXE。不带引号存入成立在空格前截断的解释C:\Program.exe 可能被抢先启动连同引号一起存入启动预期的 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 启动了却创建不出对象这种很难看懂的失败。

注册表登记与 EXE 一侧的声明该图表示:注册表登记只负责到 COM 启动 EXE 为止,启动后的 EXE 声明自己负责该 CLSID,才终于能创建出对象;漏掉声明就会变成 EXE 启动了却创建不出对象的失败。漏掉声明时注册表登记COM 启动 EXEEXE 声明自己负责该 CLSID能够创建对象启动了却创建不出来的失败

图9:注册表登记管到 EXE 启动为止,再往后如果没有 EXE 自己的声明,对象就创建不出来。

顺带一提,如果只是开发过程中试一试,把同样的结构写到 HKCU\SOFTWARE\Classes 而不是 HKLM,没有管理员权限也能完成注册(HKEY_CLASSES_ROOT 是 HKLM 与 HKCU 的合成视图)。不过 HKCU\SOFTWARE\Classes\CLSID 同样按 32 位/64 位分开对待,所以两个视图都要写这一点并没有变。

4.3. .NET Framework 与 .NET(5 及以上)的做法不同

上面的代码是 C#,但具体步骤会因为用哪一种 .NET 而有很大差别。把两者混在一起就会卡住。

  .NET Framework .NET(Core 3.0 / 5 及以上)
注册工具 有 RegAsm.exe(但它生成的是 in-proc 用的 InprocServer32 注册,所以 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 位/64 位两种客户端都能使用 随附的 *.comhost.dll 默认是 64 位的,因此只有 64 位客户端能使用

本文的构成(EXE 服务器)在 .NET(5 及以上)中超出了标准 EnableComHosting 的适用范围,所以注册处理要自己写。Microsoft 官方提供了示例 OutOfProcCOM,因此在 .NET 一侧搭建时可以以它为起点。

在 .NET(5 及以上)中搭建 EXE 服务器的路径该图表示这样一条路径:本文的 EXE 服务器构成在 .NET 5 及以上超出了标准 EnableComHosting 的适用范围,因此注册处理要自己写,而官方示例 OutOfProcCOM 可以作为起点。.NET(5 及以上)中的 EXE 服务器超出标准 EnableComHosting 的适用范围注册处理自己写官方示例 OutOfProcCOM 作为起点

图10:.NET(5 及以上)的 EXE 服务器超出了标准功能的适用范围,因此注册处理要以自己编写为前提。

5. 完整示例代码

我们把上述概念以实际可运行的形式实现出来,并在 GitHub 上公开了示例。

Call64bitDLLFrom32bitProc - GitHub

这个仓库中包含以下内容:

  • Call64bitDLLFrom32bitProc/ - 64 位 COM LocalServer (EXE)
  • X64DLL/ - 64 位 DLL(实际处理逻辑)
  • X86App/ - 32 位客户端 (WinForms)
  • scripts/ - COM 服务器注册、注销脚本

按照 README 中记载的步骤进行构建与注册,即可实际确认从 32 位进程调用 64 位 DLL 的运行效果。

6. 总结

COM 桥接并非万能,它是一种适合与不适合的工作界限相当分明的架构。在决定采用之前,请先用下表对照一下自己的情况。

适合的场景 不适合的场景
32 位应用本体无法重做(改造成本不划算) 本来就能把 32 位一侧重新构建为 64 位(那才是最短路径)
调用是粗粒度的(一次处理一张图像、一次处理一个文件等) 逐个元素调用几万次等,高频进行细粒度调用(IPC 开销会占据主导)
来回传递的是数值、字符串、数组等便于封送的类型 需要大量往返传递裸指针或复杂的自定义结构体
希望 64 位一侧的处理即使崩溃,应用本体也能继续存活(进程分离成为优点) 不想编写处理服务器端崩溃与重启的恢复逻辑
希望保留类型化的调用(IntelliSense 与编译期检查) 一次性的批处理就够了,通过标准输入输出或文件传递数据即可

作为最后一行的反面,“把 64 位一侧的处理做成一个普通的控制台 EXE,用参数和文件来交换数据”这种朴素的替代方案,也始终值得纳入考虑。COM 桥接真正发挥作用的场合,是想保留类型化调用的时候,以及想反复调用一个持有状态的服务器的时候。

COM 桥接与朴素替代方案的分水岭该图表示这一分水岭:如果需要保留类型化调用或保持状态并反复调用服务器,COM 桥接就能发挥作用;如果一次性的批处理就够了,那么做成控制台 EXE 并用参数和文件交换数据这种朴素替代方案也值得考虑。是否需要类型化调用或保持状态吗?COM 桥接控制台 EXE,用参数和文件传递数据

图11:是否需要类型化调用与状态保持,就是桥接与朴素替代方案之间的分水岭。

至于下一步的行动,推荐按这个顺序进行。

  1. 先把第 5 节的示例仓库 clone 下来,按 README 构建并注册,在手头做出一个能跑起来的状态。
  2. 从自己的 64 位 DLL 中只挑一个函数,往相当于示例中 ICalcService 的接口上加一个方法,把它打通。
  3. 打通之后,测量调用次数与每次的数据量。在这里先做完“向粗粒度靠拢”的设计决策(把多次调用合并成一次),后续返工就会减少。

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

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。

本文与以下服务页面相关联,欢迎从最接近的入口查看。

常见问题

汇总了咨询这一主题时常见的问题。

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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表