開發 COM 元件、OCX/ActiveX 時常見的坑 - 整理 Visual Studio 的 32bit/64bit、註冊、管理員權限

· · COM, ActiveX, OCX, Visual Studio, Windows 開發, 32bit, 64bit, Interop

開發 COM 元件或 OCX/ActiveX 的案子,容易卡住的地方往往不在程式碼本身,而是在 執行環境、註冊、宿主、權限 的邊界。

常見的症狀像這些:

  • 編譯可過,執行起來就 0x80040154
  • 自己機器跑得動,別台 PC 不行。
  • 執行階段可以跑,只有 Visual Studio 的 Designer 掛。
  • 用管理員起動才行,一般權限就壞。
  • regsvr32 叫了半天,怎樣都治不好。

這些通常不是單一 bug,而是 COM 的前提在某個地方對不上 的狀態。

想先釐清 COM/ActiveX/OCX 這幾個名詞,可以先看 COM/ActiveX/OCX 是什麼 - 差異與關係一次整理,整體感會比較清楚。 本文接著往下一階段:實際在哪裡最容易卡,連 Visual Studio 的位元數與管理員權限議題也一起整理。

1. 先下結論

用實務可用的講法:

  1. COM/OCX/ActiveX 的問題,多半不是邏輯錯,而是 bitness(32bit/64bit)、註冊位置、宿主、權限 對不上。
  2. Visual Studio 2022 是 64bit 行程,所以以前「將就能跑」的 32bit 設計時整合常常會壞。12
  3. regsvr32 不是 什麼都能註冊的萬靈丹,它針對原生的 in-proc COM server(DLL/OCX)。要把 .NET Framework 公開給 COM 用,要走 Regasm.exe;.NET 5+/6+/8+ 則是註冊產生的 .comhost.dll3456
  4. 「管理員可以跑就好」很危險。常常其實是 per-user 註冊剛好看得到,或是 原本該由安裝器做的註冊,只在開發機靠手動完成378

所以處理 COM/OCX/ActiveX 時,請先照下面 4 軸對齊:

  • 哪個行程在當宿主
  • 那個行程是 32bit 還是 64bit
  • 註冊到哪裡(HKCU/HKLM,32bit view/64bit view)
  • 那個操作或執行是否需要管理員權限

2. 從症狀反推,通常長這樣

症狀 先懷疑 真正原因常是
0x80040154 Class not registered 沒註冊 其實「只有另一個位元數那邊註冊了」或「只對那個使用者註冊」
DllRegisterServer failed: 0x80070005 權限不足 用標準使用者註冊、post-build 沒有提升權限就註冊
VS2022 裡只有 Designer 掛 Designer 的限制 32bit COM/ActiveX 沒辦法被 64bit 的 Visual Studio 直接載入
只有管理員啟動才跑得動 權限 本質常常不是權限,而是註冊範圍或安裝設計偏了
64bit 應用叫不到 32bit OCX COM 架構限制 in-proc server 必須和宿主同位元數才能載入
UI 執行緒可以,背景執行緒就卡 執行緒模型 STA/MTA、CoInitializeEx、訊息迴圈的前提違反

0x80040154 的正式定義是 REGDB_E_CLASSNOTREG,字面上就是 Class not registered9 regsvr320x80070005 在官方說明中也描述為 沒有管理員權限,無法寫入登錄檔或 System3210

這裡的重點是 別完全照錯誤訊息的字面去判斷。 例如 Class not registered 並不等於「完全沒註冊」;註冊到別的登錄 view只對某使用者註冊 也會造成同樣訊息。117

3. 在 Visual Studio 的位元數上卡住

3.1. Visual Studio 2022 變成 64bit

這是目前 COM/ActiveX 開發最容易踩的點。

