Is it possible to have the WiX installer reboot the target machine if the installation fails for any reason?
This is a requirement that I have.
I'm using a Bootstrapper. After the initial installation all updates will be done automatically. If the installation fails, I need to:
send appropriate logs (done)
rollback to a good state (done), and
reboot the target machine
I restart the target machine after a successful installation, but I don't know how to restart the machine if it fails.
Add these lines to each package in the chain. The first will allow it to complete without reboot if it was successful, the second will force it to reboot on any other exit code.
<ExePackage Id="MyExe" SourceFile="MySource.exe">
<ExitCode Behavior="success" Value="0" />
<ExitCode Behavior="forceReboot"/>
</ExePackage>
It sounds like you're already rebooting after a successful installation, so you might just want to remove the Value="0", so it forces a reboot for any exit code.
Related
When I'm building my MSI file, and I use a basic condition, the expected happens. For example, let's say that I have this in Setup File:
<Property Id="myProperty" Value="0"/>
<Condition Message="Value of myProperty is [myProperty], and it should be 1.">
<![CDATA[Installed OR myProperty = "1"]]>
</Condition>
If I build this and run the MSI file, it works -- that is, it displays the error message and quits.
Working condition when running MSI
However, if I put the MSI into a Bundle, it doesn't work. That is, when I put just this into my Bootstrapper ("Properties" below is the name of my Setup project -- bad name, I realize):
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)"/>
</Chain>
And then I run the setup file, I get an error. When the installation starts, it checks the condition, gives me the expected message box (same as above), and then I get this error message:
Setup Failed
Looking at the Log, I get three error messages:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
With the exit code:
Exit code: 0x643, restarting: No
I'm such a noob at WiX that I'm not even sure how to go about researching what the problem is -- I can't even ask an intelligent question. Hence, I'm reaching out to you kind folks!
(I'm using WiX 3.10 and Visual Studio 2015)
EDIT:
Thanks for getting back to me! I tried your suggestions:
In the installer file, I made the property public and I made it secure. I left the condition the same, and, since I don't think that I should get the value here as opposed to in the bootstrapper, I left the value of the property out. Here is the code that I made for the property/condition:
<Property Id="MY_PROPERTY" Secure="yes"/>
<Condition Message="MY_PROPERTY is [MY_PROPERTY]. Should be 1">
<![CDATA[Installed OR MY_PROPERTY = "1"]]>
</Condition>
Then, in the boostrapper file, I added a child element of and gave it a value:
<MsiPackage SourceFile="$(var.LCondErrorInstaller.TargetPath)">
<MsiProperty Name="MY_PROPERTY" Value="0"/>
</MsiPackage>
When I ran it, I got almost the same behaviour as I did before, except for one difference -- when I get the error message. This time, I get the pop-up screen with the Message condition and the same error message as I did before (see "Setup Failed" above), except this time I get it happens little later in the installation, making me think that the condition is, in fact, getting triggered in the bootstrapper.
As far as the log files, they look the same (I'm not sure how to get log files of the MSI when running the Burn file, what I do now is just run the Burn file with the flag "/l", like so: > file.exe /l logFile.log).
For clarity, here are the parts of the log file that appear to be important:
Error 0x80070643: Failed to install MSI package.
Error 0x80070643: Failed to execute MSI package.
Error 0x80070643: Failed to configure per-machine MSI package.
...
Exit code: 0x643, restarting: No
I should have been more specific when I initially asked the question about what kind of behaviour I'm looking for...
I will have more than just that one MSI file in the Burn file. What I want to do is this: when the Burn file installs, if there is a condition in one of the MSI files that is not met, I want that MSI file to simply not be installed, and the rest of the MSIs to be installed. I don't care about the UI.
If there's another way to do this, I'm all ears.
If you have launch conditions in the MSI you can replicate or move those launch conditions into the bootstrapper bundle itself to stop this type of behaviour.
Launch condition failure returns Fatal Error 1603 (0x643 in hex) which is what I would expect to see when the MSI launched by the bootstrapper fails due to launch condition not met.
You should see something like this in the msi's logs
Action ended 17:33:38: LaunchConditions. Return value 3.
MSI (c) (08:4C) [17:33:38:610]: Doing action: FatalError
Action 17:33:38: FatalError.
Action start 17:33:38: FatalError.
...
MSI (c) (08:4C) [17:33:41:188]: MainEngineThread is returning 1603
To elaborate, you would have to change your msi package definition to the following to get it to run properly through the bootstrapper
<Chain>
<MsiPackage SourceFile="$(var.Properties.TargetPath)">
<MsiProperty Name="MYPROPERTY" Value="1"/>
</MsiPackage>
</Chain>
Additionally if you want to pass in a property from your bootstrapper to your MSI the property must be a public property which is a property whose name is ALL CAPS.
If you want to use this property somewhere in the Install phase of your msi you must also mark this property as secure.
I've got the issue, that when uninstalling (or upgrading) the Restart Manager is complaining about a file in use situation, and so is forcing a reboot:
RESTART MANAGER: Detected that application with id 7000, friendly name 'javaw.exe', of type RmCritical and status 1 holds file[s] in use.
RESTART MANAGER: Did detect that a critical application holds file[s] in use, so a reboot will be necessary.
The service that RESTART MANAGER is complainig about, is a java-based service. The service (here called myservice.exe) is recursively starting java child processes:
myservice.exe --run
↳ javaw.exe --someArguments
↳ someother.exe --someArguments
↳ javaw.exe --someMoreArguments
The wix snippet for the service definition:
<DirectoryRef Id="BINDIR">
<Component Id="myservice.exe" Guid="PUT-GUID-HERE">
<File Id="myservice.exe" KeyPath="yes" Vital="yes"
Source="SourceDir\bin\myservice.exe"/>
<ServiceInstall Id="MyService" Type="ownProcess"
Vital="yes" Name="MyService" DisplayName="My Service"
Description="My Service" Start="auto" Account=".\LocalSystem"
ErrorControl="normal" Interactive="no" Arguments="--run"/>
<ServiceControl Id="MyService" Name="MyService" Wait="yes" Remove="uninstall" Stop="uninstall" Start="install"/>
</Component>
</DirectoryRef>
Now, the interesting part:
The service could be started when installing
on uninstall:
if not running, it will be removed
if running, and just agreeing to make a restart
it is indeed stopped within about 2-3 seconds (i guess by the StopServices action)
and successfully removed (by RemoveServices action)
The entries in the Service* Tables seems good for me so far.
ServiceControl-Table:
ServiceControl Name Event Arguments Wait Component_
MyService MyService 161 1 myservice.exe
ServiceInstall-Table:
ServiceInstall Name DisplayName ServiceType StartType ErrorControl LoadOrderGroup Dependencies StartName Password Arguments Component_ Description
MyService MyService My Service 16 2 32769 .\LocalSystem --run myservice.exe My Service
So, to break down everything:
It seems that the Restart Manager is not recognizing, that the java processes are child processes and will be stopped by the StopServices action.
I found some kind of similar problems here:
https://www.mail-archive.com/wix-users#lists.sourceforge.net/msg57924.html
Wix Installer Problem: Why does RestartManager mark Service as RMCritical and not RMService
Thanks in advance for any help to solve this issue!
You have a couple of options to resolve this issue:
-Disable "Restart Manager" by making use of MSIRESTARTMANAGERCONTROL= "Disable" in the property table. This would kick in the legacy "FilesInUse" dialog box.
In your case, The FilesinUse dialog might also not be displayed (since services do not have a window associated with them)
The "FilesinUse" dialog box does not list processes which do not have a window associated with them. So, in your case, disabling the Restart Manager, might not display any dialogs(neither FilesInUse nor RestartManager).
However, this would also mean that a reboot might be required, not necessarily because of your services but due to some other process which might be holding your files in use. If you think that there could be no other process other than your own services holding files, then go ahead and follow this approach . If you think that there could be other processes, other than your services holding files, then having "Restart Manager" enabled is ideal. Not having "Restart Manager" will either result in one of the things:
-Display the Legacy FilesInUse dialog asking you to shut down the processes listed in the dialog. This might result in you having to shut down these processes via a custom action.
Both the "RestartManager" and "FilesInUse" dialogs are displayed by the "InstallValidate" standard action. If you want to suppress both of these dialogs, then ensure that your custom action is scheduled before "InstallValidate" standard action. There is a catch here. Scheduling such a custom action before InstallValidate would have to be an immediate mode custom action(You cannot have deferred mode custom actions before "IntsallFinalize"). So , in cases where you are not running as an administrator(such as in UAC enabled scenarios), you might not have the necessary privileges to shut down applications. So , a restart might be required.
-You can also shut down applications using the using the WiX util extensions CloseApplication() function.
Evaluate your scenario and do what is right for you.
I think I might be late to the party, but here's the solution. The Installer team blog post explains how the Restart Manager decides whether to pop up the files in use dialog. Specifically (Windows Installer-Restart Manager Interaction in Detail section, item 3.b.):
If the package is authored such that the services detected by RM would be shutdown because of the authoring of the Service* tables then those services will not be displayed in the files-in-use dialogs.
(italics are mine). Helpful but not immediately helpful, because such that is not really elaborated. But since my service caused the same problem as described by the OP with
<ServiceControl Stop="uninstall" ... />
I just changed the value to both
<ServiceControl Stop="both" ... />
which was probably the only remaining thing that could make it “such that,” and boom, fireworks, magic:
MSI (s) (50:A0) [21:50:30:352]: RESTART MANAGER: Detected that application with id 6408, friendly name 'XXXX', service short name 'xxxx', of type RmService and status 1 holds file[s] in use.
MSI (s) (50:A0) [21:50:30:352]: RESTART MANAGER: Detected that the service xxxx will be stopped due to a service control action authored in the package before the files are updated. So, we will not attempt to stop this service using Restart Manager
It appears that both flags msidbServiceControlEventStop (0x002) and msidbServiceControlEventUninstallStop (0x020) need to be set in the ServiceControl table to make the RM happily conclude that the service is going to stop before files are updated.
In retrospect this makes sense. Since the uninstallation part during the upgrade is carried out using the old cached MSI database, RM does not look into it to see what is going to happen when the related product is uninstalled. Strictly speaking, there may be multiple products to uninstall, and the Installer does not require anywhere that these related products (those found by the FindRelatedProducts action, including the old version of the same upgrade code) be in fact related to the service that one is controlling in the current package. So it does not care about the service action on uninstall as scripted in the current package (it does not apply to the install action anyway!). For the sake of consistency, it requires a simple and straightforward evidence that the service is going to be stopped before the files in use are overwritten, gathering such evidence from the current package alone.
So it is quite likely that the RM cares about the msidbServiceControlEventStop (0x002) flag only during the installation.
I have run into challenging problem trying to accomplish the following:
my application installs a service (watchdog.exe) and an exe file (app.exe).
After the installation is done the service starts and creates process "app.exe".
during uninstall I want to kill the process "app.exe" (which is running under local system account, so I must be running as admin).
problem 1:
The installs says that it requires a reboot since it sees that file "app.exe" is being held (running) during the CostFinalize phase (please correct me if I'm wrong about the phase that checks if a reboot will be required). It would be much better to kill the process when the uninstallation begins. I have verified that if the process is not running during the uninstall then the install does not complain about a reboot required.
problem 2:
using a custom action to kill the process is problematic. the action must run elevated, but on the other hand it must run before the costFinalize (otherwise - it's back to problem 1).
I would appreciate any suggestion. Also, any alternative solutions (is there another way to close the process maybe during install that will not require a reboot?)
The custom action code I have now (not good since it both unnecessarily asks for a reboot and fails to kill the process due to lack of permissions):
<InstallExecuteSequence>
<!--<ScheduleReboot After="InstallFinalize" />-->
<Custom Action="MyProcess.TaskKill" Before="InstallValidate"></Custom>
</InstallExecuteSequence>
<!--<Property Id="Net">Net.exe</Property>-->
<Property Id="QtExecCmdLine" Value='"[%SYSTEMROOT]\System32\taskkill.exe" /F /IM App.exe' />
<CustomAction Id="MyProcess.TaskKill"
BinaryKey="WixCA"
DllEntry="CAQuietExec"
Execute="immediate"
Return="ignore" />
Here is the log for the failure:
CAQuietExec: Error 0x80070001: Command line returned an error.
CAQuietExec: Error 0x80070001: CAQuietExec Failed
CustomAction MyProcess.TaskKill returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 18:15:54: MyProcess.TaskKill. Return value 1603.
There are few ideas that I have, namely:
Use EventWaitHandles, which allow processes to communicate between each other, and delegate your wish to app.exe. Your app.exe can then terminate, as needed. This is clean solution and should be prefered.
If for whatever reason you decide to kill the application like you don't care about anything at all in the world, then you can:
I saw today a project that can run MSI completely elevated, in all stages. It was Visual Stdio template, however I can't find it right now, but know that it exists there.
You can also use this, maybe it works: How do I get WiX installer to request administrative privileges?
Basically there are so many hackery tricks you can do, to kill the application. Such as using WiX Burn and requiring administration rights, then doing your thing. I would go with solution#1(create your own mechanisms)
By the way, if you use ServiceControl element in WiX, it will STOP the service before REINSTALLING/UNISTALLING. You can hook to OnStop() method in Service and kill your App.exe there. If you have set Service as App.exe parent, then there should be flag that any child processes die with parent.
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.
I have a bootloader that I created with WiX 3.6/Burn and was wondering if there was a command similar to <ExitCode Behavior="forceReboot"/> that would execute only on uninstall after everything was executed in the <chain> tag.
You can put the ScheduleReboot action in one of your MSIs and condition the action to run on unistall. Burn will respect the restart request and inform the user a restart is required at the end.