From 780cd25e41f2d2982807a0a2a24a734684d27fe6 Mon Sep 17 00:00:00 2001
From: Sean Hall <r.sean.hall@gmail.com>
Date: Fri, 15 May 2020 13:48:38 +1000
Subject: WIXFEAT:6164 Add OnPlanMsiPackage.

---
 src/WixToolset.Mba.Core/BootstrapperApplication.cs |  34 ++++++-
 src/WixToolset.Mba.Core/EventArgs.cs               |  74 +++++++++++++-
 .../IBootstrapperApplication.cs                    | 109 +++++++++++++++++++++
 src/balutil/inc/BAFunctions.h                      |   1 +
 src/balutil/inc/BalBaseBAFunctions.h               |  15 +++
 src/balutil/inc/BalBaseBAFunctionsProc.h           |   1 +
 src/balutil/inc/BalBaseBootstrapperApplication.h   |  16 +++
 .../inc/BalBaseBootstrapperApplicationProc.h       |  14 ++-
 src/balutil/inc/IBootstrapperApplication.h         |  14 +++
 9 files changed, 274 insertions(+), 4 deletions(-)

(limited to 'src')

diff --git a/src/WixToolset.Mba.Core/BootstrapperApplication.cs b/src/WixToolset.Mba.Core/BootstrapperApplication.cs
index 249c17c9..9ee909dc 100644
--- a/src/WixToolset.Mba.Core/BootstrapperApplication.cs
+++ b/src/WixToolset.Mba.Core/BootstrapperApplication.cs
@@ -161,6 +161,11 @@ namespace WixToolset.Mba.Core
         /// </summary>
         public event EventHandler<PlanMsiFeatureEventArgs> PlanMsiFeature;
 
+        /// <summary>
+        /// Fired when the engine is planning an MSI or MSP package.
+        /// </summary>
+        public event EventHandler<PlanMsiPackageEventArgs> PlanMsiPackage;
+
         /// <summary>
         /// Fired when the engine has completed planning the installation of a specific package.
         /// </summary>
@@ -658,6 +663,19 @@ namespace WixToolset.Mba.Core
             }
         }
 
+        /// <summary>
+        /// Called when the engine is planning an MSI or MSP package.
+        /// </summary>
+        /// <param name="args">Additional arguments for this event.</param>
+        protected virtual void OnPlanMsiPackage(PlanMsiPackageEventArgs args)
+        {
+            EventHandler<PlanMsiPackageEventArgs> handler = this.PlanMsiPackage;
+            if (null != handler)
+            {
+                handler(this, args);
+            }
+        }
+
         /// <summary>
         /// Called when then engine has completed planning the installation of a specific package.
         /// </summary>
@@ -1288,6 +1306,18 @@ namespace WixToolset.Mba.Core
             return args.HResult;
         }
 
+        int IBootstrapperApplication.OnPlanMsiPackage(string wzPackageId, bool fExecute, ActionState action, ref bool fCancel, ref BURN_MSI_PROPERTY actionMsiProperty, ref INSTALLUILEVEL uiLevel, ref bool fDisableExternalUiHandler)
+        {
+            PlanMsiPackageEventArgs args = new PlanMsiPackageEventArgs(wzPackageId, fExecute, action, fCancel, actionMsiProperty, uiLevel, fDisableExternalUiHandler);
+            this.OnPlanMsiPackage(args);
+
+            fCancel = args.Cancel;
+            actionMsiProperty = args.ActionMsiProperty;
+            uiLevel = args.UiLevel;
+            fDisableExternalUiHandler = args.DisableExternalUiHandler;
+            return args.HResult;
+        }
+
         int IBootstrapperApplication.OnPlanPackageComplete(string wzPackageId, int hrStatus, PackageState state, RequestState requested, ActionState execute, ActionState rollback)
         {
             var args = new PlanPackageCompleteEventArgs(wzPackageId, hrStatus, state, requested, execute, rollback);
@@ -1466,9 +1496,9 @@ namespace WixToolset.Mba.Core
             return args.HResult;
         }
 