Visual Studio 2022 的 devenv.exe64bit only1 因此 WinForms 的設計時體驗中,Visual Studio 那一側 無法直接載入 32bit 元件。Microsoft 也明確指出,Visual Studio 2022 是 64bit 行程,無法載入 32bit 的 .NET/COM/ActiveX 元件。2

以往:

  • 專案 x86
  • 參照的 ActiveX 也 x86
  • Visual Studio 本身也 32bit

剛好湊得起來。VS2022 之後:

  • 執行階段的應用可以 x86 跑
  • 但 Designer 跑在 64bit 的 Visual Studio 這一側

就形成扭曲。 結果就是 執行階段活著,但只有 Designer 掛,非常煩。2

3.2. 改成 AnyCPU 也不一定解

這個誤會也很常見。

AnyCPU 不是 能讓相依端也自動中立的神奇開關。 Microsoft 指出,即使你的元件看起來是 AnyCPU,只要後面引用了固定 32bit 的 COM/ActiveX,Visual Studio 2022 的設計時就會出問題。2

所以改成 AnyCPU 還是錯時,請懷疑:

  • 該組件後面有沒有 32bit 的原生相依
  • ActiveX/OCX 是否只能 x86
  • 有沒有只有 Designer 時才會載入的程式碼

這些比亂試更快找到原因。

3.3. System32SysWOW64 的陷阱

x64 Windows 上的命名很容易令人誤會。

Microsoft Learn 指出:x64 Windows 上的 %windir%\System32 是給 64bit 應用程式用的;32bit 那側會被 WOW64 的檔案系統重導向機制引到別的位置。12 登錄檔同理,WOW64 的 registry redirector 會讓 32bit/64bit 看到 不同的邏輯 view11

所以在本機排查時,明確寫出用了哪個位元數的 regsvr32 會比較安全:

# 要註冊 64bit DLL/OCX 時
C:\Windows\System32\regsvr32.exe vendor.ocx

# 在 x64 Windows 上要註冊 32bit DLL/OCX 時
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx

這個陷阱最惡心的地方是 註冊到錯的一側,你「以為註冊了」但目標行程看不到。 最後變成:

  • regsvr32 成功
  • 但 app 端就是 0x80040154
  • 看登錄檔好像有
  • 但你看的其實是另一個 view

這種狀況。1113

3.4. regsvr32 不是什麼都能註冊

這點也很常被誤解。

原生的 in-proc COM server 一般會匯出 DllRegisterServerDllUnregisterServer 支援自我註冊。3 regsvr32 就是對這類 DLL/OCX 使用的工具。4

若是要把 .NET Framework 的組件給 COM 使用,則該用 Regasm.exe。Microsoft Learn 也明確寫道,要給 COM 使用的組件,用 Regasm.exe 來註冊514

.NET 5+/6+/8+ 的 COM 公開又有點不同:設定 <EnableComHosting>true</EnableComHosting> 編譯後會產生 *.comhost.dll,再用 regsvr32 註冊它。6

簡單歸納:

要公開的對象 常用的註冊方式
原生 C++ 的 DLL/OCX regsvr32
.NET Framework 組件 COM 公開 Regasm.exe
.NET 5+/6+/8+ COM 公開 產出的 .comhost.dllregsvr32 註冊

把這三種混在一起,常見的錯法是:

  • 對 managed DLL 跑 regsvr32
  • 當然找不到 DllRegisterServer
  • 結果誤判成「DLL 壞了」

需要型別函式庫(TLB)的世界(VBA/VB6/部分 early binding 對象),TLB 的產生與註冊 又是另一回事。.NET Framework 可以用 Regasm.exe /tlb 產生/註冊型別函式庫,Microsoft 也說明「型別註冊」與「型別函式庫註冊」是兩件獨立的事情。15

4. 在管理員權限上卡住

4.1. post-build 步驟做註冊,只有管理員才會成功

Visual Studio 的 C++ 建置裡,build event 或 custom build step 本來就可以呼叫 regsvr32.exe。Microsoft Learn 的 post-build event 範例也有提到用 regsvr32.exe 做註冊。16

