MSI Properties Empty on Passive, Basic UI, and No UI Install - wix

I am working on an installer with a handful of custom actions that look for values in the following MSI properties:
ADDLOCAL
ADDDEFAULT
REINSTALL
REMOVE
When running the installer with the full UI, these properties contain lists of comma-separated feature names as appropriate given the options that are selected by the user. However, when I run the installer in passive mode or with only a basic UI (or no UI) from the command line, I find that the properties are empty/blank. It is only when I explicitly set them from the command line that they have any value. This is an acceptable work-around, but it would be much better if these could take on some kind of default, such as "all", without requiring values to be passed on the command line. Is there some way I can specify this in WiX (which I am using to build the MSI) or do I have to do something in the custom action code (or something else entirely)?
I have looked at the property reference here, but I did not see any mention of how one could specify default features for these properties.
What I also found interesting is if I do specify a feature this way on the command line during install, it seems to be stored for the uninstall in the REMOVE property (in other words, I do not have to pass any parameters when uninstalling in any mode). Is this a feature that I can rely on? Will it automatically update if someone modifies the installation later?
I'm running WiX 3.5.2519 and using Visual Studio 2010 with Visual C++ for the custom action code. Thanks for any help you can give me!
Edit:
Actually, I was wrong. It appears that the REMOVE property is always set to "all" when uninstalling and running in one of these modes, even if I pass a different value on the command line or only install a subset of features. This seems broken. Am I doing something wrong here?
C:\> msiexec REMOVE=FeatureName /passive /l* uninstall.log /x Product.msi
It will completely ignore what I specify for "FeatureName" and use "all" in its place.

