從 32bit 應用呼叫 64bit DLL 的方法 - COM 橋接有用的案例研究

· · COM, Windows 開發, 32bit, 64bit

從 32bit 應用呼叫 64bit DLL 的方法 - COM 橋接有用的案例研究

「想從 32bit 應用呼叫 64bit DLL」這個需求,在 Windows 上相當典型。 尤其是在保留既有資產的同時,只想使用 64bit 側的功能時,COM 橋接的架構往往是相當實際的解法。

1. 假設情境

在維持 32bit 既有應用的前提下,想使用 64bit DLL 的處理 的情境。
但 32bit 行程無法載入 64bit DLL,這是 OS 層級的限制。

常見的情況如下。

  • 既有 32bit 應用作為資產已經很龐大,無法立即遷移
  • 64bit DLL 一側有新功能,或相依函式庫只有 64bit 版本
  • 想從 32bit 側「以型別」方式呼叫

此時,「在同一行程內呼叫」是不可能的

2. 解決方式

解決的基本思路是 用 Out-of-proc COM(EXE 伺服器)切開
64bit DLL 由 64bit 的 COM 伺服器(EXE)來呼叫,32bit 應用則透過 COM 使用。

流程如下。

  1. 準備 64bit 的 COM LocalServer(EXE),在其內部呼叫 64bit DLL
  2. 共享 COM 介面(IDL/TypeLib),公開型別
  3. 32bit 應用以「帶型別」的方式透過 COM 呼叫(由 Proxy/Marshal 交換)

需要注意的地方如下。

  • 32bit/64bit 的註冊是分開的(包含 WOW6432Node)
  • 自訂結構需要設計 marshalling
  • 存在 IPC 的額外成本,高頻率呼叫要留意

換句話說,「把 64bit 的處理放到另一個行程,再用 COM 搭橋」 是王道做法。

3. 處理流程(序列圖)

以下是 32bit 應用呼叫 64bit DLL 處理時的流程。

sequenceDiagram
    participant App as 32bit 客戶端應用
    box rgba(100,100,255,0.1) COM 自動處理(開發者不用特別注意)
        participant Proxy as COM Proxy
(32bit 側) participant RPC as RPC/IPC
(跨行程通訊) participant Stub as COM Stub
(64bit 側) end participant Server as 64bit COM Server
(EXE) participant DLL as 64bit DLL App->>Proxy: ICalcService.Add(1, 2) rect rgba(100,100,255,0.1) Note over Proxy: 將參數做 marshalling Proxy->>RPC: 序列化後的資料 RPC->>Stub: 跨越行程邊界傳輸 Note over Stub: 將參數做 unmarshalling end Stub->>Server: Add(1, 2) Server->>DLL: 原生函式呼叫 DLL-->>Server: 結果: 3 Server-->>Stub: 結果: 3 rect rgba(100,100,255,0.1) Note over Stub: 將回傳值做 marshalling Stub-->>RPC: 序列化後的結果 RPC-->>Proxy: 跨越行程邊界傳輸 Note over Proxy: 將回傳值做 unmarshalling end Proxy-->>App: 結果: 3

重點:

  • 32bit 應用可以透過 ICalcService 介面做型別安全的呼叫
  • COM runtime 會自動生成並管理 Proxy/Stub
  • 存在跨行程通訊的額外成本,因此與其頻繁的細碎呼叫,不如偏向批次處理

4. 範例程式(示意)

以下是 概念示意。實務上還需要註冊與 TypeLib 生成等步驟。

// 共享介面(對應 IDL)
[ComVisible(true)]
[Guid("7A4B5B23-0A2F-4D2B-9D4D-8A2A92B8B001")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICalcService
{
    int Add(int a, int b);
}

// 64bit 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)
    {
        // 在此呼叫 64bit DLL
        return a + b;
    }
}

// 32bit 應用側(Client)
Type t = Type.GetTypeFromProgID("KomuraSoft.CalcService");
var calc = (ICalcService)Activator.CreateInstance(t);
int result = calc.Add(1, 2);

做成這個樣子之後,32bit 側就能以 「帶型別」 的方式使用。
COM 會在內部透過 Proxy/Stub 經由 IPC 呼叫。

5. 完整範例程式

將上述概念做成可實際執行的範例,公開在 GitHub。

Call64bitDLLFrom32bitProc - GitHub

此儲存庫包含:

  • Call64bitDLLFrom32bitProc/ - 64bit COM LocalServer (EXE)
  • X64DLL/ - 64bit DLL(實際處理)
  • X86App/ - 32bit 客戶端 (WinForms)
  • scripts/ - COM 伺服器註冊/解除註冊腳本

依照 README 的步驟建置與註冊後,就能實際確認從 32bit 行程呼叫 64bit 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

共用相同標籤的最新文章。能以相近的主題延伸理解。

ActiveX / OCX 現在如何處理 - 保留・包裝・取代的判斷表

整理在實務專案中遇到 ActiveX 或 OCX 時的判斷流程,從 UI 部件、機器控制、報表、瀏覽器依賴到 32bit 與 64bit 的牆壁,依照保留・包裝・取代三種選項列出對照表與決策流程,並說明註冊發佈與 STA 等容易絆倒的細節,幫讀者選出最低成本的下一步。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