使用Wix Msi安装程序在安装后将CPP dll注册到COM中

时间:2017-12-11 03:36:35

标签: c++ dll wix windows-installer wix3.10

我正在尝试在Msi安装期间将CPP库注册到COM中。

我已经搜索了很多并在这里找到了很多解决方案,但我的代码中没有任何工作。我不知道有没有直接的方法。我尝试使用自定义操作直接使用ExeCommand和批处理脚本。

以下是批处理脚本的代码。

<SetProperty Id="Register" Value="&quot;[INSTALLDIR]Scripts\Register.bat&quot;" After="CostFinalize"/>
<CustomAction Id="Register" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/> 

<SetProperty Id="Unregister" Value="&quot;[INSTALLDIR]Scripts\UnRegister.bat&quot;" After="CostFinalize"/>
<CustomAction Id="Unregister" BinaryKey="WixCA" DllEntry="CAQuietExec" Execute="deferred" Return="check" Impersonate="no"/>

使用此代码,安装不会显示任何错误,但dll未注册。安装后,如果我单独运行批处理脚本,则会注册。

的Register.bat

cd&#34; C:\ Windows \ System32&#34;

regsvr32&#34; C:\ Program Files(x86)\ ABC \ Abc.dll&#34;

ping -n 15 127.0.0.1&gt; nul:

Unregister.bat

cd&#34; C:\ Windows \ System32&#34;

regsvr32 / u&#34; C:\ Program Files(x86)\ ABC \ Abc.dll&#34;

ping -n 15 127.0.0.1&gt; nul:

使用带有ExeCommand的自定义操作,它会显示错误,例如某些dll依赖项缺失。 ExeCommand代码如下所示。

<CustomAction Id="Register" Directory="INSTALLDIR" Execute="deferred" Impersonate="no"
              ExeCommand="[WindowsFolder]System32\regsvr32 &quot;[INSTALLDIR]Abc.dll&quot;" Return="check" />
<CustomAction Id="Unregister" Directory="INSTALLDIR" Execute="deferred" Impersonate="no"
             ExeCommand="[WindowsFolder]System32\regsvr32 /u &quot;[INSTALLDIR]Abc.dll&quot;" Return="check" />

下面给出了两种情况的InstallSequence。

 <InstallExecuteSequence> 
    <Custom Action="Register" Before="InstallFinalize" >NOT Installed</Custom>
    <Custom Action="Unregister" Before="RemoveFiles">Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
 </InstallExecuteSequence>

在这两种情况下,我认为它是以提升的权限运行。

这是我大部分时间都会遇到的错误。 enter image description here

修改

dll的依赖性walker视图如下所示。

enter image description here

此外,我正在添加我使用的加热命令。我已将此添加到prebuild事件以生成组件。之后,在产品文件中添加了此组件。

call&#34; $(WIX)bin \ heat.exe&#34;文件&#34; dllPath \ Abc.dll&#34; -dr&#34; INSTALLDIR&#34; -srd -gg -sfrag -suid -out&#34; $(SolutionDir)Installer \ ComRegisterComponent.wxs&#34;

生成的文件看起来像这样。

 <Fragment>
    <DirectoryRef Id="INSTALLDIR">
        <Component Id="Abc.dll" Guid="*">
            <File Id="Abc.dll" KeyPath="yes" Source="SourceDir\Abc.dll" />
        </Component>
    </DirectoryRef>
</Fragment>

这里的SourceDir路径让我感到困惑。我已经在heat命令中添加了确切的路径,即使它生成了这个SourceDir。

3 个答案:

答案 0 :(得分:3)

简短,摘要答案

您需要停止使用批处理文件和自定义操作进行COM注册(不可靠),而是使用WiX工具包中的 heat.exe 工具提取COM注册信息在编译时将COM注册添加到MSI数据库。

64位二进制文​​件存在一些复杂情况,请参阅下面的详细信息。幸运的是,您似乎正在处理基于上面显示的安装目录的32位组件。

在这种特殊情况下,它有助于在部署后在COM文件上运行heat.exe,而所有依赖项都是&#34;就位#34;正确加载COM文件。有很多&#34;调试通信&#34;在这些答案中 - 我将为将来留下一切,但首先尝试这个简单的解决方案。也许尝试新的依赖工具&#34; Dependencies.exe&#34;如下所述。

详细的答案

