Wix/Burn - condition to not run MSIPackage and thus not uninstall app - wix

I've got myself into a bit of state with a version of my WiX Bootstrapper that is out in the field and am struggling to see how I can get round my problem with the new version.
Basically the version that is out there has a couple of MSIPackage elements that installs SQL CLR & SMO using Mircosoft's standard MSIs (SQLSysClrTypes.msi & SharedManagementObjects.msi) - but unfortunately they are the x64 versions. Now that's fine for 64bit PCs (as our app can use the 64bit version) and so on those PCs all is ok. But obviously when someone tries and installs it on a 32bit machine, it fails.
So, what I'm wanting the new setup.exe to do; is to detect if the 64bit version is installed & it's version (which I do via a Registry search); if it is, then don't do anything (ie don't install the x86 version). Also detect if the x86 version is installed & it's version (again I can do this) - then only install the x86 version; if neither the x86 or the x64 version is installed ( or they are not the correct version - in this case v13.0.1601.5)
My logic for this was:
<util:RegistrySearch Id="IsSMOInstalledx86"
Root="HKLM"
Key="SOFTWARE\Microsoft\Microsoft SQL Server\SharedManagementObjects\CurrentVersion"
Value="Version"
Result="value"
Variable="SMOVersionx86"/>
<util:RegistrySearch Id="IsSMOInstalledx64"
Root="HKLM"
Key="SOFTWARE\Microsoft\Microsoft SQL Server\SharedManagementObjects\CurrentVersion"
Value="Version"
Win64="yes"
Result="value"
Variable="SMOVersionx64"/>
and
<MsiPackage SourceFile="$(var.DependenciesPath_Microsoft)\System CLR Types for SQL Server 2016\v13.0.1601.5\SQLSysClrTypes.msi"
Id="SQLCLR"
DisplayName="System CLR Types for SQL Server 2016"
Visible="yes"
InstallCondition="(SMOVersionx86 <> "13.0.1601.5") AND (SMOVersionx64 <> "13.0.1601.5")"
SuppressSignatureVerification="yes"/>
<MsiPackage SourceFile="$(var.DependenciesPath_Microsoft)\Shared Management Objects\v13.0.1601.5\SharedManagementObjects.msi"
Id="SQLSMO"
After="SQLCLR"
DisplayName="Shared Management Objects for SQL Server 2016"
Visible="yes"
InstallCondition="(SMOVersionx86 <> "13.0.1601.5") AND (SMOVersionx64 <> "13.0.1601.5")"
SuppressSignatureVerification="yes"/>
But my problem is that when this is run on a 64bit PC (where SMO is already installed) - the InstallCondition evaluates to FALSE and as the WiX documentation clearly states; if it evaluates to FALSE, then the product is UNINSTALLED - which is obviously not what I want.
There is never a situation that I want the InstallCondition to evaluate to FALSE i.e. I never should uninstall SMO (if for no other reason, than the user may have installed that for a different app to use i.e. not one of ours). I could remove the installCondition and then it would just install the x86 version on both 32bit and 64bit machines - which is fine; but a bit naff.
So, what I want to do in pseudo code, is something like:
If SMOVersionx86 <> "13.0.1601.5" AND SMOVersionx64 <> "13.0.1601.5"
then
call the MSIPackages (with no installcondition or always TRUE) to INSTALL
else
don't call the MSIPackages"
Endif
i.e. if the condition results in FALSE; then I don't want to do anything ... certainly don't want to call the MSIPackages with FALSE, as this would UNINSTALL them.
I've racked my brain with all sorts of ideas, but they all in some circumstances end up with the installCondition evaluating to false and so end up removing SMO. I thought that perhaps I could have some logic that does install the x64 version on a 64bit PC and the x86 version on a 32bit PC, but even then at least one of the MSIPackage's installcondition would evaluate to false and end up removing a version !!
Any help on this would be gratefully received !!
Cheers,
Big Chris.

MsiPackage has a property Permanent that declares whether a package can be uninstalled. Setting it to "yes" will prevent the bundle from attempting to uninstall it.
However, I'm not sure if adding that property to a new bundle will prevent the old bundle from uninstalling it.

Related

Wix Toolset checking against .Net Version always evaluates to false

I'm currently working on a bundle installer using Wix Toolset. The bundle contains three parts, one being an apropriate .Net Runtime.
I'm using the following query to get the currently installed .Net version:
<util:RegistrySearch
Root="HKLM"
Key="SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedhost"
Value="Version"
Win64="yes"
Variable="NetVersion"
/>
I then use the variable in the following ExePackage
<ExePackage SourceFile="D:\somepath\windowsdesktop-runtime-5.0.7-win-x64.exe"
Vital="no"
PerMachine="yes"
InstallCondition="VersionNT64 AND NetVersion < v5.0.7"/>
The second part of the statement: NetVersion < v5.0.7 always evaluates to false. It doesn't matter if I compare against, say version 4.0 or 7.0, the installer wont show up in the process.
Maybe the problem has something to do with the installed dotnet version on my computer, which happen to be 6.0.0-preview.5.21301.5.
I'm using an other query where I check against a more conservative version number (major.minor.build.revision) which works just fine.
Im using Wix toolset 3.11.
Any help is appreciated!
Have you tried removing the "v" in the compare-expression?

How to prevent the WIX condition detecting when the installer is running in the uninstalling process?

I am learning windows installer xml(WIX) and have a condition in my code which checks if software-A is installed before my software is installed.
I want my software could be installed when software-A has been installed but when I am uninstalling my software, this condition should not be triggered.
The bundle works fine when it is in the installing process, that means if software-A has already been installed, it will continue to install. But if software-A has not been installed at all, the bundle will trigger condition checking process, show the condition message and stop installing. I have tried two conditions "NOT Installed" and "Installed", but the condition checking process is still triggered all the time even it is in uninstalling process. That means whatever the process is the bundle always searches the same registry.
The logic of the installer is simple enough but I am a beginner on Windows installer xml technology.
<bal:Condition Message="Software-A is Required.">
<![CDATA[NOT Installed OR SoftwareAInstalled]]>
</bal:Condition>
<util:RegistrySearch Id="SoftwareAInstalled"
Root="HKLM"
Key="SOFTWARE\SoftwareA\"
Variable="SoftwareAInstalled"
Result="exists" />
I wanna know how to prevent the checking process when the installer is in the uninstalling. Or any other suggestions would be appreciate.
Since you want to detect the state of your Bundle you should have a look at Burn Built-in Variables. WixBundleInstalled will give you the installation state of the current Bundle. Therefore
WixBundleInstalled OR SoftwareAInstalled
Will allow the installer to continue if either the current bundle is already installed or if you are performing a fresh install of the bundle and Software A is already present.
I don't have time to verify this right now, but it looks like you can use:
Installed OR SoftwareAInstalled
I would uppercase the latter property, but then it can be set at the command line. I guess it should work with what you have. Can't test right now. Tip: Remember to test in silent installation mode, in modify, repair, self-repair, uninstall, major upgrade, etc... Lots to check.
Some previous answers on similar issues:
Check if prerequisites are installed before installing a windows installer package
How to add new framework version in Installment Requirement page of InstallShield?
WIX Installer: Prevent installation on Windows Server 2012 R2 (on how to debug conditions)
Comment on the OR Installed construct

Wix per user installer to detect the Visual C++ 2015 Redistributable

I am creating an .msi installer which has to determine whether the Visual C++ 2015 Redistributable is present in the system and if not then interrupt the installation with custom message. The official Wix documentation refers to the actual installation of the VC++ which I do not wish to do as my installer is "per user" , There are couple of others stackoverflow questions which refer to the bundle rather than the .msi http://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/install_vcredist.html.
Wix Burn vcredist, WIX check if VS2015 C++ redistributable is installed , https://gist.github.com/nathancorvussolis/6852ba282647aeb0c5c00e742e28eb48
So I guess the question is, how to efficiently detect the presense of Visual C++ 2015 Redistributable in the per user installer.
The latest supported Visual C++ downloads
Runtime Detection Approaches
I can find a few ways to detect the presence of the Visual C++ Runtime.
Registry
Keys used by the VCRedist installer (see section below)
Keys mentioned in my original answer - grabbed from this old answer
File Presence & Version Check
Check for presence of core runtime files
See separate section below
MSI API
You can detect whether a specific MSI is installed by looking up the product GUID
Reliable, but hard to keep track of all product GUIDs (different versions)
UPDATE: You can also use the upgrade code as described below. It should remain stable across releases and updates (for each major version and potentially between major versions as well).
Fall-Over EXE?
Suggestions are seen to use an EXE depending on the runtime
Launching it and failing means the runtime is not there or broken
Good & Bad - Evaluation: Option 1 seems to be vulnerable since the merge module variant of deploying the runtime might not write these keys. Option 3 might work well, but it is hard to keep track of all GUIDs. Option 4 seems to already have failed based on the newer runtimes removing certain registry keys. Though fixed now, this could resurface.
File Version Presence / Version Check
The more I look at this, the more I start to think that you have to check for the actual files themselves, and potentially for the right file version. The file vcruntime140.dll in the System32 folder (64-bit version) and SysWOW64 folder (32-bit version)? See files list towards bottom here.
Just adding a link for safe-keeping.
How to detect the presence of the Visual C++ 2012 redistributable package?
Redistributing Visual C++ Files
A test VBScript - for test purposes only (scripts are sometimes blocked by anti-virus):
Set fso = CreateObject("Scripting.FileSystemObject")
MsgBox fso.GetFileVersion("C:\Windows\System32\vcruntime140.dll")
You can detect file presence and version using AppSearch in an MSI file.
Below are some other stuff I wrote up, just leaving it in.
VCRedist
It seems the Visual C++ Redistributable Packages (VCRedist_x86.exe, VCRedist_x64.exe) - which is the recommende way to deploy the runtime - checks the following registry key to determine what versions of the runtime is actually installed:
HKLM\SOFTWARE\Microsoft\VisualStudio\<version>\VC\Runtimes\
The sub-keys x86 and x64 seem to all contain an "Installed" value that is set to 1 when the runtime is installed. I would assume - without having had time to test it all - that you then can check:
HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64 Installed = 1
HKLM\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x86 Installed = 1
Merge Module: After a brief check, it looks like these values are not written by the merge modules that can also be used to distribute this runtime. I do not have the time or means to check this properly now.
Astonishingly both version 2015 and version 2017 of the runtime write to the 14.0 key - since they are binary compatible. If the 2017 version is installed, then the VCRedist executable will return an error since no install is needed. Weird indeed. But for your purpose that should be besides the point. Source.
MSI API - Retrieve Product Codes
Mailbag: How to detect the presence of the VC 8.0 runtime redistributable package
Updated VC 8.0 runtime redistributable packages are included in Visual Studio 2005 SP1
How can I find the product GUID of an installed MSI setup?
UPDATE: installer.ProductState - normal installed state is 5:
I forgot about the ProductState property when writing the below.
You can check for an installed product with two lines of code if you
have the actual product code:
Dim installer : Set installer = CreateObject("WindowsInstaller.Installer")
MsgBox installer.ProductState("{00000000-0000-0000-0000-000000000001}")
Here is even one more way to do it: MSDN: How to programmatically check for the presence of a Windows Installer-based product by using its product code.
Tip: I wouldn't use this approach seeing as the product code changes frequently when products are updated. Hence I like better to
check for file versions of core-runtime files. This seems more
reliable for the future (provided version parsing is done correctly
and reliably - don't roll your own).
Mockup:
Public installer
Set installer = CreateObject("WindowsInstaller.Installer")
' Don't have the 2015 GUID
VC2015 = CheckForProductCode("{00000000-0000-0000-0000-000000000000}")
VC2017 = CheckForProductCode("{C77195A4-CEB8-38EE-BDD6-C46CB459EF6E}")
MsgBox "VC2015: " & CStr(VC2015) & vbCrLf & "VC2017: " & CStr(VC2017)
Function CheckForProductCode(productcode)
CheckForProductCode = False
For Each product In installer.ProductsEx("", "", 7)
If(LCase(productcode) = LCase(product.ProductCode)) Then
CheckForProductCode = True
Exit For
End If
Next
End Function
Update based on Zett42's suggestion to enumerate products sharing the same upgrade code:
Set installer = CreateObject("WindowsInstaller.Installer")
' Enumerate all products related to "Microsoft Visual C++ 2008 Redistributable - x86 9.0.30729.4148"
' {AA783A14-A7A3-3D33-95F0-9A351D530011} is the upgrade code
Set upgrades = installer.RelatedProducts("{AA783A14-A7A3-3D33-95F0-9A351D530011}")
For Each u In upgrades
MsgBox u, vbOKOnly, "Product Code: "
Next
Deploying The Visual Studio C++ Runtime
Beyond detection, there are several approaches for distributing the Visual Studio C++ Runtime:
Static Linking
Visual C++ Redistributable Packages
VCRedist_x86.exe, VCRedist_x64.exe, or VCRedist_arm.exe
Program Files(x86)\Microsoft Visual Studio\2017\edition\VC\Redist\MSVC\lib-version
Redistributable Merge Modules (.msm files)
Insufficient for some purposes (The universal CRT):
Redistributables for deploying C++ exe developed with Visual Studio 2015 on Windows 7
WIX merge c++ runtime
Local Application Folder
Copy DLLs to the local application folder
Not recommended for servicing reasons (updates, security fixes)
Links For Safe Keeping:
Redistributables for deploying C++ exe developed with Visual Studio 2015 on Windows 7
How to detect whether I need to install VCRedist?
WIX merge c++ runtime
How to detect if Visual C++ 2017 Redistributable is installed
Old Answer
There is this old post. I am not too fond of direct registry reads, let me see if I can find a more reliable way, but maybe have a look in the mean time: Detect if Visual C++ Redistributable for Visual Studio 2012 is installed
Just one more link, how to find the Windows Installer product code of products that are installed: How can I find the product GUID of an installed MSI setup?
You can use LaunchConditions from WiX Toolset. The detection can be done by RegistrySearch.
Until version 2015 it was just a registry key, GUID value. Since 2017 and still in 2019 the key is concat (merged), so it is not that easy anymore. This is way I used a loop from 21 to 40 to find all places. C++ Runtime Documentation.
Add the following lines to your product.wxs inside the Product elemnent:
...
<!-- Visual C++ Redistributable 2015, 2017 and 2019 (x86) -->
<Property Id="CPPRUNTIME2015X86" Secure="yes">
<!-- C++ 2015 -->
<RegistrySearch Id="mfc140x86_23026" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{74d0e5db-b326-4dae-a6b2-445b9de1836e}" Type="raw" />
<RegistrySearch Id="mfc140x86_24215" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{e2803110-78b3-4664-a479-3611a381656a}" Type="raw" />
<!-- C++ 2017 -->
<RegistrySearch Id="mfc1416x86" Root="HKCR" Key="Installer\Dependencies\VC,redist.x86,x86,14.16,bundle" Type="raw" />
<!-- C++ 2019 -->
<?foreach CPPRUNTIMEVERSIONPREFIX in 21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40?>
<RegistrySearch Id="mfc14$(var.CPPRUNTIMEVERSIONPREFIX)x86" Root="HKCR" Key="Installer\Dependencies\VC,redist.x86,x86,14.$(var.CPPRUNTIMEVERSIONPREFIX),bundle" Type="raw" />
<?endforeach ?>
</Property>
<Condition Message="Microsoft Visual C++ 2015-2019 (x86) Redistributable missing">
<![CDATA[((REMOVE="ALL")) OR Installed]]>
</Condition>
<!-- Visual C++ Redistributable 2015, 2017 and 2019 (x64) -->
<?if $(var.Platform) = x64 ?>
<Property Id="CPPRUNTIME2015X64" Secure="yes">
<!-- C++ 2015 -->
<RegistrySearch Id="mfc140x64_23026" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{e46eca4f-393b-40df-9f49-076faf788d83}" Type="raw" />
<RegistrySearch Id="mfc140x64_24215" Root="HKLM" Key="SOFTWARE\Classes\Installer\Dependencies\{d992c12e-cab2-426f-bde3-fb8c53950b0d}" Type="raw" />
<!-- C++ 2017 -->
<RegistrySearch Id="mfc1416x64" Root="HKCR" Key="Installer\Dependencies\VC,redist.x64,amd64,14.16,bundle" Type="raw" />
<!-- C++ 2019 -->
<?foreach CPPRUNTIMEVERSIONPREFIX in 21;22;23;24;25;26;27;28;29;30;31;32;33;34;35;36;37;38;39;40?>
<RegistrySearch Id="mfc14$(var.CPPRUNTIMEVERSIONPREFIX)x64" Root="HKCR" Key="Installer\Dependencies\VC,redist.x64,amd64,14.$(var.CPPRUNTIMEVERSIONPREFIX),bundle" Type="raw" />
<?endforeach ?>
</Property>
<Condition Message="Microsoft Visual C++ 2015-2019 (x64) Redistributable missing">
<![CDATA[((REMOVE="ALL")) OR Installed]]>
</Condition>
<?endif ?>
...

Wix Toolset CustomAction for determining if the OS is windows 7/xp home edition or starter edition and display message for not

I am new to Wix Toolset installation. I am using Wix 3.7 and Visual Studio 2010 SP1.
I was going through a tutorial which uses BootStrapper in which there is a conditional Message in Product.Wxs file for checking .NET framework 4.0 is installed there is a PropertyRef Id variable and Condition Message
<PropertyRef Id="NETFRAMEWORK40FULL"/>
<Condition Message="This application requires .NET Framework 4.0. Please install the .NET Framework then run
this installer again.">
<![CDATA[Installed OR NETFRAMEWORK40FULL]]>
</Condition>
How can one similarly check for Condition for Windows XP Starter/Home and Windows 7 Starter/Home/Home Premium editions and show conditional message that the installation does not support the OS listed and require Professional Editions.
I have gone through the links on Wixtoolset website, but it didn't help:
Checking Windows Versions
http://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/block_install_on_os.html
http://msdn.microsoft.com/library/aa370556.aspx
I have also tried to place the condition in the bootstrapper's Bundle.wxs file as:
<Bundle Name="!(loc.ProductName)" Compressed="yes" Version="1.2.6.0"
SplashScreenSourceFile="Resources\SplashScreen.bmp" IconSourceFile="Resources\IXMWeb.ico" Manufacturer="!
(loc.ManufacturerName)" UpgradeCode="FED377E5-8762-48C4-B123-8D4AD89B0222" Condition="((VersionNT >= v5.1) AND
(ServicePackLevel >= 3) AND NOT(NTSuitePersonal)) OR ((VersionNT >= v5.2) AND (ServicePackLevel >= 2)) OR
(VersionNT >= v6.0 AND NOT(NTSuitePersonal))">
I have gone through the post that I need to use NTSuitePersonal instead of MsiNTSuitePersonal for checking if the edition is Home edition which is being installed.
Please let me know where i am not correct in the above condition used.
For checking the Windows version (i.e. Windows XP, Vista, 7, 8, ...) you can use the VersionNT-property as described in the links provided by you. For checking the edition (i.e. Home, Premium, Professional, ...), according to this SO-question, you can use the values below the registry hive HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion.
In combination with WiX you could do a registry search that sets a property and then use this property in your condition (I could verify the exact registry key only on Windows 7 Professional where it is names EditionID):
...
<Property Id="WINDOWSEDITION" Secure="yes">
<RegistrySearch Id="WindowsEditionReg" Root="HKLM" Key="SOFTWARE\Microsoft\Windows NT\CurrentVersion" Name="EditionID" Type="raw" />
</Property>
...
See also How to: Read a registry entry during installation.
Edit: Using the properties named in your link Operating System Property Values and the How To: Read a Registry Entry During Installation and the How To: Block Installation Based on OS Version, an example of checking if the user has Windows 7 Professional with Service Pack 1 installed and deny the installation on everything else would then be (put it inside the Product-tag):
<Condition Message="This application can only be installed on Windows 7 Professional with Service Pack 1.">
<![CDATA[Installed OR (VersionNT = 601 AND WindowsBuild > 7100 AND WINDOWSEDITION ~= "Professional")]]>
</Condition>
The Installed-property on the beginning of the condition ensures that the condition is only validated if the product isn't already installed. Within the parenthesis we then find the other conditional elements. We ensure that we run on Windows 7 (VersionNT = 601 AND WindowsBuild > 7100) and that the edition is correct (WINDOWSEDITION ~= "Professional"). Note that the ~= checks the string case insensitive.
For the syntax of the conditional statements you can take a look here. You can of course combine any additional conditions using OR, AND and grouping them with parentheses where appropriate. In a real world scenario you would most probably have another condition, like Windows 7 and higher versions.

Should the WIX UpgradeCode be different between an x64 and an x86 version of a product?

I'm assuming YES.
I have an installer for the x86 version, I'm building the installer for the x64 version.
I'm trying to figure out the gotchas. So far it looks pretty straightforward. I feel like they ought to be different UpgradeCodes. I'll need to check for the VersionNT64 property within a Condition to validate the install.
<Condition Message="This package can be installed only on an x64 version of Windows.">
VersionNT64
</Condition>
Any other hints?
I would go ahead and say that yes you should probably have different upgrade codes for the two installers unless you have a specific need for them to be the same.
One scenario I can think of where matching upgrade codes may be useful is if you have previously shipped only a x86 version that was able to install on both 32-bit and 64-bit operating systems. In such a case, having a x64 version with the same upgrade code would allow you to easily handle upgrades from the older x86 to the new x64 version on 64-bit operating systems.
Edit:
I forgot to mention that you can also use the "Msix64" property to determine bitness. It's also unnecessary to add an install condition to the x64 version, since attempting to install it in a 32-bit OS will result in an error message from the Windows Installer service.
Currently I only use one condition in the x86 version of the installer
<Condition Message="You are attempting to install the x86 version in a 64-bit OS">NOT Msix64</Condition>
From my recall I can say that the code for the x86 version would be based on the code used on some 80x86 and later processors for the 16 to 32bit versions while the x64 would be different code for the 64 bit implementation asssuming it is x64 and not IA-64 or Intel64 code. The loader would be different and you may need to check the status of values in the BIOS (if present) and some environment variables to determine if 32 or 64bit mode is in operation on a computer, including the one you are developing the loader on if it supports one or both of the modes.