Search code examples
wixwindows-installercustom-action

Wix Directory Id symbol value not set for quiet install


I am having an issue with my WIX project. When I do a normal GUI install everything works as expected. However, when I do a /q (quiet) install, one of my directory id symbols is not being set. (Likely because there was no UI sequence for the quiet install. However, it is puzzling me as to exactly what I am doing wrong.)

In my Product.wxs I have a Directory tree that starts out like this:

<Directory Id="TARGETDIR" Name="SourceDir">
  <Directory Id="ProgramFilesFolder" Name="PFiles">
    <Directory Id="COMPANY" Name="Company">
      <Directory Id="BRANCHALL" Name="Branch">
        <Directory Id="INSTDIR" Name="Replaced">
          <Directory Id="BINDIR" Name="Bin">

Just under that I SetDirectories, because our application install takes an installation ID as a user input string, which becomes a part of the directory path. (It can also be passed in on the silent install command line.) Hence, just under the Directory tree definition above, I have:

<SetDirectory Id="INSTDIR" Value="[BRANCHALL]\[INSTID]" Sequence="execute" />
<SetDirectory Id="BINDIR" Value="[BRANCHALL]\[INSTID]\Bin" Sequence="execute" />

...etc

When I do a silent install, the log shows the following:

MSI (s) (F8:84) [20:55:29:702]: Product: ProductName - Instid -- Error 1606. Could not access network location \Instid.
Error 1606. Could not access network location \Instid.
Action ended 20:55:29: CostFinalize. Return value 3.
Action ended 20:55:29: INSTALL. Return value 3.
Property(S): UpgradeCode = {9AC2D8DF-5EF7-440B-A0D2-4A97FA62368C}
Property(S): INSTID = Instid
Property(S): BRANCHALL = C:\Program Files (x86)\Company\Branch\
Property(S): POWERSHELLEXE = C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
Property(S): BINDIR = \Instid\Bin

Note that while the value of BRANCHALL that is logged is correct, the BINDIR symbol is missing the BRANCHALL value, but does get the INSTID value properly.

The same log snippet when I run a GUI (non-silent) install:

Property(C): UpgradeCode = {9AC2D8DF-5EF7-440B-A0D2-4A97FA62368C}
Property(C): INSTID = Instid
Property(C): BRANCHALL = C:\Program Files (x86)\Company\Branch\
Property(C): POWERSHELLEXE = C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
Property(C): LicenseAccepted = 1
Property(C): BINDIR = C:\Program Files (x86)\Company\Branch\Replaced\Bin\

Strangely, the BRANCHALL property elaborates, while the INSTID does not. Yet the install works properly and puts all files in the correct folders. So INSTID must have elaborated after msiexec made these logs.

If this is missing needed info, please ask me. I'm a bit mystified, and certainly not a WIX expert. Thanks!


Solution

  • To understand this, first realize your log excerpt is from the end, but it reflects various operations that occurred in a specific order. As Stein alludes to in his answer, the SetDirectory elements you authored are implemented as custom actions that set properties. Specifically, <SetDirectory Id="INSTDIR" Value="[BRANCHALL]\[INSTID]" Sequence="execute" /> sets a property named INSTDIR to the value from formatting [BRANCHALL]\[INSTID] before CostFinalize.

    This is a problem, because before CostFinalize builds the directory tree, BRANCHALL has no value. When the UI is shown, CostFinalize runs once in each sequence. The first one populates BRANCHALL, and uses default pathing for INSTDIR. The second (execute) sequence then uses the populated BRANCHALL to determine INSTDIR, masking the problem. But without the UI, the empty value is used. You should be able to confirm this in your verbose log if you search for where the properties' values are changed.

    So what's the fix? It appears, at least in your excerpt, that you can predict the default value of BRANCHALL using only predefined properties, and thus should be able to modify your SetDirectory authoring accordingly:

    <SetDirectory Id="INSTDIR" Value="[ProgramFilesFolder]Company\Branch\[INSTID]" Sequence="execute" />
    <SetDirectory Id="BINDIR" Value="[ProgramFilesFolder]Company\Branch\[INSTID]\Bin" Sequence="execute" />
    

    However, there is a potential problem to doing it this way. If you allow the end user to change any of the locations that are used in BRANCHALL in your UI, this will override that. If this scenario matters to you, I believe specifying Sequence="first" instead of Sequence="execute" will address this. (But you'll have to run some tests to be certain this helps.) Furthermore, for silent installations that wish to override this, you may need to document a fuller set of properties that must be specified and how to specify them. You may accordingly need to add some conditions to this SetDirectory elements, along the lines of Not INSTDIR/Not BINDIR or Not I_CHANGED_THE_DIRECTORIES.


    If possible, I would have recommended a different approach: put the value for INSTID in the directory table instead of using custom actions to override an incorrect one. The major downside is that this requires creating and using a transform to set it, instead of just passing a value on the command line. But the benefit is that Windows Installer just handles the directories as it always would.