可以做做了安全 是兩件事。

標準使用者執行 regsvr32 時,因為無法寫入登錄檔或 System32,會得到 0x80070005。Microsoft KB 把原因歸到 沒有管理員權限10

常見事故是:

  • 用普通權限開 Visual Studio,build 可過
  • 只有 post-build 的註冊失敗
  • 大家沒發現失敗
  • 機器裡還留著之前舊的註冊,所以本機剛好還能跑
  • 到乾淨環境自然就爆

這類情境,把建置與註冊分開 是基本原則:

  • 建置只產出二進位
  • 註冊改成明確的 install step/script/installer
  • CI 上把「需要註冊的 step」拆成另一個 job

光這一層分離,事故就少很多。

4.2. per-user 與 per-machine 註冊混用會壞

只記得「COM 看 HKCR」的話,這裡會卡。

Microsoft Learn 指出:COM 會 先看 HKEY_CURRENT_USER\Software\Classes,再看整機資訊。3 HKEY_CLASSES_ROOT 本身其實是 HKLM\Software\ClassesHKCU\Software\Classes 的 merged view717

所以會出現:

  • 開發者 A 用他的帳號手動註冊
  • 只有 A 的帳號跑得動
  • 開發者 B 不行
  • 服務帳號也不行
  • 用管理員跑行為又變了

Microsoft 也建議:需要管理員權限的應用,應在安裝時把所依賴的 COM 物件註冊到 per-machine 的 COM 設定存放87

所以開發現場要把下列差異講清楚:

  • 只給自己帳號用的開發用註冊
  • 整機共用的正式註冊
  • 給服務或權限提升的應用用的註冊

不釐清,就會出現「為什麼只有管理員可以跑」「Explorer 擴充可以跑但服務不行」這類狀況。

4.3. Visual Studio 常駐「以管理員身分執行」不是解

有時候確實需要。 但長期把 Visual Studio 開在管理員模式,會 把原本該由 installer 或註冊 script 解決的問題,被 IDE 權限提升掩蓋掉

Visual Studio 本身在提升權限時的行為也會變。Microsoft Learn 說明:Visual Studio 在 elevated 下執行時,有設定會 停用 per-user extensions18

比較穩的運維是:

  • 平常開發用標準權限
  • 需要註冊的 step 才用明確提升的 Developer Command Prompt/PowerShell/installer
  • 「只有管理員能重現」的情況,把那個前提本身寫進規格

日後會省很多麻煩。

5. ActiveX/OCX 特有的坑

5.1. 設計時授權與執行時授權是分開的

OCX/ActiveX 有個陰魂不散的問題就是 授權

特別是老的 ActiveX 控制項,常會把 design-time licenserun-time license 分開。MFC 的 ActiveX 文件也說明,可以用授權檔或授權金鑰區分 design-time/run-time。1920

這個世界會發生:

  • 執行階段可以用
  • 但在表單上貼就說「找不到授權」
  • 機器 A 貼得了
  • 機器 B 貼不了

「找不到這個元件的授權」這種錯誤,在老 ActiveX 上不稀奇。21

5.2. 貼進 WinForms 時,其實已經有一層包裝

在 WinForms 裡使用 ActiveX,其實 Windows Forms 並不是直接裝載 ActiveX。 Microsoft Learn 的 Aximp.exe 文件指出,ActiveX Control Importer 會 從 COM 型別函式庫產生 WinForms 用的包裝,以 AxHost 為基底的控制項呈現。2223

所以問題不只是一層,而是:

  1. 原本的 OCX/ActiveX 本體
  2. 型別函式庫
  3. 產生的 interop/wrapper
  4. WinForms Designer/runtime

多層都可能出事。 常見的情境:

  • 更新了廠商的 OCX,事件簽名改了
  • 重新設定引用後 wrapper 被重產,diff 超多
  • 每台開發機產出的 interop 稍有差異

