I have a WiX installer that install a bunch of extension files to the app directory for another app. To ensure that the files end up in the right place, I use a bunch of nested DirectorySearch to find the app directory.
The app is usually installed in a path following this pattern:
\Program Files (x86)\CompanyName\ProductName\[version]\[environment]\[optional intermediate folder]\AppFolderName
In other words, common installation folders include:
\Program Files (x86)\CompanyName\ProductName\1.0\Prod\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.1\Prod\OptionalFolderName\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.2\Test\AppFolderName
\Program Files (x86)\CompanyName\ProductName\1.2\Test\OptionalFolderName\AppFolderName
To handle this, I'm using a nested set of DirectorySearch elements to assign the path to a Property. The following works if the optional folder (OptionalFolderName) is present:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="OptionalIntermediateFolder" Path="OptionalFolderName">
<DirectorySearch Id="AppFolder" Path="AppFolderName" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
However, since OptionalFolderName is optional, I want to use a DirectorySearch to determine if the app folder is directly under the environment folder or one level down. In an attempt to do this, I changed the search tree to:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
The later version however does not work if the optional folder is present, but it does work if it is not present. In other words, it is as if one of the the Depth attributes is isgnored; my guess is that this is because I am using the Depth attribute twice at different levels within the tree.
Any suggestions on how I can work around this?
Update - added log snippet extract from msiexec /i [msiname] /l*v [logfile]:
Action 14:38:47: AppSearch. Searching for installed applications
Action start 14:38:47: AppSearch.
AppSearch: Property: SOMEAPPFOLDER, Signature: AppFolder
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1322 2:
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1322 2:
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1324 2: [environmentname] 3: 1
MSI (c) (00:90) [14:38:47:065]: Note: 1: 1325 2: CompanyName
Action ended 14:38:47: AppSearch. Return value 1.
MSI (c) (00:90) [14:38:47:066]: Doing action: LaunchConditions
MSI (c) (00:90) [14:38:47:067]: Note: 1: 2205 2: 3: ActionText
Action 14:38:47: LaunchConditions. Evaluating launch conditions
Action start 14:38:47: LaunchConditions.
MSI (c) (00:A8) [14:38:47:069]: Font created. Charset: Req=0, Ret=0, Font: Req=MS Shell Dlg, Ret=MS Shell Dlg
Couldn't find the AppFolderName app folder for [environment/version].
MSI (c) (00:90) [14:38:48:543]: Note: 1: 2205 2: 3: Error
MSI (c) (00:90) [14:38:48:543]: Note: 1: 2228 2: 3: Error 4: SELECT `Message` FROM `Error` WHERE `Error` = 1709
MSI (c) (00:90) [14:38:48:543]: Product: [productname] -- Couldn't find the AppFolderName app folder for [environment/version].
Action ended 14:38:48: LaunchConditions. Return value 3.
Behavior of Depth
when FileSearch
is used
Depth
works differently when FileSearch
is an immediate descendant of DirectorySearch
.
In the normal case, Depth
specifies the maximum number of folder levels above the specified path. However, if FileSearch
is an immediate descendant, Depth
specifies the maximum number of folder levels to look for the file below the specified path.
In other words, there's no way to specify the depth for a directory that is an immediate parent of a file.
Example
Let's look at the code you've pasted:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
In the above, the first use of Depth
allows two directory levels between ProductFolder and EnvironmentFolder, but the second use of Depth
refers to the number of levels between AppFolder
and AppNameExe
.
Workaround
The following xml uses two searches to workaround the problem:
<!-- Locate the parent directory first -->
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="1">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="1" />
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
<!-- Now, look for the file in the above directory -->
<Property Id="APPFILEEXISTS">
<DirectorySearch Id="AppFolder" Path="SOMEAPPFOLDER">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0"
MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearch>
</Property>
The second depth
is used to search the file two levels below AppFolder
, not to search AppFolder
two levels below EnvironmentFolder
.
You can do a first folder search:
<Property Id="SOMEAPPFOLDER">
<DirectorySearch Id="ProgramFilesFolder" Path="[ProgramFilesFolder]">
<DirectorySearch Id="CompanyNameFolder" Path="CompanyName">
<DirectorySearch Id="ProductFolder" Path="ProductName">
<DirectorySearch Id="EnvironmentFolder" Path="$(var.ENVIRONMENTNAME)" Depth="2">
<DirectorySearch Id="AppFolder" Path="AppFolderName" Depth="2" AssignToProperty="yes">
<!-- <FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" /> -->
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</DirectorySearch>
</Property>
And use it as reference to verify that the app file exists:
<Property Id="APPEXISTS">
<DirectorySearchRef Id="AppFolder" Parent="EnvironmentFolder" Path="AppFolderName">
<FileSearch Id="AppNameExe" Name="AppName.exe" MinVersion="$(var.MIN_VERSION).0" MaxVersion="$(var.MAX_VERSION).999" />
</DirectorySearchRef>
</Property>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With