在我尝试回答这个问题之前(似乎围绕缺少依赖关系在批处理文件中做了一些奇怪的事情)之前,我想清理一些问题有关COM注册最佳实践的事情。

  

注意:屏幕截图似乎表明您的批处理文件中出现了一些奇怪的内容,但缺少依赖项可能仍然存在问题。

自我注册被认为是有害的

不应使用自行注册来注册COM文件。以下是对此情况的说明: MSI register dll - Self-Registration considered harmful 。然而,有一个好消息,一旦你正确设置它,通过内置的MSI机制做的事情会更简单,也更可靠。

有一种方法可以在安装过程中自行注册文件,而无需使用您尝试执行的自定义操作(the SelfReg table)。自定义操作很难正常工作,但您不应使用内置机制来运行自注册(如上面链接的答案中详细解释的那样)

不是使用自定义操作或SelfReg表,而是应该在编译时从COM文件中提取 COM注册信息 - 换句话说,当您从WiX源文件编译MSI文件时。提取的注册表数据应该用于填充MSI数据表系列,这些数据表旨在分别在安装和卸载过程中可靠地注册和注销COM文件。

WiX:&#34; heat.exe&#34;命令行工具

了解此过程的复杂细节并非必要 - 您需要知道的是使用哪些工具。 WiX提供&#34; heat.exe &#34;用于此目的的工具。它本质上是一个&#34; 收割机&#34;能够为多种目的生成有效的WiX XML源文件的工具 - 其中一个是COM提取。它还支持遍历目录 - 生成WiX源文件,可以安装遍历期间遇到的文件。一旦您知道如何使用MSI包,它本质上是一种非常快速的方法。

Dependency Walker

所以我们已经确定你应该花时间学习如何使用heat.exe来生成正确注册COM文件所需的WiX源。但是,还有一个问题缺少依赖项

要使COM文件能够自行注册 - 或者您能够使用heat.exe成功提取COM注册表数据 - COM文件必须能够正确加载。为此,所有dll依赖项都必须在可访问位置的相关系统上可用。

获取Dependency Walker的副本并使用它来扫描COM文件以查找它所依赖的文件。以下是无法加载的COM文件示例,因为找不到MMUtilities.dll

enter image description here

从设置的安装位置运行时,您很可能会发现类似于您的dll(或任何文件类型,例如OCX)的类似错误。 regsvr32.exe无法找到所需的依赖项文件,并且注册过程失败。

有一些报告遗漏的依赖关系不重要 - 我想这与Dependency Walker工具的年龄​​有关 - 据我所知,它最近没有更新过。查找您认为是自己的依赖文件或核心系统文件的文件,而不是您从未听说过的非常长的dll文件名。请记住,某些dll具有加载所需的依赖语言dll。例如,MMUtilities.dll需要MmUtilitiesEnglish.dll或同一文件夹中存在的其他语言dll才能正确加载。

以上文件的一些示例误报依赖API-MS-WIN-CORE-RTLSUPPORT-L1-1-0.DLLAPI-MS-WIN-CORE-PROCESSTHREADS-L1-1-0.DLLAPI-MS-WIN-CORE-REGISTRY-L1-1-0.DLL等...有很多。我相信,但我不确定,这些误报的根本原因在于安装到WinSxS文件夹的并排组件的问题,但这是另一个讨论 - 只是提到它。

UPDATE :我刚刚再次检查了这一点,上面看到的大多数误报依赖显然都是API-sets - 在创建Dependency Walker之后很久就引入了一个Windows功能,因此无法正确处理通过该工具。

由于链接的答案表明(请仔细阅读,上面的链接),现在还有一个在C#中重写Dependency Walker,称为&#34; 依赖关系&# 34;,可在此处获取:https://github.com/lucasg/Dependencies(在撰写本文时未经我测试,将很快进行测试)。

另外快速提一下:如果你有一个可执行文件要检查(而不是dll,ocx等...),你可以通过Profile => Start Profiling...菜单项启动它,然后你也会看到&# 34; hidden run-time dependencies&#34;未在二进制导入表中指定(其中指定了依赖项/导入)。您需要使用所有对话框中的所有功能来真正运用应用程序,以确保获得所有此类依赖项。

依赖性walker网页调用这些隐藏的依赖项显式依赖项(动态或运行时依赖项)和系统挂钩依赖项(注入依赖项)。有关详细信息,请参阅上面的链接。

