/ installer / PowerToysSetupVNext / SilentFilesInUseBA / SilentFilesInUseBAFunctions.cpp
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  }