如何从电子生成器参数化 msi 文件

Posted

技术标签:

【中文标题】如何从电子生成器参数化 msi 文件【英文标题】:How to parameterize msi file from electron builder 【发布时间】:2019-08-22 22:19:01 【问题描述】:

我正在尝试使用 electron-builder(版本 20.39.0)创建一个 .msi 安装程序文件,该文件可以在安装时进行参数化。 参数(例如服务器端点)应写入文件中。

示例: 当MsiExec /i "MyProject.msi" SERVER_ENDPOINT=myapp.example.com 那么myapp.example.com 应该出现在安装目录的文件中。

我尝试编辑 electron-builder's wix template file 添加以下内容以将 $SERVER_ENDPOINT 写入 server.txt

文件C:\...\MyProject\node_modules\electron-builder-lib\templates\msi\template.xml

...
<CustomAction Id="writeConfigFile" Directory="APPLICATIONFOLDER" Execute="commit" Impersonate="yes" ExeCommand="cmd.exe /c &quot;echo $SERVER_ENDPOINT > server.txt&quot;" Return="check" />
...
<InstallExecuteSequence>
  ...
  <Custom Action="writeConfigFile" After="InstallFinalize"/>
</InstallExecuteSequence>

一起运行
MsiExec /i "MyProject.msi" /L*v Install.log SERVER_ENDPOINT=myapp.example.com

我还没有工作。它会安装但不会在日志文件中显示writeConfigFile

您认为这是使 msi 文件参数化的正确方法吗? 或者您会推荐其他解决方案吗?

我还找到了 Orca.exe,用于创建 MST 文件,但我更喜欢简单的解决方案,无需手动步骤。

【问题讨论】:

开始写答案,但我不知道 Electron。安装后没有在应用程序启动时应用设置的功能吗? Squirrel events。稍后我可能会添加我的答案,它一般是关于 MSI 以及如何通过 MSI 表设置属性。回到基础,可能不是你想知道的。 以下用例:我们构建一个 MSI 文件并将其发送给我们的客户。客户将其集成到他的“msi 推出系统”中,并希望配置后端服务器(参数或 MST)。所以我需要一种方法来读取 MSI 参数或创建 MST 文件。我会对您关于 MSI 表中属性的一般信息非常感兴趣 我刚刚把我在下面写的东西扔了。快速浏览一下。 【参考方案1】:

设置 MSI 属性

我不熟悉 Electron builder。但是,在 MSI 术语中,您需要指定文件中的内容应替换为 MSI Property,然后您需要在 transform 中设置属性,由 command lineproperty table(嵌入在 MSI 中)。

事实上,您可以一次设置所有三个,我不确定哪个会适用 :-)。命令行当然会覆盖属性表,但我不确定在转换和命令行参数之间的战斗中什么会获胜:

变换(在命令行应用变换,变换文件中的实际设置 - mst):

msiexec.exe /i "MySetup.msi" TRANSFORMS="MyTransform.mst"

命令行(在命令行上设置 PUBLIC 属性):

msiexec.exe /i "MySetup.msi" MYPROPERTY="My Value here"

属性表(每个 MSI 中的内置属性表也可以有一个值集):


使用 MSI 属性

设置属性显然是不够的,你必须在安装过程中定义值的去向。

如果文件是 INI file,设置参数非常容易,因为这是 MSI 的内置功能。 XML file updatestext file updates 更糟糕,因为您依赖第三方解决方案,或者您通过自定义操作自行完成 (I would not do the latter)。

Advanced Installer 具有非常好的功能来替换 XML 和文本文件中的参数。 Installshield 也有这样的功能。开源的WiX toolkit也有features to support XML file updates,但它比商业工具要复杂得多。

关于 Electron,我不知道它是如何工作的。但是,无论哪种情况,中心任务都是让 MSI 包含这样的结构:

这是来自使用 Advanced Installer 编译的 MSI。您会看到我有一个参数化值 [MYVALUE]。它可以在命令行上设置,因为它是全大写属性 - 也称为 PUBLIC MSI 属性。在安装过程中,大括号中的属性将被传入的值替换。显然。


一些链接

Add a config file to an installer (msi) How to make better use of MSI files

【讨论】:

