Failing condition wix - wix

I am trying to skip installations in the following cases:
Windows OS type is = desktop OS
if HKLM\SYSTEM\CurrentControlSet\Services\MyService MYKEY= myValue
if REG HKLM\SYSTEM\CurrentControlSet\Services\MyService = DisplayName is present
AND HKLM\SYSTEM\CurrentControlSet\Services\MyService MYKEY is not present
while installation it went well, but while installation my feature is missing from installation.
Am I doing anything wrong in putting condition?
<Property Id="MYKEY" Secure="yes">
<RegistrySearch Id="MyKey"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Services\MyService"
Name="mykey"
Type="raw" />
</Property>
<Property Id="MYSERVICE" Secure="yes">
<RegistrySearch Id="MYSERVICE"
Root="HKLM"
Key="SYSTEM\CurrentControlSet\Services\MyService"
Name="DisplayName"
Type="raw" />
</Property>
<Feature Id="MyFeature" Level="" Display="" Title="" Description="" AllowAdvertise="no" ConfigurableDirectory="INSTALLDIR">
<MergeRef Id="MyFeature" Primary="yes"/>
<Condition Level="0">((MsiNTProductType=1) OR
(MYKEY="MyValue") OR
(MYSERVICE="MyService" AND MYKEY=""))</Condition>
</Condition>
</Feature>

NOTE: The below has not been tested extensively - conditions are notorious for being difficult to get right. Testing conditions requires real-world testing. A few more links:
Condition debugging
WiX launch conditions
Wix Tools update uses old custom actions (example of complex conditions)
How to add a WiX custom action that happens only on uninstall (via MSI)?
When either of those three "sub"-conditions are true, what do you want to achieve?
Abort Setup: Abort the whole setup? (LaunchConditions)
Configure Features: Prevent or enable installation of specific feature(s)? (Feature conditions)
This difference is obviously crucial - and we must know to be able to answer. Your WiX source snippet currently shows the conditions used as feature conditions. I have a feeling this is not what you want.
LaunchConditions: In order to abort the whole setup if one of these conditions are true, you could try to use LaunchCondition entries. Instead of making one complicated condition, you can just split them in three different entries that each check if the setup should be aborted - each entry for a different and specific reason. I suggest you add these LaunchCondition entries after your Package element in your WiX source file:
<Condition Message="Aborting setup: Server OS required for installation.">Installed OR MsiNTProductType=1</Condition>
<Condition Message="Aborting setup: State reason for abortion.">Installed OR MYKEY="MyValue"</Condition>
<Condition Message="Aborting setup: State reason for abortion.">Installed OR (MYSERVICE="MyService" AND MYKEY="")</Condition>
These entries will make it into the LaunchCondition table of your compiled MSI file.
LaunchConditions must always evaluate to true for the setup to be able to install / run.
Accordingly, the Installed parts of the conditions above are there to ensure that the condition is always true after installation - so you don't get the situation that the setup will not allow itself to be uninstalled or repaired because a launch condition is not met. The condition: Installed - will always be true except for a fresh install and major upgrades.
NOTE: I am not sure whether launch conditions can cause trouble during administrative installations. I don't think they do (an administrative installation features its own installation sequences). I will test and verify tomorrow. Adding OR ACTION="ADMIN" should make the launch condition true on any box when administrative installation is run.
Feature Conditions: If you don't want to abort the setup, but rather want to control feature installation status based on evaluating these conditions, you need to use the feature conditions concept instead of the launch condition concept.
When you set the Feature level to 0 in your WiX source, the feature is not show in the setup GUI and it is not going to be installed by default either. A feature condition can change this and set the feature to install if the condition evaluates to true.
You can also go the other way around and set the feature level to 1 as default (this should install the feature) and then use a feature condition to set its Feature level to 0 - if you don't want the feature installed - when the condition is true.
There are some further details under "Quick Mockup" here: WIX If...else condition using registry.
In the below WiX snippet we set a feature to install by default (Level="1") and then we use a feature condition to set the feature to not install if its associated condition evaluates to true (this is a multi-part condition). So once the condition evaluates to true we assign feature Level="0" (which means do not install feature and hide it from the setup GUI):
<Feature Id="MyFeature" Level="1"> <!--Default to install feature-->
<Condition Level="0"> <!--Do not install feature if condition is true-->
((MsiNTProductType=1) OR (MYKEY="MyValue") OR (MYSERVICE="MyService" AND MYKEY="") AND (NOT ACTION="ADMIN"))
</Condition>
</Feature>
The AND (NOT ACTION="ADMIN") part is to force the feature to be installed in an administrative installation. It effectively shuts off the other conditions from evaluating to true if the setup is run in administrative install mode - which would cause the feature to not be installed during the admin install. This last part I have to test tomorrow.
UPDATE: Testing indicates that any feature set to Level=0 as default will not be extracted during an administrative install at all,
regardless of any feature conditions setting the feature to install. I
guess the tentative conclusion is to not set any features to
Level=0, but set Level=1 and then set them to Level=0 with a
feature condition that evaluates to true. This way the feature may be
hidden in a regular installation, but all features - with associated
files - are extracted during admin installation. The AND (NOT ACTION="ADMIN") part of the condition appears to not be needed.
Leaving the sample above as it is for now.
Links:
WIX If...else condition using registry
How to execute custom action only in install (not uninstall)
How to add new framework version in Installment Requirement page of InstallShield?

