Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interrupt CA execution #647

Closed
Xaddan opened this issue May 22, 2019 · 9 comments
Closed

Interrupt CA execution #647

Xaddan opened this issue May 22, 2019 · 9 comments

Comments

@Xaddan
Copy link
Contributor

Xaddan commented May 22, 2019

Hi Oleg.
You do not know how to interrupt the execution of CA, if the user clicked "Cancel" during the installation?
I use bootstrapper.

@oleg-shilo
Copy link
Owner

If you are asking how to detect from the inside of the CA that user clicked Cancel then I don't have the answer.

I wouldn't be surprised if WiX sets some special property. If it is the case then you can simply check this property and decide if you want to interrupt the CA execution. If you find the answer then share it and I will add session.IsCanceled for convenience.

Though if you are asking how to cancel the installation from CA to achieve the same effect as when pressing the Cancel button then it is as below:

[CustomAction]
public static ActionResult MyAction(Session session)
{
    . . .
    return ActionResult.UserExit;
}

@Xaddan
Copy link
Contributor Author

Xaddan commented May 22, 2019

I'm talking about your first guess.

@oleg-shilo
Copy link
Owner

OK then.

Of course if you are using managed UI then you can add setting that special property by yourself. However since you are using bootstrapper it is problematic. WiX Burn has a long standing defect that prevents any non-stock UI from being displayed from a bootstrapped session. It is scheduled to be fixed for WiX v4.0 but who knows...

Though if user cancels the install after the the CA has started then relying on property most likely will not work anyway. Why? Because every CA starts as a separate process and the session properties are pushed to the CA at start and retreived back at the end. I am not sure one can pull a fresh set of properties from the middle of CA execution.

@Xaddan
Copy link
Contributor Author

Xaddan commented May 23, 2019

There is progress, I managed to get an exception when canceling the installation.

image

@Xaddan
Copy link
Contributor Author

Xaddan commented May 23, 2019

Judging by the Message method, we won’t get anything except an InstallCanceledException.

public MessageResult Message(InstallMessage messageType, Record record)
{
    if (record == null)
    {
        throw new ArgumentNullException("record");
    }

    int ret = RemotableNativeMethods.MsiProcessMessage((int) this.Handle, (uint) messageType, (int) record.Handle);
    if (ret < 0)
    {
        throw new InstallerException();
    }
    else if (ret == (int) MessageResult.Cancel)
    {
        throw new InstallCanceledException();
    }
    return (MessageResult) ret;
}

@Xaddan
Copy link
Contributor Author

Xaddan commented May 23, 2019

Something like this.

public static class CustomActions
{
    [CustomAction]
    public static ActionResult MyAction(Session session)
    {
        while (true)
        {
            if (session.IsCancelled())
            {
                return ActionResult.UserExit;
            }
        }
    }

    public static bool IsCancelled(this Session session)
    {
        try
        {
            session.Message(InstallMessage.ActionData, new Record());
            return false;
        }
        catch (InstallCanceledException)
        {
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

@Xaddan
Copy link
Contributor Author

Xaddan commented May 23, 2019

Only in this way an exception is written in the logs, which is not good.

Exception thrown by custom action:
System.Reflection.TargetInvocationException: Адресат вызова создал исключение. ---> Microsoft.Deployment.WindowsInstaller.InstallCanceledException: Выдано исключение типа "Microsoft.Deployment.WindowsInstaller.InstallCanceledException".
   в Microsoft.Deployment.WindowsInstaller.Session.Message(InstallMessage messageType, Record record)
   в CustomActions.MyAction(Session session) в C:\Users\Documents\GitHub\wixsharp\Source\src\WixSharp.Samples\Wix# Samples\DTF (ManagedCA)\setup.cs:строка 57
   --- Конец трассировки внутреннего стека исключений ---
   в System.RuntimeMethodHandle.InvokeMethod(Object target, Object arguments, Signature sig, Boolean constructor)
   в System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object parameters, Object arguments)
   в System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object parameters, CultureInfo culture)
   в Microsoft.Deployment.WindowsInstaller.CustomActionProxy.InvokeCustomAction(Int32 sessionHandle, String entryPoint, IntPtr remotingDelegatePtr)
CustomAction MyAction returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)
Action ended 14:04:35: MyAction. Return value 3.

oleg-shilo pushed a commit that referenced this issue May 24, 2019
@oleg-shilo
Copy link
Owner

Sounds great.
I don't see any problem with log having a InstallCanceledException message when user... well indeed canceled the installation :)

However if you prefer a clean exceptionless approach you can use an alternative extension method:

public static bool IsCancelledRaw(this Session session)
{
    // does not throw but will become broken if WiX team changes the implementation
    long ActionData = 0x09000000;
    var RemotableNativeMethods = typeof(Session).Assembly
                                        .GetTypes()
                                        .FirstOrDefault(x => x.Name == "RemotableNativeMethods");

    var MsiProcessMessage = RemotableNativeMethods.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
                                                  .FirstOrDefault(x => x.Name == "MsiProcessMessage");

    var ret = (int)MsiProcessMessage.Invoke(null, new object[]
                                            {
                                                (int)session.Handle,
                                                (uint)ActionData,
                                                (int)new Record().Handle
                                            });

    return (ret == (int)MessageResult.Cancel);
}

@oleg-shilo
Copy link
Owner

The next release will have both Session.IsCanceled and Session.IsCanceledRaw available

oleg-shilo pushed a commit that referenced this issue Jul 4, 2019
- Issue #658: Question: how to close running application or wait until user close application manually
- Issue #656: ExeFileShortcut changing folder name
- Issue #649: How to add permissions to regkey
- Issue #647: Interrupt CA execution #647;
- Implemented `Session.IsCanceled`
- Added `File.OnProcess` and `DirFiles.OnProcess` to provide custom handling on processing wildcards:
  ```
  new Files(@"..\Release Folder\test\*.exe")
  {
      OnProcess = file =>
      {
          file.OverwriteOnInstall = true;
      }
  }, . . .
  ```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants