Wix Bootstrapper: Sequence of Bootstrapper events - wix

I've started playing with Managed Boostrapper Classes and events. Making story short, I've added BoostrapperCore.dll and it would give you the namespace
Microsoft.Tools.WindowsInstallerXml.Bootstrapper
I was able to get some info from some examples present in different blogs. For instance, the Entry point is BootstrapperApplication.Run(), is called when bootstrapper application is ready to run.
Then there are events like:
BoostrapperApplication.DetectBegin
BoostrapperApplication.DetectPackageBegin
BoostrapperApplication.DetectForward
etc, etc...
Question: Is there any precise documentation/online help which provide the details and sequence of Events and Methods present in Microsoft.Tools.WindowsInstallerXml.Bootstrapper namespace?
That would definitely save a lot of time...
Regards

While the source code is on git, I'm yet to find a significant amount of documentation for these events.
As far as order goes, a the WiX bootstrapper has 3 main phases (all of which happen asynchronously)
Detect
This is when the Burn engine tries to figure out what (if anything) is already installed. The bootstrapper application starts this process by calling Engine.Detect, which you will probably want to do as soon as the bootstrapper starts, as you need the outcome of this in order to decide whether to show install, uninstall or upgrade UI.
During this phase the engine will raise the OnDetect... events to tell the bootstrapper application about what it finds.
Plan
This is when the Burn engine figures out what its going to do. The bootstrapper application starts this process by calling Engine.Plan, specifying the desired operation (e.g. Install, Uninstall, Upgrade etc...). This is normally done right before the Apply phase, e.g. after the user clicks on the "Go" button). The OnPlan... events are raised in this phase.
Apply
This is when the Burn engine actually installs or uninstalls packages in the bundle, and starts when the bootstrapper application calls Engine.Apply. The vast majority of the remaining messages are raised during this phase for a combination of progress & error reporting, or to allow the bootstrapper application to handle certain things (e.g. OnResolveSource, which can be used to prompt the user to find file that the engine cannot locate)
Apply has two sub-phases, Cache and Execute.
There are only 3 events that I can see that are not raised during one of these 3 phases:
OnStartup, which is raised when the bootstrapper first starts (the base bootstrapper application calls the Run entry point as part of handling this event)
OnShutdown, raised when the bootstrapper is exiting
OnSystemShutdown, raised when the WM_QUERYENDSESSION window message is received
The events you absolutely need to handle are OnDetectComplete, OnPlanComplete, OnApplyComplete, which will happen in that order.

Related

How can a WiX Burn managed bootstrapper handle force reboots?

I am creating a managed bootstrapper application with WiX Burn. I need to handle some ExePackage elements which may require a forced restart. This question has some tantalizing clues about how to handle the restart, but I'm having trouble getting it working.
I have <ExitCode Value="3010" Behavior="forceReboot" /> in the ExePackage elements, and I can see that triggering in the logs. I am listening to the Shutdown event that is raised by the BootstrapperApplication and setting e.Result = Result.Restart, though I haven't figured out how to capture the condition for when this should occur when the force reboot is detected in the Apply phase. I am testing if (Command.Resume == ResumeType.Reboot) in the Run method of my BootstrapperApplication going straight to the progress bar portion of my custom UI, but I'm not sure how to resume the Apply phase where it left off. Do I need to call Engine.Detect() or Engine.Apply in this case? Is there some special action I need to take to persist and restore the state to survive the reboot, or does the Burn engine handle all of that internally?
If anyone could point me to a working example of a WiX Burn managed bootstrapper application that handles reboots, I would appreciate it.
Check out the implementation of WixStandardBA, even though it's not managed. Basically it does the following:
At startup, check the WixBundleForcedRestartPackage variable and store it in m_sczAfterForcedRestartPackage.
Call Detect like normal.
Call Plan like normal.
In OnPlanPackageBegin, skip packages until the package that caused the restart.
Call Apply like normal.
Burn takes care of persisting the variables across the restart.

Using Burn as an alternate to WiX dialogs and custom actions

While digging into BootstrapperApplication (BA) methods and events, I implemented OnExecuteMsiMessage, and I was able to see the messages being returned by my MSI during installation process. That also includes ActionData with some other information.
Questions:
Can we use Burn's UI dialogs instead of the UI defined in our MSI (written mostly in WiX)?
Can we call our methods written in BA "during MSI installation"? The idea behind this is to write custom actions as part of BA and call these during MSI installation.
I'm aware that we can write managed custom actions now, but just in case there are possibilities to write methods in BA instead of custom actions and call these similarly as we schedule custom actions in MSI.
What should I do?
Two quick answers:
Yes. In fact, this is the expected behavior. Bundles are designed to create a seamless installation experience. Providing a single user interface in your BootstrapperApplication is part of that.
No. The BA does not run elevated so it cannot modify machine state. If you are changing the machine, it should be part of the transaction in an MSI. The BA should only be responsible for interacting with the user (aka: take input, show progress, etc).

How to run custom code as part of WiX burn MBA on install-complete and support rollback

I need to embed, invoke and run some custom code as part of my custom managed bootstrapper application, as a post-install step. This custom code is within a class library that I have included as a reference in my MBA project. So, right after the state becomes InstallationState.Applied I plan on invoking this custom code. However, I am unable to figure out how I could tie in a failed state of this custom code to initiate the bootstrapper to rollback, since the progress callback would have been completed by now. Any ideas?
As per WiX & Burn's lead dev Rob Mensching here, point-2, it seems any custom code that needs to facilitate rollback in the setup process should be run as a custom action, and not as part of the bootstrapper application, as I wanted to. I went ahead and did as Rob suggested, and everything works as expected. However, since the custom action runs significant code, I may in future put that in a WiX extension.

How to execute custom action present in MSI without invoking installation?

Given:
Wix 3.0 is used to create MSI.
The product consists of multiple feature.
Each feature has few sub features. It’s a standard MSI feature tree.
Each feature or sub feature depend on multiple external components.
E.g. .NET 4, ASP.NET etc
Custom action written in C# using Wix 3.0 SDK processes these
dependency and evaluates if components are present or not for a
given set of features.
At time of install if dependent component is missing for given
selection of features, installation fails.
To achieve:
Ability to execute prerequisite check, which is already done in MSI as custom action during installation, without installing MSI on a given machine.
Failed Attempts:
1)
Custom action have function signature like this
[CustomAction]
public static ActionResult ProcessFeaturePrerequisite(Session session);
In order to get session object I used following API present in Wix 3.0 SDK
Session session = Installer.OpenPackage("Pathto\\Product.msi", true); // true doesn’t install it. Also tried with false, but didn’t work.
When I invoke the above method with above session following things fail.
session.Features["SomeFeature"].CurrentState;
This throws exception.
System.ArgumentException was unhandled by user code
Message=Feature ID not registered. SomeFeature
Source=Microsoft.Deployment.WindowsInstaller
StackTrace:
at Microsoft.Deployment.WindowsInstaller.FeatureInfo.get_CurrentState()
Also below critical API which determines prerequisite status always returns false.
session.EvaluateCondition(prereq);
2)
I know a command line way to specify features to the above MSI and install it. It goes like this
msiexec /i "Product.msi" ADDLOCAL=ALL REMOVE="Foo,Bar "
I couldn’t find any API in SDK which allows me to pass additional params which returns session object without starting installation. My guess is passing such param will make session.Features more valid.
Questions:
So how do I achieve above goal?
Is there
any API in Wix SDK which allows me to call custom action without
invoking installation?
any way to invoke custom action from command line for a given MSI
without installing?
any way to make Wix to change MSI into accepting a command string
containing custom action name which only evaluates the action?
any better way to do the same?
I suppose you're trying to solve the problem with the wrong tool. As far as I understand, you would like to check the installation prerequisites from inside a certain tool, but not from the installation. As long as the functionality is implemented as a custom action in the MSI package, you'd like to utilize that functionality in order not to duplicate the code.
I would choose a different way in your situation:
Extract the functionality which actually checks for prerequisites into a separate assembly, e.g. checkprereq.dll
Refactor your custom action to reference checkprereq.dll. Note that you'll have to add checkprereq.dll to your Binary table as well as the customaction.dll. You should divide the responsibility here: the custom action part works with MSI stuff - in your case, it's defining which prerequisites to check based on the combination of features a user selected - and the functional part - the actual prerequisites verification, which is done by checkprereq.dll
Use checkprereq.dll separately when you need to check prerequisites not triggering the installation process
The attempts you've outlined here demonstrate an important false assumption: the session object at install time is the same as the installation object you get by just opening the MSI database for read only purpose. IT'S NOT TRUE! Actually, I doubt it makes any sense to reference the session object outside the installation transaction. As its name states, it is an installation session, that is, available in process - not a static thing.
The MSI package should be treated just as a database when it is just a file and not a running installation. Hence, only static information living in MSI package can be queried and used when you just open it for reading and not installing. I mean you can query the Feature table, for instance, but don't expect it to contain information which makes sense in installation time only, like whether a user chose a feature for installation or not.
Hope this makes sense and shows you the right direction.

Unattended application best practice question

We have an unattended app w/o a user interface that is is periodically run.
It is a VB.NET app. Instead of it being developed as a service, or a formless Windows application, it was developed with a form and all the code was placed in the form_load logic, with an "END" statement as the last line of code to terminate the program.
Other than producing a program that uses unneeded Windows form resources, is there a compelling reason to send this code back for rework to be changed to put the start up logic in a MAIN sub of a BAS file?
If the program is to enter and exit the mix (as opposed to running continuously) is there any point in making it a service?
If the app is developed with a Form do I have to worry about a dialog box being presented that no one will respond to even if there are no MessageBox commands in the app?
I recall there used to be something in VB6 where you could check an app as running unattended, presumably to avoid dialogs.
I don't know whether there are conditions where this will not run.
However, if the code was delivered by someone you will work with going forward, I would look at this as an opportunity to help them understand best practices (which this is not), and to help them understand that you expect best-practice code to be delivered.
First of all, you don't need it to be run in a Form.
Forms are there for Presentation, so it should not be done there.
If you don't want to mess with converting the application a Service (not difficult, but not very easy neither), you shoud create a Console Application, and then, schedule it with Windows Task Scheduler.
This way, you create a Console Application, with a Main function, that does exactly what you need.
Anyway, the programmer could show windows, so there should not be any messagebox. Any communication should be done via Logging to: local files, windows events, database.
If you want more information on any of them, ask me.
If you don't want it to be a service, nothing says that it has to be a windows service. Scheduling it to run via the Task Scheduler or something similar is a valid option.
However, it does sound like the developer should have choose a "Console App" project, instead of a "Windows Forms" project to create this app.
Send it back. The application is bulkier and slower than it needs to be, although that won't be much of an issue. It is somewhat more likely to run out of resources. But the main reason: converting it to a console app is very easy.
If you don't prefer for the Console window to popup, simply do the following.
Create a new class "Program.vb", add a public shared Main() method, and move the "OnLoad" logic from the form to this method.
Next delete the form, and change the project start up object (Available in the project properties window) to use the Program.Main instead of the Form.
This will have the same effect, without the windows forms resources being used. You can then remove the references to System.Windows.Form and System.Drawing.