如何从 electron builder 参数化 msi 文件

How to parameterize msi file from electron builder

我正在尝试使用 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 文件,但我更喜欢简单的解决方案,无需手动步骤。

设置 MSI 属性

我不熟悉 Electron 构建器。但是,在 MSI 术语中,您需要指定文件中的内容应替换为 MSI Property,然后您需要在 [=12= 中设置 属性 ],通过 command lineproperty table(嵌入在 MSI 中)。

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

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

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

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

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

属性Table(内置属性table在每个 MSI 中也可以有一个值集):


使用 MSI 属性

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

  • 如果文件是 INI file 则设置参数非常容易,因为这是 MSI 的内置功能。
  • XML file updatestext file updates 更糟,因为您依赖第三方解决方案或者您确实自己通过自定义操作 ()。

Advanced Installer has very nice features to replace parameters in XML and text files. Installshield also has such features. The open source WiX toolkit also has features to support XML file updates,但比商业工具复杂多了。

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

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


部分链接:

  • How to make better use of MSI files

在 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) { }}
    <!--  -->
    <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"

最近的配置 属性 添加到 22.11.x 和支持在编译到 MSI 之前修改 project.wxs 的版本。

msiProjectCreated

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

的路径

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