Related

Custom action of Wix failed to call vb script

I have one VB script in the package itself. I need to call it using CMD, the default way of calling the script taking too much time, so I am trying to call it with CMD and CSCRIPT but the installer raises the error while installation.
I am using the following code which is not working as expected. I searched a lot but didn't find the solution.
<Binary Id="ServiceInstall" SourceFile="..\..\..\AddVirDir.vbs" />
<CustomAction Id="InstallService" BinaryKey ="ServiceInstall"
ExeCommand="CMD /C "[#ServiceInstall]""
Execute="immediate" Return="check" HideTarget="no" Impersonate="no"/>
WiX IIS Elements: If this is all IIS, I would avoid scripting and custom actions whenever possible and use WiX's built-in IIS elements. Here is a Web-installer sample from Rainer Stropek available on github.com.
Look for <iis:WebVirtualDir ... /> et al. Find the WiX Documentation here. I believe you should be able to accomplish what you need without too many custom actions.
DISM.exe: Stropek himself uses custom actions in another sample source to set up IIS using DISM.exe. Not sure I would do it like this (no other suggestions though), but it is a sample of custom actions and IIS.
Need-For-Speed: As to your installation performance issues. Maybe you need to suppress the creation of a restore point and limit file costing? The Windows Installer engine allows this - see link below. I doubt it will be very effective though. I think there must be something else wrong in your installer. Some timeout issue? It could relate to other custom actions, slow network, or some other issue. Can you elaborate on your deployment scenario?
In any case, here is some documentation on speeding up MSI installations in general. Essentially the MSIFASTINSTALL property is the only one I would recommend. DISABLEROLLBACK can cause genuine problems.
Logging: I normally recommend that setup developers enable default verbose MSI logging - as described in the "Globally for all setups on a machine" section, to always have a log file ready when you need one. It is created with a random name for every MSI operation in the TEMP folder and you sort by modify to get the latest one. The log file might give clues as to why the installation is slow - just determine what is really going on. Apologies if this is just obvious trifles and you have this set up already.
Manual log file creation:
msiexec.exe /i C:\Path\Your.msi /L*v C:\Your.log
Interpreting an MSI log: interpreting a log file can be challenging sometimes. Here is an answer with some links to help with this.
Service Installation & Control: You should not install services with scripts. There are built-in mechanisms in MSI that are vastly superior. You simply use the ServiceInstall and ServiceControl WiX XML Elements and "declare" how the service should be registered and when and how the service should be started and stopped:
<Component>
<File Source="$(var.SourceDir)\WindowsService.exe" />
<ServiceInstall Name="MyService" ErrorControl="normal" Start="auto" Type="ownProcess" />
<ServiceControl Id="MyService" Name="MyService" Start="install" Stop="both" Remove="uninstall" Wait="yes" />
</Component>
Look! No custom actions! :-) - Just MSI auto-magic. There is no need to use any custom actions for this. MSI is full-featured and reliable provided your service executable behaves as it is supposed to.
Let me link to a similar sample on github in case the above is not clear. It is more complete and elaborate.
VBScript: I wrote this before I saw you dealt with services. I'll just throw it in: to call a script that doesn't have a function you can try something like this:
<!-- The VBScript file -->
<Binary Id='Sample.vbs' SourceFile='Sample.vbs' />
<!-- The Custom Action -->
<CustomAction Id='Sample.vbs' VBScriptCall='' BinaryKey='Sample.vbs'
Execute='immediate' Return='ignore'/>
<!-- And Insert Into Installation Sequence -->
<InstallExecuteSequence>
<Custom Action='Sample.vbs' After='AppSearch'/>
</InstallExecuteSequence>
That should work for a script like this (Sample.vbs - no funtions, just an implicit main function):
MsgBox(Session.Property("ProductName"))
There is an answer on the topic of VBScript custom actions here: WIX installer execute vbscript from CustomAction.