Heat.exe提取

一旦确定了使WiX heat.exe提取工作缺失的文件,您可以将文件置于问题&#34;&#34;旁边。您的COM dll,以便在加载过程中找到它们。您可以使用regsvr32.exe测试运行,以查看注册是否正确完成。这样手动运行注册时应该没有错误消息。 请记住从提升的命令提示符运行注册。

其他几个stackoverflow答案解释了如何使用heat.exe - 我很久没有使用它了:How to run heat.exe and register a dll in wix。这是来自WiX家伙的the official documentation for heat.exe。它可能是一个有点令人生畏的工具 - 它有很多功能。

以最简单的形式(对于路径或本地文件夹中可用的所有依赖项的普通32位COM文件),您可以运行此heat.exe命令行以生成名为YourFileName.wxs的输出WiX源文件所有必需的COM注册数据。

heat.exe file YourFileName.ocx -o YourFileName.wxs

几年前我写了一个答案,展示了如何将导出的WiX注册表数据合并到主WiX源:How to Reference a Heat Output(wxs) in Wix (Command Line)。我相信这是对程序的准确描述,但由于某种原因,有人下了决定答案。请试一试,看看它是否适合你。

  

重要!:heat.exe尚未正确处理64位COM二进制文件(2017年12月)。

我被告知WiX扩展包(不是免费的)处理64位二进制文​​件并提供其他一些功能。 我想我可以链接到它(我不隶属于FireGiant):https://www.firegiant.com/wix/wep-documentation/harvesting/。不知怎的,人们需要知道这些事情,但我不确定链接的stackoverflow礼仪。

您的二进制文件是64位吗?(它不会从您的安装文件夹中看起来那样,但我会将其添加到可能找到它的其他人)。对于64位组件,我想我们已经完整了,并且建议您使用上述扩展包功能或尝试set the file to self-register as described here。我讨厌这种自我注册&#34;解决方案&#34;,但我现在无法想到任何其他快速解决方案(无论如何我都不会推荐)。我会再检查一下。 确保检查最新的WiX版本以查看是否已修复64位问题,然后再进行此操作&#34;自行注册修复&#34; 。它至少比尝试使用自定义操作和批处理文件(不应该尝试注册)更好 - 有很多与MSI复杂的自定义动作排序,模拟/提升,调节,安装模式有关的潜在问题和交互式,更不用说安全软件的潜在干扰,并且列表继续)。

一些链接:

答案 1 :(得分:2)

只需添加另一个答案,其中包含有关如何使用regsvr32.exe在自行注册期间以有效方式使用procmon.exe 调试缺少的依赖关系的信息。

基本上你应该为你要监视和记录的事件设置一个包含过滤器,否则你会得到列出的无关事件的全能列表,很难找到你需要的东西。无用的信息:

Apply include filter in procmon.exe

  1. 要将捕获的流程信息限制为您需要的信息,请转到Filter => Filter...
  2. 将左下角设置为Process Name,然后将第二列设置为is,最后在右侧框中输入regsvr32.exe,如上图所示。
  3. 将最右边的框设置为Include。然后按OK。
  4. 现在应该禁止所有不必要的事件,并且仅显示regsvr32.exe个事件(当您开始运行它时)。
  5. 以正常方式运行regsvr32.exe并寻找&#34; NAME NOT FOUND&#34;列表中的条目(或类似的)。
  6. 在下图中找不到MMUtilities.dll。换句话说,它是一个缺失的依赖。
  7. MMUtilities.dll can not be found

    请注意,您可以通过单击下面所示的按钮打开和关闭来包含/排除某种类型的事件。 Registry events file system events network activity process and tread activity profiling events 。这可以大大降低噪音&#34;你必须在清单中处理。

    Include / Exclude

    在自行注册期间调试缺少的依赖项的另一种方法是使用 Dependency Walker ,如我在此&#34;线程&#34;中的其他答案所示。或问题或任何称之为。通常我发现 Dependency Walker 是更快的选择,但ProcMon总体上可能更好 - 除非您正在调试EXE文件,否则Dependency Walker具有优越的Profile功能( Profile => Start Profiling...)。

答案 2 :(得分:1)

这个想法不是使用bat文件来注册dll。让Windows安装程序/ WiX为您完成。过去也已回答过here