Skip to content

Commit

Permalink
Monitor UX folder and re-extract any UX payloads that were deleted fo…
Browse files Browse the repository at this point in the history
…r any reason
  • Loading branch information
nirbar committed Oct 22, 2024
1 parent f32c378 commit 0cf3bdf
Show file tree
Hide file tree
Showing 23 changed files with 447 additions and 5 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ This repository contains the PanelSwWix4: A custom WiX Toolset codebase
- [4889](https://github.com/wixtoolset/issues/issues/4889): Support custom container compressions using bundle extensions
- Not overwriting log files when retrying to execute a package
- Support sending custom messages on embedded pipe
- Best effort to log premature termination of companion process
- Best effort to log premature termination of companion process
- Monitor UX folder and re-extract any UX payloads that were deleted for any reason
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ enum BOOTSTRAPPER_APPLICATION_MESSAGE
BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGENONVITALVALIDATIONFAILURE,
BOOTSTRAPPER_APPLICATION_MESSAGE_ONUXPAYLOADDELETED,
};

enum BOOTSTRAPPER_APPLYCOMPLETE_ACTION
Expand Down Expand Up @@ -268,6 +269,13 @@ enum BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION
BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION_ACQUIRE,
};

enum BOOTSTRAPPER_UXPAYLOADDELETED_ACTION
{
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_NONE,
// Instructs the engine to try to reacquire the payload.
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_REACQUIRE,
};

enum BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION
{
BOOTSTRAPPER_CACHEVERIFYCOMPLETE_ACTION_NONE,
Expand Down Expand Up @@ -591,6 +599,20 @@ struct BA_ONCACHEPACKAGENONVITALVALIDATIONFAILURE_RESULTS
BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION action;
};

struct BA_ONUXPAYLOADDELETED_ARGS
{
DWORD cbSize;
LPCWSTR wzPayloadId;
LPCWSTR wzPayloadPath;
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION recommendation;
};

struct BA_ONUXPAYLOADDELETED_RESULTS
{
DWORD cbSize;
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action;
};

struct BA_ONCACHEPAYLOADEXTRACTBEGIN_ARGS
{
DWORD cbSize;
Expand Down
25 changes: 25 additions & 0 deletions src/api/burn/WixToolset.Mba.Core/BootstrapperApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ protected BootstrapperApplication(IEngine engine)
/// <inheritdoc/>
public event EventHandler<CachePackageNonVitalValidationFailureEventArgs> CachePackageNonVitalValidationFailure;

/// <inheritdoc/>
public event EventHandler<UxPayloadDeletedEventArgs> UxPayloadDeleted;

/// <summary>
/// Entry point that is called when the bootstrapper application is ready to run.
/// </summary>
Expand Down Expand Up @@ -1426,6 +1429,19 @@ protected virtual void OnCachePackageNonVitalValidationFailure(CachePackageNonVi
}
}

/// <summary>
/// Called by the engine, raises the <see cref="UxPayloadDeleted"/> event.
/// </summary>
/// <param name="args">Additional arguments for this event.</param>
protected virtual void OnUxPayloadDeleted(UxPayloadDeletedEventArgs args)
{
EventHandler<UxPayloadDeletedEventArgs> handler = this.UxPayloadDeleted;
if (null != handler)
{
handler(this, args);
}
}

#region IBootstrapperApplication Members

int IBootstrapperApplication.BAProc(int message, IntPtr pvArgs, IntPtr pvResults, IntPtr pvContext)
Expand Down Expand Up @@ -2212,6 +2228,15 @@ int IBootstrapperApplication.OnCachePackageNonVitalValidationFailure(string wzPa
return args.HResult;
}

int IBootstrapperApplication.OnUxPayloadDeleted(string wzPayloadId, string wzPayloadPath, BOOTSTRAPPER_UXPAYLOADDELETED_ACTION recommendation, ref BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action)
{
UxPayloadDeletedEventArgs args = new UxPayloadDeletedEventArgs(wzPayloadId, wzPayloadPath, recommendation, action);
this.OnUxPayloadDeleted(args);

action = args.Action;
return args.HResult;
}

#endregion
}
}
28 changes: 28 additions & 0 deletions src/api/burn/WixToolset.Mba.Core/EventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2831,4 +2831,32 @@ public CachePackageNonVitalValidationFailureEventArgs(string packageId, int hrSt
/// </summary>
public string PackageId { get; private set; }
}

