COM/OCX/ActiveX 开发中容易踩坑的注册与位数陷阱
· 小村 豪 · COM, ActiveX, OCX, Visual Studio, Windows 开发, 32bit, 64bit, Interop
COM 组件、OCX / ActiveX 相关的项目,比起代码本身,更容易在 运行环境、注册、宿主、权限 这些边界上踩坑。
常见的症状大概是这些:
- 编译能通过,但启动时报
0x80040154。 - 在自己的开发机上能跑,换一台电脑就不行。
- 运行时正常,但 Visual Studio 的 Designer 却挂掉。
- 以管理员身份启动就能用,普通权限下就会出问题。
- 已经执行了
regsvr32,却怎么都修不好。
这些与其说是个别 bug,不如说是 COM 的前提条件在某个环节没有对齐 的状态。
如果想先厘清 COM / ActiveX / OCX 这些术语本身,可以先读一下 COM / ActiveX / OCX 是什么 - 区别与关系整理,这样更容易抓住整体脉络。 本文作为下一阶段,整理的是 实际容易卡住的地方,也会涉及 Visual Studio 的位数与管理员权限相关的问题。
1. 先说结论
先用实务中管用的说法来讲:
- COM / OCX / ActiveX 的问题,大多不是出在代码逻辑上,而是出在 位数(32bit / 64bit)、注册位置、宿主、权限 不一致上。
- Visual Studio 2022 是 64 位进程,所以过去建立在 32 位前提上的设计时(design-time)联动,原样搬过来就会出问题。12
regsvr32不是什么都能注册的万能命令,它是给原生 in-proc COM 服务器(DLL / OCX)用的。要把 .NET Framework 公开为 COM,用的是Regasm.exe;到了 .NET 5+ / .NET 6+ / .NET 8+,则是注册生成出来的.comhost.dll。3456- 「以管理员身份能跑就行」这种想法很危险。很多时候只是 恰好因为 per-user 注册而能看到,或者 本该由安装程序完成的注册,只是在开发机上手动做了一遍。378
也就是说,处理 COM / OCX / ActiveX 时,先从这 4 个维度来看会比较安全:
- 是哪个进程在做宿主
- 该进程是 32 位还是 64 位
- 注册在哪里(HKCU / HKLM,32 位 view / 64 位 view)
- 这个操作或运行是否需要管理员权限
2. 从症状反推,大致是这样
| 症状 | 首先怀疑的点 | 常见的真正原因 |
|---|---|---|
0x80040154 Class not registered |
未注册 | 实际上很多时候是「只注册在另一个位数那一侧」或「只注册给了该用户」 |
DllRegisterServer failed: 0x80070005 |
权限不足 | 用标准用户尝试注册、post-build 阶段没有提权就直接注册 |
| VS2022 中只有 Designer 挂掉 | Designer 的限制 | 64 位的 Visual Studio 无法直接读取 32 位 COM / ActiveX |
| 只有以管理员身份启动才能用 | 权限问题 | 本质上大多不是权限本身,而是注册范围或安装设计出现了错位 |
| 64 位应用无法调用 32 位 OCX | COM 机制的限制 | in-proc 服务器必须和宿主的位数一致才能加载 |
| 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 变成了 64 位
这是当前 COM / ActiveX 开发中最容易踩坑的地方。
Visual Studio 2022 的 devenv.exe 是 纯 64 位 的。1
因此在 WinForms 的设计时体验中,Visual Studio 一侧 无法直接加载 32 位组件。Microsoft 也明确说明,由于 Visual Studio 2022 是 64 位进程,所以无法加载 32 位的 .NET / COM / ActiveX 组件。2
也就是说,以前的做法是:
- 项目是 x86
- 引用的 ActiveX 也是 x86
- Visual Studio 本身也是 32 位
在这套前提下勉强能成立,但到了 VS2022,就会出现这样拧巴的状况:
- 运行时的应用能以 x86 方式工作
- 但 Designer 却运行在 64 位的 Visual Studio 一侧
结果就是 运行时明明活着,Designer 却单独挂掉 这种相当讨厌的状态。2
3.2. 改成 AnyCPU 不一定能解决问题
这也是常见的误解。
AnyCPU 不是能让依赖链一起变得中立的魔法。
Microsoft 的说明也提到,即便组件本身看起来是 AnyCPU,只要它下游引用了固定 32 位的 COM / ActiveX,在 Visual Studio 2022 的设计时环节依然会出问题。2
所以,如果改成 AnyCPU 之后错误还没消失,不如先怀疑:
- 该程序集下游是否存在 32 位原生依赖
- ActiveX / OCX 是否固定为 x86
- 是否存在只在 Designer 阶段才会加载的代码
这样排查起来更快。
3.3. System32 与 SysWOW64 的陷阱
在 Windows x64 上,名字给人的印象和实际情况是错开的,所以很容易搞混。
Microsoft Learn 中说明,x64 Windows 上的 %windir%\System32 是 给 64 位应用程序用的。32 位一侧会被 WOW64 的文件系统重定向器(redirector)引导到别的位置。12
注册表也是同样的道理,WOW64 的注册表重定向器会让 32 位 / 64 位分别看到 不同的逻辑 view。11
因此,在本地排查时,明确写出 用的是哪个位数的 regsvr32 会更安全。
# 想注册 64 位 DLL / OCX 时
C:\Windows\System32\regsvr32.exe vendor.ocx
# 在 x64 Windows 上想注册 32 位 DLL / OCX 时
C:\Windows\SysWOW64\regsvr32.exe vendor.ocx
这个陷阱讨厌的地方在于,如果注册到了错误的一侧,明明以为注册好了,但目标进程却看不到。 结果就变成:
regsvr32执行成功- 但应用还是报
0x80040154 - 查看注册表,看起来确实存在
- 但看到的其实是另一个 view
3.4. regsvr32 并不是什么都能注册
这里也是误解比较多的地方。
原生的 in-proc COM 服务器通常会导出 DllRegisterServer / DllUnregisterServer,以支持自我注册。3
regsvr32 就是用来对付这类 DLL / OCX 的工具。4
另一方面,如果想让 .NET Framework 程序集能被 COM 使用,基本上要用 Regasm.exe。Microsoft Learn 也说明,注册要供 COM 使用的程序集,应该使用 Regasm.exe。514
另外,到了 .NET 5+ / .NET 6+ / .NET 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 |
把这些区别搞混之后,常见的模式是:
- 对托管 DLL 直接跑
regsvr32 - 自然找不到
DllRegisterServer - 于是误以为「DLL 坏了」
在需要类型库的场景(VBA / VB6,或部分早期绑定的对象)中,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 也把用 regsvr32.exe 注册作为 post-build event 的示例列了出来。16
但是,能做到 和 安全 是两件事。
用标准用户执行 regsvr32,可能因为无法写入注册表或 System32 而报 0x80070005。Microsoft 的 KB 也把这个原因归结为 没有管理员权限。10
这里容易发生的事故是:
- 以普通权限启动 Visual Studio,build 能通过
- 但 post-build 的注册环节单独失败
- 失败日志被忽略掉了
- 由于之前留下的旧注册还在,本机恰好能跑
- 在干净环境下自然就不行了
这类项目的基本原则是 把构建和注册分开:
- 构建只负责生成二进制文件
- 注册通过明确的 install step / script / installer 来完成
- 在 CI 中,把「需要注册的步骤」和 build 拆成不同的 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)。717
也就是说,很常见会出现这种情况:
- 用开发者 A 的用户手动注册过
- A 的账户下能用
- 开发者 B 用不了
- 服务账户也用不了
- 用管理员运行时行为又会变
另外 Microsoft 还指出,需要管理员权限的应用程序,应该在安装时把所依赖的 COM 对象注册到 per-machine 的 COM 配置存储中。87
所以,开发现场应该把这些区别弄清楚:
- 这是 只给自己账户用的开发用注册
- 还是 供该机器全部用户使用的正式注册
- 或者是 供服务、提权应用引用的注册
如果把这些混为一谈,就会出现「不知道为什么只有管理员能用」「Explorer 扩展能用,服务却用不了」之类的问题。
4.3. 让 Visual Studio 一直「以管理员身份运行」并不是解决方案
确实存在需要这样做的场景。 但如果一直用管理员身份启动 Visual Studio,本该由安装程序或注册脚本解决的问题,就可能被 IDE 一侧的提权给掩盖掉。
而且 Visual Studio 本身在提权状态下,per-user extension 的处理方式也会变。Microsoft Learn 也说明,存在一项设置:以 elevated 方式运行 Visual Studio 时 per-user extensions 会被禁用。18
所以在运维上,比较不会后患的做法是:
- 平时开发用普通权限
- 只有需要注册的步骤,才在明确提权后的 Developer Command Prompt / PowerShell / installer 中执行
- 如果「只有管理员才能复现」,就把这个前提本身当作规格整理清楚
5. ActiveX / OCX 特有的坑
5.1. 设计时授权与运行时授权是分开的
OCX / ActiveX 中一个不起眼却很麻烦的问题是 授权(License)。
尤其是老旧的 ActiveX 控件,有时会把 design-time license 和 run-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 使用的包装(wrapper),再把它当作基于 AxHost 的控件来处理。2223
也就是说,问题不只在一层,而是分布在多个层次上:
- 原始的 OCX / ActiveX 本体
- 类型库
- 生成出来的 interop / wrapper
- WinForms Designer / runtime
因此会出现这些情况:
- 换了一版供应商 OCX,事件签名就变了
- 重新添加引用导致 wrapper 被重新生成,产生大量差异
- 每台开发机生成出来的 interop 结果都有些微妙的不同
在 Choose Toolbox Items 里能看到,并不代表就万事大吉。
Designer 能否放上去、运行时事件能否触发、部署到目标环境后连 wrapper 一起能否成立,最好分开来看,这样更安全。
5.3. 轻视 STA / MTA 与消息循环,会导致卡死
COM 要求每个使用它的线程都用 CoInitializeEx 单独初始化。Microsoft Learn 也明确说明,每个使用 COM 的线程都需要各自调用 CoInitializeEx。24
另外,STA(single-threaded apartment,单线程套间)需要有消息循环。2425
尤其是 UI 类的 OCX / ActiveX,大多以 STA 为前提,所以会出现这些讨厌的问题:
- 在 UI 线程上能正常运行
- 一旦丢给
Task.Run或 ThreadPool 就会卡死 - 事件回不来
- 只是偶尔才能复现
此外在 STA 中还有一个前提:不能把接口指针原样复制到别的线程去用,必要时要做封送(marshaling)。2425
这类问题不像 0x80040154 那么「友好」,表现出来就只是 卡死、回不来、偶尔崩溃,所以排查起来和注册表类问题一样费时间。
6. 现场实用的排查顺序
在实务中,与其一开始就深挖,按下面这个顺序切入会更快。
6.1. 先固定「哪个进程是多少位」
首先要看的是这里:
- 宿主是什么(Visual Studio Designer / 自研应用 / Office / Access / Explorer / 浏览器兼容环境)
- 该宿主是 32 位还是 64 位
- 目标 DLL / OCX 是 32 位还是 64 位
- 它是 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\ClassesHKLM\Software\Classes- 必要时还有 32 位 / 64 位的 registry view
- 目标的 ProgID / CLSID / TypeLib
InprocServer32/LocalServer32- ThreadingModel
- 引用的 DLL 实体路径
仅凭「HKCR 里有」是不够的,只有连同 是谁在看、以什么位数、看的是哪个 view 都对上了,才有意义。711
6.4. 最后看「是不是被权限掩盖了」
最后要确认的是,问题究竟是不是真的出在权限上,还是 只是权限不同导致看到了不同的注册结果:
- 标准用户 / 管理员之间行为是否会变
- 提权运行 Visual Studio 之后会有什么变化
- 换成服务账户或其他用户是否还能复现
- 走安装程序部署到干净环境下是否依然成立
如果只在一台开发机上验证,这里很容易判断失误。
7. 提前定好能减少事故的运维方式
在 COM / ActiveX / OCX 的开发与维护中,有时候 运维层面的约定方式 比实现技巧更管用。
7.1. 先定好位数方针
最先要定的是:
- 是坚持固定 x86
- 还是以 x64 为准
- 还是两者都支持
- 该组件是否必须是 in-proc
尤其是当供应商 OCX 固定为 x86 时,如果无视这一点只把应用改成 x64,之后就会卡住。 这个话题,作为更大规模架构的问题,也和 从 32 位应用调用 64 位 DLL 的方法 - COM 桥接派上用场的案例研究 相关。
7.2. 定好注册策略
注册这件事,也不建议临时应付。
- 供整台机器使用 → 通过 installer 做 per-machine 注册
- 只供该用户使用 → 有意识地使用 per-user
- 只在自己的应用内部闭环 → 考虑 registration-free COM
- 只是开发时需要 → 收进明确的 dev setup script 里
registration-free COM 把激活信息放进 manifest 而不是注册表,是减少「注册地狱」的有效手段。Win32 一侧的 registration-free COM,以及 .NET 一侧的 RegFree COM,官方都有相应说明。26627
7.3. 不要把生成物过多地放在 source control 之外
OCX / ActiveX 系统常见的问题是依赖物散落各处:
- OCX 本体
- 依赖 DLL
- TLB
.lic- interop DLL
- AxHost wrapper
- 注册脚本
- 示例宿主
如果这些东西只存在于某个人的本机上,几个月后必然会出事。
至少下面这些内容,最好和代码放在同一个地方留存:
- 前提是哪个版本
- 按什么顺序安装什么
- 用什么命令注册
- 面向 x86 还是 x64
8. 这类咨询比较合适
这个主题,哪怕只是在全面改造之前先做 排查 和 方针整理,也很容易产生价值。
比如,以下这类咨询就特别合适:
- 想把
0x80040154、0x80070005的原因,按位数 / 注册 / 权限拆开来整理 - 升级到 Visual Studio 2022 后 Designer 挂了,想看看能救到什么程度
- 想在保留供应商 OCX 的前提下,把周边逐步转向 .NET 或 C#
- 想确定 x86 固定资产还能延续到哪一步,从哪里开始 bridge / wrap / replace
- 想摆脱手动
regsvr32的依赖,重新设计 install / deploy 流程
关于替换、封装、保留的判断,也可以参考 ActiveX / OCX 现在该如何处理 - 保留、封装、替换的判断表。
9. 总结
在 COM 组件、OCX / ActiveX 开发中踩坑时,原因大致都能归结到这 4 点:
- 位数没对齐
- 注册方式用错了
- 注册范围(HKCU / HKLM、32 位 / 64 位 view)出现了偏差
- 把「恰好因为权限而能看到」的状态误当成了正常
随着 Visual Studio 2022 变为 64 位,过去那种「说不清为什么就能跑」的设计,变得相当容易暴露出来。12 正因为如此,接触 COM / OCX / ActiveX 时,在写代码之前先把环境前提统一好,才是捷径。
与其纠结 regsvr32 要敲几次,不如先把这些理清楚,解决速度会快得多:
- 是哪个进程在做宿主
- 该进程是多少位
- 应该注册在哪里
- 这个注册真的需要管理员权限吗
- 有没有把 Designer 和 runtime 分开来看
参考
-
Microsoft Learn, Visual Studio 2022 version 17.0 Release Notes —
devenv.exe is now 64-bit only. ↩ ↩2 ↩3 -
Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 是 64 位进程,无法直接加载 32 位的 .NET / COM / ActiveX,以及 out-of-process designer 的限制。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Classes and Servers — COM 的注册、HKCU / HKCR、self-registration 与
DllRegisterServer。 ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, 向 COM 注册程序集 — .NET Framework 的 COM 注册使用
Regasm.exe。 ↩ ↩2 -
Microsoft Learn, 向 COM 公开 .NET Core 组件 —
EnableComHosting、生成的.comhost.dll、regsvr32、EnableRegFreeCom。 ↩ ↩2 ↩3 -
Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR 是 HKLM 与 HKCU 的合并视图(merged view)。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, HKEY_CLASSES_ROOT Key — 需要管理员权限的应用推荐注册到 per-machine COM 配置中。 ↩ ↩2
-
Microsoft Learn, COM Error Codes (Generic) (Winerror.h) —
REGDB_E_CLASSNOTREG (0x80040154)等。 ↩ -
Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — 权限不足导致 DLL 注册失败的典型案例。 ↩ ↩2
-
Microsoft Learn, Registry Redirector — WOW64 中 32 位 / 64 位的注册表 view。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, File System Redirector — x64 Windows 的
%windir%\System32与 WOW64 的文件系统重定向。 ↩ -
Microsoft Learn, 64 位版本 Windows 上 32 位程序兼容性注意事项概述 — WOW64 带来的文件 / 注册表重定向。 ↩
-
Microsoft Learn, Regasm.exe (Assembly Registration Tool) —
Regasm.exe的作用及/tlb等选项。 ↩ -
Microsoft Learn, Packaging a .NET Framework Assembly for COM — 类型库与
Regasm.exe /tlb。 ↩ -
Microsoft Learn, Understanding Custom Build Steps and Build Events — 在 post-build event 中使用
regsvr32.exe的示例。 ↩ -
Microsoft Learn, 面向高级用户的 Windows 注册表 —
HKCU\Software\Classes与HKLM\Software\Classes、以及 HKCR 的行为。 ↩ -
Microsoft Learn, Find, install, and manage extensions for Visual Studio — elevated 运行时对 per-user extension 的处理方式。 ↩
-
Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time license、
.LIC。 ↩ -
Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — 运行时授权(license)的生成与
.lic文件。 ↩ -
Microsoft Learn, License information for this component not found. You don’t have an appropriate license to use this functionality in the design environment. ↩
-
Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — 将 ActiveX 转换为供 WinForms 使用的包装(wrapper)。 ↩
-
Microsoft Learn, AxHost Class — ActiveX Control Importer 生成的基于 AxHost 的包装。 ↩
-
Microsoft Learn, 初始化 COM 库 —
CoInitializeEx、按线程各自初始化、STA 的消息循环。 ↩ ↩2 ↩3 -
Microsoft Learn, Single-Threaded Apartment(单线程套间) — STA 的消息循环、封送(marshaling)、
ThreadingModel。 ↩ ↩2 -
Microsoft Learn, 创建 Registration-Free COM 对象 — 通过 activation context 实现的免注册 COM。 ↩
-
Microsoft Learn, 无需注册的 COM 互操作功能 — .NET Framework 的 registration-free COM interop。 ↩
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
Office 2024/Microsoft 365 中 ActiveX 无法运行的原因与排查步骤
梳理在 Office 2024/Microsoft 365 中 ActiveX 无法运行时,按默认禁用、32bit/64bit、COM 注册、依赖 DLL、IE 模式、Click-to-Run 日志的顺序进行排查的方法。
COM / ActiveX / OCX 是什么 - 区别与关系整理
从实务角度厘清 COM、ActiveX、OCX 三者的区别与关系:COM 是 Windows 组件互操作的二进制契约底层,ActiveX 是以 COM 为基础的可嵌入控件语境,OCX 则是 ActiveX 控件常见的扩展名。读完就能分清机制、组件、文件这三层边界。
ActiveX / OCX 现在该如何处理 - 保留、封装、替换的判断表
本文整理发现 ActiveX / OCX 时应该选择保留、封装还是替换,涵盖 32bit / 64bit、注册、浏览器依赖、供应商维护等因素。
32 位应用调用 64 位 DLL 的 COM 桥接实例
本文整理当 32 位应用无法直接调用 64 位 DLL 时,通过 COM 桥接进行集成的思路,并结合 Windows 的限制、架构设计与处理流程加以说明。
委托开发 Windows 应用程序前该梳理的事项
在委托外包开发 Windows 应用程序之前,梳理现有软件改造、设备联动、COM/ActiveX、发布与更新、维护等需要注意的要点。
常见问题
汇总了咨询这一主题时常见的问题。
- 0x80040154(Class not registered)应该怎么排查?
- 官方定义是 REGDB_E_CLASSNOTREG,字面意思是「类未注册」,但并不代表完全没有注册。也可能是只注册在另一个位数的注册表 view 里,或者只注册在只有该用户可见的 HKCU 一侧。最快的排查方式是:先确定宿主进程是 32 位还是 64 位,再检查注册手段是否正确,最后依次确认注册位置到底落在 HKCU / HKLM 以及 32 位 / 64 位 view 的哪一处。
- 用 regsvr32 注册之后还是不能用,是为什么?
- regsvr32 是给导出 DllRegisterServer 的原生 in-proc COM 服务器(DLL / OCX)用的,并不是什么都能注册的万能命令。.NET Framework 程序集要公开为 COM 组件,走的是 Regasm.exe;.NET 5 及之后版本则是先通过 EnableComHosting 生成 .comhost.dll,再用 regsvr32 注册它。另外,在 x64 Windows 上,64 位组件要用 System32 下的 regsvr32,32 位组件要用 SysWOW64 下的 regsvr32,如果注册到了错误的一侧,就会出现「注册显示成功,但目标进程却看不到」的情况。
- 为什么升级到 Visual Studio 2022 之后,只有 Designer 会挂掉?
- 因为 Visual Studio 2022 的 devenv.exe 是 64 位进程,无法直接加载 32 位的 COM / ActiveX 组件。哪怕运行时的应用本身能以 x86 方式正常工作,Designer 却是在 Visual Studio 那一侧的 64 位进程里运行的,于是就出现「运行时明明正常,却只有 Designer 挂掉」这种拧巴的状况。即便把项目设为 AnyCPU,只要引用链的下游还依赖固定 32 位的 COM / ActiveX,问题依然存在。
- 为什么以管理员身份运行就能用,普通权限却不行?
- 本质上往往不是权限本身的问题,而是注册范围或安装设计出现了错位。COM 会先查看 HKCU\Software\Classes,而 HKEY_CLASSES_ROOT 其实是 HKLM 与 HKCU 的合并视图(merged view)。如果是开发者 A 用自己的账户手动注册过,那么在 A 的账户下能用,换成其他用户或服务账户就用不了,这是很常见的现象。基本原则是要区分「仅供开发用的 per-user 注册」和「由安装程序完成的正式 per-machine 注册」,并把构建与注册这两个步骤分开。
作者简介
本文作者的个人简介页面。
Go Komura
小村软件有限公司 代表
以 Windows 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。