Due to user confusion, our app requires separate installers for 32-bit and 64-bit versions of Windows. While the 32-bit installer runs fine on win64, it has the potential to create support headaches and we would like to prevent this from happening.
I want to prevent the 32-bit MSI installer from running on 64-bit Windows machines. To that end I have the following condition:
<Condition Message="You are attempting to run the 32-bit installer on a 64-bit version of Windows.">
<![CDATA[Msix64 AND (NOT Win64)]]>
</Condition>
With the Win64 defined like this:
<?if $(var.Platform) = "x64"?>
<?define PlatformString = "64-bit"?>
<?define Win64 ?>
<?else?>
<?define PlatformString = "32-bit"?>
<?endif?>
Thing is, I can't get this check to work right. Either it fires all the time, or none of the time. The goal is to check presence of the run-time msix64 variable against the compile-time Win64 variable and throw an error if these don't line up, but the logic is not working how I intend it to. Has anyone come up with a better solution?
Include the Condition element only in your 32-bit package (i.e., using ?if? preprocessor statement). The Condition would be "NOT Msix64": Launch conditions are things that must be true, so if Msix64 is set, the launch condition would fail and that means it's an x64 OS and a 32-bit package and the correct thing to do is to block.
We use the following...
<?if $(var.ProcessorArchitecture)=x86 ?>
<Condition Message="!(loc.LaunchCondition_Error64)">
<![CDATA[Installed OR Not VersionNT64]]>
</Condition>
<?endif?>
The condition element works with windows installer properties, which exist during an installation.
However, you are defining a Win64 as a wix variable, not a windows installer property. Wix variables only exist while the setup is created. You have to reference them as $(var.MyWixVariable) where you use them, and the wix preprocessor will then replace them with their defined value.
I would try this instead:
<?if $(var.Platform) = "x64"?>
<?define PlatformString = "64-bit"?>
<Property Id="Win64" Value="1" />
<?else?>
<?define PlatformString = "32-bit"?>
<?endif?>
If $(var.Platform) has the right value when the setup is created, then this will cause a "Win64" property to be recorded in the windows installer database (i.e. the MSI file) and the property will be available during installation for use in a condition element.
Add this condition
<Condition Message="This is designed for 32bit OS">%PROCESSOR_ARCHITECTURE ~= "x86" AND %PROCESSOR_ARCHITEW6432 <> "amd64"></Condition>
You could create one installer with a 32bit component and a 64bit component and put these two conditions in the respective components
<Component Id="bit32Component" Guid="..." Feature="ProductFeature">
<Condition>%PROCESSOR_ARCHITECTURE~="x86" AND %PROCESSOR_ARCHITEW6432<>"amd64"></Condition>
</Component>
<Component Id="bit64Component" Guid="..." Feature="ProductFeature">
<Condition>%PROCESSOR_ARCHITECTURE~="amd64" OR %PROCESSOR_ARCHITEW6432~="amd64"></Condition>
</Component>
here is a reference I used
http://blogs.msdn.com/david.wang/archive/2006/03/26/HOWTO-Detect-Process-Bitness.aspx
Related
I have multiple WiX installer projects in my solution. And all of the installers in the main wxs file under the Product element have the same list of launch conditions, such as privileges, OS version on the target machine, whether NET48 framework is installed or not, etc.
<Condition Message="You need to be an administrator to install this product.">
Privileged
</Condition>
<Condition Message='This product is designed to be installed on Windows 7 or higher Windows Operating System'>
<![CDATA[VersionNT64 >= 601]]>
</Condition>
<PropertyRef Id='WIXNETFX4RELEASEINSTALLED'/>
<Condition Message='This setup requires the .NET Framework 4.8 to be installed.'>
<![CDATA[Installed OR (WIXNETFX4RELEASEINSTALLED >= "#528040")]]>
</Condition>
So I was wondering if it is possible to have these conditions moved into a shared common project in my solution that I can refer to that project from all my installer projects and inject the conditions from that shared place?
If you are only sharing those Launch Conditions then consider using an include file. The conditions will be compiled separately for each project, rather than once, but then you can include them easily without needing to create a shared wixlib.
<?xml version="1.0" encoding="UTF-8"?>
<!-- LaunchConditions.wxi -->
<Include>
<Condition Message="You need to be an administrator to install this product.">
Privileged
</Condition>
<Condition Message='This product is designed to be installed on Windows 7 or higher Windows Operating System'>
<![CDATA[VersionNT64 >= 601]]>
</Condition>
<PropertyRef Id='WIXNETFX4RELEASEINSTALLED'/>
<Condition Message='This setup requires the .NET Framework 4.8 to be installed.'>
<![CDATA[Installed OR (WIXNETFX4RELEASEINSTALLED >= "#528040")]]>
</Condition>
</Include>
then in each project include it with
<?include LaunchConditions.wxi?>
WiX doesn't have a ConditionRef element. It's a shame cause it probably should. However fragements are consumed atomically so if you put them in with something else like a Property you could ref the property and get it all together.
I don't know how clean you want your Condition table but another concept is just put all your conditions in one fragement and share them with all your projects. Then for each condition you an enabling property. This way the condition only evaluates if the property is set.
Here's something I wrote 16 years ago.... I'd be happy to assist if you want to do something like this.
http://blog.iswix.com/2006/07/short-comings-of-launchconditions.html
I have a large .wxs file and a requirement to change the Product Name from MyApp Workstation version to MyApp Server version when the installer is launched on a Windows Server operating system. I read a lot and I found out that I need to relate to the following MSI property MsiNTProductType which returns the value 1 for workstation and 2 or 3 for server operating systems.
<?xml version="1.0" encoding="UTF-8"?>
<?define ProductName = "MyApp Workstation version" ?>
<Wix>
<Product Name="$(var.ProductName)" ... >
...
</Product>
</Wix>
If I use a condition like the following, it detects that I am on a workstation operating system (in my case Windows 10), but this is not what I want:
<Condition Message="This installer is for Windows Server only!">
<![CDATA[(MsiNTProductType > 1)]]>
</Condition>
I tried the preprocessor variable approach, but evidently it does not work:
<?if MsiNTProductType = 1 ?>
<?define ProductName = "MyApp Workstation version" ?>
<?else ?>
<?define ProductName = "MyApp Server version" ?>
<?endif ?>
I also tried SetProperty and CustomAction without any luck. I am stuck at this.
Is it possible to do something like this? What changes do I have to make to accomplish this? I am very new to WIX and I do not understand how it is supposed to work. Thank you.
LATER EDIT AND SOLUTION
After many days of trying tens of combinations, I was able to do this with the following code which I put before any CustomActions that were already defined:
<Property Id="ConditionalProductName" Value="MyApp Workstation version" />
<SetProperty Id="ConditionalProductName" Before="LaunchConditions" Value="MyApp Server version" >
<![CDATA[(MsiNTProductType > 1)]]>
</SetProperty>
<SetProperty Id ="ProductName" Before ="LaunchConditions" Value ="[ConditionalProductName]"/>
I hope this may help others too. Good luck.
The ProductName property can only be changed prior to the installation starting. Once MSI is running it's immutable.
The preprocessor statements you are trying effect build time not installation time.
The only way to do what you want is create a transform with the different name and then use a bootstrapper to install the MSI without a transform on a non server OS and install the MSI with a transform on a server OS.
Personally this isn't typical practice so I would push back on the requirement from the PM. The only reason I would go through the effort would be the business really really differentiated the branding on the marketing side. Otherwise this just isn't normally done.
We are using Wix V3.11 to build an msi setup for our C#-Application. We have a 32 Bit and an 64 Bit build for each version:
The preallocated installation path for the 32 Bit build is: 'C:\Program Files (x86)'.
The preallocated installation path for the 64 Bit setup is: 'C:\Program Files'.
We use the following declaration to set the paths:
<?define bitness = $(var.Platform) ?>
<?if $(var.Platform) = "x86" ?>
<?define ProgramFilesPath = ProgramFilesFolder?>
<?define Win64 = no?>
<?else?>
<?define ProgramFilesPath = ProgramFiles64Folder?>
<?define Win64 = yes?>
<?endif?>
Using the variable here:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="$(var.ProgramFilesPath)" Name="$(var.ProgramFilesPath)">
[... more folder ...]
</Directory>
</Directory>
The Problem: When the consumer changes the path for the first installation to e.g. 'C:/MyFolder' and executes an updade, then the msi setup moves the software to 'C:\Program Files (x86)' or 'C:\Program Files'.
Question: How can I keep the custom installation path 'C:/MyFolder' of the first installation on an update? Perhaps is there any 'update' condition I can use? My imagination:
<?define bitness = $(var.Platform) ?>
<?if UPDATE ?> <!-- here -->
<?if $(var.Platform) = "x86" ?>
<?define ProgramFilesPath = ProgramFilesFolder?>
<?define Win64 = no?>
<?else?>
<?define ProgramFilesPath = ProgramFiles64Folder?>
<?define Win64 = yes?>
<?endif?>
<?endif?>
Let's assume that by "update" you mean a major upgrade because it would be extremely strange if a patch or minor update moved all the files from one location to another.
If you are using the usual WiX property name, then the directory chosen by the user will be INSTALLFOLDER, although it's not clear if you are using that or TARGETDIR. Basically you can store the user's final choice in a registry item (or use the WiX "remember property" pattern). On a major upgrade you can retrieve that property and set your INSTALLFOLDER value to that directory on condition that WIX_UPGRADE_DETECTED is set (the default property id used in the major upgrade element). Presumably you will also suppress the browse dialog that allows the user to choose the folder.
Having said that:
It's not clear why you don't want the user to install the major upgrade to a new location if they want to, if the app continues to work.
Those default folders aren't really preallocated - they are just the standard defaults. They are the recommended locations so it might be better to disallow changing them because of the opportunities for unexpected behavior. For example, attempting to install the 32-bit MSI to the native program files folder on an x64 system will result in redirection to the program files (x86) location, which may surprise the user.
Hey Guys hopefully somebody could help me with this problem. I am trying to compare the value of an Property with an String which is manually defined.
I am not sure if it should work like this here is my Code.
<Variable Name="VS2013Installed" />
<Variable Name="VS2015Installed" />
<!-- Should Search the Registry for the Keys -->
<!-- Searches for the Key of Visual Studio 2013 -->
<Property Id="VS2013" Secure="yes" >
<RegistrySearch Id="SEARCH_VS2010" Type="raw" Root="HKCR" Key="VisualStudio.accessor.12.0\shell\Open\ddeexec\Application" >
</RegistrySearch>
</Property>
<!-- Searches for the Key of Visual Studio 2015 -->
<Property Id="VS2015" Secure="yes" >
<RegistrySearch Id="SEARCH_VS2015" Type="raw" Root="HKCR" Key="VisualStudio.accessor.14.0\shell\Open\ddeexec\Application" >
</RegistrySearch>
</Property>
<!-- Should compare the value of the property with the String-->
<?if [VS2013] = "VisualStudio.12.0" ?>
<?define VS2013Installed= "1" ?>
<?else ?>
<?define VS2013Installed= "0" ?>
<?endif?>
<!-- Should compare the value of the property with the String-->
<?if [VS2015] = "VisualStudio.14.0" ?>
<?define VS2015Installed= "1" ?>
<?else ?>
<?define VS2015Installed= "0" ?>
<?endif?>
<!-- This Condition is only here to get an Message Window with the values of the variables-->
<Condition Message="$(var.VS2013Installed)$(var.VS2015Installed)">
<![CDATA[0 = 1 ]]>
</Condition>
As a result of the Condition I am getting: 0,0
WiX conditional compilation is for WiX variables. You want conditions on Windows Installer properties. It looks like you have a handle on setting properties and using property expressions; just make it all properties and conditions. (Your use of WiX define and var is fine, though.)
You are using the WiX Toolset to build a Windows Installer package (.msi file). A Windows Installer package is a relational database as you can see visually with tools like InstEd. The Windows Installer engine (msiexec) uses the database to perform standard and custom actions for an operation (install, repair, uninstall, etc) on a product. Actions can set, pass and retrieve data via Windows Installer properties. A property is usually referenced via its name in square brackets, e.g., [VS2015]. Except for WiX-provided custom action (they you might or might not use), everything that is happens at install-time is Windows Installer.
WiX variables are simply a way of avoiding repetition in your WiX source. They are "compiled away" when WiX builds the package. So, their values are fixed. WiX's conditional compilation (define, if, etc) are also compiled away at build-time.
In your code, it seems you expect to use a property value in a conditional compilation statement. Property values are not set until msiexec runs. So, you have find another way of using the information you are gathering with registry search. One way might be, if you have a Feature that supports VS2015 and another that supports 2013, is to enable or disable features based on expressions that reference the property values.
If you want to detect which versions of Visual Studio are installed then look at the WiX-supplied WixVSExtension and just use the properties it supplies.
http://wixtoolset.org/documentation/manual/v3/customactions/wixvsextension.html
and it does include VS 2013 and 2015.
What's up with my launch condition? It's supposed to prevent the x86 installer from running on a 64-bit system, but it seems to have no effect.
<!-- Launch Condition to check that x64 installer is used on x64 systems -->
<Condition Message="64-bit operating system was detected, please use the 64-bit installer.">
<![CDATA[VersionNT64 AND ($(var.Win64) = "no")]]>
</Condition>
var.Win64 is derived from the MSBuild variables like this:
<!-- Define platform-specific names and locations -->
<?if $(var.Platform) = x64 ?>
<?define ProductName = "$(var.InstallName) (x64)" ?>
<?define Win64 = "yes" ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?define PlatformCommonFilesFolder = "CommonFiles64Folder" ?>
<?else ?>
<?define ProductName = "$(var.InstallName) (x86)" ?>
<?define Win64 = "no" ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?define PlatformCommonFilesFolder = "CommonFilesFolder" ?>
<?endif ?>
I would like to solve my problem, but I'd also be interested to hear about strategies for troubleshooting this type of problem.
According to the LaunchCondition table definition:
Expression that must evaluate to True for installation to begin.
Your condition consists of 2 parts: the first one evaluates at install time, the other one evaluates at build time. So, for x86 package the second part of condition will evaluate to "no" = "no" at build time, which obviously gives True at install time. And the first part - VersionNT64 - is defined (and thus, True) on x64 machines. That's why the whole condition is True and installation starts.
You can rewrite your condition as follows:
<Condition Message="64-bit operating system was detected, please use the 64-bit installer.">
<?if $(var.Win64) = "yes" ?>
VersionNT64
<?else?>
NOT VersionNT64
<?endif?>
</Condition>
Hence, in 64-bit package the condition will be just VersionNT64, and will pass and start install. Form x86 package the condition will be NOT VersionNT64, which will obviously fail on 64-bit, but start on 32-bit.