wixtoolset / issues

WiX Toolset Issues Tracker
http://wixtoolset.org/
129 stars 36 forks source link

If Launch after Repair with standard bootstrapper, error if then try to Change or Uninstall. #6130

Open kmackey1 opened 4 years ago

kmackey1 commented 4 years ago

Please provide answers to the following questions to help us narrow down, reproduce, and fix the problem. Fill out one section and delete the others.

3.11.1.2318

15.9.19 2017

1.0.0.4

4.8.03752

Windows 7 and Windows 10

  1. Install product using MsiPackage in standard bootstrapper.
  2. In Programs and Features (not Apps and Features) choose Change (runs standard bootstrapper).
  3. In bootstrapper's Modify Setup dialog click Repair.
  4. At end of Repair, click Launch to launch the product application.
  5. If in Programs and Features click Uninstall or Change, see error: "Please wait until the current program is finished uninstalling or being changed."
  6. If close the launched app, then no error if click Uninstall or Change.

Note that this only happens using Programs and Features on Windows 10 and Windows 7, and not Apps and Features on Windows 10.

It also only happens with Launch after Repair and not Launch after a fresh install, so something about the way Programs and Features launches the standard bootstrapper when choose Change.

The error appears even if Launch opens a non-product application like Notepad.

I originally found the bug when using my custom bootstrapper, which targets .Net 3.5.

It's an edge case, but would be nice if there was a fix.

No error message when choose to Change or Uninstall.

kmackey1 commented 4 years ago

Thanks for the tip that Programs and Features may be using a Job.

I found a C# fix using Win32_Process.Create:

using System.Management; // ObjectGetOptions, ManagementPath, ManagementClass
. . .
        [DllImport("user32.dll")]
        static extern bool SetForegroundWindow(IntPtr hWnd);
. . .
            ObjectGetOptions objectGetOptions = new ObjectGetOptions();
            ManagementPath managementPath = new ManagementPath("Win32_Process");
            ManagementClass classInstance = new ManagementClass(managementPath, objectGetOptions);

            ManagementBaseObject inParams = classInstance.GetMethodParameters("Create");
            string installDir = Bootstrapper.Engine.FormatString(Bootstrapper.Engine.StringVariables["InstallDir"]);
            string pathAndFile = Path.Combine(installDir, "myapp.exe");
            inParams["CommandLine"] = pathAndFile;
            try
            {
                ManagementBaseObject outParams = classInstance.InvokeMethod("Create", inParams, null);
                UInt32 result = (UInt32)outParams["ReturnValue"];
                if (result != 0)
                {
                    this.Bootstrapper.Engine.Log(LogLevel.Standard, $"Error from Win32_Process.Create when attempting to start myapp. Error code: " + result.ToString());
                    return;
                }
                UInt32 processId = (UInt32)outParams["ProcessId"];
                IntPtr hwndToMyApp = GetWindowHandleFromProcessId(processId); // Uses EnumWindows and GetWindowThreadProcessId.
                SetForegroundWindow(hwndToMyApp); // Bring window to top so user can see it.
            }
            catch (System.Exception e)
            {
                this.Bootstrapper.Engine.Log(LogLevel.Standard, $"Error when attempting to start myapp: {e} File: " + pathAndFile);
            }