-        int IBootstrapperApplication.OnExecutePackageBegin(string wzPackageId, bool fExecute, ActionState action, ref bool fCancel)
+        int IBootstrapperApplication.OnExecutePackageBegin(string wzPackageId, bool fExecute, ActionState action, INSTALLUILEVEL uiLevel, bool fDisableExternalUiHandler, ref bool fCancel)
         {
-            ExecutePackageBeginEventArgs args = new ExecutePackageBeginEventArgs(wzPackageId, fExecute, action, fCancel);
+            ExecutePackageBeginEventArgs args = new ExecutePackageBeginEventArgs(wzPackageId, fExecute, action, uiLevel, fDisableExternalUiHandler, fCancel);
             this.OnExecutePackageBegin(args);
 
             fCancel = args.Cancel;
diff --git a/src/WixToolset.Mba.Core/EventArgs.cs b/src/WixToolset.Mba.Core/EventArgs.cs
index ca0fa173..e739a853 100644
--- a/src/WixToolset.Mba.Core/EventArgs.cs
+++ b/src/WixToolset.Mba.Core/EventArgs.cs
@@ -995,6 +995,64 @@ namespace WixToolset.Mba.Core
         public FeatureState State { get; set; }
     }
 
+    /// <summary>
+    /// Additional arguments used when the engine is planning an MSI or MSP package.
+    /// </summary>
+    [Serializable]
+    public class PlanMsiPackageEventArgs : CancellableHResultEventArgs
+    {
+        /// <summary>
+        /// Creates a new instance of the <see cref="PlanMsiPackageEventArgs"/> class.
+        /// </summary>
+        /// <param name="packageId">The identity of the package planned for.</param>
+        /// <param name="shouldExecute">Whether the package is planned to execute or roll back.</param>
+        /// <param name="action">The action planned for the package.</param>
+        /// <param name="cancelRecommendation">The recommendation from the engine.</param>
+        /// <param name="actionMsiProperty">The requested MSI property to add.</param>
+        /// <param name="uiLevel">The requested internal UI level.</param>
+        /// <param name="disableExternalUiHandler">Whether Burn is requested to set up an external UI handler.</param>
+        public PlanMsiPackageEventArgs(string packageId, bool shouldExecute, ActionState action, bool cancelRecommendation, BURN_MSI_PROPERTY actionMsiProperty, INSTALLUILEVEL uiLevel, bool disableExternalUiHandler)
+            : base(cancelRecommendation)
+        {
+            this.PackageId = packageId;
+            this.ShouldExecute = shouldExecute;
+            this.Action = action;
+            this.ActionMsiProperty = actionMsiProperty;
+            this.UiLevel = uiLevel;
+            this.DisableExternalUiHandler = disableExternalUiHandler;
+        }
+
+        /// <summary>
+        /// Gets identity of the package planned for.
+        /// </summary>
+        public string PackageId { get; private set; }
+
+        /// <summary>
+        /// Gets whether the package is planned to execute or roll back.
+        /// </summary>
+        public bool ShouldExecute { get; private set; }
+
+        /// <summary>
+        /// Gets the action planned for the package.
+        /// </summary>
+        public ActionState Action { get; private set; }
+
+        /// <summary>
+        /// Gets or sets the requested MSI property to add.
+        /// </summary>
+        public BURN_MSI_PROPERTY ActionMsiProperty { get; set; }
+
+        /// <summary>
+        /// Gets or sets the requested internal UI level.
+        /// </summary>
+        public INSTALLUILEVEL UiLevel { get; set; }
+
+        /// <summary>
+        /// Gets or sets whether Burn is requested to set up an external UI handler.
+        /// </summary>
+        public bool DisableExternalUiHandler { get; set; }
+    }
+
     /// <summary>
     /// Additional arguments used when then engine has completed planning the installation of a specific package.
     /// </summary>
