什麼是 Reg-Free COM - 免註冊使用 COM 的機制,以及合用與不合用的情境

· · COM, Reg-Free COM, Registration-Free COM, Windows 開發, Legacy 技術

COM/ActiveX/OCX 的案子,每次發布與更新總會冒出一堆老問題。

  • 需要 regsvr32
  • 常常要管理員權限
  • 和其他應用裝的別版本打架
  • 解除安裝後還把別的產品一起拖下水
  • 開發機能跑,但乾淨環境跑不起來

能把這個泥巴戰相當程度地化解的,就是 Reg-Free COM。 不過它的名字聽起來豪氣,並不是「COM 的一切麻煩都消失的魔法」。消掉的主要是 被全域註冊牽著走的那種麻煩。bitness、相依 DLL、type library、執行緒模型的難題並不會跟著消失。

本文以 在 Windows 桌面應用上把 COM DLL/OCX 關在應用本地使用 的脈絡為主軸,來整理 Reg-Free COM。

1. 先下結論(一句話)

先給一個粗但實用的說法:

  • Reg-Free COM 是把 COM 的註冊資訊從登錄檔改放到 manifest 裡
  • 執行時,CoCreateInstanceCLSIDFromProgID 解析會 先看 Activation Context
  • 所以 COM DLL/OCX 可以以應用為單位放成 private
  • 主要好處是 好做 XCOPY 發布容易避開版本衝突解除安裝較不會波及別人
  • 32bit/64bit 的問題不會消失,這邊靠意志力也過不去
  • 另外 相依 DLL、type library、設計時參考、非標準的註冊依賴 還是要另外考量
  • 實務上,想把應用專屬的 COM 零件放在身邊 時相當合用

總之,Reg-Free COM 是 把 COM 的 activation 拉回以應用為單位 的機制。

2. 本文所說的 Reg-Free COM

Reg-Free COM 是 Registration-Free COM 的縮寫。

這裡的「免註冊」指的是 使用 COM 時不必全面依賴 HKCR / CLSID / InprocServer32 這類全域登錄。 並不是「COM 本身消失」,也不是「不再需要 GUID」。

本文主要涵蓋下列對象:

  • 原生 COM DLL
  • 基於 ATL 的 COM server
  • ActiveX / OCX
  • 以 .NET Framework 為基礎的 COM interop
  • 透過 .NET 5+ / .NET 8 的 COM host 發布

另外要特別強調的兩點:

  1. Reg-Free COM 是「activation」的議題
  2. type 資訊的發布與設計時的參考設定,可能會作為另一條線留存

這兩件事一混在一起,討論就會變混濁。

3. 先用一張圖收攏

先把整體用一張圖看會比較快。

MyApp.exeApplication ManifestdependentAssemblyComponent / Assembly Manifestfile / comClass / typelibVendorControl.dll / .ocxActivation ContextCLSIDFromProgID / CoCreateInstance

一般 COM 在 CoCreateInstance 時,會去登錄檔找「應該載入哪個 DLL」。 Reg-Free COM 則是先看 當下有效的 activation context,從那裡的 manifest 資訊做解析。

因此,同一台機器上,應用 A 與應用 B 可以 各自帶著不同版本的同系列 COM 零件 跑, COM 的共享文化被稍微拉回「以應用為單位」。

4. 為什麼一般 COM 發布會變得沉重

一般 COM 發布之所以沉重,與其說 COM 本身有問題,不如說是 全域註冊的前提 造成的。

概略來說,使用 COM 類別需要下列資訊:

資訊 角色
CLSID 唯一識別類別的 GUID
ProgID 人比較好用的名稱
InprocServer32 要載入哪個 DLL
ThreadingModel Apartment/Both 等前提
TypeLib 型別資訊

把這些放到登錄檔裡,對整台機器來說方便,因為多個應用可以共享。

但實務上這個「共享」經常反咬一口:

  • 某產品的安裝覆蓋掉另一個產品的 COM 註冊
  • 解除安裝器「只想清掉自己」卻把共享 COM 弄壞
  • 開發機剛好有的註冊,生產機卻沒有
  • 32bit 與 64bit 的註冊對不起來,只剩詭異的行為偏差

也就是說,讓人頭痛的常常不是 COM 本身,而是發布模型。 Reg-Free COM 就是用來減輕這種發布模型痛點的機制。

5. Reg-Free COM 的機制

5.1 在 application manifest 寫上相依關係

首先,應用端要在 application manifest 中寫 自己相依於哪個 side-by-side assembly

這份 manifest 可以:

  • 放在 EXE 旁邊的 MyApp.exe.manifest
  • 或作為資源嵌入 EXE

實務上,想讓發布與替換直觀就放外部檔;想讓發布單純、不易壞就用嵌入式,兩種都常見。

另外,當外部檔與嵌入版同時存在時,檔案系統上的 manifest 會優先

5.2 在 component manifest 寫上 COM 資訊

