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

· 更新日期: · · COM, ActiveX, OCX, Visual Studio, Windows 開發, 32bit, 64bit, Interop

更新紀錄(1 筆,最後更新 2026年08月30日)

本文的修改紀錄。已保存的更新前版本,可透過附有 DOI 的永久連結閱讀。

補充了 DllSurrogate 的說明:為什麼 regsvr32 成功之後 64 位元行程仍然無法載入 32 位元的 InprocServer32;如何在 CLSID 上加 AppID、並在該 AppID 機碼下寫入空字串的 DllSurrogate,就能不寫新的 EXE 把 DLL 放進 dllhost.exe;為什麼啟動的 dllhost.exe 位元數取決於 DLL 而不是用戶端;為什麼已註冊的 LocalServer32 會優先於代理行程;以及如何驗證結果。文中也明確指出代理行程並不會消除位元數差異:消失的只有同一行程的限制,呼叫會變成跨行程,並帶來封送處理與 IPC 的成本。 查看更新前的版本 (DOI: 10.5281/zenodo.21616408)
初次發布
引用本文(DOI: 10.5281/zenodo.21616407)

本文保存於 Zenodo。以下同時提供一律指向最新版本的 DOI,以及固定於您正在閱讀版本的 DOI。

小村 豪(2026)。〈開發 COM 元件、OCX/ActiveX 時常見的坑 - 整理 Visual Studio 的 32bit/64bit、註冊、管理員權限〉。小村軟體有限公司。https://doi.org/10.5281/zenodo.21616407 https://comcomponent.com/zh-TW/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/

DOI(最新版本)
10.5281/zenodo.21616407
DOI(此版本)
10.5281/zenodo.22170296

開發 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.dll。3456
  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 registered。9 regsvr32 的 0x80070005 在官方說明中也描述為 沒有管理員權限,無法寫入登錄檔或 System32。10

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

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

3.1. Visual Studio 2022 變成 64bit

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

Visual Studio 2022 的 devenv.exe 是 64bit only。1 因此 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. System32 與 SysWOW64 的陷阱

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

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

所以在本機排查時,明確寫出用了哪個位元數的 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 一般會匯出 DllRegisterServer/DllUnregisterServer 支援自我註冊。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.dll 以 regsvr32 註冊

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

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

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

3.5. 想從 64bit 行程使用 32bit in-proc 時 ── DllSurrogate

3.3 與 3.4 講的是「註冊的位置與手段有沒有對上」。 這裡再補上一條出路,用來處理 註冊明明是對的,bitness 卻對不上 的情況。

先講結論。

  • 就算 regsvr32 執行成功,64bit 行程也讀不了 32bit 的 InprocServer32。這不是註冊沒做好,而是 in-proc 這個前提本身。
  • 如果不想另外寫一個新的 EXE,又想從 64bit 這側使用那個 DLL,最小的處置就是 給 CLSID 加上 AppID,並在對應的 AppID 機碼寫入空字串的 DllSurrogate。16
  • 這時候起來的,不是用戶端那側,而是 InprocServer32 所指向的 DLL 那側 bitness 的 dllhost.exe。

為什麼維持 in-proc 就是做不到

第 2 章症狀表裡列出的「64bit 應用叫不到 32bit OCX」,就算長得跟 0x80040154 一樣,內容卻是兩回事。

InprocServer32 字面上就是「在呼叫端的行程裡面執行的 server」這個指定。64bit 行程要載入寫在那裡的 32bit DLL,不管怎麼修登錄檔都不會發生。3.3 裡登錄檔的 view 之所以分開,追根究柢也是因為這個限制。

所以從這裡開始,話題不再是「把註冊修好」,而是 「挪到另一個行程去」。

代理行程(surrogate)做的事

DllSurrogate 是一個指定,用來 把 in-proc 的 DLL server 放進代理行程,以 Local Server 的形式對外提供。17 把值設成空字串,就會使用 Windows 內建的預設代理行程(dllhost.exe)。18

如果是 32bit 的 DLL,就會啟動 32bit 的代理行程,DLL 在其中 照舊以 in-proc 的方式 被載入。64bit 的用戶端則是從那個行程外面,透過 proxy 來操作物件。

這裡很容易被誤解,所以明確寫出來。 代理行程並不會消除 bitness 的差異。 消失的只是「必須放在同一個行程裡」這個限制。呼叫會變成 out-of-proc,還會多出 marshalling 與行程間通訊的成本。前提是 來往的型別要能夠 marshalling(IDispatch、已註冊的 proxy / stub、標準 marshaler 能處理的型別,三者之一)。

還有一點。用代理行程比較好處理的,是靠方法呼叫與事件就能收尾的自動化物件。貼在表單上負責繪製的視覺型 OCX,前提是視窗要在宿主的行程裡面,所以光靠這層處置是搞不定的。

啟用最後會落在 in-proc 還是代理行程本圖表示以包含 in-proc 的 CLSCTX 提出要求時,若與用戶端同一 view 的 CLSID 下有 InprocServer32,就先以 in-proc 載入;若沒有,而存在 EXE server 的註冊,則 EXE 會比代理行程先啟動;若沒有 EXE 註冊而有空的 DllSurrogate,就啟動 DLL 那側 bitness 的 dllhost;若以上都沒有,就無法啟用。有沒有有沒有有沒有用戶端提出啟用要求以包含 in-proc 的 CLSCTX 提出要求同一 view 的 CLSID 下有 InprocServer32 嗎照舊以 in-proc 載入有 EXE server 的註冊嗎EXE 比代理行程先啟動有空的 DllSurrogate 嗎啟動 DLL 那側 bitness 的 dllhost無法啟用,0x80040154

圖 1:啟動時先看是否能看到相同位元數的 InprocServer32,看不到時才依序落到 EXE 伺服器與空的 DllSurrogate。

註冊的最小集合

Microsoft Learn 把 DLL server 能放進代理行程的條件列成下面這樣。16

  1. CLSID 機碼下有 AppID 這個值,而且對應的 AppID 機碼存在
  2. 啟用的呼叫中有設 CLSCTX_LOCAL_SERVER,而且 CLSID 機碼下 沒有 LocalServer32 / LocalServer / LocalService
  3. CLSID 機碼下有 InprocServer32
  4. InprocServer32 所指向的 DLL 確實存在
  5. AppID 機碼下面有 DllSurrogate 這個值

如果要讓那個 DLL 單獨跑在一個代理行程裡,Microsoft 建議的寫法是 把 AppID 設成與 CLSID 相同的 GUID。16

註冊只要 3 行就夠。下面是從 64bit 宿主使用 32bit COM DLL 的情形,GUID 是佔位符。前提是 已經先用 SysWOW64 那側的 regsvr32 把 InprocServer32 寫進去了(參見 3.3)。

:: 以管理員權限的命令提示字元執行
set CLSID={11111111-2222-3333-4444-555555555555}
set APPID={11111111-2222-3333-4444-555555555555}

:: 1) 給 CLSID 加上 AppID。CLSID 底下 32/64 是分開的,所以要明確指定 view
::    如果反過來是要從 32bit 宿主使用 64bit 的 COM DLL,這裡就讀成 /reg:64
reg add "HKLM\SOFTWARE\Classes\CLSID\%CLSID%" /v AppID /t REG_SZ /d "%APPID%" /f /reg:32

:: 2) 建立對應的 AppID 機碼。Classes\AppID 在 32/64 之間是共用的,所以不需要指定 view
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /f

:: 3) 把 DllSurrogate 設成空的 REG_SZ。省略 /d 就會以空字串建立
reg add "HKLM\SOFTWARE\Classes\AppID\%APPID%" /v DllSurrogate /t REG_SZ /f

為什麼要設成空字串

DllSurrogate 是 REG_SZ,值本身就是代理行程的路徑。設成空字串(或 NULL)就會使用 系統預設的代理行程,寫上路徑則會嘗試啟動該路徑的自訂代理行程。1718

也就是說,出於好意把 dllhost.exe 的路徑寫進去反而會適得其反,留空這件事本身就是「請用預設的那個」的指定。

另外,HKLM\SOFTWARE\Classes\AppID 從 Windows 7/Windows Server 2008 R2 之後,是 32bit/64bit 共用 的機碼,不區分 view。19 而 CLSID 底下的 view 是分開的,所以如果是 32bit 的 server,AppID 值就得加在 32bit view 的 CLSID 那側。沒生效的時候,請務必把這兩個地方成對確認。

這裡看起來跟 3.3、6.3 的「只註冊在 32bit 那側的話,從 64bit 行程看就是 0x80040154」互相矛盾,但那邊講的是 in-proc 的情況。在 out-of-proc 的啟用裡,當用戶端與 server 兩邊都沒有提出 bitness 的要求時,COM 會找符合用戶端 bitness 的 server,找不到就啟動另一個 bitness 的 server。20 所以這套步驟,在 CLSID 那側仍然放在 32bit view 的狀態下就能成立。不需要把 InprocServer32 複製到 64bit view。

起來的 dllhost.exe 是哪個 bitness

這裡最常被誤解。 代理行程的 bitness,不是由用戶端那側,而是由 InprocServer32 所指向的 DLL 那側 決定的。DLL 既然是在代理行程裡面以 in-proc 被載入,當然就是這樣。