@@ -1503,13 +1561,17 @@ namespace WixToolset.Mba.Core
         /// <param name="packageId">The identity of the package to act on.</param>
         /// <param name="shouldExecute">Whether the package is being executed or rolled back.</param>
         /// <param name="action">The action about to be executed.</param>
+        /// <param name="uiLevel">The internal UI level (if this is an MSI or MSP package).</param>
+        /// <param name="disableExternalUiHandler">Whether Burn will set up an external UI handler (if this is an MSI or MSP package).</param>
         /// <param name="cancelRecommendation">The recommendation from the engine.</param>
-        public ExecutePackageBeginEventArgs(string packageId, bool shouldExecute, ActionState action, bool cancelRecommendation)
+        public ExecutePackageBeginEventArgs(string packageId, bool shouldExecute, ActionState action, INSTALLUILEVEL uiLevel, bool disableExternalUiHandler, bool cancelRecommendation)
             : base(cancelRecommendation)
         {
             this.PackageId = packageId;
             this.ShouldExecute = shouldExecute;
             this.Action = action;
+            this.UiLevel = uiLevel;
+            this.DisableExternalUiHandler = disableExternalUiHandler;
         }
 
         /// <summary>
@@ -1526,6 +1588,16 @@ namespace WixToolset.Mba.Core
         /// Gets the action about to be executed.
         /// </summary>
         public ActionState Action { get; private set; }
+
+        /// <summary>
+        /// Gets the internal UI level (if this is an MSI or MSP package).
+        /// </summary>
+        public INSTALLUILEVEL UiLevel { get; private set; }
+
+        /// <summary>
+        /// Gets whether Burn will set up an external UI handler (if this is an MSI or MSP package).
+        /// </summary>
+        public bool DisableExternalUiHandler { get; private set; }
     }
 
     /// <summary>
diff --git a/src/WixToolset.Mba.Core/IBootstrapperApplication.cs b/src/WixToolset.Mba.Core/IBootstrapperApplication.cs
index c4daaf32..fa655282 100644
--- a/src/WixToolset.Mba.Core/IBootstrapperApplication.cs
+++ b/src/WixToolset.Mba.Core/IBootstrapperApplication.cs
@@ -219,6 +219,18 @@ namespace WixToolset.Mba.Core
             [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
             );
 
+        [PreserveSig]
+        [return: MarshalAs(UnmanagedType.I4)]
+        int OnPlanMsiPackage(
+            [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
+            [MarshalAs(UnmanagedType.Bool)] bool fExecute,
+            [MarshalAs(UnmanagedType.U4)] ActionState action,
+            [MarshalAs(UnmanagedType.Bool)] ref bool fCancel,
+            [MarshalAs(UnmanagedType.U4)] ref BURN_MSI_PROPERTY actionMsiProperty,
+            [MarshalAs(UnmanagedType.U4)] ref INSTALLUILEVEL uiLevel,
+            [MarshalAs(UnmanagedType.Bool)] ref bool fDisableExternalUiHandler
+            );
+
         [PreserveSig]
         [return: MarshalAs(UnmanagedType.I4)]
         int OnPlanPackageComplete(
@@ -393,6 +405,8 @@ namespace WixToolset.Mba.Core
             [MarshalAs(UnmanagedType.LPWStr)] string wzPackageId,
             [MarshalAs(UnmanagedType.Bool)] bool fExecute,
             [MarshalAs(UnmanagedType.U4)] ActionState action,
+            [MarshalAs(UnmanagedType.U4)] INSTALLUILEVEL uiLevel,
+            [MarshalAs(UnmanagedType.Bool)] bool fDisableExternalUiHandler,
             [MarshalAs(UnmanagedType.Bool)] ref bool fCancel
             );
 
@@ -832,4 +846,99 @@ namespace WixToolset.Mba.Core
         Restart,
         ReloadBootstrapper,
     }