感谢您的详细解释 :) INI 方式听起来简单而有趣。我刚试过这个:MsiExec /i "myApp.msi" SAVEINI=Response.ini SERVER_ENDPOINT=myapp.example.com 但是没有创建 Response.ini。是你想到的命令吗? MSI 中有一个IniFile table,它将更新/创建一个 INI 文件。 MSI 具有合并和回滚条目(中止安装)的功能。您甚至可以尝试修改完成的电子 MSI 进行尝试。您需要为外键 Component_ 引用现有组件,但除此之外您可以只添加一行。仅在虚拟机上测试!下载 a trial version of a commercial deployment tool 将帮助您创建测试 MSI,无需任何编码即可快速学习。 好的,我试过了。使用&lt;Directory Id="TARGETDIR" Name="SourceDir"&gt; &lt;Component Id="SomeComponent" Guid="01234567-89AB-CDEF-0123-456789ABCDEF" KeyPath="yes" Feature="SomeFeature"&gt; &lt;IniFile Id="ServerAddress" Name="myfile.ini" Directory="APPLICATIONFOLDER" Section="ConnectionObserver" Key="ServerAddress" Value="[SERVERADDRESS]" Action="addLine"/&gt; &lt;/Component&gt; 并使用MsiExec /i "myapp.msi" SERVERADDRESS=myapp.example.com 运行它会将我的 serveraddres=myapp.example.com 写入已经存在的 ini 文件。但它只工作了一次......我需要进行更多实验;) 据我所知,这应该可以正常工作。发生了什么? 在创建 msi 之前,我将 myfile.ini 放在了我的源文件目录中(因此它在目标目录中可用)。出于某种原因,它在安装过程中变为只读,并弹出一个窗口,告诉我该文件不可写。手动删除只读标志后,我在对话框上按了“重试”。然后将值正确写入文件。当我尝试再次安装时,它不会更新该值。不知道为什么。【参考方案2】:

在 Stein Åsmul 的帮助下,这是我目前的解决方案:

我采用了当前的WiX template of electron-builder 并添加了一个将变量写入ini 文件的选项。

<Property Id="MYSERVER" Value="notDefined"/>
<Property Id="MYSECONDPROPERTY" Value="notDefined"/>
...
<Directory Id="APPLICATIONFOLDER" Name="$installationDirectoryWixName">
  <Component Id="AddLineTo_AppConfig.ini" Guid="4171FB60-FDC5-46CF-A4D8-4AE9CADB4BE9" KeyPath="yes" Feature="ProductFeature">
    <IniFile Id="AddLineTo_AppConfig.ini1" Name="AppConfig.ini" Directory="APPLICATIONFOLDER" Section="AppConfig" Key="Server" Value="&quot;[MYSERVER]&quot;" Action="addLine"/>
    <IniFile Id="AddLineTo_AppConfig.ini2" Name="AppConfig.ini" Directory="APPLICATIONFOLDER" Section="AppConfig" Key="SecondProp" Value="&quot;[MYSECONDPROPERTY]&quot;" Action="addLine"/>
  </Component>
</Directory>