/// <summary>
/// Event arguments for <see cref="IDefaultBootstrapperApplication.UxPayloadDeleted"/>
/// </summary>
[Serializable]
public class UxPayloadDeletedEventArgs : ActionEventArgs<BOOTSTRAPPER_UXPAYLOADDELETED_ACTION>
{
/// <summary>
/// This class is for events raised by the engine.
/// It is not intended to be instantiated by user code.
/// </summary>
public UxPayloadDeletedEventArgs(string wzPayloadId, string wzPayloadPath, BOOTSTRAPPER_UXPAYLOADDELETED_ACTION recommendation, BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action)
: base(0, recommendation, action)
{
this.PayloadId = wzPayloadId;
this.PayloadPath = wzPayloadPath;
}

/// <summary>
/// Gets the identity of the missing UX payload.
/// </summary>
public string PayloadId { get; private set; }

/// <summary>
/// Gets the relative path of the missing UX payload.
/// </summary>
public string PayloadPath { get; private set; }
}
}
28 changes: 28 additions & 0 deletions src/api/burn/WixToolset.Mba.Core/IBootstrapperApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,18 @@ int OnCachePackageNonVitalValidationFailure(
BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION recommendation,
ref BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION action
);

/// <summary>
/// See <see cref="IDefaultBootstrapperApplication.UxPayloadDeleted"/>.
/// </summary>
[PreserveSig]
[return: MarshalAs(UnmanagedType.I4)]
int OnUxPayloadDeleted(
[MarshalAs(UnmanagedType.LPWStr)] string wzPayloadId,
[MarshalAs(UnmanagedType.LPWStr)] string wzPayloadPath,
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION recommendation,
ref BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action
);
}

/// <summary>
Expand Down Expand Up @@ -1765,6 +1777,22 @@ public enum BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION
Acquire,
}

/// <summary>
/// The available actions for <see cref="IDefaultBootstrapperApplication.CachePackageNonVitalValidationFailure"/>
/// </summary>
public enum BOOTSTRAPPER_UXPAYLOADDELETED_ACTION
{
/// <summary>
/// Instructs the engine to not take any special action.
/// </summary>
None,

/// <summary>
/// Instructs the engine to try to reacquire the UX payload.
/// </summary>
Reacquire,
}

/// <summary>
/// The available actions for <see cref="IDefaultBootstrapperApplication.CacheVerifyComplete"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ public interface IDefaultBootstrapperApplication : IBootstrapperApplication
/// </summary>
event EventHandler<CachePackageNonVitalValidationFailureEventArgs> CachePackageNonVitalValidationFailure;

/// <summary>
/// Fired when the engine detects that a UX payloads was deleted. This may happen for example, when a cleaning tool deletes files from %TEMP% folder.
/// Note that, the event might be fired multiple times for each missing payload.
/// </summary>
event EventHandler<UxPayloadDeletedEventArgs> UxPayloadDeleted;

/// <summary>
/// Fired when the engine begins the extraction of the payload from the container.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/api/burn/balutil/inc/BAFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ enum BA_FUNCTIONS_MESSAGE
BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL = BOOTSTRAPPER_APPLICATION_MESSAGE_ONEXECUTEPROCESSCANCEL,
BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE,
BA_FUNCTIONS_MESSAGE_ONCACHEPACKAGENONVITALVALIDATIONFAILURE = BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGENONVITALVALIDATIONFAILURE,
BA_FUNCTIONS_MESSAGE_ONUXPAYLOADDELETED = BOOTSTRAPPER_APPLICATION_MESSAGE_ONUXPAYLOADDELETED,

BA_FUNCTIONS_MESSAGE_ONTHEMELOADED = 1024,
BA_FUNCTIONS_MESSAGE_WNDPROC,
Expand Down
10 changes: 10 additions & 0 deletions src/api/burn/balutil/inc/BalBaseBAFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -933,6 +933,16 @@ class CBalBaseBAFunctions : public IBAFunctions
return S_OK;
}

virtual STDMETHODIMP OnUxPayloadDeleted(
__in_z LPCWSTR /*wzPayloadId*/,
__in_z LPCWSTR /*wzPayloadPath*/,
__in BOOTSTRAPPER_UXPAYLOADDELETED_ACTION /*recommendation*/,
__inout BOOTSTRAPPER_UXPAYLOADDELETED_ACTION* /*pAction*/
)
{
return S_OK;
}

