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. 先说结论

先用实务中管用的说法来讲:

  1. COM / OCX / ActiveX 的问题,大多不是出在代码逻辑上,而是出在 位数(32bit / 64bit)、注册位置、宿主、权限 不一致上。
  2. Visual Studio 2022 是 64 位进程,所以过去建立在 32 位前提上的设计时(design-time)联动,原样搬过来就会出问题。12
  3. regsvr32 不是什么都能注册的万能命令,它是给原生 in-proc COM 服务器(DLL / OCX)用的。要把 .NET Framework 公开为 COM,用的是 Regasm.exe;到了 .NET 5+ / .NET 6+ / .NET 8+,则是注册生成出来的 .comhost.dll3456
  4. 「以管理员身份能跑就行」这种想法很危险。很多时候只是 恰好因为 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 registered9 另外,regsvr320x80070005,官方文档也说明是 因为没有管理员权限,无法写入注册表或 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. System32SysWOW64 的陷阱

在 Windows x64 上,名字给人的印象和实际情况是错开的,所以很容易搞混。

Microsoft Learn 中说明,x64 Windows 上的 %windir%\System32给 64 位应用程序用的。32 位一侧会被 WOW64 的文件系统重定向器(redirector)引导到别的位置。12 注册表也是同样的道理,WOW64 的注册表重定向器会让 32 位 / 64 位分别看到 不同的逻辑 view11

因此,在本地排查时,明确写出 用的是哪个位数的 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

这样的状态。1113

3.4. regsvr32 并不是什么都能注册

这里也是误解比较多的地方。

原生的 in-proc COM 服务器通常会导出 DllRegisterServer / DllUnregisterServer,以支持自我注册。3 regsvr32 就是用来对付这类 DLL / OCX 的工具。4

另一方面,如果想让 .NET Framework 程序集能被 COM 使用,基本上要用 Regasm.exe。Microsoft Learn 也说明,注册要供 COM 使用的程序集,应该使用 Regasm.exe514

另外,到了 .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,之后再处理整台机器范围的信息。3HKEY_CLASSES_ROOTHKLM\Software\ClassesHKCU\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 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 使用的包装(wrapper),再把它当作基于 AxHost 的控件来处理。2223

也就是说,问题不只在一层,而是分布在多个层次上:

  1. 原始的 OCX / ActiveX 本体
  2. 类型库
  3. 生成出来的 interop / wrapper
  4. WinForms Designer / runtime

