I'm using Windows Installer 4.5 new features and WiX to generate MSI packages.
I have created an MSI chain installation in order to install a collection of other MSI packages as a transaction. Each package is using the new Embedded UI option so the UI can be WPF. Everything works OK this far.
Except one of the goals would be to display a common progress bar for all installs. At this moment, I have a progress bar in the chain installer, but this one reaches 100% before the other packages start to run.
I have read a post, Fun with MsiEmbeddedChainer, that states that what I want can be achieved. But I can't get it to work. I would like a bit more detailed explanations and maybe some code samples.
You can manually control the status of the progress bar by issuing INSTALLMESSAGE_PROGRESS
messages to the installer. Details can be found here:
http://msdn.microsoft.com/en-us/library/aa370354.aspx
In particular, you'll need a custom action to manage the status bar (it is what will be responsible for making the appropriate calls to MsiProcessMessage
. I recommend that you also use it to spawn the sub-installers. Here is some pseudocode to illustrate what I have in mind:
LONG LaunchSubinstallersCA(MSIHANDLE current_installer)
{
// Initialize the progress bar range and position
MsiProcessMessage(current_installer, reset_message); // see MSDN for details
for each (subinstaller in list_of_installers)
{
launch subinstaller; // see MSDN for details
// Update the progress bar to reflect most recent changes
MsiProcessMessage(current_installer, increment_message); // see MSDN for details
}
return (result);
}
The main down-side is that the progress bar will progress in a somewhat choppy fashion. If you really wanted to get fancy and make it smoother, you could launch a separate "listener" thread that would wait for updates from the sub-installer to make finer-grained increments to the progress bar. Something like:
LONG LaunchSubinstallersCA(MSIHANDLE current_installer)
{
// Initialize the progress bar range and position
MsiProcessMessage(current_installer, reset_message); // see MSDN for details
launch_listener_thread(); // launches listener_thread_proc (see below)
for each (subinstaller in list_of_installers)
{
launch subinstaller; // see MSDN for details
}
tell_listener_thread_to_stop();
optionally_wait_for_listener_thread_to_die();
return (result);
}
void listener_thread_proc()
{
// Loop until told to stop
while (!time_for_me_to_stop)
{
// Listen for update from sub-installer
timed_wait_for_update(); // probably required IPC, perhaps a named event?
// Only update the progress bar if an update message was actually received
if (!timeout)
{
// Update the progress bar to reflect most recent changes
MsiProcessMessage(current_installer, increment_message); // see MSDN for details
}
}
}
Obviously each sub-installer would have to be able to signal the main installer that progress has been made, so this will potentially require more extensive changes across your product. Whether or not that is worth the effort is up to you.