在裡面被載入的 COM DLL 啟動的 dllhost.exe
32bit %SystemRoot%\SysWOW64\dllhost.exe
64bit %SystemRoot%\System32\dllhost.exe

在工作管理員或 Process Explorer 看命令列,會看到它是以 dllhost.exe /Processid:{...} 的形式起來的。這裡的 GUID 是 AppID 而不是 CLSID。當成 CLSID 去找會找不到,請特別注意。

有 LocalServer32 時 EXE 會贏

Microsoft Learn 明確寫道,當存在 LocalServer / LocalServer32 / LocalService 時,啟動 EXE server 或服務永遠優先於把 DLL 放進代理行程。16

也就是說,代理行程是「沒有 EXE server 時的出路」。對已經註冊了 EXE server 的 CLSID 再加上 DllSurrogate,那邊也不會被使用。這裡被優先的,終究只是 相對於代理行程,而不是相對於 in-proc。

把對既有呼叫端的影響也一併掌握會比較安心。 就算加上 DllSurrogate,用包含 in-proc 的 CLSCTX 建立物件的用戶端,只要 bitness 對得上,就仍然照舊維持 in-proc。因為多個 CLSCTX 用 OR 傳進去時會 按列舉順序(in-proc → local → remote)依序嘗試,而在 in-proc 這一段,InprocServer32 機碼 只要存在就會被使用。20 像 CLSCTX_ALL、CLSCTX_SERVER 這種同時傳了 in-proc 與 local 的呼叫,就屬於這種情況。反過來,只用 CLSCTX_INPROC_SERVER 呼叫的地方,就算加上 DllSurrogate 也不會轉到代理行程。

而且從 64bit 的用戶端來看,放進 32bit view 的 InprocServer32 在 64bit view 裡並不存在。in-proc 這一段會以「沒有對應的機碼」直接跳過,於是就照原樣進入 local 那側(代理行程)的啟用。這並不是嘗試載入 bitness 不同的 DLL 然後失敗。2019

確認方法

有沒有寫進去,明確指定 view 讀回來最確實。

reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}\InprocServer32" /ve /reg:32
reg query "HKLM\SOFTWARE\Classes\CLSID\{11111111-2222-3333-4444-555555555555}" /v AppID /reg:32
reg query "HKLM\SOFTWARE\Classes\AppID\{11111111-2222-3333-4444-555555555555}" /v DllSurrogate

請不要省略第 1 行。reg add 在指定的機碼不存在時會建立它,所以 GUID 打錯,或者註冊到別的 view,就會做出一個只掛著 AppID 值的空 CLSID 機碼,而第 2 行照樣會通過。要看到 InprocServer32 也在同一個 view 裡,才算是真的確認過。

DllSurrogate 只要以 值保持空的 狀態出現就是對的。之後再從 64bit 的用戶端建立物件,看看 SysWOW64 那側的 dllhost.exe 會不會起來。

  • 仍然是 0x80040154 → 重新檢查 AppID 值是不是加在 32bit view 的 CLSID 那側,以及 InprocServer32 指向的 DLL 是否確實存在
  • dllhost.exe 有起來,但一呼叫就掛 → 確認 marshalling 的前提(IDispatch / proxy-stub / 標準 marshaler)
  • 起來的是別的 EXE → 那個 CLSID 上還留著 LocalServer32 之類的東西

到這裡為止都是「註冊」的話題

代理行程夠不夠用,還是應該在 32bit 那側自己寫一個輔助用的 EXE,這不是註冊,而是 構成怎麼選 的問題。 那部分整理在 ActiveX / OCX 現在如何處理 - 保留・包裝・取代的判斷表 的 5.2「想把 32bit OCX 帶到 64bit 側」。

4. 在管理員權限上卡住

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

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

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

標準使用者執行 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\Classes 與 HKCU\Software\Classes 的 merged view。722

所以會出現:

  • 開發者 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 extensions。23

比較穩的運維是:

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

日後會省很多麻煩。

5. ActiveX/OCX 特有的坑

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

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

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

這個世界會發生:

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

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

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

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

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

  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 的執行緒都要各自呼叫 CoInitializeEx。29

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

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

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

這些很煩人。

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

這類 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
  • InprocServer32/LocalServer32
  • 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 皆有官方介紹。31632

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

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

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

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

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

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

8. 這類諮詢很契合

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

