Custom action rollback on Cancel button click

May 30, 2016 at 4:47 PM
Edited May 30, 2016 at 4:49 PM
Hello

I have a custom deferred action and a rollback custom action which is in the execute sequence before that deferred one. When there is a fail returned in the deferred action the rollback custom action is executed as expected. However when I press 'Cancel' button in the Progress dialog during executing the deferred action the rollback action is not executed.

Here is the code where custom actions are added to project:
var onInstallCustomAction = new Id("OnInstallCustomActions");
project.Actions = new Action[]
{
    new ElevatedManagedAction(onInstallCustomAction, OnInstall.OnInstallCustomActions, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed)
    {
        Execute = Execute.deferred
    },
    new ElevatedManagedAction(OnUninstall.OnUninstallCustomActions, Return.check, When.Before, Step.RemoveFiles, Condition.BeingRemoved)
    {
        Execute = Execute.deferred
    },
    new ElevatedManagedAction(OnUninstall.OnRollbackCustomActions, Return.ignore, When.Before, new Step(onInstallCustomAction), Condition.Always)
    {
        Execute = Execute.rollback
    }
};
Am I doing something wrong or is something missing?
May 31, 2016 at 8:43 AM
Thank you for reporting this.

You have just discovered the problem - canceling from Progress dialog doesn't work reliably. It does for native UI but not for managed one.

I assume you are using ManagedUI. At least it is what my testing shows that this problem happens with ManagedUI but not with native one.

ManagedUI is based on the "EmbeddedUI" hosting model implemented by MSI for the cases of completely custom UI. During the actual installation phase (when progress is displayed) the only interface UI has to the MSI runtime is the ProcessMessage method of Microsoft.Deployment.WindowsInstaller.IEmbeddedUI:
public interface IEmbeddedUI
{
    bool Initialize(Session session, string resourcePath, ref InstallUIOptions internalUILevel);
    MessageResult ProcessMessage(InstallMessage messageType, Record messageRecord...);
    void Shutdown(); 
}
The interface is very limited and it has no dedicated Cancel/Abort method thus Wix# simply returns MessageResult.Abort from the first ProcessMessage method after user clicks 'Cancel' button. Apparently this doesn't lead to the desired result.

What is even worse is that when ProgressDialog is displayed the session handle is no longer valid so it's not possible to call session.DoAction(<action that throws the error>) to trigger rollback indirectly.

Saying that I have came up with the work around and it seems to work reasonably well. I will make it available very soon. Just as I finish the testing.