public: // IBAFunctions
virtual STDMETHODIMP OnPlan(
)
Expand Down
1 change: 1 addition & 0 deletions src/api/burn/balutil/inc/BalBaseBAFunctionsProc.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ static HRESULT WINAPI BalBaseBAFunctionsProc(
case BA_FUNCTIONS_MESSAGE_ONEXECUTEPROCESSCANCEL:
case BA_FUNCTIONS_MESSAGE_ONDETECTRELATEDBUNDLEPACKAGE:
case BA_FUNCTIONS_MESSAGE_ONCACHEPACKAGENONVITALVALIDATIONFAILURE:
case BA_FUNCTIONS_MESSAGE_ONUXPAYLOADDELETED:
hr = BalBaseBootstrapperApplicationProc((BOOTSTRAPPER_APPLICATION_MESSAGE)message, pvArgs, pvResults, pvContext);
break;
case BA_FUNCTIONS_MESSAGE_ONTHEMELOADED:
Expand Down
10 changes: 10 additions & 0 deletions src/api/burn/balutil/inc/BalBaseBootstrapperApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,16 @@ class CBalBaseBootstrapperApplication : public IBootstrapperApplication
return S_OK;
}

virtual STDMETHODIMP OnUxPayloadDeleted(
__in_z LPCWSTR /*wzPayloadId*/,
__in_z LPCWSTR /*wzPayloadPath*/,
__in BOOTSTRAPPER_UXPAYLOADDELETED_ACTION /*recommendation*/,
__inout BOOTSTRAPPER_UXPAYLOADDELETED_ACTION* /*pAction*/
)
{
return S_OK;
}

