WIX Installer: Prevent installation on Windows Ser

2019-01-28 17:20发布

问题:

I have a WIX project that must only be installed on Windows Server 2016 (or newer).

Looking at Microsoft documentation, VersionNT for:

Windows Server 2016 is 603, Windows Server 2012 is 602.

The VersionNT for Windows Server 2012R2 has never been mentioned.

When I use below line of code:

<Condition Message="!(loc.RequireServer2016)"><![CDATA[INSTALLED OR (VersionNT >= 603)]]></Condition>

it still lets me install on Windows Server 2012R2.

How can I limit the installation of my software to only Server 2016 and prevent it from installing on Server 2012R2?

回答1:

INSTALLED should be Installed. Properties are case sensitive and you must definitely fix that in your condition - or else that part of the condition will never be true - even if the product is installed.

The rest of the condition looks OK actually. Just some ideas to determine what is wrong:

  1. WiX Source Element: Are you sure you have included this condition in the right location in the WiX source?

    • I would put it directly under the Product element. The proof is in the compiled MSI file. Look if there are entries in the LaunchCondition table.
    • I assume you have Orca to check with, otherwise use one of the other, free MSI viewers (towards bottom): How can I compare the content of two (or more) MSI files?
  2. Versionlessness: There have been massive changes to how the OS version is detected in Windows 10. I don't know if this also affects Windows Server 2012R2 as well.

    • It seems the overall idea is that Windows is now "evergreen versionless" (how is that for a term) - meaning that VersionNT does not necessarily report the correct version of the OS at all!
    • Please read this answer rather than me repeating things here: Windows 10 not detecting on installshield.

In order to determine what the value of VersionNT really is in your setup, I'd use one of two ways to check properties at runtime. The latter option (logging) is generally quicker and easier, whereas the first option allows you to also evaluate complex conditions as the setup runs and show whether they are true or false at runtime by using the Session.EvaluateCondition method (I use this method call when conditions are complex and confusing and I want some runtime proof that they behave like I expect):

  1. Property Debugger VBScript: I have a property debugger VBScript I use to display a bunch of property values at runtime for an MSI file and to evaluate conditions at runtime to show whether they evaluate to true or not - as stated above. If I were you I would use such a script to display VersionNT at runtime (and whatever other property or condition you want to check).

    • It is recommended that you don't use VBScript for production setups - debugging only. Rob is the WiX creator - I agree with all his anti-script arguments, but personally still conclude that VBScript is useful for debugging - quick, easy & embedded source with no compilation. Setting up a whole compiled custom action just to retrieve a few properties for testing is overly time consuming (debugging would be better though and easier with "real code" such as C++).
    • In corporate environments VBScript has seen lots of use because of the embedded source - you can always find the real custom action source embedded in the MSI - not so with compiled custom actions where the source you compiled from could be unavailable (due to the inherent chaos of all things technical - in the real world) - which is very bad indeed.
    • Another benefit is the simplicity of VBScripts and how corporate application packagers master the complexity better than C++ / C#. But overall script custom actions always cause problems along the lines of what Rob mentions (anti-virus blocking, poor debugging and lackluster coding without proper error handling).
    • Crucially you will have control of security software / anti virus in corporate deployment scenarios. You can deal with problems in a logical way and tweak things until the package deploys reliably.
    • Finally corporate desktops are SOEs - standardized platforms with much less variability than regular PCs (which can be in a much greater diversity of states and hence see more deployment problems).
    • The real problem is basically custom actions altogether, they are very error prone: Why is it a good idea to limit the use of custom actions in my WiX / MSI setups? A lot more information than you need I guess. The moral of the story for me is: use whatever gets the job done quickest, but don't think scripts are good enough for production code for world-wide distribution.
    • For corporate deployment one should eliminate ad-hoc script custom actions in favor of well-tested C++ custom actions with full rollback support driven by custom MSI tables which yields full declaration of what should happen during the install.
  2. Logging: Just create a log file for the setup and check the value of VersionNT in it. I like to enable logging for all setups as explained here: installsite.org on how to do logging - see the "Globally for all setups on a machine"-section). Despite the performance hit, I always have a log file ready in the TEMP folder for debugging. The same link will show you how to make an ad-hoc log for a single install only as well (essentially: msiexec.exe /i "c:\filename.msi" /QN /L*V "C:\msilog.log" REBOOT=R - silent install with logging and suppressed reboot - even more logging info).


Property Debugger Demo: This is beyond what you asked, but I think you may struggle to debug this issue and similar ones for your server deployments, and I want to give you a quick demo on how to evaluate MSI conditions in VBScript.

Your condition above is generally too simple to bother with this, but this is a general approach for very complicated conditions - for example when you want to run a custom action only on repair or on major upgrade initiated uninstalls - the proof is always in testing, no matter how hard you think about things.

Your condition evaluated at runtime using VBScript:

MsgBox "Condition: " & CBool( Session.EvaluateCondition("Installed OR (VersionNT >= 603)"))

Such a VBScript custom action can be inserted in different sequences and in different locations as you wish. Property values may differ depending on your sequencing (!) and also what installation mode you are in (install, uninstall, repair, modify, self-repair, major upgrade (involves one MSI being installed and one being uninstalled), minor upgrade, minor upgrade patch, major upgrade patchetc...) and whether you are running in deferred or immediate context or whether you are running silently or interactively, and whatever variable I have forgotten - lots of moving parts in MSI. For example if you insert the custom action at the beginning of the UI sequence, then AppSearch has not run yet, and some properties are not yet set. You can also insert property debugging constructs in the administrative- and advertisement-installation sequences if need be.

And maybe a couple of further conditions for testing purposes:

  • "NOT Installed AND NOT WIX_UPGRADE_DETECTED"
  • "NOT Installed AND NOT REMOVE~="ALL""

And then some that warrant testing due to complexity (not my conditions, they are from here):

  • Installed AND (NOT REMOVE="ALL" OR UPGRADINGPRODUCTCODE)
  • NOT Installed OR Installed AND (NOT REMOVE="ALL" OR UPGRADINGPRODUCTCODE)

I hope that was a clear concept. I have a VBScript for such property debugging, but it is too large and messy to put here.



回答2:

In light of this article about the values of VersionNT values for Windows 10 and Server 2016:

https://support.microsoft.com/en-us/help/3202260/versionnt-value-for-windows-10-and-windows-server-2016

I would start with a condition of:

VersionNT=603 and MsiNTProductType > 1

where the latter indicates a server system:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa370329(v=vs.85).aspx

Beyond that, look at the WindowsBuild property to see if it has a useful value for Server 2016 (or a value you can use to exclude Server 2012). You could also look at a custom action that calls GetSystemMetrics:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms724385(v=vs.85).aspx

to see if SM_SERVERR2 is set in 2012.

You do not need a condition that includes the Installed property. When you first do the install the Installed value is not set. If you try to install the same MSI again you won't get as far as the launch condition because Windows will see that this particular ProductCode is already installed and go into maintenance mode (repair, uninstall, feature change type stuff). So it's not clear why you think you need it. If you need to be sure that launch conditions apply only on the initial install the add the condition "Not Installed" to the LaunchConditions action in the UI and execute sequences.