+
+    /// <summary>
+    /// The property Burn will add so the MSI can know the planned action for the package.
+    /// </summary>
+    public enum BURN_MSI_PROPERTY
+    {
+        /// <summary>
+        /// No property will be added.
+        /// </summary>
+        None,
+
+        /// <summary>
+        /// Add BURNMSIINSTALL=1
+        /// </summary>
+        Install,
+
+        /// <summary>
+        /// Add BURNMSIMODFIY=1
+        /// </summary>
+        Modify,
+
+        /// <summary>
+        /// Add BURNMSIREPAIR=1
+        /// </summary>
+        Repair,
+
+        /// <summary>
+        /// Add BURNMSIUNINSTALL=1
+        /// </summary>
+        Uninstall,
+    }
+
+    /// <summary>
+    /// From msi.h
+    /// https://docs.microsoft.com/en-us/windows/win32/api/msi/nf-msi-msisetinternalui
+    /// </summary>
+    [Flags]
+    public enum INSTALLUILEVEL
+    {
+        /// <summary>
+        /// UI level is unchanged
+        /// </summary>
+        NoChange = 0,
+
+        /// <summary>
+        /// default UI is used
+        /// </summary>
+        Default = 1,
+
+        /// <summary>
+        /// completely silent installation
+        /// </summary>
+        None = 2,
+
+        /// <summary>
+        /// simple progress and error handling
+        /// </summary>
+        Basic = 3,
+
+        /// <summary>
+        /// authored UI, wizard dialogs suppressed
+        /// </summary>
+        Reduced = 4,
+
+        /// <summary>
+        /// authored UI with wizards, progress, errors
+        /// </summary>
+        Full = 5,
+
+        /// <summary>
+        /// display success/failure dialog at end of install
+        /// </summary>
+        EndDialog = 0x80,
+
+        /// <summary>
+        /// display only progress dialog
+        /// </summary>
+        ProgressOnly = 0x40,
+
+        /// <summary>
+        /// do not display the cancel button in basic UI
+        /// </summary>
+        HideCancel = 0x20,
+
+        /// <summary>
+        /// force display of source resolution even if quiet
+        /// </summary>
+        SourceResOnly = 0x100,
+
+        /// <summary>
+        /// show UAC prompt even if quiet
+        /// Can only be used if on Windows Installer 5.0 or later
+        /// </summary>
+        UacOnly = 0x200,
+    }
 }
diff --git a/src/balutil/inc/BAFunctions.h b/src/balutil/inc/BAFunctions.h
index 1338253d..8101afdb 100644
--- a/src/balutil/inc/BAFunctions.h
+++ b/src/balutil/inc/BAFunctions.h
@@ -64,6 +64,7 @@ enum BA_FUNCTIONS_MESSAGE
     BA_FUNCTIONS_MESSAGE_ONAPPLYCOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONAPPLYCOMPLETE,
     BA_FUNCTIONS_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN = BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN,
     BA_FUNCTIONS_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE,
+    BA_FUNCTIONS_MESSAGE_ONPLANMSIPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE,
 
     BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
     BA_FUNCTIONS_MESSAGE_WNDPROC,
diff --git a/src/balutil/inc/BalBaseBAFunctions.h b/src/balutil/inc/BalBaseBAFunctions.h
index 411524fb..4e095fb8 100644
--- a/src/balutil/inc/BalBaseBAFunctions.h
+++ b/src/balutil/inc/BalBaseBAFunctions.h
@@ -298,6 +298,19 @@ public: // IBootstrapperApplication
         return S_OK;
     }
 