Your custom actions probably shouldn't examine those properties. Instead they should examine the feature and/or component states of the product, depending on what they're trying to do. In conditional statement syntax, this looks like $component-action or &feature-action (where you use the name of the component or feature whose action you are trying to condition against). In C++ (for inside the custom action) this looks like MsiGetFeatureState or MsiGetComponentState, and these are of course made available through similar means in most other languages (such as session.FeatureRequestState / session.ComponentRequestState in a language you shouldn't use).

It is a recommended practice that all properties to be used by the installation be entered into the Property table with an initial value. The installer sets the properties to these values at the launch of the installation. Properties for which a blank is an acceptable value and properties built into the installer do not need to be initialized.
You can then change the default value programmatically or on the command line as described here: Using Properties.

Related

Is is possible to pass a variable from the build process to Visual Basic code?

My goal is to create build definitions within Visual Studio Team Services for both test and production environments. I need to update 2 variables in my code which determine which database and which blob storage the environment uses. Up till now, I've juggled this value in a Resource variable, and pulled that value in code from My.Resources.DB for a library, and Microsoft.Azure.CloudConfigurationManager.GetSetting("DatabaseConnectionString") for an Azure worker role. However, changing 4 variables every time I do a release is getting tiring.
I see a lot of posts that get close to what I want, but they're geared towards C#. For reasons beyond my influence, this project is written in VB.NET. It seems I have 2 options. First, I could call the MSBuild process with a couple of defined properties, passing them to the .metaproj build file, but I don't know how to get them to be used in VB code. That's preferable, but, at this point, I'm starting to doubt that this is possible.
I've been able to set some pre-processor constants, to be recognized in #If-#Else directives.
#If DEBUG = True Then
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.xxx")
#Else
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.133")
#End If
msbuild CalbertNG.sln.metaproj /t:Rebuild /p:DefineConstants="DEBUG=False"
This seems to work, though I need to Rebuild to change the value of that constant. Should I have to? Should Build be enough? Is this normal, or an indication that I don't have something set quite right?
I've seen other posts that talk about pre-processing the source files with some other builder, like Ant, but that seems like overkill. It feels like I'm close here. But I want to zoom out and ask, from a clean sheet of paper, if you're given 2 variables which need to change per environment, you're using VB.NET, and you want to incorporate those variable values in an automated VS Team Services build process upon code check-in, what's the best way to do it? (I want to define the variables in the VSTS panel, but this just passes them to my builder, so I have to know how to parse the call to MSBuild to make these useful.)
I can control picking between 2 static strings, now, via compiler directives, but I'd really like to reference the Build.BuildNumber that comes out of the MSBuild process to display to the user, and, if I can do that, I can just feed the variables for database and blob container via the same mechanism, and skip the pre-processor.
You've already found the way you can pass data from the MsBuild Arguments directly into the code. An alternative is to use the Condition Attribute in your project files to make certain property groups optional, it allows you to even include specific files conditionally. You can control conditions by passing in /p:ConditionalProperty=value on the MsBuild command. This at least ensures people use a set of values that make sense together.
The problem is that when MsBuild is running in Incremental mode it is likely to not process your changes (as you've noticed), the reason for this, is that the input files remain unchanged since the last build and are all older than the last generated output files.
To by-pass this behavior you'd normally create a separate solution configuration and override the output location for all projects to be unique for that configuration. Combined with setting the Compiler constants for that specific configuration you're ensured that when building that Configuration/Platform combination, incremental builds work as intended.
I do want to echo some of the comments from JerryM and Daniel Mann. Some items are better stored in else where or updated before you actually start the compile phase.
Possible solutions:
Store your configuration data in config files and use Configuration Transformation to generate the right config file base don the selected solution configuration. The process is explained on MSDN. To enable configuration transformation on all project types, you can use SlowCheetah.
Store your ocnfiguration data in the config files and use MsDeploy and specify a Parameters.xml file that matches the deploy package. It will perform the transformation on deploy time and will actually allow your solution to contain a standard config file you use at runtime, plus a publish profile which will post-process your configuration. You can use a SetParameters.xml file to override the variables at deploy time.
Create an installer project (such as through Wix) and merge the final configuration at install time (similar to the MsDeploy). You could even provide a UI which prompts for specific values (and can supply default values).
Use a CI server, like the new TFS/VSTS 2015 task based build engine and combine it with a task that can search&replace tokens, like the Replace Tokens task, Tokenization Task, Colin's ALM Corner Build and Release Tasks. And a whole bunch that specifically deal with versioning. Handling these things in the CI server also allows you to do a quick build locally at all times and do these relatively expensive steps on the build server (patching source code breaks incremental build in MsBuild, because there are always newer input files.
When talking specifically about versioning, there are a number of ways to set the AssemblyVersion and AssemblyFileVersion just before compile time, usually it involves overriding the AssemblyInfo.cs file before compilation. Your code could then use reflection to read the value at runtime. You can use the AssemblyInformationalversion to specify something like you do in the example above which contains .xxx or other text. It also ensures that the version displayed always reflects the information obtained when reading the file properties through Windows Explorer.

WiX Installer Bootstrapper - How to set a string variable based on a condition?

I am using a Wix bootstrapper. I have a custom Theme that has a radio button that sets variables called "Client" or "Server", based on which button is selected.
What I'm searching for is a way to have the "InstallCommand" in an "ExePackage" be different based on whether "Client" or "Server" is set. (They are boolean "1" or "0") variables.
I could always duplicate the "ExePackage" and run the same installer executable with a different InstallCommand based on the variable. That has worked for me in the past. However, I am trying to avoid including the ExePackage twice, since it's rather large and would unnecessarily bloat the bootstrapper executable.
I was thinking of trying to set a variable or property based on whether one or the other is set, then use that variable or property in the InstallCommand. Any thoughts on how to go about doing this? I've searched and I can't find a solution.
My research has indicated that this simply cannot be done with the Wix managed bootstrapper application. You have to "roll your own" custom bootstrapper application to achieve this.

run script of batch file from within WIX

I implement wix to generate some msi. I'd like to maintain .bat file (that is packed within this wix project) to contain some work to do (and to be activated with some custom action)
I added the .bet to my wix project in VS2010.
My question is
how do I actually wrap it within the msi that on target machine the script will be available to run?
How do I actually refer to that embedded batch file in the custom section element
This might not be the answer you're looking for, but I strongly recommend you NOT going this way. Running batch file from inside the MSI package has a number of disadvantages, which will shoot you one day:
antivirus might block the execution
as for any deferred custom action (the one which changes the target system state) you'll have to create a rollback action, and for a batch file which might include a number of steps of different nature it could be much more difficult
you don't have the progress (I doubt this is at all possible for batch script)
Instead, I encourage you to do the following:
analyze your batch script and make a list of exact things it does to the target system
take a look at the standard Windows Installer functionality and WiX extensions to see what's there out of the box
design your installation to use standard functionality as much as possible and custom actions as little as possible
if you still need a custom action, stick to the DLL ones and make sure rollback actions are present for deferred actions
You're looking for what I think is a type 18 custom action:
The executable is generated from a file installed with the application. The
Source field of the CustomAction table contains a key to the File table. The
location of the custom action code is determined by the resolution of the target
path for this file; therefore this custom action must be called after the file
has been installed and before it is removed.
The CustomAction element has the ExeCommand attribute for just this sort of occasion. It would look something like this:
<CustomAction Id="ExecuteMyBatchFile"
FileKey="[#FileKey]"
ExeCommand="Arguments passed to batch file"
Execute="deferred"/>
Of course, this is assuming the batch file is installed by the msi.

WiX: RemoveExistingProducts + ADDLOCAL

I'm using Wix 2. I am using RemoveExistingProducts, but want to keep my previously installed features. In addition, I'd like to add new features via ADDLOCAL parameters given via command line.
However, according to this article ADDLOCAL does set the Preselected flag.
Therefore MigrateFeatureStates does not fire:
Skipping MigrateFeatureStates action: feature settings already made
Is there a possibility to use them both?
No, not both. The documentation is pretty clear that ADDLOCAL (and all the other feature state controls via the command-line Properterties) take over the selection manager. It, unfortunately, makes using the command-line to control feature states very tricky.

WIX: How to Select Features depending on properties

I want to select features based on certain properties' values.
Similar to this question:
WIX: How to Select Features From Command Line
It seems that setting the ADDLOCAL property value is one way to do it, but is this the right way? Microsoft seems to warn against doing it in this article:
http://msdn.microsoft.com/en-us/library/aa367536(VS.85).aspx
Features can be conditionally installed as well. See the Condition element under the Feature element. Component Conditions may work as well. Depends on your package structure.
Why not use components instead of features? Components can be conditionally installed depending on various properties.
We do this having "pretend features" that a user can select via a checkbox, then a complicated set of conditionals to install various components depending on their Office version AND the state of the checkbox.