public: //CBalBaseBootstrapperApplication
virtual STDMETHODIMP Initialize(
__in const BOOTSTRAPPER_CREATE_ARGS* pCreateArgs
Expand Down
12 changes: 12 additions & 0 deletions src/api/burn/balutil/inc/BalBaseBootstrapperApplicationProc.h
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,15 @@ static HRESULT BalBaseBAProcOnCachePackageNonVitalValidationFailure(
return pBA->OnCachePackageNonVitalValidationFailure(pArgs->wzPackageId, pArgs->hrStatus, pArgs->recommendation, &pResults->action);
}

static HRESULT BalBaseBAProcOnUxPayloadDeleted(
__in IBootstrapperApplication* pBA,
__in BA_ONUXPAYLOADDELETED_ARGS* pArgs,
__inout BA_ONUXPAYLOADDELETED_RESULTS* pResults
)
{
return pBA->OnUxPayloadDeleted(pArgs->wzPayloadId, pArgs->wzPayloadPath, pArgs->recommendation, &pResults->action);
}

/*******************************************************************
BalBaseBootstrapperApplicationProc - requires pvContext to be of type IBootstrapperApplication.
Provides a default mapping between the new message based BA interface and
Expand Down Expand Up @@ -1060,6 +1069,9 @@ static HRESULT WINAPI BalBaseBootstrapperApplicationProc(
case BOOTSTRAPPER_APPLICATION_MESSAGE_ONCACHEPACKAGENONVITALVALIDATIONFAILURE:
hr = BalBaseBAProcOnCachePackageNonVitalValidationFailure(pBA, reinterpret_cast<BA_ONCACHEPACKAGENONVITALVALIDATIONFAILURE_ARGS*>(pvArgs), reinterpret_cast<BA_ONCACHEPACKAGENONVITALVALIDATIONFAILURE_RESULTS*>(pvResults));
break;
case BOOTSTRAPPER_APPLICATION_MESSAGE_ONUXPAYLOADDELETED:
hr = BalBaseBAProcOnUxPayloadDeleted(pBA, reinterpret_cast<BA_ONUXPAYLOADDELETED_ARGS*>(pvArgs), reinterpret_cast<BA_ONUXPAYLOADDELETED_RESULTS*>(pvResults));
break;
}
}

Expand Down
9 changes: 9 additions & 0 deletions src/api/burn/balutil/inc/IBootstrapperApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,4 +765,13 @@ DECLARE_INTERFACE_IID_(IBootstrapperApplication, IUnknown, "53C31D56-49C0-426B-A
__in BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION recommendation,
__inout BOOTSTRAPPER_CACHEPACKAGENONVITALVALIDATIONFAILURE_ACTION* pAction
) = 0;

// OnUxPayloadDeleted - called when the engine detects that a UX payloads was deleted. This may happen for example, when a cleaning tool deletes files from %TEMP% folder.
// Note that, the event might be fired multiple times for each missing payload.
STDMETHOD(OnUxPayloadDeleted)(
__in_z LPCWSTR wzPayloadId,
__in_z LPCWSTR wzPayloadPath,
__in BOOTSTRAPPER_UXPAYLOADDELETED_ACTION recommendation,
__inout BOOTSTRAPPER_UXPAYLOADDELETED_ACTION* pAction
) = 0;
};
58 changes: 58 additions & 0 deletions src/burn/engine/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -487,3 +487,61 @@ extern "C" HRESULT ContainerFindById(
LExit:
return hr;
}

HRESULT ContainerReextractUX(
__in BURN_ENGINE_STATE* pEngineState
)
{
HRESULT hr = S_OK;
BURN_CONTAINER_CONTEXT containerContext = { };
LPWSTR sczStreamName = NULL;
int nNumReextracted = 0;

// Check which payloads are missing
for (DWORD i = 0; i < pEngineState->userExperience.payloads.cPayloads; ++i)
{
BURN_PAYLOAD* pPayload = pEngineState->userExperience.payloads.rgPayloads + i;

if (pPayload->sczLocalFilePath && *pPayload->sczLocalFilePath && (BURN_PAYLOAD_STATE_ACQUIRED == pPayload->state) && !FileExistsEx(pPayload->sczLocalFilePath, NULL))
{
// Notify and let BA decide whether or not to extract the payload
BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action = BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_REACQUIRE;

hr = UserExperienceOnUxPayloadDeleted(&pEngineState->userExperience, pPayload->sczKey, pPayload->sczFilePath, &action);
ExitOnFailure(hr, "Failed to get UX action for deleted payload.");

LogId(REPORT_WARNING, MSG_UX_PAYLOAD_MISSING, pPayload->sczKey, pPayload->sczFilePath, LoggingUxPayloadDeletedAction(action));

if (BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_REACQUIRE == action)
{
pPayload->state = BURN_PAYLOAD_STATE_NONE;
++nNumReextracted;
}
}
}

if (!nNumReextracted)
{
ExitFunction();
}

// Open attached UX container.
hr = ContainerOpenUX(&pEngineState->section, &containerContext);
ExitOnFailure(hr, "Failed to open attached UX container.");

// Skip manifest.
hr = ContainerNextStream(&containerContext, &sczStreamName);
ExitOnFailure(hr, "Failed to open manifest stream.");

hr = ContainerSkipStream(&containerContext);
ExitOnFailure(hr, "Failed to skip manifest stream.");

hr = PayloadExtractUXContainer(&pEngineState->userExperience.payloads, &containerContext, pEngineState->userExperience.sczTempDirectory);
ExitOnFailure(hr, "Failed to extract bootstrapper application payloads.");

LExit:
ContainerClose(&containerContext);
ReleaseStr(sczStreamName);

return hr;
}
4 changes: 4 additions & 0 deletions src/burn/engine/container.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ extern "C" {
// Forward declarations
typedef struct _BURN_EXTENSION BURN_EXTENSION;
typedef struct _BURN_EXTENSIONS BURN_EXTENSIONS;
typedef struct _BURN_ENGINE_STATE BURN_ENGINE_STATE;

// constants

Expand Down Expand Up @@ -180,6 +181,9 @@ HRESULT ContainerOpenUX(
__in BURN_SECTION* pSection,
__in BURN_CONTAINER_CONTEXT* pContext
);
HRESULT ContainerReextractUX(
__in BURN_ENGINE_STATE* pEngineState
);
HRESULT ContainerOpen(
__in BURN_CONTAINER_CONTEXT* pContext,
__in BURN_CONTAINER* pContainer,
Expand Down
7 changes: 7 additions & 0 deletions src/burn/engine/engine.mc
Original file line number Diff line number Diff line change
Expand Up @@ -1309,3 +1309,10 @@ SymbolicName=MSG_PAYLOAD_HARD_LINK_FAILED
Language=English
Creating a hard link to '%1!ls!' failed with error code %2!u!. Resorting to plain copy.
.
MessageId=706
Severity=Warning
SymbolicName=MSG_UX_PAYLOAD_MISSING
Language=English
UX payload deletion was detected, Id: '%1!ls!', path: '%2!ls!', action: %3!ls!.
.
14 changes: 14 additions & 0 deletions src/burn/engine/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,20 @@ extern "C" LPWSTR LoggingStringOrUnknownIfNull(
return wz ? wz : L"Unknown";
}

extern "C" LPCWSTR LoggingUxPayloadDeletedAction(
__in BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action
)
{
switch (action)
{
case BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_NONE:
return L"None";
case BOOTSTRAPPER_UXPAYLOADDELETED_ACTION_REACQUIRE:
return L"Reacquire";
default:
return L"Invalid";
}
}

// internal function declarations

Expand Down
4 changes: 4 additions & 0 deletions src/burn/engine/logging.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,10 @@ LPWSTR LoggingStringOrUnknownIfNull(
__in LPCWSTR wz
);

LPCWSTR LoggingUxPayloadDeletedAction(
__in BOOTSTRAPPER_UXPAYLOADDELETED_ACTION action
);


#if defined(__cplusplus)
}
Expand Down
Loading

0 comments on commit 0cf3bdf

Please sign in to comment.