+    virtual STDMETHODIMP OnPlanMsiPackage(
+        __in_z LPCWSTR /*wzPackageId*/,
+        __in BOOL /*fExecute*/,
+        __in BOOTSTRAPPER_ACTION_STATE /*action*/,
+        __inout BOOL* /*pfCancel*/,
+        __inout BURN_MSI_PROPERTY* /*pActionMsiProperty*/,
+        __inout INSTALLUILEVEL* /*pUiLevel*/,
+        __inout BOOL* /*pfDisableExternalUiHandler*/
+        )
+    {
+        return S_OK;
+    }
+
     virtual STDMETHODIMP OnPlanPackageComplete(
         __in_z LPCWSTR /*wzPackageId*/,
         __in HRESULT /*hrStatus*/,
@@ -490,6 +503,8 @@ public: // IBootstrapperApplication
         __in_z LPCWSTR /*wzPackageId*/,
         __in BOOL /*fExecute*/,
         __in BOOTSTRAPPER_ACTION_STATE /*action*/,
+        __in INSTALLUILEVEL /*uiLevel*/,
+        __in BOOL /*fDisableExternalUiHandler*/,
         __inout BOOL* /*pfCancel*/
         )
     {
diff --git a/src/balutil/inc/BalBaseBAFunctionsProc.h b/src/balutil/inc/BalBaseBAFunctionsProc.h
index da0a71f7..f6ebd9f6 100644
--- a/src/balutil/inc/BalBaseBAFunctionsProc.h
+++ b/src/balutil/inc/BalBaseBAFunctionsProc.h
@@ -99,6 +99,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
         case BA_FUNCTIONS_MESSAGE_ONAPPLYCOMPLETE:
         case BA_FUNCTIONS_MESSAGE_ONLAUNCHAPPROVEDEXEBEGIN:
         case BA_FUNCTIONS_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE:
+        case BA_FUNCTIONS_MESSAGE_ONPLANMSIPACKAGE:
             hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
             break;
         case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
diff --git a/src/balutil/inc/BalBaseBootstrapperApplication.h b/src/balutil/inc/BalBaseBootstrapperApplication.h
index 269777a6..1d014419 100644
--- a/src/balutil/inc/BalBaseBootstrapperApplication.h
+++ b/src/balutil/inc/BalBaseBootstrapperApplication.h
@@ -311,6 +311,20 @@ public: // IBootstrapperApplication
         return S_OK;
     }
 