Install a MSI based on whether a windows service is disabled or not

I have a Bundle setup such that it installs a combination about 4 msis, like, MSI_1 -> MSI_2 -> MSI_3 -> MSI_4.
I want the MSI_4 to be installed only if a particular windows service is NOT disabled, i.e. Don't install if the windows service (say ABC) is disabled. I can probably use a CustomAction and run the command
"Get-WMIObject win32_service -filter "name='ABC'" -computer "." | select -expand state"
to check for the state. But how do I use that(can I use that?) to determine whether to execute the installer or not. Mind you, all the 4 MSIs have their own project and they are combined under the umbrella of one VS solution. I don't have to go the CustomAction route, so if you have anything else in mind I am open to try that as well.
Thanks for your time and help in advance. Let me know if something is not clear.
Will answer my own question here, since I found the solution. What I didn't know was the services listing was also available in the registry setting and one can use Util:RegistrySearch element in the wxs where we are doing the Chain element. One can modify the state of the service from Running to Disabled to observe the change in the value and can set the InstallCondition accordingly.
<util:RegistrySearch Root="HKLM"
Key="System\CurrentControlSet\Services\ABC"
Win64="yes"
Value="Start"
Variable="State" />
And the Chain element will be like:
<MsiPackage Id="package"
Cache="no" Vital="yes"
DisplayInternalUI="no"
ForcePerMachine="yes"
SourceFile="package.msi"
InstallCondition="(State = <condition>)" />
Hope this helps.

How to run custom action based on condition?

I'm trying to run a custom action (delete a certain file) based on the windows version.
I know how to check for the windows version:
<Condition Message="Windows version xxx required...">
<![CDATA[Installed OR (VersionNT >= 600)]]>
</Condition>
However, I do not want to display a message, but delete a file. I can't find an example on how to use such a condition to run oder not to run a custom action!
You need to specify the condition inside the Custom element which runs your custom action. (This allows you to run the custom action multiple times in different locations in your sequence and with different conditions each time if desired).
Example:
<InstallExecuteSequence>
<Custom Action="CreateRegistryEntries" After="CostInitialize">
NOT Installed AND NOT PATCH
</Custom>
</InstallExecuteSequence>

WiX Property passthrough not working (sometimes)

