更新紀錄(3 筆,最後更新 2026年09月04日)
本文的修改紀錄。已保存的更新前版本,可透過附有 DOI 的永久連結閱讀。
- 已將繁體中文版改寫為日文原文的完整翻譯。先前的繁體中文版只譯出日文原文的一部分,遺漏了章節、表格、Mermaid 圖、圖說與 FAQ。本次依日文原文將這些內容全部補回,並新增本文的知識地圖章節。技術主張與日文版一致。 查看更新前的版本 (DOI: 10.5281/zenodo.22279352)
- 補上了日文原文中已有的諮詢引導(consultation_services)。內文沒有改動。 查看更新前的版本 (DOI: 10.5281/zenodo.22170277)
- 在 5.2 中補充了 DllSurrogate,作為動手寫自己的輔助 EXE 之前值得先考慮的一個選項。如果目標本身是 in-proc 的 COM 伺服器,只要給它的 CLSID 加上 AppID,並在該 AppID 機碼下寫入空字串的 DllSurrogate,就能讓它跑到行程外,自己一行程式碼都不用寫。不過 surrogate 並不會消除位元差異:消失的只是必須裝進同一個處理程序這條限制,呼叫會變成跨處理程序,封送與處理程序間通訊的開銷照舊。新增的表格劃出了 surrogate 夠用的情形與仍然需要自己寫輔助 EXE 的情形之間的界線,並補充了兩條參考資料。註冊步驟本身放在另一篇文章裡,這裡只給出連結。 查看更新前的版本 (DOI: 10.5281/zenodo.21616262)
- 初次發布
引用本文(DOI: 10.5281/zenodo.21616261)
本文保存於 Zenodo。以下同時提供一律指向最新版本的 DOI,以及固定於您正在閱讀版本的 DOI。
Go Komura(2026)。〈ActiveX / OCX 現在該怎麼處理 - 保留、包裝、取代的判斷表〉。小村軟體有限公司。https://doi.org/10.5281/zenodo.21616261 https://comcomponent.com/zh-TW/blog/2026/03/12/001-activex-ocx-keep-wrap-replace-decision-table/
- DOI(最新版本)
- 10.5281/zenodo.21616261
- DOI(此版本)
- 10.5281/zenodo.22297090
只要案子裡出現 ActiveX / OCX 這個詞,氣氛大概都會沉一點。
- VB6 或老的 C++ / MFC 應用程式還在第一線跑
- 工業設備或量測儀器的 SDK 只提供 OCX
- 公司內部的 Web 系統以 ActiveX 為前提,離不開 IE 模式
- 想把整體收攏到 64 位元,卻被一個 OCX 擋住
不過在這裡,「因為舊所以全部丟掉」和「因為還在動所以永久保存」,兩種說法都很草率。 真正重要的是分辨這個 ActiveX / OCX 只是單純的 UI 元件,還是一道內含業務規格或設備規格的邊界面。
本文梳理的是:發現 ActiveX / OCX 之後, 保留、包起來、取代這三條路該怎麼選,並依照容易判斷的順序排開。
適用的情境例如以下這些。
- VB6 / MFC / WinForms 這一系的既有桌面應用程式
- 往 C# / .NET 的階段性遷移
- 含有 WebBrowser / IE 模式的舊畫面
- 內含廠商製 ActiveX 控制項的 Windows 應用程式
目錄
- 先講結論(一句話)
- 本文所說的 ActiveX / OCX
- 先看這張判斷表
- 3.1. 全貌
- 3.2. 選擇保留
- 3.3. 選擇包起來
- 3.4. 選擇取代
- 3.5. 瀏覽器相依性要另外看
- 容易讓判斷失準的幾個重點
- 4.1. 是 UI 元件,還是內含規格的元件
- 4.2. 32 位元 / 64 位元與處理程序邊界
- 4.3. 註冊、散發、權限、授權
- 4.4. STA / 訊息迴圈 / 回呼
- 4.5. 有沒有測試,能不能觀測
- 依典型情境給的建議
- 5.1. 現在仍穩定運轉的公司內部桌面應用程式
- 5.2. 想把 32 位元 OCX 帶到 64 位元這一側
- 5.3. 以 IE / WebBrowser 為前提的畫面
- 5.4. 內含裝置控制或專屬規格的 ActiveX
- 常見的反面模式
- 開始遷移時的檢查清單
- 大致的取捨
- 總結
- 這類諮詢特別合適
- 參考資料
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 24 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
1. 先講結論(一句話)
- 看到 ActiveX / OCX,最先要判斷的不是「舊不舊」,而是這個元件承擔了什麼
- 如果只是單純的 UI 元件,取代相對容易
- 如果內含裝置控制、報表、專屬檔案格式,或是長年累積下來的維運習慣,比起直接重做,先包起來會安全得多
- 如果在桌面上穩定運轉,而且變更範圍很小,選擇保留也完全站得住腳
- 瀏覽器上的 ActiveX 相依性,雖然可以延續使用,未來的路卻很窄,建議以取代為優先來看
- 32 位元 OCX 沒辦法直接載入 64 位元處理程序。這條線不是靠拼勁就跨得過去的
- 註冊、相依 DLL、系統管理員權限、授權、STA / MTA 這些實作以外的摩擦,往往才是真正的難關
- 「總之先全面重寫」和「因為害怕就永久凍結」,兩種做法出事的機率都很高
換句話說,判斷的順序是這樣。
- 那個 OCX 手上握著什麼
- 是不是非得在同一個處理程序裡使用
- 會不會卡在 32 位元 / 64 位元、註冊或瀏覽器相依性上
- 是不是該先做出可測試的邊界,再動手取代
照這個順序看下來,整件事會好整理很多。
flowchart TB
accTitle: 判斷的順序
accDescr: 依序檢視那個 OCX 手上握著什麼、是否必須在同一個處理程序裡使用、會不會卡在 32 位元 / 64 位元與註冊與瀏覽器相依性上、是否該先做出可測試的邊界再取代的圖。
s1["那個 OCX 握著什麼"] --> s2["是否需要同一個處理程序"]
s2 --> s3["位元數、註冊、瀏覽器會不會卡住"]
s3 --> s4["是否先做邊界再取代"]
圖 1: 不是看「舊不舊」,而是照這個順序看,保留、包起來、取代就好整理了。
2. 本文所說的 ActiveX / OCX
先把本文對這些詞的用法定下來。
| 詞彙 | 在本文中的意思 |
|---|---|
| COM | Windows 的二進位相容元件模型。公開介面、註冊、Apartment Model 等都建立在它上面 |
| ActiveX / OCX | 在實務上,這個說法常被拿來統稱以 COM 為基礎的控制項及其周邊資產。尤其常包含 .ocx 的 UI 控制項,以及嵌入 IE 或容器裡的元件 |
| WebBrowser / IE 系相依性 | 就算本身不是 ActiveX,只要是以「IE 的世界觀」為前提的內嵌瀏覽器或介接都算在內。從判斷的角度看,是相當接近的問題 |
嚴格說來,ActiveX 和 COM 並不是同一件事。 不過在實務上會卡住的地方,兩者相當接近。
- 位元數對不對得上
- 註冊和相依 DLL 要怎麼發下去
- 會在哪個承載端 / 容器裡執行
- 會不會卡在 STA、訊息迴圈、回呼上
- 還有沒有殘留的瀏覽器相依性
本文要一起處理的,就是這些實務上的判斷點。
flowchart TB
accTitle: 實務上會卡住的地方
accDescr: 嚴格說來 ActiveX 與 COM 並不相同,但位元數對不對得上、註冊與相依 DLL 的散發、在哪個承載端執行、STA 與回呼的前提、瀏覽器相依性這些實務上會卡住的地方相當接近的圖。
ax["ActiveX / OCX 案子"] --> p1["位元數對不對得上"]
ax --> p2["註冊與相依 DLL 的散發"]
ax --> p3["在哪個承載端執行"]
ax --> p4["STA 與回呼的前提"]
p4 -.-> p5["還有沒有殘留的瀏覽器相依性"]
圖 2: ActiveX 和 COM 嚴格說來是兩回事,實務上會卡住的地方卻集中在這一帶。
另外,後面會理所當然地出現一些縮寫,先在這裡整理起來。因為第 7 章的檢查清單要你「把 ProgID 和 CLSID 清查列出」時,這裡不清楚就動不了。
| 用語 | 全稱 | 意思 |
|---|---|---|
| CLSID | Class ID | 唯一指向某個 COM 元件實作(類別)的 GUID。登錄檔的註冊也是以這個值為鍵 |
| ProgID | Programmatic Identifier | 掛在 CLSID 上、人看得懂的名字。像 Excel.Application 這樣的字串 |
| IID | Interface ID | 唯一指向某個 COM 介面的 GUID。和 CLSID 是不同的東西 |
| TLB | Type Library | 以二進位形式保存介面、方法、引數型別等型別資訊的檔案。VB6 和 .NET 能「帶型別地」呼叫,靠的就是它 |
| RegAsm | Assembly Registration Tool | .NET Framework 隨附的工具。把 .NET 組件註冊進登錄檔,讓 COM 能夠使用 |
| AxHost | — | 在 Windows Forms 上承載 ActiveX 控制項用的基底類別 |
| AxImp | ActiveX Control Importer | 從 OCX 產生 Windows Forms 用包裝組件的工具 |
| in-proc / out-of-proc | 處理程序內 / 處理程序外 | 是和呼叫端在同一個處理程序裡執行(DLL 或 OCX),還是在另一個處理程序裡執行(EXE 形式的伺服器) |
| LocalServer | — | 讓 COM 伺服器以 EXE 的形式在另一個處理程序裡執行的形態。可以跨過位元數的高牆,也可以隔離當機 |
| Reg-Free COM / side-by-side | 免註冊 COM | 不做登錄檔註冊,改以應用程式資訊清單裡寫的內容來解析 COM 的機制 |
| design-time / runtime 授權 | 開發階段 / 執行階段 | 廠商製的控制項有時會把「在開發機上貼到畫面時」和「在散發目的地執行時」的授權條件分開 |
| adapter / facade | — | 把既有的細碎 API 換成對自己方便的粗粒度 API,這種設計上的型式 |
| STA / MTA | Single / Multi Threaded Apartment | COM 的執行緒模型。可以從哪個執行緒呼叫的約定會跟著改變 |
3. 先看這張判斷表
3.1. 全貌
先從這張表看起,大致的方針就定得下來了。
| 狀況 | 先選哪一條 | 理由 |
|---|---|---|
| 相依於瀏覽器上的 ActiveX | 偏向取代 | 因為 Edge 本身不支援 ActiveX,IE 模式的定位是延續使用的過渡手段 |
| 桌面應用程式裡的 OCX 穩定運轉,變更範圍也小 | 偏向保留 | 因為現在把它拆掉的成本,通常反而更高 |
| 只想把周邊 .NET 化,但看不懂控制項的行為 | 偏向包起來 | 因為先把邊界整理好比較安全 |
| 想把 32 位元 OCX 直接放進 64 位元處理程序 | 包起來 / 改架構 | 因為這是 in-proc 跨不過去的邊界 |
| 只當成 UI 元件在用,而且有替代品 | 偏向取代 | 因為換掉表層通常就夠了 |
| 廠商收攤、簽署、註冊、相依 DLL 每次都出事 | 偏向取代 | 因為維運成本已經以技術債的形式浮上檯面 |
| 內含裝置控制、報表、專屬通訊協定 | 偏向包起來 | 因為不先把行為固定下來,就估不出取代的成本 |
flowchart TD
start["有 ActiveX / OCX"] --> q1{"相依於瀏覽器?"}
q1 -- "是" --> p1["優先取代<br/>IE 模式是延續使用的手段"]
q1 -- "否" --> q2{"主要是 UI 元件?"}
q2 -- "是" --> q3{"有同等的替代品?"}
q3 -- "是" --> p2["考慮取代"]
q3 -- "否" --> p3["先包起來整理邊界"]
q2 -- "否" --> q4{"內含裝置控制、專屬規格、報表邏輯?"}
q4 -- "是" --> p4["先包起來<br/>備齊測試後再階段取代"]
q4 -- "否" --> q5{"註冊、位元數、散發很痛?"}
q5 -- "是" --> p5["重新檢視架構<br/>考慮 out-of-proc / 跨處理程序介接 / Reg-Free COM"]
q5 -- "否" --> p6["保留也很實際"]
圖 3: 第一個分歧點是有沒有相依於瀏覽器,之後再看是不是 UI 元件、有沒有替代品、內不內含規格來決定方針。
以下依序看每一種情境。
3.2. 選擇保留
不是掛著 ActiveX / OCX 就馬上成為取代對象。只要湊齊下面這些條件,保留反而最省,這種情況其實很常見。
- 使用範圍是封閉的,公司內部散發或隨設備出貨等維運環境是固定的
- 那個控制項現在仍穩定運轉,變更需求也不大
- 廠商還在,或是自家至少能做最低限度的維護
- 不相依於瀏覽器,在桌面既有的承載程式上就能完結
- 32 位元 / 64 位元的前提短期內不需要改動
這裡重要的是,保留不等於放著不管。既然要保留,至少下面這些事情要先做起來。
- 把支援的 OS、位元數、需要的相依 DLL、註冊步驟用文字寫下來
- 把安裝、註冊、取消註冊從人工筆記收攏到指令碼或安裝程式裡
- 準備在乾淨環境上跑的冒煙測試
- 不要把對控制項的呼叫散到整個應用程式,盡量集中到一個地方
最糟的情況,是「因為還在動所以不要碰」持續了十年,最後沒有人講得出當初的前提。 越是選擇保留,把前提攤開寫清楚就越重要。
flowchart TB
accTitle: 保留與把前提攤開寫清楚是一組的
accDescr: 保留的判斷不是放著不管,而是把前提寫成文件、把註冊步驟指令碼化、準備乾淨環境上的冒煙測試、把呼叫點集中起來一起做的圖。
keep["選擇保留"] --> d1["把前提用文字寫下來"]
keep --> d2["把註冊步驟指令碼化"]
keep --> d3["準備冒煙測試"]
keep --> d4["把呼叫收攏到一個地方"]
圖 4: 保留不等於放著不管,越是選擇保留,越要同時把前提攤開寫清楚。
3.3. 選擇包起來
在實務上,這個選項最常變成實際的工作。
這裡說的「包起來」,是把 ActiveX / OCX 關在一道很窄的邊界內側, 對周邊只以新的 API 或新的畫面元件呈現。
這招相當有效。 因為在還讀不透舊元件行為的階段就進入全面重做,很容易變成一邊挖規格一邊重現缺陷的蠟燭兩頭燒。 先把舊元件隔離起來,只把邊界整理好會安全得多。
flowchart TB
accTitle: 包起來這個選項的結構
accDescr: 把 ActiveX / OCX 關在一道很窄的邊界內側,對周邊只以新的 API 或畫面元件呈現,藉此避開在還讀不透行為的階段全面重做所帶來的挖規格與重現缺陷兩頭燒的圖。
old["ActiveX / OCX"] --> wall["關在很窄的邊界內側"]
wall --> api["以新的 API 呈現"]
api --> app["周邊只看得到新的窗口"]
wall -.-> safe["避開挖規格與重現缺陷的兩頭燒"]
圖 5: 「包起來」做的是隔離舊元件並打造新窗口,在全面重做之前這一步很有用。
包的方式有幾種型式。
| 包法 | 適合的情境 | 要看的重點 |
|---|---|---|
| WinForms 承載 + AxHost / Aximp | 想嵌進既有的桌面畫面,只想留下少數幾個畫面 | STA、事件、設計階段的相依性、授權 |
| 32 位元 helper EXE / COM LocalServer / 跨處理程序橋接 | 想收攏到 64 位元、想隔離當機 | 處理程序間通訊、啟動順序、監控、部署 |
| .NET 這一側的 COM 相容窗口 | 想保留既有的 COM 呼叫端,同時更新內部實作 | IID / CLSID / TLB / 註冊方式 / 位元數 |
方針定下來之後,第一步該從哪裡下手,這裡也寫出來。
| 包法 | 第一步要做的事 | 詳細步驟 |
|---|---|---|
| WinForms 承載 + AxHost | 在 Visual Studio 裡對工具箱按右鍵 →「選擇工具箱項目」→ 從「COM 元件」索引標籤挑出對象。命令列的話就用 aximp |
COM/OCX/ActiveX 開發中會踩到的註冊與位元數陷阱 |
| 32 位元 helper EXE / LocalServer | 把 32 位元的 EXE 註冊成 COM 伺服器,64 位元這一側以 out-of-proc 呼叫 | COM 派得上用場的案例研究 - 想從 32 位元應用程式呼叫 64 位元 DLL 時 |
| Reg-Free COM | 在應用程式的資訊清單裡寫上 file 與 comClass,不做登錄檔註冊也能解析 |
Reg-Free COM 是什麼 - 免註冊使用 COM 的機制 |
| .NET 這一側的 COM 相容窗口 | 把 .NET 這一側對 COM 公開,必要時用 dscom 產生 TLB | 如何從 VBA 帶型別地使用 .NET 8 DLL - COM 公開與 dscom TLB |
aximp 要從 Visual Studio 的 Developer Command Prompt 執行。
aximp C:\path\to\MyControl.ocx
這樣會產生兩個東西:一個是 COM 型別的 runtime callable wrapper,另一個是從 AxHost 衍生出來的 Windows Forms 用包裝層。檔名不是取自原本的檔名,而是 由 ProgID 決定,只有這一點要特別注意。以 Microsoft 文件的例子來說,msdxm.ocx 會產出 MediaPlayer.dll 和 AxMediaPlayer.dll。要把後者加入參考,再把 AxMediaPlayer 貼到表單上。
flowchart TB
accTitle: aximp 會產生什麼
accDescr: 把 OCX 交給 aximp 之後會產生 COM 型別的 runtime callable wrapper 與 AxHost 衍生的 Windows Forms 用包裝層兩個檔案,後者要加入參考再貼到表單上,而檔名不是取自原檔名而是由 ProgID 決定的圖。
ocx["對象 OCX"] --> tool["執行 aximp"]
tool --> rcw["COM 型別的包裝 DLL"]
tool --> ax["AxHost 衍生的包裝 DLL"]
ax --> form["加入參考並貼到表單上"]
tool -.-> name["檔名由 ProgID 決定"]
圖 6: aximp 的輸出是兩個 DLL,貼到表單上的是 AxHost 衍生的那一個包裝層。
如果走 Reg-Free COM,應用程式這一側的資訊清單最小長這樣。
<?xml version="1.0" encoding="utf-8"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32" name="MyApp" version="1.0.0.0" />
<file name="MyControl.ocx">
<comClass
clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
threadingModel="Apartment"
progid="MyCompany.MyControl.1" />
</file>
</assembly>
如果走 LocalServer 的方式,登錄檔上會在 HKEY_CLASSES_ROOT\CLSID\{CLSID}\LocalServer32 放進 EXE 的路徑。用 ATL 或 MFC 寫的 EXE 伺服器,照慣例多半可以用 MyServer.exe /regserver 和 /unregserver 自行註冊與取消註冊。不過登錄檔的檢視會依位元數分開,所以 32 位元的 EXE 會註冊到 32 位元那一側的登錄檔 這一點務必要記在心上。
flowchart TB
accTitle: LocalServer 註冊與登錄檔的檢視
accDescr: LocalServer 方式會把 EXE 的路徑放進 CLSID 底下的 LocalServer32,可以照 regserver 的慣例自行註冊,但登錄檔的檢視會依位元數分開,32 位元的 EXE 會註冊到 32 位元那一側的圖。
exe["EXE 伺服器"] --> reg["把路徑註冊到 LocalServer32"]
reg --> view{"EXE 是幾位元?"}
view -->|"32 位元"| v32["註冊到 32 位元那一側的檢視"]
view -->|"64 位元"| v64["註冊到 64 位元那一側的檢視"]
圖 7: LocalServer 註冊到哪裡,由依位元數分開的登錄檔檢視決定。
特別重要的是,包起來的時候不要把舊 API 原封不動複製 200 個過去。 那樣做只是把舊時代的方便,原封不動搬進新的程式碼裡而已。
包起來的時候,注意下面這幾點就會好很多。
- 收成粗粒度的方法
- 不讓畫面程式碼直接碰 OCX
- 在邊界上取得失敗時需要的日誌
- 在邊界上把逾時、重試、例外轉換的職責定下來
- 讓未來的取代對象也能用同一個介面換上去
也有些情況是想在新的 .NET 這一側只留下 COM 的入口。 這時候,「內部實作照樣更新,只維持 COM 契約」這種架構是實際可行的。 不過,用 .NET Framework 時代的感覺以為「先跑個 RegAsm 就好」,未必行得通。 現在 .NET 的 COM host、TLB、位元數、Registry-Free COM 該怎麼處理,先設計好後面會輕鬆很多。 這一帶的內容,在 如何從 VBA 帶型別地使用 .NET 8 DLL - COM 公開與 dscom TLB 和 Reg-Free COM 是什麼 - 免註冊使用 COM 的機制 裡各自寫到了實際步驟。
flowchart TB
accTitle: 在邊界上定下來的職責
accDescr: 包起來的時候要收成粗粒度的方法、不讓畫面程式碼直接碰 OCX、把日誌與逾時與例外轉換的職責定在邊界上,並讓未來的取代對象也能用同一個介面換上去的圖。
b["包起來的邊界"] --> r1["粗粒度的方法"]
b --> r2["在邊界上取得日誌"]
b --> r3["逾時與例外轉換"]
b --> r4["同一個窗口,將來可換"]
r1 -.-> ng["不要複製 200 個舊 API"]
圖 8: 包起來的價值在於把職責集中到邊界上,照抄舊 API 是拿不到這個價值的。
3.4. 選擇取代
適合取代的,主要是問題出在表層老舊的情況。
以下這些狀況,建議以取代為優先來看。
- 那個 ActiveX 只被當成 UI 元件在用
- 廠商已經推出 .NET / WPF / WebView2 版的後繼品
- 瀏覽器相依性或 IE 前提正在拖累整體
- 註冊、簽署、系統管理員權限、安全性設定每次都絆一跤
- 有能夠驗證替代實作的測試或業務情境
反過來說,只因為外觀老舊,就想把連裝置控制或報表邏輯都一併內含的元件一口氣丟掉,多半會陷入泥沼。
要取代的話,先從 UI 開始。
- 格線
- 行事曆
- 樹狀檢視
- 瀏覽器顯示區
- 單純的輸入輔助
這一帶相對容易換掉。另一方面,也有些東西看起來是 UI,裡面卻很紮實。
- 廠商製的裝置控制 ActiveX
- 和列印或報表產生綁在一起的控制項
- 內含專屬檔案格式讀寫的控制項
- 內含 COM 回呼或執行緒前提的控制項
看錯這個差別,工時估算會一口氣崩掉。
flowchart TB
accTitle: 取代判斷的分辨方式
accDescr: 只被當成 UI 元件在用而且有替代品的話容易取代,但看起來像 UI 卻內含裝置控制、報表、專屬格式、執行緒前提的元件裡面很紮實,一口氣丟掉會陷入泥沼的圖。
q{"外觀底下藏著什麼"}
q -->|"只是表層老舊"| easy["容易取代"]
q -->|"內含一整塊規格"| heavy["一口氣丟掉會陷入泥沼"]
heavy --> wrap["轉往先包起來的判斷"]
圖 9: 適不適合取代,取決於分辨得出「表層的老舊」和「內容的紮實」。
3.5. 瀏覽器相依性要另外看
這一段要另外算。
瀏覽器上的 ActiveX 和桌面的 OCX 不同, 往後繼續在上面加碼的理由相當薄弱。
理由很簡單:現代的瀏覽器基礎並沒有把那裡當成主戰場。 Microsoft Edge 本身並不支援 ActiveX。 另一方面,IE 模式會對設定過的網站改用 IE 系的引擎,可以當成讓包含 ActiveX 在內的部分 IE 功能繼續運作的相容層。
也就是說,
- 為了現在能動而延續使用,是做得到的
- 但就長期的設計來說,前面的路並不寬
就是這麼一回事。
flowchart TB
accTitle: 瀏覽器上 ActiveX 的定位
accDescr: Microsoft Edge 本身不支援 ActiveX,IE 模式是對設定過的網站改用 IE 系引擎的相容層,所以為了現在能動而延續使用做得到,但就長期設計而言前面的路並不寬的圖。
bax["瀏覽器上的 ActiveX"] --> edge["在 Edge 本體上跑不起來"]
bax --> iem["可以用 IE 模式延續使用"]
iem --> future["長期的路很窄"]
future --> rep["以取代為優先來看"]
圖 10: 瀏覽器相依的 ActiveX,要把延續使用和長久設計分開想,並以取代為優先來看。
同樣的事,也會發生在嵌進 Windows 應用程式的 WebBrowser 控制項上。
WebBrowser 拖著 IE 系的世界觀,所以如果只是想顯示 HTML,從現在開始的新工作把 WebView2 當成第一順位會比較自然。
不過這裡要注意的是,WebView2 並不是 WebBrowser 的完整替換零件。
- 以 IE DOM 為前提的指令碼
- ActiveX 相依性
window.external周邊的前提- 以安全性區域或內部網路為前提的行為
這一帶不會原封不動搬過去。 要取代的話,除了轉譯引擎之外,連瀏覽器與原生之間的介接面也得重新設計。
flowchart TB
accTitle: 不會直接搬到 WebView2 的東西
accDescr: WebView2 並不是 WebBrowser 控制項的完整替換零件,以 IE DOM 為前提的指令碼、ActiveX 相依性、window.external 周邊的前提、以安全性區域為前提的行為都不會原封不動搬過去的圖。
wb["從 WebBrowser 到 WebView2"] --> ok["只要顯示 HTML 就是第一順位"]
wb --> ng["不會原封不動搬過去的東西"]
ng --> n1["以 IE DOM 為前提的指令碼"]
ng --> n2["ActiveX 相依性"]
ng --> n3["window.external 前提"]
ng --> n4["安全性區域的行為"]
圖 11: WebView2 是轉譯引擎的替換零件,卻沒有接手 IE 世界觀的那道介接面。
4. 容易讓判斷失準的幾個重點
4.1. 是 UI 元件,還是內含規格的元件
這是最重要的一點。
如果是老舊的格線或行事曆,只要看外觀與事件的相容性,事情就能推得很順。 另一方面,內含裝置控制、報表或專屬格式的 ActiveX,外觀底下藏著一整塊規格。
同樣看起來都是「畫面上的控制項」,實際上的落差有這麼大。
- 只做清單顯示的元件
- 用專屬通訊協定對設備送指令的元件
- 內部連逾時、重連、重送、例外吸收都做掉的元件
- 背著列印或匯出格式相容性的元件
把後者直接拿去重做,多半會變成一個挖規格的專案。 這裡先包起來會安全得多。
flowchart TB
accTitle: 是 UI 元件還是內含規格的元件
accDescr: 同樣看起來都是畫面上的控制項,只做清單顯示的元件與連對設備送指令、重連、報表相容性都背著的元件落差很大,後者直接重做會變成挖規格的專案的圖。
look["畫面上的控制項"] --> ui["只做顯示的元件"]
look --> spec["內含一整塊規格的元件"]
ui --> go["看相容性就能推進"]
spec --> dig["直接重做會變成挖規格"]
dig --> wrap["先包起來比較安全"]
圖 12: 最重要的分辨。外觀一樣,但背後有沒有一整塊規格,走法完全不同。
4.2. 32 位元 / 64 位元與處理程序邊界
這一點很常被漏掉,卻相當本質。
in-proc 的 OCX 必須和載入它的處理程序位元數一致。 換句話說,32 位元 OCX 沒辦法直接載入 64 位元應用程式。
這時候實際可行的選項,大致就是這三條。
- 短期內讓主應用程式這一側也維持 32 位元
- 把它關進 32 位元的另一個處理程序,和 64 位元這一側用 IPC 或 out-of-proc COM 連接
- 從能拆掉這個 OCX 相依性的地方先取代
在這裡想著「反正是 Any CPU 應該有辦法」,多半沒有用。 就算要在新的 .NET 這一側做 COM 相容窗口,受管理程式碼看起來的樣子和實際 COM host 的位元數也是兩回事。 這一段草率開工,就會冒出「建置過得了,到了散發目的地卻跑不起來」這種很討厭的狀況。
flowchart TB
accTitle: 32 位元 OCX 與 64 位元化的三選一
accDescr: 32 位元 OCX 無法以 in-proc 載入 64 位元處理程序,所以只剩下讓承載端維持 32 位元、關進 32 位元的另一個處理程序並以 IPC 或 out-of-proc COM 連接、從能拆掉相依性的地方先取代這三條路的圖。
wall["32 位元 OCX 無法 in-proc"] --> o1["讓承載端維持 32 位元"]
wall --> o2["關進 32 位元的另一個處理程序"]
wall --> o3["從能拆的地方先取代"]
o2 -.-> ipc["以 IPC 或 out-of-proc COM 連接"]
圖 13: 位元數的高牆不是靠拼勁跨得過的,實際的選項就落在這三條上。
4.3. 註冊、散發、權限、授權
技術上呼叫得動,卻死在散發這一關。 這在 ActiveX / OCX 上相當常見。
容易成為難關的是這一帶。
regsvr32的前提變成依賴特定的人- 相依 DLL 該放哪裡變成心照不宣
- 明明需要系統管理員權限,卻沒有寫進維運步驟裡
- 廠商製控制項的 design-time / runtime 授權是分開的
- 開發機上跑得動,乾淨環境卻跑不動
這一帶就算一行程式碼都不碰,也能讓專案停擺。
免註冊的架構或 side-by-side 的擺法確實有時會輕鬆一些,但那不是仙丹。 還是得確認和容器端以及散發方式合不合得來。
換句話說,ActiveX / OCX 的遷移不只是實作,也是散發設計。 把這一段往後拖,最後會跌得很難看。
flowchart TB
accTitle: 卡在散發上的難關
accDescr: regsvr32 依賴特定的人、相依 DLL 的擺放心照不宣、系統管理員權限沒有寫進維運步驟裡、授權在 design-time 與 runtime 上分開,這些就算一行程式碼都不碰也能讓專案停擺的圖。
dist["散發設計"] --> d1["regsvr32 依賴特定的人"]
dist --> d2["相依 DLL 心照不宣"]
dist --> d3["系統管理員權限不在步驟裡"]
dist --> d4["授權被切成兩半"]
d1 -.-> stopx["不碰程式碼也會停擺"]
圖 14: 技術上呼叫得動卻死在散發,這種難關要和實作分開來設計。
4.4. STA / 訊息迴圈 / 回呼
ActiveX / OCX 不只是單純的 DLL 呼叫。 它可能帶著 COM 的執行緒模型或訊息迴圈的前提。
特別要小心的是下面這幾種情況。
- 只有在 UI 執行緒上才穩定
- 明明以 STA 為前提,卻隨手從 MTA 那一側呼叫
- 同步呼叫進行到一半,回呼就打回來了
- 接收事件的執行緒前提含糊不清
這一帶一開始都以「偶爾會卡住」「偶爾收不到事件」這種像鬼故事的面貌出現。 但拆開來看,裡面多半都是違反前提。
所以不論是要包起來還是要取代, 在哪個執行緒建立、從哪個執行緒呼叫、在哪裡接收事件,都最好先固定下來。
flowchart TB
accTitle: 先把執行緒的前提固定下來
accDescr: 不先固定在哪個執行緒建立、從哪個執行緒呼叫、在哪裡接收事件,就會變成偶爾卡住、偶爾收不到事件這種違反前提的鬼故事的圖。
fix["先固定的三件事"] --> t1["在哪個執行緒建立"]
fix --> t2["從哪個執行緒呼叫"]
fix --> t3["在哪裡接收事件"]
t1 -.-> ghost["含糊不清就變成違反前提的鬼故事"]
圖 15: 「偶爾會卡住」的內容多半是違反前提,先把執行緒的約定固定下來就能防住。
4.5. 有沒有測試,能不能觀測
取代之所以困難,不只是因為程式碼老舊。 更是因為沒有東西可以說明「憑什麼算是跑得一樣」。
光是有下面這些東西,情況就差很多。
- 每個操作情境的冒煙測試
- 輸入輸出的樣本
- 畫面截圖或報表樣本
- 錯誤樣態與預期行為
- 逾時或設備未連線時的日誌
尤其一牽扯到設備或報表,就會出現「實物的行為比規格書更像真相」這種怪事。 在這裡如果沒有觀測手段,取代就會變成考古挖掘。
flowchart TB
accTitle: 觀測手段撐住取代
accDescr: 沒有冒煙測試、輸入輸出樣本、報表樣本、錯誤樣態與預期行為這些觀測手段,就沒有東西能說明憑什麼算是跑得一樣,取代會變成考古挖掘的圖。
q{"有沒有觀測手段"}
q -->|"有"| ok["說得出跑得一樣"]
q -->|"沒有"| dig["取代變成考古挖掘"]
ok -.-> ex["冒煙測試與各種樣本"]
圖 16: 取代的難度與其說在程式碼老舊,不如說取決於有沒有能說出「跑得一樣」的觀測手段。
5. 依典型情境給的建議
5.1. 現在仍穩定運轉的公司內部桌面應用程式
建議是偏向保留。
符合下面這些條件的話,多半不要硬把它剝下來比較好。
- 只在公司內部使用
- 目標電腦或 OS 有一定程度是固定的
- 那個 OCX 只用在幾個畫面上
- 改版需求不大,生命週期也看得出來
不過,不要就這樣裸著放在那裡, 至少把呼叫的地方先收攏起來,之後會很有用。
也就是說,方針是這樣。
- 現在先保留
- 但把邊界整理好
- 做成一旦需要取代,就能從那裡下手的樣子
這三段式的做法最直接了當。
5.2. 想把 32 位元 OCX 帶到 64 位元這一側
建議是包起來 / 改架構。
這一段正面硬幹會走不下去。 因為 32 位元 OCX 沒辦法以 in-proc 放進 64 位元處理程序。
實際上,把它關進 32 位元的輔助處理程序或 LocalServer 那一側, 再和 64 位元應用程式以粗粒度的 API 通訊,這種架構比較好操作。
sequenceDiagram
participant App as 64 位元 .NET 應用程式
participant Bridge as 32 位元輔助程式 / LocalServer
participant Ocx as 32 位元 OCX
App->>Bridge: 以粗粒度的 API 提出請求
Bridge->>Ocx: in-proc 呼叫
Ocx-->>Bridge: 結果 / 事件
Bridge-->>App: 轉換後的結果
圖 17: 把 OCX 關進 32 位元輔助程式,再讓 64 位元應用程式隔著粗粒度 API 使用的架構。
這裡的重點是,不要把細碎的方法原封不動全部轉送過去。 處理程序之間的邊界,只要灌進大量細碎的呼叫就會馬上變得難受。
- 收攏到大約「1 個操作 = 1 個請求」的粒度
- 把傳回值與錯誤整理成有意義的單位
- 在邊界上取得日誌
做成這個樣子,之後真的要換掉內部實作時也會輕鬆。
到這裡都是以自己寫的輔助 EXE 為前提在講,不過在那之前還有一層,值得先確認一下。
如果那個 OCX / DLL 是 in-proc 的 COM 伺服器,也就是能以 InprocServer32 註冊的東西,就可以把它掛到 Windows 內建的 surrogate 處理程序上,當成 out-of-proc 的 Local Server 對外提供。
只要在 CLSID 上加一個 AppID,再往那個 AppID 機碼寫入空字串的 DllSurrogate,自己這邊一行程式碼都不用寫。
註冊的步驟整理在 COM/OCX/ActiveX 開發中會踩到的註冊與位元數陷阱 的 3.5。
不過,surrogate 並不會替你消掉位元數的差異。 消掉的只有「必須放在同一個處理程序裡」這一條限制,呼叫本身會變成 out-of-proc,封送處理與處理程序間通訊的開銷照樣加在上面。
要選哪一邊,大致由這張表決定。
| 狀況 | 選什麼 | 理由 |
|---|---|---|
| 靠方法呼叫與事件就能完結的自動化物件 | 先用 surrogate | 因為只要註冊就能變成 out-of-proc,一行程式碼都不用寫 |
往來的型別可以用 IDispatch 或已註冊的 proxy / stub 做封送處理 |
先用 surrogate | 因為跨邊界的配套已經現成 |
那個 CLSID 上已經有 LocalServer32 之類的 EXE 註冊 |
surrogate 沒有出場機會 | 因為永遠會優先啟動 EXE 伺服器或服務 |
| 想當成貼在表單上繪製的視覺化控制項來用 | 另尋別的架構 | 因為它的視窗是以位於承載端的處理程序內為前提 |
| 細碎的呼叫頻率很高,原封不動轉送會很重 | 自己寫輔助 EXE | 因為需要自己握著一層把它們束成粗粒度 API |
| 專屬介面沒有封送器可用 | 自己寫輔助 EXE | 因為準備 proxy / stub,或自己決定邊界上的型別,反而比較快 |
| 想自己掌控初始化順序、重連、逾時、日誌 | 自己寫輔助 EXE | 因為 surrogate 的處理程序生命週期是交給 COM 決定的 |
換句話說,surrogate 是值得先試一下的最小一步,自己寫 EXE 則是想親手設計邊界時的做法。 3.1 那張表裡「想把 32 位元 OCX 直接放進 64 位元處理程序 → 包起來 / 改架構」這一條並沒有變。請把它讀成:那個「包起來」裡面分成兩段。
5.3. 以 IE / WebBrowser 為前提的畫面
建議是以取代為優先。
這一塊是「現在能動」和「往後也好維持」很難同時成立的領域。 IE 模式在相容性上幫助確實很大,但前提終究還是 IE 系的那一套。
所以,想法上這樣切開會比較清楚。
- 為了不讓公司內部業務停擺,用 IE 模式延續使用
- 但不要把延續使用和長久設計混為一談
- 取代的對象可以從 WebView2、純 Web、原生 UI + Web 的混合等選項裡挑
延續使用這一側的具體配套也寫出來。IE 模式並不是「裝了 Edge 就自動生效」的東西,要用原則指定對象網站之後才會以 IE 系的引擎開啟。設定的入口有這三個。
| 做法 | 設定 | 補充 |
|---|---|---|
| 逐一列出網站 | 在 Microsoft Edge 78 以後的群組原則「Configure the Enterprise Mode Site List」裡,指定企業模式網站清單 XML 的位置 | 這是最基本的形式 |
| 沿用舊 IE 那一側的清單 | Internet Explorer 的原則「Use the Enterprise Mode IE website list」 | 只要 Edge 那一側有原則,就以 Edge 的為準 |
| 把整個內部網路都導過去 | 啟用 Microsoft Edge 77 以後的群組原則「Send all intranet sites to Internet Explorer」 | 涵蓋範圍會變得很廣,不能拿來代替盤點 |
前提是:Windows 和 Edge 都裝了最新的更新、已經匯入 Microsoft Edge 的系統管理範本、並且在 Windows 功能中啟用了 Internet Explorer 11。這些缺一項,IE 模式就會失敗。
另外,IE 模式裡 ActiveX 控制項和 Browser Helper Object 都是會動的。也就是說,延續使用真的做得到。正因為如此,不先訂好結束條件就一直用下去,最後會走不出來。怎麼把它剝掉,整理在 擺脫 IE 模式相依系統的指南。
flowchart TB
accTitle: IE 模式延續使用的想法
accDescr: IE 模式要用原則指定對象網站之後才會生效,ActiveX 也會動所以延續使用真的做得到,但不要把延續使用和長久設計混為一談,不訂結束條件就會走不出來的圖。
policy["用原則指定對象網站"] --> iem["以 IE 模式開啟"]
iem --> alive["連 ActiveX 一起延續使用"]
alive --> cond["訂好結束條件再用"]
cond -.-> exitw["不訂就走不出來"]
圖 18: IE 模式的延續使用「真的有效」,正因為如此才要和結束條件成套使用。
尤其如果只是把 WebBrowser 控制項當成單純的 HTML 檢視器在用,
取代的優先度就很高。
另一方面,如果瀏覽器裡的 ActiveX 還扛著本機檔案、設備、簽署、專屬增益集這類角色, 那就不是換掉轉譯引擎,而是重新設計原生介接。 這一段話題會沉重一些。
5.4. 內含裝置控制或專屬規格的 ActiveX
建議是先包起來。
這一類東西,裡面比外表紮實得多。 就算 SDK 的資料很單薄,在第一線跑了很多年之後, 下面這些行為往往已經默默堆在裡面。
- 連線失敗時怎麼等
- 逾時之後怎麼重試
- 事件的先後順序
- 吸收實機脾氣的迴避處理
- 例外與錯誤碼的解讀方式
這種元件用「反正也舊了」的態度重做,實機試驗會有很高的機率燒起來。
所以從這裡開始比較安全。
- 把既有元件關進邊界內側
- 加上日誌,讓正在發生什麼變得看得見
- 蒐集測試情境與實機的樣態
- 之後再切出可以取代的範圍
看起來不起眼,但在實務上這樣做最有用。
flowchart TB
accTitle: 內含規格的 ActiveX 的推進方式
accDescr: 把既有元件關進邊界內側、加上日誌讓正在發生什麼變得看得見、蒐集測試情境與實機樣態,之後再切出可以取代的範圍,依這個順序推進的圖。
s1["關進邊界內側"] --> s2["加上日誌讓過程看得見"]
s2 --> s3["蒐集情境與實機樣態"]
s3 --> s4["切出可以取代的範圍"]
圖 19: 內含裝置控制或專屬規格的元件,照這個順序推進,實機試驗比較不會燒起來。
6. 常見的反面模式
| 反面模式 | 難受在哪裡 | 先怎麼修 |
|---|---|---|
| 因為有 ActiveX 就全面重寫 | 容易發生規格遺漏與工時爆炸 | 先盤點,再切出邊界 |
| 想把 32 位元 OCX 直接放進 64 位元應用程式 | 原理上做不到 | 隔離到 32 位元那一側,或是改架構 |
| 從畫面各處直接呼叫控制項的 API | 容易變成無法取代 | 收攏到 adapter / facade |
靠人工在跑 regsvr32 的步驟 |
環境差異每次都讓它出事 | 考慮安裝程式、指令碼、資訊清單化 |
| 因為有 IE 模式就放心了 | 容易把延續使用和長久對策混為一談 | 訂出取代計畫與結束條件 |
| 取代之前沒有記錄行為 | 沒辦法判定是否完成 | 備齊冒煙測試、樣本資料、日誌 |
這裡面在實務上特別常見的有三個。
- 急著全面重寫
- 輕看位元數的高牆
- 把 API 散到整個應用程式
光是避開這三個,出事的機率就會低不少。
flowchart TB
accTitle: 特別常見的三個反面模式
accDescr: 急著全面重寫、輕看位元數的高牆、把控制項 API 散到整個應用程式,光是避開這三個,出事的機率就會低不少的圖。
a1["急著全面重寫"] --> avoid["避開這三個"]
a2["輕看位元數的高牆"] --> avoid
a3["把 API 散到整體"] --> avoid
avoid --> down["出事的機率明顯下降"]
圖 20: 反面模式當中遇到率最高的就是這三個,光是避開就很有效果。
7. 開始遷移時的檢查清單
ActiveX / OCX 的案子與其一開始就進實作,不如先盤點會順利得多。順序大致是這樣。
- 清查列出正在使用的 OCX / DLL
- 檔名、版本、ProgID、CLSID、廠商、有沒有授權
- 清查列出用在哪些地方
- 畫面、功能、報表、設備、批次、Office 介接等
- 確認位元數與承載端的條件
- 32 位元 / 64 位元、in-proc / out-of-proc、STA 前提、瀏覽器相依性
- 確認散發條件
- 註冊方式、相依 DLL、系統管理員權限、無訊息安裝、乾淨環境的重現
- 做出冒煙測試
- 不只是正常情境,也要包含失敗時、未連線時、逾時的情況
- 做出邊界
- adapter、service、facade、跨處理程序橋接等
- 以 1 個畫面、1 個功能、1 台設備這種小單位先試
- 從順利的邊界開始,依序把保留 / 包起來 / 取代擴大出去
跳過這些步驟,之後連「當初到底難在哪裡」都會很難講清楚。
8. 大致的取捨
| 狀況 | 先選什麼 |
|---|---|
| 只在公司內部使用、穩定運轉、變更也小 | 保留 |
| 只想把周邊 .NET 化 | 包起來 |
| 32 位元 / 64 位元撞在一起 | 包起來 / 改架構 |
| 相依於 IE / WebBrowser / 瀏覽器上的 ActiveX | 取代 |
| 只是單純的 UI 元件而且有替代品 | 取代 |
| 內含裝置控制、報表、專屬規格 | 包起來 |
| 註冊或散發每次都摔跤 | 包起來或取代 |
猶豫不決的時候,先分辨它是 UI 元件還是內含規格的邊界面,就不太會判斷失準。
9. 總結
ActiveX / OCX 該怎麼處理,不是靠「因為老舊所以討厭」就能決定的事。
首先要看的重點有四個。
- 這個元件只是單純的 UI,還是一道內含規格的邊界面
- 是不是非得在同一個處理程序裡使用
- 會不會卡在 32 位元 / 64 位元、註冊、瀏覽器相依性、授權上
- 取代之前,能不能觀測它的行為
這四點看清楚之後,大致可以整理成這樣。
- 運轉穩定、生命週期也看得出來的,就保留
- 只想把周邊現代化的,就包起來
- UI 元件或相依於瀏覽器的,就取代
- 內含一整塊規格的元件,先包起來再階段性取代
舊技術不是拿來笑的對象,而是塞滿了歷史與契約的實物。 只不過,要和實物相處,還是需要邊界設計。
一旦能把「保留、包起來、取代」混在一起思考, ActiveX / OCX 的案子就會突然變成處理得動的問題。
flowchart TB
accTitle: 總結的整理
accDescr: 運轉穩定且生命週期看得出來就保留,只想把周邊現代化就包起來,UI 元件或相依於瀏覽器就取代,內含一整塊規格的元件先包起來再階段性取代的圖。
q["ActiveX / OCX 的處理方式"] --> k["運轉穩定就保留"]
q --> w["要現代化周邊就包起來"]
q --> r["UI 與瀏覽器相依就取代"]
q --> p["一整塊規格就先包再階段取代"]
圖 21: 只要這四個該看的重點清楚了,保留、包起來、取代就能整理成這個形狀。
10. 這類諮詢特別合適
這個主題就算還沒進開發,光是先釐清方針,也很容易產生價值。
例如下面這些諮詢就相當合適。
- 想盤點出到底哪些 OCX 真的該取代
- 想先把 32 位元 / 64 位元的卡關處整理出來
- 想收攏到 .NET,但只想保留 COM 的入口
- 想比較廠商已收攤的 ActiveX 該延續使用還是該規劃撤退
- 想看看 IE / WebBrowser 相依性可以從哪裡先剝起
- 想先把 1 個畫面、1 個功能安全地分離出來
ActiveX / OCX 的案子,勝負往往在實作之前,就落在邊界要怎麼切上。 在全面改造之前,光是從現況梳理、架構比較、遷移順序的設計開始,就已經相當有意義。
11. 參考資料
- Microsoft Learn: AxHost Class (System.Windows.Forms)
- https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.axhost
- Microsoft Learn: Aximp.exe (Windows Forms ActiveX 控制項匯入工具)
- https://learn.microsoft.com/en-us/dotnet/framework/tools/aximp-exe-windows-forms-activex-control-importer
- Microsoft Learn: How to: Add ActiveX Controls to Windows Forms
- https://learn.microsoft.com/en-us/dotnet/desktop/winforms/controls/how-to-add-activex-controls-to-windows-forms
- Microsoft Learn: Expose .NET Core components to COM
- https://learn.microsoft.com/en-us/dotnet/core/native-interop/expose-components-to-com
- Microsoft Learn: Registration-Free COM Interop
- https://learn.microsoft.com/en-us/dotnet/framework/interop/registration-free-com-interop
- Microsoft Learn: DllSurrogate
- https://learn.microsoft.com/en-us/windows/win32/com/dllsurrogate
- Microsoft Learn: Registering the DLL Server for Surrogate Activation
- https://learn.microsoft.com/en-us/windows/win32/com/registering-the-dll-server-for-surrogate-activation
- Microsoft Learn: 關於 Microsoft Edge 的常見問題
- https://learn.microsoft.com/en-us/deployedge/microsoft-edge-frequently-asked-questions
- Microsoft Learn: What is Internet Explorer (IE) mode?
- https://learn.microsoft.com/en-us/deployedge/edge-ie-mode
- Microsoft Learn: WebBrowser Class (System.Windows.Forms)
- https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.webbrowser
- Microsoft Learn: Introduction to Microsoft Edge WebView2
- https://learn.microsoft.com/en-us/microsoft-edge/webview2/
- KomuraSoft Blog: 為了避開 COM 的 STA/MTA 造成停止回應所需的基礎知識
- /zh-TW/blog/2026/01/31/000-sta-mta-com-relationship/
- KomuraSoft Blog: 從 C# 使用 C++ 的原生 DLL 時,為什麼用 C++/CLI 做包裝層比較好
- /zh-TW/blog/2026/03/07/000-cpp-cli-wrapper-for-native-dlls/
- KomuraSoft Blog: COM 派得上用場的案例研究 - 想從 32 位元應用程式呼叫 64 位元 DLL 時
- /zh-TW/blog/2026/01/25/002-com-case-study-32bit-to-64bit/
- KomuraSoft Blog: COM / ActiveX / OCX 是什麼 - 一次說清楚差別與關係
- /zh-TW/blog/2026/03/13/000-what-is-com-activex-ocx/
- KomuraSoft Blog: 如何從 VBA 帶型別地使用 .NET 8 DLL - COM 公開與 dscom TLB
- /zh-TW/blog/2026/03/16/007-dotnet8-dll-typed-vba-com-dscom-tlb/
- KomuraSoft Blog: Reg-Free COM 是什麼 - 免註冊使用 COM 的機制
- /zh-TW/blog/2026/03/16/011-what-is-reg-free-com/
- KomuraSoft Blog: COM/OCX/ActiveX 開發中會踩到的註冊與位元數陷阱
- /zh-TW/blog/2026/04/15/001-com-ocx-activex-pitfalls-visual-studio-bitness-admin-rights/
- KomuraSoft Blog: 擺脫 IE 模式相依系統的指南
- /zh-TW/blog/2026/04/25/003-ie-mode-internal-web-system-life-extension-and-exit/
- KomuraSoft Blog: IE 模式之後直接用 WebView2 就好了嗎 ── ActiveX 不能運作的限制與務實的遷移設計
- /zh-TW/blog/webview2-embed-web-ui-in-windows-apps/
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
開發 COM 元件、OCX/ActiveX 時常見的坑 - 整理 Visual Studio 的 32bit/64bit、註冊、管理員權限
整理開發 COM、OCX、ActiveX 元件時最容易卡關的四個面向:宿主行程的 32bit/64bit、Visual Studio 2022 變成 64bit 後的設計時整合、regsvr32 與 Regasm 的註冊位置、以及管理員權限與 HKCU/HKLM 的關係,協...
COM / ActiveX / OCX 是什麼 - 差異與關係一次整理
從實務角度梳理 COM 是什麼、ActiveX 是什麼、OCX 是什麼,涵蓋三者的差異與關係、與 OLE 的關聯、實際用在哪些地方,以及現在該怎麼看待它們。
Arm 版 Windows 能執行業務應用程式嗎 ── x64 模擬(Prism)與原生 DLL・COM 的現實
本文為開發者・資訊系統部門解答「Arm 版 Windows 能執行業務應用程式嗎」這個問題,整理 x64 模擬(Prism)的運作原理、驅動程式等無法執行的層級、.NET 的 AnyCPU 與 P/Invoke 問題,以及 Arm 對應檢查清單。
VB6 應用程式能用到什麼時候 ── 執行環境的支援現況與務實的 .NET 遷移做法
VB6 應用程式究竟能用到什麼時候?本文整理 VB6 執行環境的支援政策(Windows 11 也在支援範圍內)與 IDE 支援早已終止這種不對稱現況,並以實務指南的形式,說明全面重寫、自動轉換、階段性遷移的判斷表、遷移前的資產盤點、VB6 與 .NET 的不相容之處,以及...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
ActiveX 遷移
整理保留、包裝或替換 COM / ActiveX / OCX 資產的階段性判斷的主題頁面。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
既有資產活用 & 遷移支援
要判斷 COM / ActiveX / OCX 該保留、該包起來還是該取代,這本身就是既有資產活用與遷移支援的核心主題。
技術諮詢 & 設計審查
如果還在實作之前、想先把邊界的劃分與取代順序定下來,用技術諮詢與設計審查的方式把方針敲定會比較合適。
常見問題
整理諮詢這個主題時常見的問題。
- ActiveX / OCX 應該取代掉嗎?
- 判斷依據不是「舊不舊」,而是這個元件承擔了什麼。如果只是單純的 UI 元件而且有替代品,就取代;如果內含裝置控制、報表或專屬檔案格式,先包起來把邊界整理好;如果運轉穩定而且變更範圍很小,選擇保留也很實際。「總之先全面重寫」和「因為害怕就永久凍結」,這兩種做法出事的機率都很高。
- 可以從 64 位元應用程式使用 32 位元 OCX 嗎?
- 以 in-proc 的方式不行。OCX 必須和載入它的處理程序位元數一致,這是原理上的限制。實際可行的選項有三個:暫時讓主應用程式也維持 32 位元;把它關進 32 位元的另一個處理程序(自己寫的輔助 EXE 或 COM LocalServer),再用 IPC 或 out-of-proc COM 和 64 位元端連接;從能拆掉這個 OCX 相依性的地方先取代。
- 瀏覽器上的 ActiveX 相依性該怎麼處理?
- 建議以取代為優先。Microsoft Edge 本身並不支援 ActiveX,IE 模式的定位是延續使用的過渡手段。WebBrowser 控制項同樣拖著 IE 系的世界觀,所以如果只是想顯示 HTML,WebView2 就是第一順位。不過 WebView2 並不是完整的替換零件,以 IE DOM 為前提的指令碼,以及 window.external 周邊的前提,都不會原封不動地跟著搬過去。
- 把 ActiveX / OCX「包起來」具體上是在做什麼?
- 把 ActiveX / OCX 關在一道很窄的邊界內側,對周邊只以新的 API 或新的畫面元件呈現。常見的型式有 WinForms 承載加上 AxHost、用 32 位元輔助 EXE 或 LocalServer 做跨處理程序的橋接,以及在 .NET 這一側開一個 COM 相容窗口。包的時候不要把舊 API 原封不動大量複製過來,而要收成粗粒度的方法,在邊界上把日誌、逾時與例外轉換的職責定下來,並讓未來的取代對象也能用同一個介面換上去。