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

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 ?>
...

Related

WIX merge c++ runtime

I have merged msm for vs 2015 crt:
<DirectoryRef Id="TARGETDIR" >
<Merge Id = "Microsoft_VC140_CRT_x64.msm" FileCompression = "yes" Language = "1033" SourceFile = "..\\..\\..\\..\\..\\..\\..\\external\\tools\\systemsetups\\merge_modules\\Microsoft_VC140_CRT_x64.msm" DiskId = "1" />"
</DirectoryRef>
<Feature>
<Feature Id="Complete" Title="Complete" Absent="allow" Level="1">
...
<MergeRef Id="Microsoft_VC140_CRT_x64.msm"/>
...
</Feature>
but I still receiving:
---------------------------
MyApp.exe - System Error
---------------------------
The program can't start because mfc140u.dll is missing from your computer. Try reinstalling the program to fix this problem.
---------------------------
OK
---------------------------
Any ideas how to merge it properly?
It seems Microsoft recommends you use one of the redist executables (vcredist_x86.exe, vcredist_x64.exe) instead of the merge modules (see towards bottom) these days since the 2015 versions of the runtimes are more complicated than before and the merge modules are basically insufficient: "There will not be a merge module for the Universal CRT".
UPDATE: How to install the exe binary VCRedist along with your MSI?.
This is fully explained by Bob Arnson in this answer: Redistributables for deploying C++ exe developed with Visual Studio 2015 on Windows 7 (must read link - it is the answer, I just added this one to throw in some further links as well).
Also, I don't have the list of merge modules in front of me on this Linux box, but perhaps there is an x86 version that you need - you are installing the x64 version? Just checking - these bitness issues are driving us all crazy.
Some Links:
Redistributing Visual C++ Files
Walkthrough: Deploying a Visual C++ Application By Using the Visual C++ Redistributable Package
Redistributing Components By Using Merge Modules

Working sample for installing a VSIX extension for VS2017 using WixToolset?

I'm trying to install a VSIX Extension into Visual Studio 2017 from a Setup created with the WIX Toolset.
I found this page:
http://wixtoolset.org/development/wips/5433-add-support-to-detect-and-install-vsix-packages-into-vs15/
But its not fully clear whether the "proposals" on that page were really implemented. I did a number of experiments, with no success.
Have the features posposed on the above page been implemented in WiX v3.11.1?
There seems to be some support for VS2017 in WIX v3.11.1, but when I use the VSExtension:VsixPackage element in my Product.wxs file, it seems that the latest VSIXInstaller.exe (of my VS2017 community) isn't found and my Setup Fails.
Can someone provide a working example for this?
Thanks!
I can only tell you my strategy for IsWiX:
https://github.com/iswix-llc/iswix
iswix/Source/Application/IsWiXNewAddIn/
iswix/Source/Installer/IsWiXNewAddInMM/
iswix/Source/Installer/IsWiX/Code/Product.wxs
I'm able to install to VS2013-2017 this way.
What is IsWiX?
https://github.com/iswix-llc/iswix-tutorials
PS- 60 min complimentary dev to dev screenshares are available.
Installing extensions in Visual Studio versions newer than 2015 is not supported by WiX, despite it being able to detect VS 2017 and VS 2019 instances … which is kind of odd to be honest.
There is still the option of using the detected installations to launch the installer, though that might break unexpectedly.
You can try this:
Add WiX VSExtension (obviously):
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:VSExtension="http://schemas.microsoft.com/wix/VSExtension">
Define and set a property containing the VSIX Installer location (done here for Visual Studio 2019):
<Property Id="Vs16VsixInstaller" Value="0" />
<SetProperty Action="SetVs16VsixInstaller" Id="Vs16VsixInstaller" Value="[VS2019_IDE_DIR]VSIXInstaller.exe" Sequence="both" After="AppSearch" />
Define a component containing the extension:
<Component Id="MyVSExtension" Directory="VisualStudioExtensionsFolder" Guid="PUT-GUID-HERE">
<RegistryValue KeyPath="yes" Root="HKMU" Key="Software\[Manufacturer]\[ProductName]" Name="MyVSExtensionInstalled" Type="string" Value="" />
<VSExtension:VsixPackage File="MyVSExtension.vsix" PackageId="PUT-PACKAGE-ID-HERE" VsixInstallerPathProperty="Vs16VsixInstaller" />
</Component>
This worked in my case and I don't think there is a much better way of doing this while also keeping the solution simple.

WIX Conditional Message on Exit

I have a WIX installer which I need to also install the VC++ 2015 runtime executable. I'm using the vcredist_x64.exe as opposed to the merge modules (see this thread). I can successfully launch the vcredist_x64.exe after my msi finishes installing my application by using a custom action... however, what I'd like to do is first check to see if the runtime files already exist. If they do, then I'll just finish without running the vcredist_x64.exe. Otherwise, I'll run the custom action to install the runtimes as well.
It took some digging, but I was able to find out that the 2015 runtimes have a registry key shown below:
HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64
with an Installed value of 1 if they exist.
So, in my .wxs file I have the following registry search:
<!-- Visual C++ 2015 x64 -->
<Property Id="VCREDISTRUNTIMES2015INSTALLED">
<RegistrySearch Id="VCREDISTRUNTIMES2015SEARCH" Root="HKLM" Key="SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" Name="Installed" Type="raw" />
</Property>
Now, what I'd like to do is show a message on my exit dialog which says that if the runtimes aren't detected, then it will launch an installer to install them upon exit. Something like this:
<Property Id="WIXUI_EXITDIALOGOPTIONALTEXT" Value="Visual C++ Redistributable for Visual Studio 2015 is Required. Installation will proceed on exit.">
<Condition>VCREDISTRUNTIMES2015INSTALLED</Condition>
</Property>
However, this doesn't work. I get an error on the conditional tag and the project wont build. Assuming my registry search is setup correctly, can someone tell me how to properly add a conditional message on my exit dialog? Thanks!
Answering my own question... but here goes. It turns out that my registry search was just fine... but I needed to use "SetProperty" instead. So, something like this:
<SetProperty Id="WIXUI_EXITDIALOGOPTIONALTEXT" After="AppSearch" Value="The Visual C++ Redistributable Package for Visual Studio 2015 is Required. Installation will now install run-time components that are required to run C++ applications built using Visual Studio 2015.">
NOT VCREDISTRUNTIMES2015INSTALLED
</SetProperty>
Now, if the VCREDISTRUNTIMES2015INSTALLED is null (or false) then it will show the message on the exit dialog. Otherwise, there will be no message shown. Hope that helps.

Unable to check registry for Visual C++ 2008 redistributables using WIX Bootsraper

I am able to check registry for Visual C++ 2010 redistributables using WIX Bootsrapper using below code
util:RegistrySearch Id="VC2010x86" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\10.0\VC\VCRedist\x86" Variable="CPP2010Redistx86" Value="Installed" Result="value"
But I do not know how I need check registry for Visual C++ 2008 redistributables using WIX Bootsraper.
What will be the key value I need to use for VC++ 2008 instead of Key="SOFTWARE\Microsoft\VisualStudio\10.0\VC\VCRedist\x86" which I uses for VC++ 2010.
Kindly suggest.
This seems to cover the ProductCodes you can search for:
http://blogs.msdn.com/b/astebner/archive/2009/01/29/9384143.aspx
or use them to look at the uninstall registry key.
or use them as dependencies. To be honest, I usually don't bother. I assume you have a redist exe that you are distributing anyway, so just run that exe and it will do the right thing. The issue is that MS might ship a security fix package at any time and you cannot predict the key value or ProductCodes, plus you need to woyry about the architecture etc. Is there a reason you can't just run the redist and let it discover whether it needs to be installed or not?

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.