Using WiX 3.6 I'm trying to create an MSI for silent installation of an in-house product.
One some machines, my installer works fine, but on others the installer fails with an error message indicating the property value hasn't been passed through from the UI phase to the Install phase of the process:
This error comes from a guard clause in my WiX source:
<Condition Message="The property SERVER must be defined on the commandline. Installation will abort.">
<![CDATA[Installed or SERVER]]>
</Condition>
Why is my Guard clause triggering even though I provide the property SERVER on the command line?
The installation commandline:
msiexec /i 'C:\work\Excel.2010(x86).msi' SERVER=fsis-app-server ENVIRONMENT=Production SKIN=Black
According to everything I can find by consultation with Professor Google, all I should need is to make my property public (by using all capitals for the name) and to mark it Secure="yes":
<Property Id="SERVER"
Secure="yes"/>
I've done this, but it's still not working - any ideas what I've missed?
Other details ...
... my installer is configured to run as a MajorUpgrade every time.
... I'm deploying exclusively to Windows 7. On my machine (which does have this problem), I have Windows Installer. V 5.0.7601.17514
... Most machines upgrade fine, but a handful (<10 out of 200) fail with this error. My machine is one (helpful for troubleshooting). The previous version is NOT listed under Programs and Features on this machine.
Update #1
Very interesting, reading the logs. My properties (e.g. SERVER) are not losing their values as the install progresses.
If I'm reading the log right, it seems my error in being shown by the previous MSI, not the one I'm currently running:
MSI (s) (D8:D0) [13:20:19:213]: Product: FSIS Plugin for Excel 2010 (32 bit) v4.7.1047.0 -- The property SERVER must be defined on the commandline. Installation will abort.
The installer I'm using is v4.8.9999.0, so the old version number is my clue as to what's happening ... though this doesn't match the v4.5.0.0 in my original screenshot.
Looks like I have two jobs ahead of me ...
... napalm the old installer to clean up this machine
... alter the installer to prevent recurrence.
Update #2
I've verified that my failure happens on UNinstallation.
<Condition Message="The property SERVER must be defined on the commandline. Installation will abort.">
<![CDATA[Installed or SERVER]]>
</Condition>
Why does this guard clause trigger and abort uninstallation?
I thought the point of having "Installed or" was to allow it to work properly when removing the product.

WiX - trying to figure out install sequences