接著,COM 這一端要把 原本放登錄檔的資訊 寫到 component manifest 裡。

裡面會有例如:

  • comClass
  • clsid
  • progid
  • threadingModel
  • typelib
  • 必要時再加 proxy / stub 或 window class 等

也就是 用 XML 描述 COM 的長相,取代登錄檔。

這份 manifest 也可以:

  • 做成與 DLL 分開的檔案
  • 嵌入 DLL 作為資源

實務上通常 以 private assembly 嵌入 DLL 比較不易出事。分離檔雖然直觀,但容易在「檔名與 assemblyIdentity 的對應」「放在哪個資料夾」「漏拷貝」等地方絆倒。

5.3 執行時先看 activation context

Reg-Free COM 的核心就在這。

當應用呼叫 CLSIDFromProgIDCoCreateInstance 時,COM runtime 會先去看 目前啟用的 activation context。 若其中有必要的 ProgID → CLSID、CLSID → DLL 資訊,就能不靠登錄檔解析。

反之,若 manifest 缺少必要資訊,它就會掉回一般的以登錄檔為主的解析路徑。 這個行為會造就經典陷阱:開發機剛好能跑。以為做成 Reg-Free 了,實際上卻是被本機既有註冊救了一命。

這是 Reg-Free COM 最惱人的坑。

6. 好處是什麼

Reg-Free COM 的好處在實務上相當明顯。

6.1 好做 XCOPY 發布

可以把必要檔案都放進應用資料夾,安裝器與註冊步驟都會輕很多。 當然,若要寫到 Program Files 下,權限還是另一件事,但至少可以減少 為了 COM 註冊而做的管理員動作

6.2 減少版本衝突

同一台機器上有多個版本的 COM 零件時,各應用可以各用各的版本。 「因為別的產品被安裝導致行為突然改變」這類事故會相當程度減少。

6.3 常常不必大改現有程式碼

Reg-Free COM 動的是 怎麼解析,而不是 呼叫方的寫法。 所以如果條件契合,往往 CoCreateInstance 那側的程式碼幾乎不動就能導入。

6.4 刪除與 rollback 變輕鬆

因為被鎖在以應用為單位,所以更新或 rollback 都很直觀。 極端地說,就是 可以用整包資料夾換掉 的思維。

7. 合用與不合用的情境

7.1 合用的情境

下列情境,Reg-Free COM 相當有力:

狀況 契合度
想把應用專屬的 COM DLL/OCX 一起封裝 非常好
想讓同一台 PC 多版本共存 非常好
想避免廠商零件的註冊事故
想在既有桌面應用裡 private 使用 ActiveX/OCX
想讓發布變輕,但既有呼叫端儘量不動

典型上與 業務桌面應用、設備整合工具、VB6/MFC/WinForms 既有資產 的契合度相當高。

7.2 不合用,或要小心觀察的情境

反之下列情境要謹慎:

狀況 說明
想在整台機器層級共享 COM Reg-Free 的優勢被稀釋
bitness 根本沒對上 Reg-Free 解不了
強烈依賴非標準的註冊資訊或自訂安裝流程 不容易改寫成 manifest
相依 DLL 或 VC++ runtime 的發布還沒整理 終究會在別的地方絆倒
設計時工具或 IDE 的參考設定以登錄檔為前提 需要另一套運維設計

最後一點特別關鍵。 Reg-Free COM 幫的是 執行時的 activation,並無法一次把 設計時 UI 的參考設定所依賴的前提 也一起改掉。

8. 常見誤解

8.1 Reg-Free COM 能讓 bitness 問題消失

不會。 32bit 行程只能載入 32bit in-proc COM DLL,64bit 行程只能載入 64bit DLL。 這一點 Reg-Free 與否都一樣。

8.2 Reg-Free COM 完全不看登錄檔

也不對。 manifest 缺資訊時,仍會掉回以登錄檔為主的解析路徑。 所以 開發機能跑 ≠ Reg-Free 架構正確

8.3 Reg-Free COM 也會自動處理 type library

這個只對一半。 manifest 裡是可以寫 typelib,但 VBA 的參考設定、C++ 的 #import、.NET 設計時 interop 的生成 等 type 資訊的處理,通常還要另做設計。

Reg-Free COM 先解決的是 能啟動怎麼帶型別做開發 是下一個議題。

8.4 Reg-Free COM 能讓任何 ActiveX/OCX 都直接上

也很危險。 若元件走的是標準 COM 註冊資訊,那還好辦;但若涉及自訂登錄、額外安裝、授權處理、對其他模組群的濃相依,Reg-Free 化會突然變吃力。

8.5 Reg-Free COM 在 .NET Framework 與 .NET 8 差不多

表面相似,但工具鏈差異大。 .NET Framework + RegAsm 的脈絡與 .NET 5+ / .NET 8 + comhost 的脈絡,即便都是 COM,底盤其實不同。

9. 原生/.NET Framework/.NET 5+/.NET 8 的差異