所以 Choose Toolbox Items 裡有看到並不代表沒事。 Designer 能貼、執行階段能拿到事件、部署端 wrapper 還能對上,這三件事要分開確認。

5.3. 輕看 STA/MTA 與訊息迴圈就會卡死

COM 要求每個使用它的執行緒都要呼叫 CoInitializeEx 做初始化。Microsoft Learn 寫明:每條使用 COM 的執行緒都要各自呼叫 CoInitializeEx24

而且 STA(single-threaded apartment)需要訊息迴圈2425

UI 類的 OCX/ActiveX 多半假設 STA,所以:

  • UI 執行緒跑得動
  • Task.Run 或 ThreadPool 一跑就卡
  • 事件回不來
  • 偶爾才出事

這些很煩人。

STA 還有前提是:不能直接把介面指標複製到別的執行緒,要用就得做 marshalling。2425

這類 bug 不像 0x80040154 那麼親切,只會呈現「卡住/沒回應/偶爾崩」,排查起來跟登錄檔的麻煩一樣吃時間。

6. 現場管用的排查順序

實務上別一開始就深挖,照這順序切比較快。

6.1. 先固定「哪個行程是幾位元」

先看這個。

  • 宿主是什麼(Visual Studio Designer/自家應用/Office/Access/Explorer/瀏覽器相容環境)
  • 那個宿主是 32bit 還是 64bit
  • 對象的 DLL/OCX 是 32bit 還是 64bit
  • 它是 in-proc 還是 out-of-proc

這邊沒釐清就急著看登錄檔,多半迷路。

6.2. 接著確認「註冊的種類」

再來看 正確的註冊方式到底是什麼

  • 原生 DLL/OCX → regsvr32
  • .NET Framework COM 公開 → Regasm.exe
  • .NET 5+/6+/8+ COM 公開 → .comhost.dll
  • 本來就不支援 self-registration 的 DLL → 用 regsvr32 是錯的

光這個分類就能擋掉一大堆誤判。

6.3. 之後才看「註冊在哪裡」

光看 HKCR 不夠。

  • HKCU\Software\Classes
  • HKLM\Software\Classes
  • 必要時區分 32bit/64bit 的 registry view
  • 對象的 ProgID/CLSID/TypeLib
  • InprocServer32LocalServer32
  • ThreadingModel
  • 相依 DLL 的實體路徑

「HKCR 裡有」並不夠,還要看 是誰、以哪個 bitness、透過哪個 view 才有意義。711

6.4. 最後看「權限有沒有把問題蓋住」

最後確認問題真的是權限,或者只是 權限差異讓你看到不同的註冊

  • 標準使用者/管理員下行為是否不同
  • Visual Studio 提升權限後有什麼變
  • 服務帳號或其他使用者是否能重現
  • 透過 installer 在乾淨環境是否成立

只在一台開發機上驗證,這裡最容易看錯。

7. 先定下來能減事故的運維

COM/ActiveX/OCX 的開發與維護,常常是 運維的決定方式 比實作技巧更重要。

7.1. 先定 bitness 策略

先決定:

  • 固定走 x86 嗎
  • 以 x64 為主嗎
  • 兩者都支援嗎
  • 這個元件非 in-proc 不可嗎

尤其廠商 OCX 只能 x86 時,硬把應用升到 x64 一定後面會卡。 相關主題也可參考 從 32bit 應用呼叫 64bit DLL 的方法 - COM 橋接有用的案例研究

7.2. 定下註冊策略

別臨機應變處理註冊:

  • 整機共用 → 以 installer 做 per-machine 註冊
  • 只給該使用者用 → 刻意走 per-user
  • 自家應用內部自用 → 考慮 registration-free COM
  • 只在開發用 → 明確封裝在 dev setup script