I'm installing a large app, and part of it is a custom written tool called "DbUpdateManager" to mass execute SQL scripts against our target database.
Right now, the WiX 2.x install works - but it has one flaw: during install, I also install a couple of Windows services, which can be optionally started right away. Those however will fail, if the DbUpdateManager hasn't been run yet.
So what I'm trying to accomplish is this:
Install DbUpdateManager and my services from my MSI
Run DbUpdateManager BEFORE any of the services start up
My current WiX source looks something like this:
<Directory Id='INSTALLDIR' Name='DbUpdMgr' LongName='DbUpdateManager' >
<!-- DbUpdateManager component with the necessary files -->
<Component Id='DbUpdateManagerComponent' Guid='...' DiskId='1'>
<File Id='DbUpdateManagerFile' LongName='DbUpdateManager.Wizard.exe'
Name='DbUmWz.exe' src='DbUpdateManager.Wizard.exe' KeyPath='no' />
</Component>
<!-- Component to install one of my Windows services -->
<Component Id='InstallServiceComponent' Guid='...' DiskId='1'>
<File Id='InstallServiceFile' LongName='MyService.exe'
Name='MyServic.exe' src='MyService.exe' KeyPath='yes'/>
<ServiceInstall Id='InstallMyService' Name='MyService'
Description='My Service' ErrorControl='normal'
Start='auto' Type='ownProcess' Vital='yes' />
<ServiceControl Id='UninstallMyService' Name='MyService'
Remove='uninstall' Wait='yes' />
</Component>
<!-- Feature for the DbUpdateManager referencing the above component -->
<Feature Id='DbUpdateManager' ConfigurableDirectory='INSTALLDIR'
AllowAdvertise='no' Description='DbUpdateManager' Level='1'
Title='Database Update Manager'>
<ComponentRef Id='DbUpdateManagerComponent'/>
</Feature>
<!-- Custom action for running DbUpdateManager -->
<CustomAction Id='RunDbUpdateManagerAction' FileKey='DbUpdateManagerFile'
ExeCommand='' Return='asyncWait' />
<!-- Calling the custom action in the install sequence -->
<InstallExecuteSequence>
<RemoveExistingProducts After='InstallInitialize' />
<Custom Action='RunDbUpdateManagerAction'
After='InstallFinalize'>&DbUpdateManager=3</Custom>
I inherited this WIX, and it works - but as I said - the DbUpdateManager gets called too late in the process (only "After=InstallFinalize") and thus the services will fail to start up properly at first (the run fine the second time around when you restart them manually after DbUpdateManager has run).
I poked around the MSI documentation a bit and found a nice step called "StartServices", so my hunch was to just change my calling the custom action to this:
<InstallExecuteSequence>
<Custom Action='RunDbUpdateManagerAction'
Before='StartServices'>&DbUpdateManager=3</Custom>
Unfortunately, in this case, nothing at all happens - DbUpdateManager NEVER gets called....
Any ideas why? Debugging the MSI/WiX stuff is really really tricky, and I can't seem to see the forest for the trees anymore....
Thanks!
Marc
EDIT: The "RunDbUpdateManagerAction" is placed in the right position in the InstallExecuteSequence table in my MSI - right AFTER InstallServices and just BEFORE StartServices - and yet it doesn't work.... DbUpdateManager (a Winforms utility) does not show up during installation :-(
EDIT 2: now my action appears to be executed and at the right time - unfortunately, I'm just not seeing my wizard :-( What I'm seeing is an error code "return value 1631" which means something like "MSI Service could not be started" - wtf ???
MSI (s) (2C:D8) [20:53:36:383]: Doing action: RunDbUpdateManagerAction
Action 20:53:36: RunDbUpdateManagerAction.
Action started at 20:53:36: RunDbUpdateManagerAction.
MSI (s) (2C:D8) [20:53:36:383]: Doing action: StartServices
Action 20:53:36: StartServices. Services are being started
Action started at 20:53:36: StartServices.
Action finished at 20:53:36: RunDbUpdateManagerAction. Return value 1631.
Well, I finally got it working - with a bit of help from everyone who responded, and by consulting some of the WiX tutorials and help pages out there on the web. MSI installer stuff isn't easy to figure out and learn......
Basically, I changed execution of my custom action to "deferred" (as suggested by Rob) and I moved the point in the sequence where it gets executed to "After=InstallFiles". I also changed the condition in the <Custom> tag to "NOT Installed" which seems to work just fine in my scenario.
Contrary to Rob's fear, the Db Update Manager and its UI come up quite nicely this way, and the process of updating our database is now completed before any of our services (that depend on the database) get started.
Looking forward to a full RTM release of WiX 3.0 (and its future) !
Thanks to everyone - unfortunately, I could only accept one answer - all would have deserved it.
Marc
It appears that your CustomAction depends on the 'DbUpdateManagerFile' being installed. That means that your CustomAction needs to be scheduled after InstallFiles executes. Remember there are two passes to the InstallExecuteSequence. First, the "immediate" (or "scheduled" or "script generation") actions are executed to build the transaction log (aka: "install script"). Second, the "deferred" actions in the transaction log are executed.
Right now your CustomAction is "immediate" (the default) so it is trying to run before the files are actually copied to the machine. The InstallFiles action is in the script before your CustomAction but it hasn't been executed yet.
So, you need to mark your CustomAction "deferred" to get it to run after your files are installed.
Note: you are not probably not able to show UI from a deferred CA. I wasn't sure if this tool of yours was expected to show UI.
PS: sorry if I didn't explain that well, it's been a long day.
Try getting a log file of the Installation, and look for the sequence order in there and the value of the condition to perform the Custom Action
Use this in the command line:
msiexec /i [msiname] /l*v [filename]
EDIT: After reading your comment have a look at this page here you could try to add NOT INSTALLED in the condition
EDIT2: I found this page Search for your error Number 1631
You can open the .msi in Orca and look at the InstallExecuteSequence table to see what order things are actually happening in. This may give you a good idea of what's actually happening when.