2

I am trying to create a wix v4 installer for a windows service which is based on .net 8.

According to the official docu it should be possible to accomplish this.

What I want to do is to get rid of the licence agreement and add two custom dialogs (just like in the docu). I have to add that I am quite new to wix but I did my best to study the topic.

From what is explained in the docu and what I found on the internet I was able to generate an installer but the installer does not include my custom dialog.

I already searched for quite some time and also found some suggestions but none of them work.

This is my Package.wxs file:

<?xml version="1.0" encoding="UTF-8"?>

<!-- Define the variables in "$(var.*) expressions" -->
<?define Name = "Service Client" ?>
<?define Manufacturer = "Cyberdyne Systems" ?>
<?define Version = "1.0.0.4" ?>
<?define UpgradeCode = "9ED3FF33-8718-444E-B44B-69A2344B7E98" ?>
<?define CustomActionPath = "..\CustomActions\bin\Release\net48" ?>

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">

  <Package
    Name="$(Name)"
    Manufacturer="$(Manufacturer)"
    Version="$(Version)"
    UpgradeCode="$(var.UpgradeCode)"
    Compressed="yes"
    Scope="perMachine">

    <MediaTemplate EmbedCab="yes" />

    <ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER"/>

    <!-- Allow upgrades and prevent downgrades -->
    <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />

    <Property Id="INSTALLFOLDER" Value="D:\test\Windows Services\Service Client" />
    <StandardDirectory Id="TARGETDIR">
      <Directory Id="DDrive" Name="D">
        <Directory Id="Test" Name="Test">
          <Directory Id="WindowsServices" Name="Windows Services">
            <Directory Id="INSTALLFOLDER" Name="$(Name)" />
          </Directory>
        </Directory>
      </Directory>
    </StandardDirectory>

    <DirectoryRef Id="INSTALLFOLDER">
      <Component Id="ServiceExecutable" Guid="d39f02cf-db54-4067-9827-720f839458f5" Bitness="always64">
        <File Id="ServiceClient.exe" Source="$(var.ServiceClient.TargetDir)publish\win-x64\ServiceClient.exe" KeyPath="true" />
        <File Id="appsettings.json" Source="$(var.ServiceClient.TargetDir)publish\win-x64\appsettings.json" KeyPath="false" />
        <RemoveFile Id="ALLFILES" Name="*.*" On="both" />
        <ServiceInstall Id="ServiceInstaller" Type="ownProcess" Name="ServiceClient" DisplayName="$(Name)" Description="Service responsible for various file operations." Start="auto" ErrorControl="normal" />
        <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="ServiceClient" Wait="true" />
      </Component>
    </DirectoryRef>

    <Binary Id="CustomActions" SourceFile="$(var.CustomActionPath)\CustomActions.CA.dll" />

    <CustomAction Id="SetCustomActionData" Property="UpdateAppSettings" Value="INSTALLFOLDER=[INSTALLFOLDER];APIURL=[APIURL];USERNAME=[USERNAME];PASSWORD=[PASSWORD]" Execute="immediate" Return="check" />
    <CustomAction Id="UpdateAppSettings" BinaryRef="CustomActions" DllEntry="UpdateAppSettings" Execute="deferred" Return="check" />
    <CustomAction Id="ExtractUserCredentials" BinaryRef="CustomActions" DllEntry="ExtractUserCredentials" Execute="immediate" Return="check" />
    <CustomAction Id="TestAPI" BinaryRef="CustomActions" DllEntry="TestAPI" Execute="immediate" Return="check" />

    <InstallExecuteSequence>
      <Custom Action="SetCustomActionData" After="InstallFiles" Condition="NOT Installed AND NOT PATCH" />
      <Custom Action="UpdateAppSettings" After="SetCustomActionData" Condition="NOT Installed AND NOT PATCH" />
    </InstallExecuteSequence>

    <UI>
      <UIRef Id="MyUI" />
    </UI>

    <Feature Id="Service" Title="ServiceClient Setup" Level="1">
      <ComponentRef Id="ServiceExecutable" />
    </Feature>

  </Package>

</Wix>

