Search code examples
windows-installerelectron-builder

How to parameterize msi file from electron builder


I'm trying to create an .msi installer file with electron-builder (version 20.39.0), that can be parameterized during install time. The parameters (e.g. server endpoint) should be written in a file.

Example:
when MsiExec /i "MyProject.msi" SERVER_ENDPOINT=myapp.example.com
then myapp.example.com should appear in a file in the installation dir.

I tried to edit electron-builder's wix template file adding the following to write ${SERVER_ENDPOINT} to server.txt

File 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>

Running with

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

I does not work yet. It installs but does not show writeConfigFile in the log file.

Do you think this is the right approach to make the msi file parameterized?
Or would you recommend another solution?

I also found Orca.exe, to create an MST file, but I would prefer a simple solution, without manual steps.


Solution

  • With the help of Stein Åsmul, this is my current solution:

    I took the current WiX template of electron-builder and added an option to write variables to an ini file.

    <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>
    

    The complete template looks like this:

    <?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://stackoverflow.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>
    
    

    Using [email protected], I create the MSI with

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

    And then install the MSI with

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

    After installation finished, I got the AppConfig.ini in my installdir (%USERPROFILE%\AppData\Local\Programs\MyApp\AppConfig.ini)

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