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?
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:
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?
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):
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.
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.
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.
Related
My WiX bundle behaves very badly on a specific XP machine (x86). It stalls for six minutes during the "initializing" phase (before any bundled installers are run). From the logs it seems extremely likely it is due to the dodgy state of windows update on the machine:
[090C:0FC0][2018-03-27T19:31:17]i358: Pausing automatic updates.
[090C:0FC0][2018-03-27T19:37:23]w308: Automatic updates could not be paused due to error: 0x80080005. Continuing...
[090C:0FC0][2018-03-27T19:37:23]i360: Creating a system restore point.
I am using WiX v3.10 to make the bundle. I can find very similar situations from google search but the solution always revolves around updating the target machine. I need this installer to work as expected regardless. When I test a regular msi on the same machine it goes through fine.
Is there any way I can mitigate this issue? E.g. can I stop the WiX bundle from trying to pause windows update or something similar? The fact that the installer doesn't even notify what the issue is is extremely problematic, any user is likely to be confused.
The whole point of this WiX installer is to make an install package portable and simple but this actually seems LESS portable than just the msi...
Update: Also tried building the bundle with WiX 3.11 but got same problem. Though now it produces an actual error rather than installing regardless, which I suppose is an improvement.
UPDATE: after looking in the WiX source code it looks like WiX's Burn feature is making a few calls to Microsoft.Update.AutoUpdate (%SystemRoot%\System32\usoapi.dll - %SystemRoot%\SysWOW64\wuapi.dll) and a few other COM objects here and there.
See towards the bottom (screenshot) for a hot COM tip to quickly get an overview of different COM object models.
It must be this AutoUpdate call which hangs causing a timout along the lines of what is described in this blog. I believe you can find the exact source code location by searching for hr = WuaPauseAutomaticUpdates(); in the elevation.cpp WiX Burn source file (Github link). The actual calls to the COM object are in wuautil.cpp.
I am not familiar with the Windows Update Agent Object Model, but I suppose you could try to call the Pause function in a test VBScript just to see what kind of error you get on your problem system (if any). I can't see how you would get anything but a lockup on your problem system, so maybe try first on your main box? This is obviously at your own risk. I would assume a reboot or a call to Resume will continue the process as normal. I also see a call to SystemInformation.RebootRequired in the C++ code, which I have also added to the VBScript:
Set autoupdate = CreateObject("Microsoft.Update.AutoUpdate")
autoupdate.Pause()
MsgBox Err.Number & " " & Err.Description
Set sys = CreateObject("Microsoft.Update.SystemInfo")
MsgBox sys.RebootRequired
' autoupdate.Resume() ' Enable to resume AutoUpdate
Set sys = Nothing
Set autoupdate = Nothing
Let's face it: Windows Update is broken on your XP machine - isn't it? Maybe the WiX guys can add a shorter timeout? I am not sure what is better - 1) to shorten the timeout, 2) to remove the whole call or 3) to just bomb out telling the user that Windows Update is broken? Frankly the latter would probably alert the user to something very serious (often malware).
UPDATE: as you state yourself, Windows Update is almost certainly broken on this particular machine. I would try the MSI properties suggested below for testing, and then zap Windows Update as suggested here (same link as below) (techical).
But wait, maybe a malware check is in order, before wasting time on anything else?
Maybe try this free Sysinternals tool? Checks all running processes by hash using almost 70 scanning engines (no heuristics though). Fire up all you got (only running processes are checked)
Go File => Show Details for All Processes and then elevate to get to scan system processes as well. Now click "System" to scan drivers for example (*.sys files).
Often a great way to get your system admin to agree to rebuild a problematic machine for everyone. Suddenly you go from "deployment problem" to "machine specific problem" - and it is out of your hair. GIGO problem.
Nicely formatted and phrased question BTW. And just for reference: Sysinternals.
Workaround?
I have never seen this, but I have seen some MSI files suddenly pausing for a long time whilst installing whereas they would install quickly during test installs just minutes before.
My guess is that this could be related to system restore and the creation of a restore point at certain "intervals". I am not sure what algorithm is used to determine when such a restore point is created and not, but I wrote an answer many years ago on the issue of speeding up MSI installations: How can I speed up MSI package install and uninstall?
As you will see, you can disable the creation of a restore point for your setup by setting an appropriate command line involving the property MSIFASTINSTALL (and a few other tweaks - please just read the linked serverfault.com answer). I would try this to see if your setup stops locking up / hanging.
Causes?
Some hits from the web:
Could it be that Windows Updates is also trying to create a restore point at the same time as your MSI-package? Setup hang when trying to disable windows updates (PhilDW)
Could it be that the Windows Update feature is corrupted?
Similar issue: WiX Toolset installs VC++ redistributable (x86) 2015 too slowly
I would first try the MSI properties mentioned as a "workaround" above, and if that doesn't work, I would try to see if fixing Windows Update as explained in the link directly above works. Crucially I would also let Windows Update complete its task of installing all available updates before running your bundle again.
Hot Tip (COM)
I don't like to recommend commercial tools, but we all need some quick tricks and quick wins at times - which is what this is about. Get hold of VbsEdit and use its light weight object browser to quickly see details from any COM object model. Just do a CreateObject and you will instantly see the object model in the object browser to the right in the application window (View => Object Browser if it is not there).
Just type in something like this:
Set installer = CreateObject("WindowsInstaller.Installer")
I find this to be a time-saver when I need to deal with legacy COM stuff and Visual Studio is very sluggish. I just have the VbsEdit trial version, and it allows basic editing. And let's throw in a rant: why on earth do they not make a Javascript version? I am missing something - as usual :-).
Throwing in a second screen shot to show a more interesting object model information tidbit:
Should you want to try it, here are some other CreateObject statements you can try:
Set autoupdate = CreateObject("Microsoft.Update.AutoUpdate")
Set fso = CreateObject("Scripting.FileSystemObject")
Set scriptshell = CreateObject ("WScript.Shell")
Set dictionary = CreateObject("Scripting.Dictionary")
Set shell = CreateObject("Shell.Application")
We have a unique requirement to create an msi using wix,the catch is that the msi must support multiple instance installation. User should be able to use the msi to install the product more than once on a particular system, so in order to achieve this we should disable repair mode and upgrades in msi, so literally each invocation of msi should be treated as fresh install.
Is the above requirement technically feasible with Wix? I am aware that having unique product code and package code for every invocation of msi will treat the installation as fresh install. Can this be achieved using a wrapper around the msi?
You could do this with a wrapper around the MSI. In general your wrapper program would start with the base MSI, alter the ProductCode and PackageCode using the Windows Installer APIs, copy this MSI to a location on the system then install it from there.
I say "copy and install" because it's futile trying to disable repair - it's too baked into the architecture of Windows Installer. If you have each separate MSI product cached somewhere then repair will work using that unique MSI, and it also means that you could modify as well. Repair is too unpredictable to completely prevent. Any shared files can trigger repair from other installed products that share them. What do clients do if they accidentally delete files or registry entries? And there's this about keeping the source MSI available, although the list of reasons doesn't come with a lot of explanation, Rule 31:
https://blogs.msdn.microsoft.com/windows_installer_team/2006/05/24/tao-of-the-windows-installer-part-3/
There are a lot of other issue that aren't mentioned, so it's not clear if you care about patches and maintenance in general, about multiple entries in Programs/Features (that you could suppress, but how do you uninstall?) and so on. It's also not clear that each of these separate installs don't conflict with each other in any way, such as common files/registry entries, service names, files being installed in the same location with the same name.
Before sending you to the MSI SDK I should add that I dislike this instance transform concept myself, and have not used it in practice. It might be that I have become a grumpy old man! :-).
MSINEWINSTANCE: Please investigate the MSINEWINSTANCE property and read up on the MSI SDK topic: "Installing Multiple Instances of Products and Patches". And here is perhaps a better example - more practically oriented.
Also some context on why I am not too keen on this feature. Carolyn Napier was on the original MSI team - this is straight from the horse's mouth as they say.
I know some people claim success with these instance transforms (Chris Painter might be able to illuminate my claims here), but I'd rather virtualize in scenarios such as these. Please see this age-old post from serverfault.com: I want to install an MSI twice (please skim all the other answers in that "thread" too).
APP-V: I have almost zero App-V experience, but my guess is that this is what you should try to convince your manager to spend some time on investigating. Maybe call in some favors and talk to guys who are operative in current deployment. They always have the current prevailing prejudice to report - with the tricks that make things work in the real world.
Great if you can let us know how you end up solving the problem.
Disable Repair & Modify: And for the record: disabling repair and modify (buttons only) can be done by setting the ARPNOMODIFY and ARPNOREPAIR properties. But this is not what you are looking for to allow multiple instance installations. MSI is not easily fooled and knows what you have installed and when - no reason to waste time testing these "options". All these two properties do is to hide or disable the modify and repair buttons - yields nothing you need apart from that.
I have a big program in WiX that uses a bunch of MSIs, C# custom action programs, UIs, bootstrapper, you name it, it's there.
I'm having this problem: when I run a major upgrade, the previous version isn't being erased. That is, if I upgrade from version 1.0.0.x to 1.1.0.x, Programs & Features shows that both versions are installed on the machine.
This is a common problem, with many solutions here on SO. None of them are working for me -- if there's a post of SO about this, I've tried it.
I've been told that there's a one-to-one relationship between components in a major upgrade. That is, for every component that is removed, another component has to be added. When it's NOT a one-to-one relationship is when the old version doesn't get removed -- because there are still old components hanging.
Is there a way to determine what components are hanging? Like, in the log files or something? If I could determine what MSI is having the problem I could be far more proactive in solving the issue.
EDIT:
Although I haven't solved the problem, thanks to Mr. Urman's suggestions I may be on the right track.
I created that registry key, but... it didn't seem to do anything. However, I did search my uninstall logs for the word "Disallow", and I found this phrase 9 times:
Disallowing uninstallation of component: {GUID-HERE} since another client exists.
Also, this phrase appears before each grouping of the "Disallow" phrase:
PROPERTY CHANGE: Adding INSTALLLEVEL property. It's value is '1'.
This gives me something to go on. However, I can't seem to find the GUIDs that are mentioned! They're not in my solution nor are they searchable in the registry. Besides searching the registry, is there a way (Windows 7 32 bit) to find out what component a specific GUID corresponds to?
I've been told that there's a one-to-one relationship between components in a major upgrade. That is, for every component that is removed, another component has to be added. When it's NOT a one-to-one relationship is when the old version doesn't get removed -- because there are still old components hanging.
This is not strictly true. It's quite true of minor upgrades, and in certain configurations (those involving a late RemoveExistingProducts) major upgrades are just as picky. But your typical major upgrade functions more like the user had chosen to uninstall the old version, then to install the new version. Start by verifying your assumptions: make sure you have a proper major upgrade (you changed your ProductVersion and Product Code, and have the right entries in your Upgrade table, right?). Then diagnose.
How best to identify what's going on? In my experience, log files are your best bet. Since the older version is being uninstalled indirectly, you cannot use command lines to log it. So instead set the Logging policy by creating or setting the following registry value. (Remove it later when you want to revert the setting.)
HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer
Value (Reg_SZ): Logging
Data: voicewarmup
Then run your major upgrade, find the appropriate log file that was generated in %temp%. (Consider cleaning out %temp% ahead of time to make finding it easier. Or sort by date.) Look especially at the uninstallation (which you can identify by ProductVersion, or the presence of UPGRADINGPRODUCTCODE). I'd look especially for lines like Disallowing uninstallation of component ... that contain a component GUID.
Once you have that GUID, you have to figure out what component it is, and how it got into its current state. You can manually examine your built .msi files (with a tool like Orca) to find the component, but few tools will tell you all the clients. My employer's product comes with a helper tool called InstallShield Msi Sleuth that can list all the installed products referencing a component code, or you can build your own from MsiEnumProducts or Installer.ComponentClients. You cannot just search the registry directly, because Windows Installer stores GUIDs in a compressed or packed form.
Then identifying the "why" could be the hard part. Or it could be as simple as an incorrect Shared DLL reference count, especially if you've only encountered this on a test machine that has seen non-released versions of your product.
As a related alternative, but only relevant to a minor upgrade or small update, you could set the EnforceUpgradeComponentRules Policy. This helps reveal problems as you hit them, rather than allowing Windows Installer to do its best to continue anyway.
I have a small question.
Which action removes installed product info (which was advertised with PublishProduct action) during uninstallation?
Thank's in advance.
Short answer: InstallFinalize.
Long answer: it comes down to the InstallExecuteSequence being the thing that always runs removal. I have a deeper investigation on my blog.
Frankly I never use advertising, but if I were to guess I would say that the advertisement sequence is defining a subset of the InstallExecuteSequence that is run during advertisement, and that the actual uninstall of the advertised product is actually running the full InstallExecuteSequence in uninstall mode to run standard actions such as UnPublishComponents, UnPublishFeatures, etc... I think this is logical since certain features could already have been installed by installation on demand, and a full uninstall is then necessary to clean up.
One way to test this is to insert message boxes in the InstallExecuteSequence to determine what sequence is run. I don't have the tools required to test this here and now. Again, until I have tested this with debugging scripts inserted into the sequences I can't be sure of anything.
The MSI SDK and Installshield's help file may provide some clues. In concluding I would like to ask you why you need to know what action uninstalls the product info? Perhaps we can provide a different way to achive what you want to do. Much of the point of MSI is to leave most of the work to standardized actions, and it is not good to interfere with these processes. Custom
actions should be used only for stuff that can never be achived with standard actions.
I'm new to WiX installer but I need to solve a situation. After searching for some time I haven't found any solution.
I need to read a value from SQL Server while running the WiX installer and keep it, but I'm not sure if it is possible. All I've found are 'updates' or 'insert' query examples, but no 'select'.
Has someone done something like this?
When you need to read something from somewhere during the installation process, you have several options:
if it has a trace in the registry, then RegistrySearch is the proper choice
if it has a file installed to the well-known location, then DirectorySearch/FileSearch approach fits best
otherwise, you always have the last resort - a custom action
Obviously, if you need to read something from SQL Server, custom action is the only choice for you. So, to tell it short, you should create an immediate custom action which will read the data you need and place it to the property (or a number of properties). You should use this property later in your installation.
Try to avoid custom actions as much as you can, but if you have no other choice, still try to find the tested ones. For this particular case, take a look at the Community MSI extensions.
Although you might solve this particular problem fast, I would still encourage you to dive deeper into the topic and understand how Windows Installer (the underlying technology behind WiX Toolset) works. Start from MSDN.
You could use the registry extended stored procedures to insert the value you need into the registry and then do a Registry search to pull the values into your installer.
As the blog post says the sp's are undocumented and thus unsupported but if it works for you it will save writing a custom action.