例如下列請求特別契合:

  • 想把 0x80040154、0x80070005 依 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 Notes — devenv.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, regsvr32 — regsvr32 的語法與角色。 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 COM — EnableComHosting、產生的 .comhost.dll、regsvr32、EnableRegFreeCom。 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, Registering the DLL Server for Surrogate Activation — 能放進代理行程的條件、存在 LocalServer / LocalServer32 / LocalService 時優先啟動 EXE server 或服務、把 AppID 設成與 CLSID 相同 GUID 的構成。 https://learn.microsoft.com/en-us/windows/win32/com/registering-the-dll-server-for-surrogate-activation ↩ ↩2 ↩3 ↩4

  17. Microsoft Learn, DllSurrogate — AppID 底下的 DllSurrogate 是 REG_SZ,空字串時使用系統預設的代理行程,寫上路徑則使用該路徑的自訂代理行程。 https://learn.microsoft.com/en-us/windows/win32/com/dllsurrogate ↩ ↩2

  18. Microsoft Learn, Using the system-supplied surrogate — 指定空字串或 NULL 時會啟動系統預設的代理行程,以及代理行程內部的執行緒模型與行程生存期的處理。 https://learn.microsoft.com/en-us/windows/win32/com/using-the-system-supplied-surrogate ↩ ↩2

  19. Microsoft Learn, Registry Keys Affected by WOW64 — HKLM\SOFTWARE\Classes\CLSID 與 HKCU\SOFTWARE\Classes\CLSID 屬於重導向對象,HKLM\SOFTWARE\Classes\AppID 從 Windows 7/Windows Server 2008 R2 之後是共用(shared)的,以及 HKCR 是兩者的 merged view。 https://learn.microsoft.com/en-us/windows/win32/winprog64/shared-registry-keys ↩ ↩2

  20. Microsoft Learn, CLSCTX enumeration — 多個 CLSCTX 用 OR 傳入時會按列舉順序依序嘗試,在 in-proc 這一段只要 InprocServer32 機碼存在就會使用它,以及用戶端與 server 都沒有提出 bitness 要求時會選擇符合用戶端 bitness 的 server、沒有就啟動另一側。 https://learn.microsoft.com/en-us/windows/win32/api/wtypesbase/ne-wtypesbase-clsctx ↩ ↩2 ↩3

  21. 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 ↩

  22. Microsoft Learn, Windows Registry for advanced users — HKCU\Software\Classes 與 HKLM\Software\Classes、HKCR 的行為。 https://learn.microsoft.com/en-us/troubleshoot/windows-server/performance/windows-registry-advanced-users ↩

  23. 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 ↩

  24. 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 ↩

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

  26. 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 ↩

  27. 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 ↩

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

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

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

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

  32. 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 ↩

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

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

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

常見問題

整理諮詢這個主題時常見的問題。

出現 0x80040154 Class not registered 該怎麼查?
這個錯誤不等於「完全沒註冊」,常見的真正原因是只註冊到另一個位元數的登錄 view,或只對某個使用者註冊。先固定「哪個行程當宿主、它是 32bit 還是 64bit、目標 DLL/OCX 是哪個位元數」,再確認正確的註冊方式與註冊位置(HKCU/HKLM、32bit/64bit view),比急著看登錄檔更快找到原因。
為什麼 Visual Studio 2022 裡只有 Designer 掛掉?
Visual Studio 2022 的 devenv.exe 是 64bit only,無法直接載入 32bit 的 .NET、COM、ActiveX 元件。所以執行階段的應用可以 x86 跑,但 Designer 跑在 64bit 的 Visual Studio 那一側就會壞。改成 AnyCPU 也不一定解,只要後面引用了固定 32bit 的 COM/ActiveX,設計時仍會出問題。
regsvr32 什麼都能註冊嗎?
不能。regsvr32 針對的是匯出 DllRegisterServer 的原生 in-proc COM server(DLL/OCX)。.NET Framework 組件要用 Regasm.exe 註冊;.NET 5+/6+/8+ 則是設定 EnableComHosting 產生 .comhost.dll,再用 regsvr32 註冊它。對 managed DLL 直接跑 regsvr32 會找不到 DllRegisterServer,容易誤判成 DLL 壞了。
只有用管理員啟動才能跑,是權限問題嗎?
本質常常不是權限,而是註冊範圍或安裝設計偏了。HKEY_CLASSES_ROOT 是 HKLM 與 HKCU 的 merged view,COM 會先看 HKCU\Software\Classes,所以「只有某個帳號手動註冊過」也會造成這種症狀。整機共用的正式註冊應由 installer 做 per-machine 註冊,只給自己帳號用的開發用註冊要明確分開,長期把 Visual Studio 開在管理員模式只會把問題掩蓋掉。

作者檔案

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

Go Komura

小村軟體有限公司 代表

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

回到部落格一覽