An my UI.wxs file:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
  <Fragment>
    <UI Id="MyUI" xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">

      <ui:WixUI Id="WixUI_InstallDir" InstallDirectory="INSTALLFOLDER"/>
      
      <Dialog Id="APIDlg" Width="370" Height="270" Title="!(loc.APIDlgTitle)">

        <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.NextButtonText)">
          <Publish Event="NewDialog" Value="InstallDirDlg" />
        </Control>
        <Control Id="Prev" Type="PushButton" X="172" Y="243" Width="56" Height="17" Text="!(loc.PrevButtonText)">
          <Publish Event="NewDialog" Value="VerifyReadyDlg" />
        </Control>

        <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.CancelButtonText)">
          <Publish Event="EndDialog" Value="Exit" Condition="1" />
        </Control>
        <Control Id="APILabel" Type="Text" X="20" Y="80" Width="290" Height="15" Text="!(loc.APILabel)" />
        <Control Id="APIURL" Type="Edit" X="20" Y="95" Width="290" Height="18" Property="APIURL" />
        <Control Id="TestAPI" Type="PushButton" X="320" Y="95" Width="40" Height="18" Text="Test">
          <Publish Event="DoAction" Value="TestAPI" />
        </Control>
      </Dialog>


      <!-- Navigation: Insert your custom dialog into the sequence -->
      <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="APIDlg" Condition="NOT Installed"/>
      <Publish Dialog="APIDlg" Control="Prev" Event="NewDialog" Value="InstallDirDlg"  Condition="1"/>
      <Publish Dialog="APIDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="1"/>
    </UI>
  </Fragment>
</Wix>

No matter what I do, the custom dialog wont show up. Does anybody have a hint what to change?

If I follow a very similar approach like mentioned here: Wix issue inserting a custom dialog into a built-in dialog set I get lots of errors like:

  • WIX0092 Location of symbol related to previous error. ...\WixUI_InstallDir.wxs
  • WIX0091 Duplicate TextStyle with identifier 'WixUI_Font_Title' found. Access modifiers (global, library, file, section) cannot prevent these conflicts. Ensure all your identifiers of a given type (Directory, File, etc.) are unique. ...\Package.wxs
  • WIX0130 The primary key 'BrowseDlg/OK/SpawnDialog/InvalidDirDlg/NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID<>"1"' is duplicated in table 'ControlEvent'. Please remove one of the entries or rename a part of the primary key to avoid the collision. ...Package.wxs
  • and many more
1
  • I believe that to provide instructions on when the UI elements should be run, there is a <InstallUISequence> that includes a set of <Show> elements that point to the dialog box and describe their order (before/after). This doesn't address the errors you're getting, but it might explain why you're not seeing your dialogs called. Commented Jan 2 at 0:09

2 Answers 2

4

I have also faced this issue and been trying to recreate the official docu use case for many hours, and finally found a solution.

Keep in mind that I'm also a beginner to Wix, so if anyone has any improvements feel free to add.

  1. You need to download or copy the WixUI_InstallDir.wxs file from the official github repository into your project file.

  2. Then you need to change the ids of the UIs in the file:

    <UI Id="WixUI_InstallDir_Custom_$(WIXUIARCH)">
    ...
    <UI Id="WixUI_InstallDir_Custom_ExtendedPathValidation_$(WIXUIARCH)">
    ...
    <UI Id="file WixUI_InstallDir_Custom">
    

    Also change the Id in the package file:

    <ui:WixUI Id='WixUI_InstallDir_Custom' InstallDirectory='INSTALLFOLDER'/>
    
  3. If you try to build your project now, you may face an error saying:

    The identifier 'WixUI:WixUI_InstallDir' is inaccessible due to its protection level.

    ... and pointing to the first UIRef in the file. You just need to also change it to the new custom name:

    <UIRef Id="WixUI_InstallDir_Custom" />
    

    I'm not sure why it only points to the first reference.

  4. Now if you try to build you'll face the same error you mentioned above:

    The primary key 'BrowseDlg/OK/SetTargetPath/[_BrowseProperty]/1' is duplicated in table 'ControlEvent'. Please remove one of the entries or rename a part of the primary key to avoid the collision.

    To fix this, You just need to comment out the publish statements for BrowseDlg in the WixUI_InstallDir_Custom UI.

    <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="LicenseAgreementDlg" />
    <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="3" />
    <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
    <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
    
    <!--<Publish Dialog="BrowseDlg" Control="OK" Event="SetTargetPath" Value="[_BrowseProperty]" Order="3" />-->
    <!--<Publish Dialog="BrowseDlg" Control="OK" Event="EndDialog" Value="Return" Order="4" />-->
    
  5. Now you will face another error related to the license page, but since you and I don't need it, we can just remove it just like in the offical docu, and edit the next and back attributes for the WelcomeDlg and InstallDirDlg:

    <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Condition="NOT Installed" />
    <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
    
  6. You can now build it and the license page is no more. You can also now add your custom dialog with no issues, put its Id in the next and back attributes in the same way as the official docu.