registration-free COM 可以把啟用資訊放在 manifest,而不是登錄檔,對 減少註冊地獄 相當有用。Win32 與 .NET 皆有官方介紹。26627

7.3. 不要把產出物放在版控之外

OCX/ActiveX 常會把相依物散落:

  • OCX 本體
  • 相依 DLL
  • TLB
  • .lic
  • interop DLL
  • AxHost wrapper
  • 註冊 script
  • 範例宿主

這些只存在某人本機,幾個月後一定出事。

至少下列資訊要與程式碼放在同個地方:

  • 以哪個版本為前提
  • 以什麼順序放什麼
  • 用哪個指令註冊
  • 是 x86 還是 x64

8. 這類諮詢很契合

這個主題光是在全面改造前 做切分與方向的整理,就很有價值。

例如下列請求特別契合:

  • 想把 0x800401540x80070005 依 bitness/註冊/權限拆開看清
  • 升到 Visual Studio 2022 後 Designer 壞了,想知道能救多少
  • 想在留下廠商 OCX 的同時,把週邊搬到 .NET 或 C#
  • 想決定 x86 固定資產要延命到哪、從哪裡開始 bridge/wrap/replace
  • 想擺脫手動 regsvr32 的依賴,重新設計 install/deploy

而「留、包、換」的整體判斷,也可參考 今天的 ActiveX/OCX 該怎麼處理 - 留、包、換的判斷表

9. 總結

COM 元件或 OCX/ActiveX 開發會卡住的原因,大致是這 4 個:

  1. bitness 沒對齊
  2. 註冊方法選錯了
  3. 註冊範圍(HKCU/HKLM、32bit/64bit view)偏掉
  4. 誤把「只是被權限看見」的狀態當成正常

Visual Studio 2022 改成 64bit 後,以前「將就能動」的設計變得更容易暴露出問題。12 所以動 COM/OCX/ActiveX 時,先把環境前提對齊再寫程式 是最快的路。

與其 regsvr32 敲很多次,不如先釐清:

  • 哪個行程在宿主
  • 那個行程是幾位元
  • 應該註冊到哪裡
  • 那個註冊是不是真的需要管理員
  • Designer 與 runtime 是不是分開看的

這些先整理,往往比硬湊快得多。