因此会出现这些情况:

  • 换了一版供应商 OCX,事件签名就变了
  • 重新添加引用导致 wrapper 被重新生成,产生大量差异
  • 每台开发机生成出来的 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 中还有一个前提:不能把接口指针原样复制到别的线程去用,必要时要做封送(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\Classes
  • HKLM\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. 这类咨询比较合适

这个主题,哪怕只是在全面改造之前先做 排查方针整理,也很容易产生价值。

比如,以下这类咨询就特别合适:

  • 想把 0x800401540x80070005 的原因,按位数 / 注册 / 权限拆开来整理
  • 升级到 Visual Studio 2022 后 Designer 挂了,想看看能救到什么程度
  • 想在保留供应商 OCX 的前提下,把周边逐步转向 .NET 或 C#
  • 想确定 x86 固定资产还能延续到哪一步,从哪里开始 bridge / wrap / replace
  • 想摆脱手动 regsvr32 的依赖,重新设计 install / deploy 流程

关于替换、封装、保留的判断,也可以参考 ActiveX / OCX 现在该如何处理 - 保留、封装、替换的判断表

9. 总结

在 COM 组件、OCX / ActiveX 开发中踩坑时,原因大致都能归结到这 4 点:

  1. 位数没对齐
  2. 注册方式用错了
  3. 注册范围(HKCU / HKLM、32 位 / 64 位 view)出现了偏差
  4. 把「恰好因为权限而能看到」的状态误当成了正常

随着 Visual Studio 2022 变为 64 位,过去那种「说不清为什么就能跑」的设计,变得相当容易暴露出来。12 正因为如此,接触 COM / OCX / ActiveX 时,在写代码之前先把环境前提统一好,才是捷径。

与其纠结 regsvr32 要敲几次,不如先把这些理清楚,解决速度会快得多:

  • 是哪个进程在做宿主
  • 该进程是多少位
  • 应该注册在哪里
  • 这个注册真的需要管理员权限吗
  • 有没有把 Designer 和 runtime 分开来看

参考

  1. Microsoft Learn, Visual Studio 2022 version 17.0 Release Notesdevenv.exe is now 64-bit only 2 3

  2. Microsoft Learn, Troubleshoot 32-bit problems - Windows Forms — Visual Studio 2022 是 64 位进程,无法直接加载 32 位的 .NET / COM / ActiveX,以及 out-of-process designer 的限制。  2 3 4 5

  3. Microsoft Learn, Classes and Servers — COM 的注册、HKCU / HKCR、self-registration 与 DllRegisterServer。  2 3 4

  4. Microsoft Learn, regsvr32regsvr32 的语法与作用。  2

  5. Microsoft Learn, 向 COM 注册程序集 — .NET Framework 的 COM 注册使用 Regasm.exe。  2

  6. Microsoft Learn, 向 COM 公开 .NET Core 组件EnableComHosting、生成的 .comhost.dllregsvr32EnableRegFreeCom。  2 3

  7. Microsoft Learn, Merged View of HKEY_CLASSES_ROOT — HKCR 是 HKLM 与 HKCU 的合并视图(merged view)。  2 3 4 5

  8. Microsoft Learn, HKEY_CLASSES_ROOT Key — 需要管理员权限的应用推荐注册到 per-machine COM 配置中。  2

  9. Microsoft Learn, COM Error Codes (Generic) (Winerror.h)REGDB_E_CLASSNOTREG (0x80040154) 等。 

  10. Microsoft Learn, You receive 0x80070005 error when you try to register a DLL by using Regsvr32.exe — 权限不足导致 DLL 注册失败的典型案例。  2

  11. Microsoft Learn, Registry Redirector — WOW64 中 32 位 / 64 位的注册表 view。  2 3 4

  12. Microsoft Learn, File System Redirector — x64 Windows 的 %windir%\System32 与 WOW64 的文件系统重定向。 

  13. Microsoft Learn, 64 位版本 Windows 上 32 位程序兼容性注意事项概述 — WOW64 带来的文件 / 注册表重定向。 

  14. Microsoft Learn, Regasm.exe (Assembly Registration Tool)Regasm.exe 的作用及 /tlb 等选项。 

  15. Microsoft Learn, Packaging a .NET Framework Assembly for COM — 类型库与 Regasm.exe /tlb。 

  16. Microsoft Learn, Understanding Custom Build Steps and Build Events — 在 post-build event 中使用 regsvr32.exe 的示例。 

  17. Microsoft Learn, 面向高级用户的 Windows 注册表HKCU\Software\ClassesHKLM\Software\Classes、以及 HKCR 的行为。 

  18. Microsoft Learn, Find, install, and manage extensions for Visual Studio — elevated 运行时对 per-user extension 的处理方式。 

  19. Microsoft Learn, MFC ActiveX Controls: Licensing an ActiveX Control — design-time / run-time license、.LIC。 

  20. Microsoft Learn, Application Settings, MFC ActiveX Control Wizard — 运行时授权(license)的生成与 .lic 文件。 

  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. 

  22. Microsoft Learn, Aximp.exe (Windows Forms ActiveX Control Importer) — 将 ActiveX 转换为供 WinForms 使用的包装(wrapper)。 

  23. Microsoft Learn, AxHost Class — ActiveX Control Importer 生成的基于 AxHost 的包装。 

  24. Microsoft Learn, 初始化 COM 库CoInitializeEx、按线程各自初始化、STA 的消息循环。  2 3

  25. Microsoft Learn, Single-Threaded Apartment(单线程套间) — STA 的消息循环、封送(marshaling)、ThreadingModel。  2

  26. Microsoft Learn, 创建 Registration-Free COM 对象 — 通过 activation context 实现的免注册 COM。 

  27. Microsoft Learn, 无需注册的 COM 互操作功能 — .NET Framework 的 registration-free COM interop。 

共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。

COM / ActiveX / OCX 是什么 - 区别与关系整理

从实务角度厘清 COM、ActiveX、OCX 三者的区别与关系:COM 是 Windows 组件互操作的二进制契约底层,ActiveX 是以 COM 为基础的可嵌入控件语境,OCX 则是 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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表