WIX If…else condition using registry

2020-04-18 07:31发布

问题:

I am trying to use if...else condition in WIX by checking the registry value.

<Property Id="WINDOWS_VERSION">
  <RegistrySearch Id='WinVersion' Type='raw'
    Root='HKLM' Key='SOFTWARE\Microsoft\Windows NT\CurrentVersion' Name='ProductName' />
</Property>

<?if [WINDOWS_VERSION] = "Windows 10 Enterprise"?>
        <Directory Id="INSTALLDIR" Name="ETMS">
      <Directory Id ="BinDir" Name ="BIN">
        <Directory Id ="AssemblyDir" Name ="Assembly">
          <Component Id ="BinAssemblyFilesFse" Guid ="$(var.BinAssemblyFilesGuid)">
            <!-- This section should include any files that need to be deployed to Bin Assembly folder -->
            <?include BinAssemblyFiles.wxi?>
          </Component>
          <!-- Due to a bug in WIX, the shortcut is always pointing to the first file in a component
           as a result shortcuts need to be put in their own components. -->
          <Component Id="MainExecutable" Guid="$(var.MainExecutableGuid)">
            <File Id="EtmsFse.exe" Name ="EtmsFse.exe" Source ="$(var.BuiltComponents)">
              <!-- Add shortcut for this file to the created ETMS Program Menu Folder. -->
              <Shortcut Advertise="yes" Id="StartMenuEtmsFse" Directory="EtmsProgramMenuDir" Name="ETMS"
                        WorkingDirectory='Bin' Icon="EtmsFse.exe" IconIndex="0" >
                <!-- Set the AppID in order to get toasts to work -->
                <ShortcutProperty Key="System.AppUserModel.ID" Value="FSE"></ShortcutProperty>
                <!-- Set the ToastActivatorCLSID in order to get notifications working in Action Center -->
                <ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{3BD0C45A-B130-4709-BF6F-E786195E7FF9}"></ShortcutProperty>
              </Shortcut>
              <Shortcut Advertise="yes" Id="desktopEtmsFse" Directory="DesktopFolder" Name="ETMS"
                        WorkingDirectory='Bin' Icon="EtmsFse.exe" IconIndex="0" >
                <!-- Set the AppID in order to get toasts to work -->
                <ShortcutProperty Key="System.AppUserModel.ID" Value="FSE"></ShortcutProperty>
                <!-- Set the ToastActivatorCLSID in order to get notifications working in Action Center -->
                <ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{3BD0C45A-B130-4709-BF6F-E786195E7FF9}"></ShortcutProperty>
              </Shortcut>
            </File>
            <RemoveFolder Id="DelProgramMenuDir" On="uninstall" Directory="EtmsProgramMenuDir"/>
          </Component>
        </Directory>
<?else?>
        <Directory Id="INSTALLDIR" Name="ETMS">
      <Directory Id ="BinDir" Name ="BIN">
        <Directory Id ="AssemblyDir" Name ="Assembly">
          <Component Id ="BinAssemblyFilesFse" Guid ="$(var.BinAssemblyFilesGuid)">
            <!-- This section should include any files that need to be deployed to Bin Assembly folder -->
            <?include BinAssemblyFiles.wxi?>
          </Component>
          <!-- Due to a bug in WIX, the shortcut is always pointing to the first file in a component
           as a result shortcuts need to be put in their own components. -->
          <Component Id="MainExecutable" Guid="$(var.MainExecutableGuid)">
            <File Id="EtmsFse.exe" Name ="EtmsFse.exe" Source ="$(var.BuiltComponents)">
              <!-- Add shortcut for this file to the created ETMS Program Menu Folder. -->
              <Shortcut Advertise="yes" Id="StartMenuEtmsFse" Directory="EtmsProgramMenuDir" Name="ETMS"
                        WorkingDirectory='Bin' Icon="EtmsFse.exe" IconIndex="0" >

              </Shortcut>
              <Shortcut Advertise="yes" Id="desktopEtmsFse" Directory="DesktopFolder" Name="ETMS"
                        WorkingDirectory='Bin' Icon="EtmsFse.exe" IconIndex="0" >

              </Shortcut>
            </File>
            <RemoveFolder Id="DelProgramMenuDir" On="uninstall" Directory="EtmsProgramMenuDir"/>
          </Component>
        </Directory>
<?endif?>

I am very sure that the value I from the registry is "Windows 10 Enterprise" but somehow it doesn't goes to the if condition. Does anyone know what's wrong with it?

Thanks!

[Update] I wanted to add Toast notification but this feature only available starting from Windows 8. That's why I am trying to do a if...else condition to check the Windows version. Toast notification need the line below...

 <!-- Set the AppID in order to get toasts to work -->
            <ShortcutProperty Key="System.AppUserModel.ID" Value="FSE"></ShortcutProperty>
            <!-- Set the ToastActivatorCLSID in order to get notifications working in Action Center -->
            <ShortcutProperty Key="System.AppUserModel.ToastActivatorCLSID" Value="{3BD0C45A-B130-4709-BF6F-E786195E7FF9}"></ShortcutProperty>

回答1:

As Brian has pointed out you are using a pre-processor construct / directive which only affects things at compile time. These constructs are useful if you want to compile a separate MSI for the enterprise components - using the same WiX source as you use to compile other MSI editions. If you want to put everything in one setup you normally resort to using features.

So, I guess the answer is that you need to use a feature for the enterprise components and then use the property you populate from that registry search as the condition to determine if it is set for installation or not.

I have written a couple of answers recently describing feature use, perhaps I'll just link to them:

  • On the differences between preprocessor constructs and features (long, but hopefully useful - describes the difference between pre-processor constructs, features and setup.exe launchers and some other details).
  • How to install feature based on the property set in custom action? (explains how you can control feature selection via user interaction, command lines, feature conditions or custom actions)

Essentially you can use the INSTALLLEVEL and feature install level mechanism described in the second link above to just define a feature condition for the feature in question. However, sometimes this approach fails during deployment because corporate application packagers max out the INSTALLLEVEL property to prevent any features from not installing (wrong practice, but very common). How serious this is depends on the product. In some cases it may install files that should never be installed - for example Tablet OS features, or in the olden days ANSI versions of files that should be Unicode - and there were further examples that escape me.

In addition to technical issues such as those listed, some features can contain components that are unsafe to install for users who do not need them. Developer debugging tools for example - they should probably not be available for everyone for security reasons, and also because they take up a lot of disc space and machine resources.

The alternative is to use a custom action to programmatically modify the feature selection at runtime after inspecting the system directly. Both the above approaches are described in the links above. Particularly in the second link.

Finally, as you will see in one of the links above, you can create a setup.exe bootstrapper that will install an enterprise MSI if the user is installing on an enterprise OS.

So in short: bundle everything in the same setup by using features and control the feature selection for installation in some fashion (feature conditions, custom action), or compile separate setups with the normal and enterprise components respectively using pre-processor constructs. Then you can install the MSI files via a setup.exe bootstrapper / launcher in an intelligent fashion where you skip installing the enterprise MSI if it is not needed.


Quick Mock-Up

I do not have time to test this properly tonight, but in the interest of getting you going, let's give it a try. This is a little experiment I made to see if we can prevent the problem where packagers max out the INSTALLLEVEL property:

<Property Id="MYCHECK">
  <RegistrySearch Id='WinVersion' Type='raw'
    Root='HKLM' Key='SOFTWARE\Microsoft\Windows NT\CurrentVersion' Name='ProductName' />
</Property>
<Feature Id="TestFeature" Title="TestFeature" Level="0">
  <Condition Level="1"><![CDATA[MYCHECK="Windows 10 Enterprise" OR ACTION="ADMIN"]]></Condition>
  <!--<ComponentGroupRef Id="TestComponents" />-->
</Feature>

UPDATE: Testing indicates that any feature set to Level=0 as default will not be extracted during an administrative install at all, regardless of any feature conditions setting the feature to install. I guess the tentative conclusion is to not set any features to Level=0, but set Level=1 and then set them to Level=0 with a feature condition that evaluates to true. This way the feature may be hidden in a regular installation, but all features - with associated files - are extracted during admin installation. The OR ACTION="ADMIN") part of the condition appears to not be needed. Leaving the sample above as it is for now.

A few things to point out:

  • The default INSTALLLEVEL for WiX setups appears to be 1. Each feature has a level attribute (see above). If a feature's feature-level is equal or lower than the INSTALLLEVEL, it is defaulted for installation. Otherwise it is deselected by default.
  • Setting a feature level to 0 - like I do above - will prevent its display, and I believe, its installation (probably also if ADDLOCAL is set to ALL - technical detail, don't worry about it yet).
  • So setting the TestFeature level to 0, should prevent its display and installation unless your condition evaluates to true (we are on an Enterprise machine).
  • Once the condition is true, the feature's level is set to the level specified by the condition. In this case 1 - this is sufficient to show it and default it for installation.
  • UPDATE: This section is not 100% correct. Please read block quote in color above. Note the OR in the condition. I try to account for the fact that a feature with level 0 will apparently not be installed by an administrative installation either. I have not had time to test this - it will happen tomorrow. This condition should be true during administrative installations on all computers, even if they are not Enterprise installations.

As you understand there is a lot to test here, which I can not do right now. Please have a go at this, and test. I am also not sure that the way you get the Windows 10 edition is the right way, and that it will work in all locales. What do you think that key reads in Chinese or Korean or Arabic for example? It could be the same, but how do we know? A DWORD or similar would be more reliable.

There are some deployment-specific forums (just a small selection off the top of my head) - where you can check for more information:

  • http://forum.installsite.net/
  • http://www.itninja.com/question
  • http://community.installshield.com/
  • https://www.advancedinstaller.com/forums/

Links:

  • Failing condition wix


回答2:

These are preprocessor directives which are evaluated at compile time, not at run-time. Only the part that is true at compile time is even included in your final MSI.

Here is more information about wix preprossor.

I don't know what you are trying to do in the blah blah sections but you will probably just need to control things based on the value of the registry you just read. Update the question with what you are actually trying to do inside the if else and I can update the answer (if I remember) with an example to do what you are trying to do.