This is how my WixUI_InstallDir.wxs file looked by the end:

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<?foreach WIXUIARCH in X86;X64;A64 ?>
<Fragment>
    <UI Id="WixUI_InstallDir_Custom_$(WIXUIARCH)">
        <Publish Dialog="BrowseDlg" Control="OK" Event="CheckTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />

        <Publish Dialog="InstallDirDlg" Control="Next" Event="CheckTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
        <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" />
    </UI>

    <UIRef Id="WixUI_InstallDir_Custom" />
</Fragment>
<?endforeach?>

<?foreach WIXUIARCH in X86;X64;A64 ?>
<Fragment>
    <UI Id="WixUI_InstallDir_Custom_ExtendedPathValidation_$(WIXUIARCH)">
        <Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="1" />
        <Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="2" Condition="WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />

        <Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath_$(WIXUIARCH)" Order="1" />
        <Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="2" Condition="WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
        <Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
    </UI>

    <UIRef Id="WixUI_InstallDir" />
</Fragment>
<?endforeach?>

<Fragment>
    <UI Id="file WixUI_InstallDir_Custom">
        <TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
        <TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
        <TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />

        <Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />

        <DialogRef Id="BrowseDlg" />
        <DialogRef Id="DiskCostDlg" />
        <DialogRef Id="ErrorDlg" />
        <DialogRef Id="FatalError" />
        <DialogRef Id="FilesInUse" />
        <DialogRef Id="MsiRMFilesInUse" />
        <DialogRef Id="PrepareDlg" />
        <DialogRef Id="ProgressDlg" />
        <DialogRef Id="ResumeDlg" />
        <DialogRef Id="UserExit" />

        <Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />

        <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Condition="NOT Installed" />
        <Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />


        <Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
        <Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="3" />
        <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
        <Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />

        <!--<Publish Dialog="BrowseDlg" Control="OK" Event="SetTargetPath" Value="[_BrowseProperty]" Order="3" />-->
        <!--<Publish Dialog="BrowseDlg" Control="OK" Event="EndDialog" Value="Return" Order="4" />-->

        <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1" Condition="NOT Installed" />
        <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
        <Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />

        <Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />

        <Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
        <Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
        <Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />

        <Property Id="ARPNOMODIFY" Value="1" />
    </UI>

    <UIRef Id="WixUI_Common" />
</Fragment>

and my package file only has the simple reference:

<ui:WixUI Id='WixUI_InstallDir_Custom' InstallDirectory='INSTALLFOLDER'/>
Sign up to request clarification or add additional context in comments.

2 Comments

Doesn't this effectively break the browse dialog by preventing the user's selection from taking effect?
@AaronKlotz No, it doesn't, and I'm not sure why. I just tested it again, and I'm still able to edit the install path. But your answer, to edit the .wxs file from the version tag instead of the main branch, is the better solution.
1

I suspect that your problem is caused by applying your customizations to a .wxs file obtained from the main branch of the WiX GitHub repo, instead of the revision from the git tag corresponding to the release that you're actually using locally.

The DialogRefs and UIRefs in your customized file are referencing the local definitions, not the definitions from the main branch. If there have been changes to the affected files in main since the release of your local version, you're going to have a bad time.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.