完整的模板如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
  <!-- extended Template from https://github.com/electron-userland/electron-builder/blob/7f0ede7182ab6db8efb0cf4bf3cb183be712fb4e/packages/app-builder-lib/templates/msi/template.xml -->
  <!-- https://blogs.msdn.microsoft.com/gremlininthemachine/2006/12/05/msi-wix-and-unicode/ -->
  <Product Id="*" Name="$productName" UpgradeCode="$upgradeCode" Version="$version" Language="1033" Codepage="65001" Manufacturer="$manufacturer">
    <Package Compressed="yes" InstallerVersion="500"/>

    <Condition Message="Windows 7 and above is required"><![CDATA[Installed OR VersionNT >= 601]]></Condition>

    <!--
    AllowSameVersionUpgrades:
      When set to no (the default), installing a product with the same version and upgrade code (but different product code) is allowed and treated by MSI as two products.
      When set to yes, WiX sets the msidbUpgradeAttributesVersionMaxInclusive attribute, which tells MSI to treat a product with the same version as a major upgrade.

      So, AllowSameVersionUpgrades="yes" allows to build and test MSI with the same version, and previously installed app will be removed.
    -->
    <MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage='A newer version of "[ProductName]" is already installed.'/>
    <MediaTemplate CompressionLevel="$compressionLevel" EmbedCab="yes"/>

    <Property Id="ApplicationFolderName" Value="$installationDirectoryWixName"/>
    <Property Id="WixAppFolder" Value="WixPerUserFolder"/>
    <Property Id="MYSERVER" Value="notDefined"/>
    <Property Id="MYSECONDPROPERTY" Value="notDefined"/>

     if (iconPath)  
    <Icon Id="icon.ico" SourceFile="$iconPath"/>
    <Property Id="ARPPRODUCTICON" Value="icon.ico"/>
      -

     if (isAssisted || isRunAfterFinish)  
    <CustomAction Id="runAfterFinish" FileKey="mainExecutable" ExeCommand="" Execute="immediate" Impersonate="yes" Return="asyncNoWait"/>
      -

    <Property Id="ALLUSERS" Secure="yes" Value="2"/>
     if (isPerMachine)  
    <Property Id="MSIINSTALLPERUSER" Secure="yes"/>
      else  
    <Property Id="MSIINSTALLPERUSER" Secure="yes" Value="1"/>
      -

     if (isAssisted)  
    <!-- Check "Run after finish" checkbox by default -->
    <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1"/>
    <Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Run $productName"/>

    <UIRef Id="WixUI_Assisted"/>
      else if (isRunAfterFinish)  
    <!-- https://***.com/questions/1871531/launch-after-install-with-no-ui -->
    <InstallExecuteSequence>
      <Custom Action="runAfterFinish" After="InstallFinalize"/>
    </InstallExecuteSequence>
      -

    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="$programFilesId">
        <Directory Id="APPLICATIONFOLDER" Name="$installationDirectoryWixName">
          <Component Id="AddLineTo_AppConfig.ini" Guid="4171FB60-FDC5-46CF-A4D8-4AE9CADB4BE9" KeyPath="yes" Feature="ProductFeature">
            <IniFile Id="AddLineTo_AppConfig.ini1" Name="AppConfig.ini" Directory="APPLICATIONFOLDER" Section="AppConfig" Key="Server" Value="&quot;[MYSERVER]&quot;" Action="addLine"/>
            <IniFile Id="AddLineTo_AppConfig.ini2" Name="AppConfig.ini" Directory="APPLICATIONFOLDER" Section="AppConfig" Key="SecondProp" Value="&quot;[MYSECONDPROPERTY]&quot;" Action="addLine"/>
          </Component>
        </Directory>
      </Directory>

      <!-- Desktop link -->
       if (isCreateDesktopShortcut)  
      <Directory Id="DesktopFolder" Name="Desktop"/>
        -

      <!-- Start menu link -->
       if (isCreateStartMenuShortcut)  
      <Directory Id="ProgramMenuFolder"/>
        
    </Directory>

    <!-- Files -->
    <Feature Id="ProductFeature" Absent="disallow">
      <ComponentGroupRef Id="ProductComponents"/>
    </Feature>

    -dirs

    <ComponentGroup Id="ProductComponents" Directory="APPLICATIONFOLDER">
      -files      
    </ComponentGroup>
  </Product>
</Wix>

使用 electron-builder@20.39.0,我创建了 MSI

set DEBUG=electron-builder:*
cp template.xml .\node_modules\app-builder-lib\templates\msi\template.xml
electron-builder

然后安装 MSI

MsiExec /i "myapp.msi" MYSERVER=myapp.example.com MYSECONDPROPERTY=helloworld /L*v Install.log

安装完成后,我在 installdir (%USERPROFILE%\AppData\Local\Programs\MyApp\AppConfig.ini) 中获得了 AppConfig.ini

[AppConfig]
Server="myapp.example.com"
SecondProp="helloworld"

【讨论】:

很好的解决方案。你认为这是你会考虑向electron-builder 公关的事情吗?似乎它对其他人非常有用。 另外.. 模板中是否需要Guid,如果需要 - 它是如何派生的? 这是不久前的事了,但我认为 Guid 是使其工作所必需的。否则我认为有错误。我还没有考虑花时间在 Github 上创建 PR。我为这个主题创建了一个问题,但没有回应,它被关闭了。见github.com/electron-userland/electron-builder/issues/3784【参考方案3】:

在 22.11.x 和版本中添加了一个最近的配置属性,支持在 project.wxs 编译成 MSI 之前对其进行修改。

msiProjectCreated

它接受一个函数或一个函数的字符串,并接受一个参数作为project.wxs的路径

这应该可以让您更好地利用扩展或根据安装时间属性创建自定义 ini 文件。

【讨论】:

以上是关于如何从电子生成器参数化 msi 文件的主要内容,如果未能解决你的问题,请参考以下文章

loadrunner从数据库中取值进行参数化

jmeter使用函数助手实现参数化

参数化,内存溢出问题

文件参数化-utp框架之根据yaml文件自动生成python文件

Jmeter完成接口项目参数化测试(十七)

如何将参数从exe文件传递到msi?