SilentFilesInUseBAFunctions.cpp
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. 2 3 #include "pch.h" 4 #include "BalBaseBAFunctions.h" 5 #include "BalBaseBAFunctionsProc.h" 6 7 class CSilentFilesInUseBAFunctions : public CBalBaseBAFunctions 8 { 9 public: // IBootstrapperApplication 10 virtual STDMETHODIMP OnDetectBegin( 11 __in BOOL fCached, 12 __in BOOTSTRAPPER_REGISTRATION_TYPE registrationType, 13 __in DWORD cPackages, 14 __inout BOOL* pfCancel 15 ) 16 { 17 HRESULT hr = S_OK; 18 19 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running detect begin BA function. fCached=%d, registrationType=%d, cPackages=%u, fCancel=%d", fCached, registrationType, cPackages, *pfCancel); 20 21 return hr; 22 } 23 24 public: // IBAFunctions 25 virtual STDMETHODIMP OnPlanBegin( 26 __in DWORD cPackages, 27 __inout BOOL* pfCancel 28 ) 29 { 30 HRESULT hr = S_OK; 31 32 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running plan begin BA function. cPackages=%u, fCancel=%d", cPackages, *pfCancel); 33 34 //------------------------------------------------------------------------------------------------- 35 // YOUR CODE GOES HERE 36 // BalExitOnFailure(hr, "Change this message to represent real error handling."); 37 //------------------------------------------------------------------------------------------------- 38 39 return hr; 40 } 41 42 virtual STDMETHODIMP OnExecuteBegin( 43 __in DWORD cExecutingPackages, 44 __inout BOOL* pfCancel 45 ) 46 { 47 HRESULT hr = S_OK; 48 49 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running execute begin BA function. cExecutingPackages=%u, fCancel=%d", cExecutingPackages, *pfCancel); 50 51 return hr; 52 } 53 54 virtual STDMETHODIMP OnExecuteFilesInUse( 55 __in_z LPCWSTR wzPackageId, 56 __in DWORD cFiles, 57 __in_ecount_z(cFiles) LPCWSTR* rgwzFiles, 58 __in int nRecommendation, 59 __in BOOTSTRAPPER_FILES_IN_USE_TYPE /* source */, 60 __inout int* pResult 61 ) 62 { 63 HRESULT hr = S_OK; 64 65 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION CALLED *** Running OnExecuteFilesInUse BA function. packageId=%ls, cFiles=%u, recommendation=%d", wzPackageId, cFiles, nRecommendation); 66 67 // Log each file that's in use 68 for (DWORD i = 0; i < cFiles; i++) 69 { 70 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** FILE IN USE [%u]: %ls", i, rgwzFiles[i]); 71 } 72 73 /* 74 * Summary: Why we return IDIGNORE here 75 * 76 * - Goal: Keep behavior consistent with our previous WiX 3 installer to avoid "files in use / close apps" prompts and preserve silent installs (e.g., winget). 77 * - WiX 5 change: We can no longer suppress that dialog the same way. Combined with winget adding /silent, this BAFunction returns IDIGNORE to continue without prompts. 78 * - Main trigger: Win10-style context menu uses registry + DLL; Explorer/dllhost.exe (COM Surrogate) often holds locks. Killing them is disruptive; this is a pragmatic trade-off. 79 * - Trade-off: Some file replacements may defer until reboot (PendingFileRename), but installation remains non-interruptive. 80 * - Full fix: Rewrite a custom Bootstrapper Application if we need complete control over prompts and behavior. 81 * - Note: Even with this handler, a full-UI install (e.g., double-clicking the installer) can still show a FilesInUse dialog; this primarily targets silent installs. 82 */ 83 *pResult = IDIGNORE; 84 85 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION RETURNING IDIGNORE - SILENTLY CONTINUING ***"); 86 87 return hr; 88 } 89 90 virtual STDMETHODIMP OnExecuteComplete( 91 __in HRESULT hrStatus, 92 __inout BOOL* pfCancel 93 ) 94 { 95 HRESULT hr = S_OK; 96 97 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CUSTOM BA FUNCTION SYSTEM ACTIVE *** Running execute complete BA function. hrStatus=0x%x, fCancel=%d", hrStatus, *pfCancel); 98 99 return hr; 100 } 101 102 public: 103 // 104 // Constructor - initialize member variables. 105 // 106 CSilentFilesInUseBAFunctions( 107 __in HMODULE hModule 108 ) : CBalBaseBAFunctions(hModule) 109 { 110 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION CONSTRUCTOR *** CSilentFilesInUseBAFunctions created"); 111 } 112 113 // 114 // Destructor - release member variables. 115 // 116 ~CSilentFilesInUseBAFunctions() 117 { 118 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** BA FUNCTION DESTRUCTOR *** CSilentFilesInUseBAFunctions destroyed"); 119 } 120 }; 121 122 123 HRESULT WINAPI CreateBAFunctions( 124 __in HMODULE hModule, 125 __in const BA_FUNCTIONS_CREATE_ARGS* pArgs, 126 __inout BA_FUNCTIONS_CREATE_RESULTS* pResults 127 ) 128 { 129 HRESULT hr = S_OK; 130 CSilentFilesInUseBAFunctions* pBAFunctions = NULL; 131 132 // First thing - log that we're being called 133 BalInitialize(pArgs->pEngine); 134 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS CALLED *** BA Function DLL is being loaded!"); 135 136 pBAFunctions = new CSilentFilesInUseBAFunctions(hModule); 137 ExitOnNull(pBAFunctions, hr, E_OUTOFMEMORY, "Failed to create new CSilentFilesInUseBAFunctions object."); 138 139 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS *** Created CSilentFilesInUseBAFunctions object"); 140 141 hr = pBAFunctions->OnCreate(pArgs->pEngine, pArgs->pCommand); 142 ExitOnFailure(hr, "Failed to call OnCreate CPrereqBaf."); 143 144 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS *** OnCreate completed successfully"); 145 146 pResults->pfnBAFunctionsProc = BalBaseBAFunctionsProc; 147 pResults->pvBAFunctionsProcContext = pBAFunctions; 148 pBAFunctions = NULL; 149 150 BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "*** CREATEBAFUNCTIONS SUCCESS *** BA Function system initialized"); 151 152 LExit: 153 if (FAILED(hr)) 154 { 155 BalLog(BOOTSTRAPPER_LOG_LEVEL_ERROR, "*** CREATEBAFUNCTIONS FAILED *** hr=0x%x", hr); 156 } 157 ReleaseObject(pBAFunctions); 158 159 return hr; 160 }