1

I have a WiX installer project that I'm creating where I'd like the installer to check to see if another application is already installed on the user's machine. If it is, then I'd like to set the install level of one of the features to "1", otherwise it should remain hidden (i.e. install level = 0). To find out where the application is installed, I first do a registry search:

<Property Id="MYAPPINSTALLFOLDER">
  <RegistrySearch Id='InstallPathRegistry' 
        Type='raw' 
        Root='HKLM' 
        Key='SOFTWARE\SomeLongAppPath' Name='FileName' 
        Win64='yes'/>
</Property>

You'll notice that the registry value that I end up getting is actually the directory of the installed application including the actual program name with extension (let's say myapp.exe). So, once I get the full path of the installed application, I check to see if the file exists:

<Property Id="MYAPPINSTALLED">
  <DirectorySearch Id="CheckFileDir" Path="[MYAPPINSTALLFOLDER]" AssignToProperty="yes">
    <FileSearch Id="CheckFile" Name="myapp.exe" />
  </DirectorySearch>
</Property>

Now, what I would expect to see is that if the file actually exists in that location, then the Property called "MYAPPINSTALLED" would be set to 1, otherwise it would be 0. Then, when I setup my features I use something like this:

<Feature Id="ThirdPartyPlugins" Title="Third Party Plugins" Level="0">
  <Condition Level="1">MYAPPINSTALLED</Condition>
  <ComponentGroupRef Id="MyAppPlugin" />
</Feature>

However, when I run my installer the third party plugin feature is always hidden. I've enabled msi datalogging by setting the property like this:

<Property Id="MsiLogging" Value="voicewarmupx"/>

And when I check the log file I can definitely see that the MYAPPINSTALLFOLDER property gets changed to the correct file path when it does the registry search. However, if I search the log for the property MYAPPINSTALLED, then I can see the following:

AppSearch: Property: MYAPPINSTALLED, Signature: CheckFileDir 
Action ended 15:55:06: AppSearch. Return value 1.

So, it looks like it worked, however it doesn't seem to ever set the Property to equal the search value. Am I doing something wrong? Can someone explain why my feature install level never gets set to 1 even though the application file exists?

Edit

Ok, after more debugging... I think the issue is that the directory search is trying to use a path that includes the file name and extension (i.e. C:/Program Files/MyApp/myapp.exe") instead of just the directory where the file comes from. This is because the registry search has the full path including the file name stored (but not just the install directory). If I do a directory search just using the correct absolute directory (not using the registry search) then the process works. So, my follow up question is... my Property MYAPPINSTALLFOLDER contains the full path with file name and extension. Is there a way to strip the file name and extension from this property so that I just have the proper directory name to search for?

andyopayne
  • 1,348
  • 3
  • 24
  • 49

2 Answers2

2

You're checking to see if another application is installed but that's rather a long way around. Also, the file search returns a path, not zero or 1, but either way a full verbose log should tell you if the properties are being set. It might help if you could post the entire log somewhere rather than the parts you think are the only relevant ones. e.g. There's probably an AppSearch in the execute sequence for silent installs.

It's requently easier to do a single search for other applications that were installed with MSI packages in these ways:

  1. If you know the other product's UpgradeCode (and version ranges if applicable) then add Upgrade/UpgradeVersion elements with onlydetect set to yes, and that search will set a property if the product is detected.

  2. If you know (or can find out) the Component id of any of the relevant components from that other product, then you can use them in a WiX ComponentSearch. If you get the target property set then that component is installed. This post contains a couple of ways to find out component guids: How to find out which application requires a certain assembly from GAC?

It's also puzzling that the AppSearch log extract you posted only refers to one property. The Directory/FileSearch is also an AppSearch, so if the MSI actually contains two searches in AppSearch there should be references to all the properties being set. Again, that's a reason to post the entire log and look in the MSI file for those searches. The RegLocator search is documnented to occur before the DRLocator, so why is there no MYAPPINSTALLFOLDER property in the AppSearch log entry? You're not on a 32-bit system are you? (noticing the win64 search).

Community
  • 1
  • 1
PhilDW
  • 20,260
  • 1
  • 18
  • 28
1

Per the WiX documentation:

Use the AssignToProperty attribute to search for a file but set the outer property to the directory containing the file. When this attribute is set to yes, you may only nest a FileSearch element with a unique Id or define no child element [of the DirectorySearch].

I added the text in the brackets to make it more clear.

So, after reading this sentence a few times and cross referencing your WiX XML, I think I see what the problem is with your current WiX XML. You perform a separate registry search from the directory search. Instead, you should nest these. There are two ways to perform the search, depending on what you want to do. One way is to simply retrieve the registry value from the registry, and if the value exists, then you make the assumption that the feature's required application is installed, at which point you appropriately set a property that would enable hiding/showing the feature within your installer's feature selection tree. The other way is to actually find the file you're interested in, using the results of the registry search as the basis for the file search.

Below is the XML for just a registry search, which doesn't check that the file actually exists on disk. You're making the assumption that if this registry value exists, the file is installed and available.

<Property Id="MYAPPINSTALLFOLDER">
  <RegistrySearch Id='InstallPathRegistry' 
      Type='raw' 
      Root='HKLM' 
      Key='SOFTWARE\SomeLongAppPath' Name='FileName' 
      Win64='yes'/>
</Property>

<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
  <!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
       evaluates to TRUE; otherwise, it evaluates to FALSE.
  -->
  MYAPPINSTALLFOLDER
</SetProperty>

<!-- You could also be more explicit:

<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
  <![CDATA[MYAPPINSTALLFOLDER <> ""]]>
</SetProperty>

-->

<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
    Display="[SHOW_APP_FEATURE]">
    ... <!-- Component/ComponentRefs go here -->
</Feature>

If you want to ensure that, even if the registry value exists in the registry, that the file it points to is 1) actually a file path; and 2) that the file actually exists on disk, then you need to perform a nested file search within a directory search, which itself is nested within a registry search. You would again need to use a SetProperty custom action to set a property that would enable the hiding/showing of the feature within your installer's feature selection tree. Here's the XML for this search:

<!-- Performing a FileSearch nested within a DirectorySearch,
     which is itself nested within a RegistrySearch

     This search twill ensure that the file exists on disk, and
     if so, assign the full filename and path to the 
     MYAPPINSTALLFOLDER property.
-->
<Property Id="MYAPPINSTALLFOLDER">
  <RegistrySearch Id='InstallPathRegistry' 
      Type='raw' 
      Root='HKLM' 
      Key='SOFTWARE\SomeLongAppPath' Name='FileName' 
      Win64='yes'>
    <DirectorySearch Id='InstallPathDirectory' AssignToProperty='yes'>
      <FileSearch Id='InstallPathFile' Name='myapp.exe' />
    </DirectorySearch>
  </RegistrySearch>
</Property>

<Property Id="SHOW_APP_FEATURE" Value="hidden" />
<SetProperty Id="SHOW_APP_FEATURE" Value="collapse" Sequence="both" After="CostFinalize">
  <!-- If MYAPPINSTALLFOLDER is defined and contains any non-empty value, this
       evaluates to TRUE; otherwise, it evaluates to FALSE.
  -->
  MYAPPINSTALLFOLDER
</SetProperty>

<Feature Id="MyAwesomeFeature" Title="My Awesome App Feature"
    Display="[SHOW_APP_FEATURE]">
    ... <!-- Component/ComponentRefs go here -->
</Feature>

This should allow you to accomplish what you're trying to achieve.

fourpastmidnight
  • 4,032
  • 1
  • 35
  • 48