這邊容易混,所以分開看一下。

類別 重點整理
原生 COM DLL/OCX 基本以 application manifest + component manifest 思考
.NET Framework 版 COM interop 除了 Win32 風格的 application manifest,還要有 managed 元件側的 manifest
.NET 5+/.NET 8 對 COM 發布 EnableComHosting 建 COM host,EnableRegFreeCom 可產生 Reg-Free 用 manifest

9.1 .NET Framework 版 COM

在 .NET Framework 版 COM 上會變成 COM 應用端的 Win32 風格 application manifestmanaged 元件端的 component manifest 兩層。

這比原生 COM 稍微更麻煩。 會出現「Reg-Free COM 我懂了,但一牽到 managed component,manifest 又多一張」的狀況,對話路面又泥了一點。

9.2 .NET 5+/.NET 8 發布 COM

.NET 5+/.NET 8 的 COM 發布入口變成 *.comhost.dll。 設定 EnableRegFreeCom=true 時,會額外產出 Reg-Free COM 用的 side-by-side manifest

這裡仍要強調:Reg-Free COM 與 TLB 策略是兩件事。 .NET Core/.NET 5+ 不像 .NET Framework 時代那樣「從組件自然長出 TLB」。如果要帶型別使用,TLB 的生成、嵌入、註冊要另外規劃。

10. 最小組合示意

以下示意 MyApp.exe 以 Reg-Free COM 使用 Vendor.CameraControl.dll 的最小組合。

10.1 檔案組成示意

MyApp.exe
MyApp.exe.manifest
Vendor.CameraControl.dll
Vendor.Helper.dll

上例中 假設 component manifest 已嵌入 DLL。 外部檔也可以,但剛開始嵌入會比較好整理。

10.2 application manifest 示意

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="KomuraSoft.MyApp"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Vendor.CameraControl.Asm"
        version="1.0.0.0"
        processorArchitecture="amd64" />
    </dependentAssembly>
  </dependency>
</assembly>

10.3 component manifest 示意

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Vendor.CameraControl.Asm"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <file name="Vendor.CameraControl.dll">
    <comClass
      clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
      progid="Vendor.CameraControl.1"
      threadingModel="Apartment"
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}" />

    <typelib
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}"
      version="1.0"
      helpdir="" />
  </file>
</assembly>

這個例子裡真正重要的,不是 XML 細節,而是 應用端的 dependentAssembly 與元件端的 assemblyIdentity 必須一致。 不一致會讓你靜悄悄地折騰很久。

另外,上面的 GUID 與名稱只是示範。實務上要依元件實際公開的 CLSID/TLBID/ProgID/threading model 正確填入。

11. 常見坑

11.1 開發機能跑,發布端跑不起來

先懷疑的是 其實是被本機的登錄檔救了一命。 Reg-Free COM 最好要在 乾淨環境 驗證。

11.2 出現「side-by-side configuration is incorrect」啟動不了

這一類通常源於 manifest 不一致、相依 DLL 缺件、VC++ runtime 缺件、架構對不起來。 訊息本身相當不親切,靠 Event Logsxstrace 追蹤是常用做法。

11.3 component manifest 與 application manifest 的對應偏掉

  • name 不同
  • version 不同
  • processorArchitecture 不同
  • 拷貝過去的 manifest 其實是舊版

看起來只是小差異,但啟動時影響很大。

11.4 漏帶相依 DLL

只盯著 Vendor.CameraControl.dll 而沒注意到它會再去載入 Vendor.Helper.dll、VC++ runtime、proxy/stub DLL 等。 Reg-Free COM 處理的是 COM 註冊問題原生相依解析問題 並沒有被一併處理。

11.5 把 type library 與參考設定的運維往後拖

就算 runtime activation 通了,當:

  • 想從 VBA 做 early binding
  • C++ 想 #import
  • .NET 端想在設計時產生 interop

這些需求出現時,就需要一套 type 資訊的發布方式。 Reg-Free COM 不會自動搞定這一塊,所以 runtime 與 design-time 要分開思考

12. 總結

一句話概括:Reg-Free COM 是 把 COM 的註冊資訊從整台機器收回到以應用為單位 的機制。

因此能帶來:

  • 把 COM DLL/OCX 關在應用本地
  • 減少版本衝突
  • 簡化發布與 rollback

但是以下仍然重要:

  • 32bit/64bit
  • 相依 DLL
  • TLB/參考設定
  • 非標準的註冊相依
  • 在乾淨環境做驗證

所以導入 Reg-Free COM 的基本姿態:

  1. 看作是 activation 的議題
  2. 拆開 runtime 與 design-time 的討論
  3. 在乾淨環境驗證
  4. 先把 bitness 與相依 DLL 弄齊

按這個順序做,事故會少很多。

13. 相關文章

14. 參考資料

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

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

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

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

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

作者檔案

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

Go Komura

小村軟體有限公司 代表

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

回到部落格一覽