+    virtual STDMETHODIMP OnPlanMsiPackage(
+        __in_z LPCWSTR /*wzPackageId*/,
+        __in BOOL /*fExecute*/,
+        __in BOOTSTRAPPER_ACTION_STATE /*action*/,
+        __inout BOOL* pfCancel,
+        __inout BURN_MSI_PROPERTY* /*pActionMsiProperty*/,
+        __inout INSTALLUILEVEL* /*pUiLevel*/,
+        __inout BOOL* /*pfDisableExternalUiHandler*/
+        )
+    {
+        *pfCancel |= CheckCanceled();
+        return S_OK;
+    }
+
     virtual STDMETHODIMP OnPlanPackageComplete(
         __in_z LPCWSTR /*wzPackageId*/,
         __in HRESULT /*hrStatus*/,
@@ -604,6 +618,8 @@ public: // IBootstrapperApplication
         __in_z LPCWSTR wzPackageId,
         __in BOOL fExecute,
         __in BOOTSTRAPPER_ACTION_STATE /*action*/,
+        __in INSTALLUILEVEL /*uiLevel*/,
+        __in BOOL /*fDisableExternalUiHandler*/,
         __inout BOOL* pfCancel
         )
     {
diff --git a/src/balutil/inc/BalBaseBootstrapperApplicationProc.h b/src/balutil/inc/BalBaseBootstrapperApplicationProc.h
index 35bc0a9e..d25af1f7 100644
--- a/src/balutil/inc/BalBaseBootstrapperApplicationProc.h
+++ b/src/balutil/inc/BalBaseBootstrapperApplicationProc.h
@@ -402,7 +402,7 @@ static HRESULT BalBaseBAProcOnExecutePackageBegin(
     __inout BA_ONEXECUTEPACKAGEBEGIN_RESULTS* pResults
     )
 {
-    return pBA->OnExecutePackageBegin(pArgs->wzPackageId, pArgs->fExecute, pArgs->action, &pResults->fCancel);
+    return pBA->OnExecutePackageBegin(pArgs->wzPackageId, pArgs->fExecute, pArgs->action, pArgs->uiLevel, pArgs->fDisableExternalUiHandler, &pResults->fCancel);
 }
 
 static HRESULT BalBaseBAProcOnExecutePatchTarget(
@@ -504,6 +504,15 @@ static HRESULT BalBaseBAProcOnLaunchApprovedExeComplete(
     return pBA->OnLaunchApprovedExeComplete(pArgs->hrStatus, pArgs->dwProcessId);
 }
 
+static HRESULT BalBaseBAProcOnPlanMsiPackage(
+    __in IBootstrapperApplication* pBA,
+    __in BA_ONPLANMSIPACKAGE_ARGS* pArgs,
+    __inout BA_ONPLANMSIPACKAGE_RESULTS* pResults
+    )
+{
+    return pBA->OnPlanMsiPackage(pArgs->wzPackageId, pArgs->fExecute, pArgs->action, &pResults->fCancel, &pResults->actionMsiProperty, &pResults->uiLevel, &pResults->fDisableExternalUiHandler);
+}
+
 /*******************************************************************
 BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
                                      Provides a default mapping between the new message based BA interface and
@@ -689,6 +698,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
         case BOOTSTRAPPER_APPLICATION_MESSAGE_ONLAUNCHAPPROVEDEXECOMPLETE:
             hr = BalBaseBAProcOnLaunchApprovedExeComplete(pBA, reinterpret_cast<BA_ONLAUNCHAPPROVEDEXECOMPLETE_ARGS*>(pvArgs), reinterpret_cast<BA_ONLAUNCHAPPROVEDEXECOMPLETE_RESULTS*>(pvResults));
             break;
+        case BOOTSTRAPPER_APPLICATION_MESSAGE_ONPLANMSIPACKAGE:
+            hr = BalBaseBAProcOnPlanMsiPackage(pBA, reinterpret_cast<BA_ONPLANMSIPACKAGE_ARGS*>(pvArgs), reinterpret_cast<BA_ONPLANMSIPACKAGE_RESULTS*>(pvResults));
+            break;
         }
     }
 
diff --git a/src/balutil/inc/IBootstrapperApplication.h b/src/balutil/inc/IBootstrapperApplication.h
index 30b456c7..e17d2589 100644
--- a/src/balutil/inc/IBootstrapperApplication.h
+++ b/src/balutil/inc/IBootstrapperApplication.h
@@ -194,6 +194,18 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
         __inout BOOL* pfCancel
         ) = 0;
 
+    // OnPlanMsiPackage - called when the engine plans an MSI or MSP package.
+    //
+    STDMETHOD(OnPlanMsiPackage)(
+        __in_z LPCWSTR wzPackageId,
+        __in BOOL fExecute, // false means rollback.
+        __in BOOTSTRAPPER_ACTION_STATE action,
+        __inout BOOL* pfCancel,
+        __inout BURN_MSI_PROPERTY* pActionMsiProperty,
+        __inout INSTALLUILEVEL* pUiLevel,
+        __inout BOOL* pfDisableExternalUiHandler
+        ) = 0;
+
     // OnPlanPackageComplete - called after the engine plans a package.
     //
     STDMETHOD(OnPlanPackageComplete)(
@@ -390,6 +402,8 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
         __in_z LPCWSTR wzPackageId,
         __in BOOL fExecute, // false means rollback.
         __in BOOTSTRAPPER_ACTION_STATE action,
+        __in INSTALLUILEVEL uiLevel,
+        __in BOOL fDisableExternalUiHandler,
         __inout BOOL* pfCancel
         ) = 0;
 
-- 
cgit v1.2.3-55-g6feb