Install a chain of embedded MSI packages each usin

2019-03-20 15:54发布

问题:

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.

回答1:

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.