參考

  1. Microsoft Learn, Visual Studio 2022 version 17.0 Release Notesdevenv.exe is now 64-bit only. https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-notes-v17.0  2 3

  2. Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 是 64bit 行程,無法載入 32bit 的 .NET/COM/ActiveX,以及 out-of-process designer 的限制。 https://learn.microsoft.com/en-us/dotnet/desktop/winforms/visualstudio/troubleshoot-32bit  2 3 4 5

  3. Microsoft Learn, Classes and Servers — COM 的註冊、HKCU/HKCR、self-registration 與 DllRegisterServer。 https://learn.microsoft.com/en-us/windows/win32/com/classes-and-servers  2 3 4

  4. Microsoft Learn, regsvr32regsvr32 的語法與角色。 https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/regsvr32  2

  5. Microsoft Learn, Registering assemblies with COM — .NET Framework 的 COM 註冊用 Regasm.exe。 https://learn.microsoft.com/en-us/dotnet/framework/interop/registering-assemblies-with-com  2

  6. Microsoft Learn, Expose .NET Core components to COMEnableComHosting、產生的 .comhost.dllregsvr32EnableRegFreeCom。 https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com  2 3

  7. Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR 是 HKLM 與 HKCU 的 merged view。 https://learn.microsoft.com/en-us/windows/win32/sysinfo/merged-view-of-hkey-classes-root  2 3 4 5

  8. Microsoft Learn, HKEY_CLASSES_ROOT Key — 需要管理員權限的應用應採 per-machine COM 設定註冊。 https://learn.microsoft.com/en-us/windows/win32/sysinfo/hkey-classes-root-key  2

  9. Microsoft Learn, COM Error Codes (Generic) (Winerror.h)REGDB_E_CLASSNOTREG (0x80040154) 等。 https://learn.microsoft.com/en-us/windows/win32/com/com-error-codes-1 

  10. Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — 權限不足導致 DLL 註冊失敗的典型例。 https://learn.microsoft.com/en-us/troubleshoot/windows-client/shell-experience/dllregisterserver-error  2

  11. Microsoft Learn, Registry Redirector — WOW64 上 32bit/64bit 的登錄檔 view。 https://learn.microsoft.com/en-us/windows/win32/winprog64/registry-redirector  2 3 4

  12. Microsoft Learn, File System Redirector — x64 Windows 的 %windir%\System32 與 WOW64 的檔案系統重導向。 https://learn.microsoft.com/en-us/windows/win32/winprog64/file-system-redirector 

  13. Microsoft Learn, Compatibility considerations for 32-bit programs on 64-bit versions of Windows — WOW64 的檔案/登錄檔重導向。 https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/compatibility-limitations-32-bit-programs-64-bit-system 

  14. Microsoft Learn, Regasm.exe (Assembly Registration Tool)Regasm.exe 的角色與 /tlb 等選項。 https://learn.microsoft.com/en-us/dotnet/framework/tools/regasm-exe-assembly-registration-tool 

  15. Microsoft Learn, Packaging a .NET Framework Assembly for COM — 型別函式庫與 Regasm.exe /tlb。 https://learn.microsoft.com/en-us/dotnet/framework/interop/packaging-an-assembly-for-com 

  16. Microsoft Learn, Understanding Custom Build Steps and Build Events — post-build event 中呼叫 regsvr32.exe 的範例。 https://learn.microsoft.com/en-us/cpp/build/understanding-custom-build-steps-and-build-events 

  17. Microsoft Learn, Windows Registry for advanced usersHKCU\Software\ClassesHKLM\Software\Classes、HKCR 的行為。 https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users 

  18. Microsoft Learn, Find, install, and manage extensions for Visual Studio — elevated 執行時 per-user extension 的行為。 https://learn.microsoft.com/en-us/visualstudio/ide/finding-and-using-visual-studio-extensions 

  19. Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time/run-time license、.LIC。 https://learn.microsoft.com/en-us/cpp/mfc/mfc-activex-controls-licensing-an-activex-control 

  20. Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — 執行時授權產生與 .lic 檔。 https://learn.microsoft.com/en-us/cpp/mfc/reference/application-settings-mfc-activex-control-wizard 

  21. Microsoft Learn, License information for this component not found. You don’t have an appropriate license to use this functionality in the design environment. https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/license-information-for-this-component-not-found-you-don-t-have-an-appropriate-l 

  22. Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — 把 ActiveX 轉成 WinForms 用包裝。 https://learn.microsoft.com/en-us/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer 

  23. Microsoft Learn, AxHost Class — ActiveX Control Importer 產生的 AxHost 包裝。 https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.axhost 

  24. Microsoft Learn, Initializing the COM libraryCoInitializeEx、每條執行緒的初始化、STA 訊息迴圈。 https://learn.microsoft.com/en-us/windows/win32/learnwin32/initializing-the-com-library  2 3

  25. Microsoft Learn, Single-Threaded Apartments — STA 的訊息迴圈、marshalling、ThreadingModel。 https://learn.microsoft.com/en-us/windows/win32/com/single-threaded-apartments  2

  26. Microsoft Learn, Creating Registration-Free COM Objects — 透過 activation context 的免註冊 COM。 https://learn.microsoft.com/en-us/windows/win32/sbscs/creating-registration-free-com-objects 

  27. Microsoft Learn, Registration-Free COM Interop — .NET Framework 的 registration-free COM interop。 https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop 

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

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

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

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

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

作者檔案

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

Go Komura

小村軟體有限公司 代表

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

回到部落格一覽