在 VBA 中启动包含 COM 对象实例化的 Excel 电子表格 - 来自 NSIS "Finish" 页面 - 为管理员用户提供 Class 未注册错误
Launching Excel Spreadsheet Containing COM Object Instantiation in VBA - From NSIS "Finish" Page - Gives Class not registered Error For Admin User
我在为标准用户和管理员安装时遇到了奇怪的情况。
我的安装程序请求“最高”身份验证级别,然后使用导入到 HKCU 的注册表文件继续注册 COM dll(而不是直接使用 regasm.exe)。 COM 用于 VBA in Excel。这允许我在每个用户的基础上安装。
安装后,我在“完成”页面上选中了“运行 应用程序”复选框。过程是:
- 用户点击“完成”,NSIS 启动我写的 VB.net exe
- VB.net exe 将一些额外的注册表项写入 HKCU(与 COM 无关)然后启动 Microsoft Excel
- Excel 打开并尝试在 VBA.
中实例化 COM 对象
这是奇怪的区别:
对于“标准”用户,这一切都完美无缺。
对于“管理员”用户,我得到:
运行-时间错误'-2147221164(80040154':
Class未注册
奇怪的是,如果我随后关闭 Excel 并双击桌面快捷方式 Excel 可以毫无问题地为 Admin 用户打开并实例化对象。
因此,实际上,此错误仅在初始安装过程中发生在管理员用户身上(例如,当 NSIS 启动 VB.net exe,然后启动 Excel)。
在 Windows 10 64 位和 Office 365 64 位以及 Window 10 32 位和 Office 365 32 位上都会出现此问题。所以,应用程序的位数与它无关。
这几乎就像注册表在初始安装期间没有为管理员用户“刷新”。
关于发生了什么以及修复它的代码有什么想法吗?
马修
我猜您 运行 正在使用 UAC 安全功能。当 运行 提升时,COM 将不会读取 HKCU 中的条目(因为未提升的应用程序可以在那里写入并等待提升的应用程序使用被劫持的 COM class 注册)。
话虽这么说,你在做什么没有意义。如果您使用 RequestExecutionLevel Highest
那么您应该在将 SetShellVarContext
设置为 Current
或 All
之后写入 ShCtx
注册表根目录,具体取决于您是否被提升在 .onInit
.
如果您想忽略我的建议并继续使用您现有的设计,请定义 MUI_FINISHPAGE_RUN_FUNCTION
并使用 ShellExecAsUser, StdUtils.ExecShellAsUser 或丑陋的 Explorer hack (Exec '"$WinDir\Explorer.exe" "$InstDir\MyApp.exe"'
)。
我在为标准用户和管理员安装时遇到了奇怪的情况。
我的安装程序请求“最高”身份验证级别,然后使用导入到 HKCU 的注册表文件继续注册 COM dll(而不是直接使用 regasm.exe)。 COM 用于 VBA in Excel。这允许我在每个用户的基础上安装。
安装后,我在“完成”页面上选中了“运行 应用程序”复选框。过程是:
- 用户点击“完成”,NSIS 启动我写的 VB.net exe
- VB.net exe 将一些额外的注册表项写入 HKCU(与 COM 无关)然后启动 Microsoft Excel
- Excel 打开并尝试在 VBA. 中实例化 COM 对象
这是奇怪的区别:
对于“标准”用户,这一切都完美无缺。
对于“管理员”用户,我得到:
运行-时间错误'-2147221164(80040154':
Class未注册
奇怪的是,如果我随后关闭 Excel 并双击桌面快捷方式 Excel 可以毫无问题地为 Admin 用户打开并实例化对象。
因此,实际上,此错误仅在初始安装过程中发生在管理员用户身上(例如,当 NSIS 启动 VB.net exe,然后启动 Excel)。
在 Windows 10 64 位和 Office 365 64 位以及 Window 10 32 位和 Office 365 32 位上都会出现此问题。所以,应用程序的位数与它无关。
这几乎就像注册表在初始安装期间没有为管理员用户“刷新”。
关于发生了什么以及修复它的代码有什么想法吗?
马修
我猜您 运行 正在使用 UAC 安全功能。当 运行 提升时,COM 将不会读取 HKCU 中的条目(因为未提升的应用程序可以在那里写入并等待提升的应用程序使用被劫持的 COM class 注册)。
话虽这么说,你在做什么没有意义。如果您使用 RequestExecutionLevel Highest
那么您应该在将 SetShellVarContext
设置为 Current
或 All
之后写入 ShCtx
注册表根目录,具体取决于您是否被提升在 .onInit
.
如果您想忽略我的建议并继续使用您现有的设计,请定义 MUI_FINISHPAGE_RUN_FUNCTION
并使用 ShellExecAsUser, StdUtils.ExecShellAsUser 或丑陋的 Explorer hack (Exec '"$WinDir\Explorer.exe" "$InstDir\MyApp.exe"'
)。