3

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!

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
SteveSims
  • 35
  • 4
  • Can we ask what this instance ID is for and whether it is really necessary? Please take no offence, but out of experience constructs can be unnecessary based on other features that work better. Hence we always want to know why you do something. – Stein Åsmul Jun 02 '18 at 11:44
  • thanks for the question.It is possible that we may be able to do away with the INSTID variable if WIX cannot handle this install scenario. But to answer your question,this WIX project is a conversion of a legacy application that formerly used a different install technology (InstallShield). In the past the application could be installed multiple times on the same system,and each installation was segregated by INSTID.As time went by this "feature"of the application was so little used that "bugs",crept into the multi-install scenario that went unnoticed, but also a dependence on INSTID crept in. – SteveSims Jun 02 '18 at 19:59
  • Our need to change installer technologies is a bit more urgent than our need to eliminate the application dependence on INSTID, which would be more sizeable (resource intensive) than merely changing the installer to WIX and tolerating the use of INSTID. (A possible workaround would be to establish a "fixed value" for INSTID.) Also, if you or anyone can suggest tests that I can do to get more information, I'd be happy to do that and post the results. – SteveSims Jun 02 '18 at 20:00
  • @SteveSims I see your question about comments. You can [edit] your question. SO has a Q&A format in direct opposition to a message board format. The goal is to end up with a good question and a few good answers—hopefully with one of the answers accepted at an appropriate time. By then, the comments should be detritus. – Tom Blodget Jun 02 '18 at 22:48
  • @Tom Ah, ok, Tom, I see that now. What I did prior to your comment was to chop up the post into two smaller comments, and then delete the post. Thanks for the info. – SteveSims Jun 03 '18 at 17:00

2 Answers2

1

I don't have time to write a full answer right now, but here are some hints:

Technical: When you reference another directory property in your set property / set directory custom action, I believe you need to run the custom action after CostFinalize for the other property to be set to any value at all. When you do this, you need to use a Type 35 custom action rather than a Type 51 custom action. See MSDN: Changing the Target Location for a Directory. My smoke test using a Type 35 action seemed to work. I will test more tomorrow and change this answer with more detail.


N.B! Not tested properly:

<SetProperty Id="INSTDIR" Value="[BRANCHALL]\[INSTID]" After="CostFinalize" />

You can try to use the above markup instead of your current SetDirectory elements. Once you compile your MSI you must "hotfix" it to change the custom action from Type 51 to Type 35 in the CustomAction Table (just change 51 to 35 in the Type column) - and then run a test install. Use Orca, or equivalent to do this hotfixing. I just add this so you can try it, I haven't tested it properly.


Alternatives?: Overall I recommend you do not redirect folder properties this way. I find it to be unreliable and riddled with unexpected problems. In fact, I don't see how this approach will allow you to install your MSI several times at all - due to the overall Windows Installer design (which does not allow concurrent installs outright). If you want to install the same MSI multiple times, then there are a couple of pre-existing answers that you might want to skim:

I don't know if you are in a corporate setting or not, but App-V is in use by a lot of corporations. It is not my cup of tea (currently at least), but should be mentioned for you to orient yourself with regards to what can satisfy your requirements (that we still need to properly understand).

This is incomplete, will check in tomorrow.

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164
1

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.

Michael Urman
  • 15,737
  • 2
  • 28
  • 44
  • Thanks Michael. Your answer addressed all of the elements I needed to find a solution, and possibly multiple solutions. After reading your post a few times I noticed that if I passed the BRANCHALL= value on the silent install command line it worked. I therefore reasoned that all I needed to do was to set a value for it, conditionally if it was not set already, so adding the line: BRANCHALL="" – SteveSims Jun 03 '18 at 19:12
  • Worked except for the order of evaluation which was easily fixed. I will check out your suggestion for using a transform. – SteveSims Jun 03 '18 at 19:16
  • Nice alternative! I didn't think of just conditionally filling in BRANCHALL. It rapidly becomes a trade-off between kinds of complexity, but I think I prefer your idea. I may edit it into my answer later. – Michael Urman Jun 03 '18 at 21:21
  • What if the user selects a different installation path from the setup's custom installation dialog? Or passes another `INSTALLDIR` from the command line? Especially problematic if done from the GUI, and then you run uninstall in silent mode. The MSI design to allow any folder to be overridden is flawed in my opinion - only folders suitable for redirection should be marked as "redirectable". It is possible to simply disallow installation anywhere other than under `ProgramFiles`. Folder redirection is an old and useless `anti-pattern` anyway - in my opinion, but the future might be different? – Stein Åsmul Jun 04 '18 at 16:39
  • @SteinÅsmul yes, that's the point of my "However" and "If possible" paragraphs. You can make folders non-redirectable by using non-public properties for their name, but that would block the requested use case. It is far too easy to author *any* of these approaches incorrectly. – Michael Urman Jun 04 '18 at 20:06
  • @SteinAsmul, I tested each scenario. If the user specifies (either via the GUI or the command line), the install works because the conditional prevents the added SetDirectory from having any effect. – SteveSims Jun 04 '18 at 23:07
  • Did you also test silent uninstall after redirecting elsewhere? Invoking the uninstall from Add / Remove Programs? (click `Remove`). Just checking. Did it remove all files and folders that were installed? Also, I would try to install from an administrative image - to verify that it works correctly as well. These folder redirects are hairy. I am too tired to think clearly as well :-). – Stein Åsmul Jun 04 '18 at 23:22
  • @Stein, yes, it appears that WIX remembers where it installed to, and uninstalls the unmodified files there regardless of what code logic got them installed there. In each case, redirected or not, the uninstall worked as advertised. – SteveSims Jun 05 '18 at 08:48