@joebigelow / wix / commits / b86beafe

Add dialog for non-RM FilesInUse and Netfx FilesInUse.

Fixes 5208

Sean Hall committed Jul 24, 2022 at 14:09 UTC b86beafe859dea600209d19502bc194affd5fe2b
3 files changed +246 -28
src/ext/Bal/wixstdba/Resources/HyperlinkTheme.wxl
+4
@@ -65,6 +65,10 @@
65 <String Id="FailureCloseButton">&amp;Close</String>
66 <String Id="FilesInUseTitle">Files In Use</String>
67 <String Id="FilesInUseLabel">The following applications are using files that need to be updated:</String>
68 + <String Id="FilesInUseNetfxCloseRadioButton">Close the &amp;applications.</String>
69 <String Id="FilesInUseCloseRadioButton">Close the &amp;applications and attempt to restart them.</String>
70 <String Id="FilesInUseDontCloseRadioButton">&amp;Do not close applications. A reboot will be required.</String>
71 + <String Id="FilesInUseRetryButton">&amp;Retry</String>
72 + <String Id="FilesInUseIgnoreButton">&amp;Ignore</String>
73 + <String Id="FilesInUseExitButton">E&amp;xit</String>
74 </WixLocalization>
src/ext/Bal/wixstdba/Resources/RtfTheme.wxl
+4
@@ -62,6 +62,10 @@
62 <String Id="FailureCloseButton">&amp;Close</String>
63 <String Id="FilesInUseTitle">Files In Use</String>
64 <String Id="FilesInUseLabel">The following applications are using files that need to be updated:</String>
65 + <String Id="FilesInUseNetfxCloseRadioButton">Close the &amp;applications.</String>
66 <String Id="FilesInUseCloseRadioButton">Close the &amp;applications and attempt to restart them.</String>
67 <String Id="FilesInUseDontCloseRadioButton">&amp;Do not close applications. A reboot will be required.</String>
68 + <String Id="FilesInUseRetryButton">&amp;Retry</String>
69 + <String Id="FilesInUseIgnoreButton">&amp;Ignore</String>
70 + <String Id="FilesInUseExitButton">E&amp;xit</String>
71 </WixLocalization>
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+238 -28
@@ -604,6 +604,8 @@ public: // IBootstrapperApplication
604 m_fStartedExecution = FALSE;
605 m_dwCalculatedCacheProgress = 0;
606 m_dwCalculatedExecuteProgress = 0;
607 + m_nLastMsiFilesInUseResult = IDNOACTION;
608 + m_nLastNetfxFilesInUseResult = IDNOACTION;
609
610 return __super::OnApplyBegin(dwPhaseCount, pfCancel);
611 }
@@ -1049,15 +1051,15 @@ public: // IBootstrapperApplication
1051
1052 if (!m_fShowingInternalUiThisPackage && !m_fPrereq && wzPackageId && *wzPackageId)
1053 {
1052 - // If this is an MSI package, display the files-in-use dialog.
1053 - BAL_INFO_PACKAGE* pPackage = NULL;
1054 - BalInfoFindPackageById(&m_Bundle.packages, wzPackageId, &pPackage);
1054 + BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Package %ls has %d applications holding files in use.", wzPackageId, cFiles);
1055
1056 - if (pPackage && BAL_INFO_PACKAGE_TYPE_MSI == pPackage->type)
1056 + switch (source)
1057 {
1058 - BalLog(BOOTSTRAPPER_LOG_LEVEL_VERBOSE, "Package %ls has %d applications holding files in use.", wzPackageId, cFiles);
1059 -
1060 - return ShowFilesInUse(cFiles, rgwzFiles, source);
1058 + case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI:
1059 + case BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM:
1060 + return ShowMsiFilesInUse(cFiles, rgwzFiles, source, pResult);
1061 + case BOOTSTRAPPER_FILES_IN_USE_TYPE_NETFX:
1062 + return ShowNetfxFilesInUse(cFiles, rgwzFiles, pResult);
1063 }
1064 }
1065
@@ -2213,10 +2215,11 @@ private: // privates
2215 }
2216
2217
2216 - int ShowFilesInUse(
2218 + HRESULT ShowMsiFilesInUse(
2219 __in DWORD cFiles,
2220 __in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
2219 - __in BOOTSTRAPPER_FILES_IN_USE_TYPE /*source*/
2221 + __in BOOTSTRAPPER_FILES_IN_USE_TYPE source,
2222 + __inout int* pResult
2223 )
2224 {
2225 HRESULT hr = S_OK;
@@ -2226,9 +2229,9 @@ private: // privates
2229
2230 // If the user has chosen to ignore on a previously displayed "files in use" page,
2231 // we will return the same result for other cases. No need to display the page again.
2229 - if (IDIGNORE == m_nLastFilesInUseResult)
2232 + if (IDIGNORE == m_nLastMsiFilesInUseResult)
2233 {
2231 - nResult = m_nLastFilesInUseResult;
2234 + nResult = m_nLastMsiFilesInUseResult;
2235 }
2236 else if (BOOTSTRAPPER_DISPLAY_FULL == m_command.display) // Only show files in use when using full display mode.
2237 {
@@ -2246,8 +2249,16 @@ private: // privates
2249 }
2250
2251 // Show applications using the files.
2249 - hr = ShowFilesInUseDialog(sczFilesInUse, &nResult);
2250 - ExitOnFailure(hr, "Failed to show files-in-use task dialog.");
2252 + if (BOOTSTRAPPER_FILES_IN_USE_TYPE_MSI_RM == source)
2253 + {
2254 + hr = ShowRestartManagerMsiFilesInUseDialog(sczFilesInUse, &nResult);
2255 + ExitOnFailure(hr, "Failed to show RM files-in-use dialog.");
2256 + }
2257 + else
2258 + {
2259 + hr = ShowStandardMsiFilesInUseDialog(sczFilesInUse, &nResult);
2260 + ExitOnFailure(hr, "Failed to show files-in-use dialog.");
2261 + }
2262 }
2263 else
2264 {
@@ -2260,13 +2271,14 @@ private: // privates
2271 ReleaseStr(sczFilesInUse);
2272
2273 // Remember the answer from the user.
2263 - m_nLastFilesInUseResult = FAILED(hr) ? IDERROR : nResult;
2274 + m_nLastMsiFilesInUseResult = FAILED(hr) ? IDERROR : nResult;
2275 + *pResult = m_nLastMsiFilesInUseResult;
2276
2265 - return m_nLastFilesInUseResult;
2277 + return hr;
2278 }
2279
2280
2269 - int ShowFilesInUseDialog(
2281 + int ShowRestartManagerMsiFilesInUseDialog(
2282 __in_z_opt LPCWSTR sczFilesInUse,
2283 __out int* pnResult
2284 )
@@ -2283,28 +2295,28 @@ private: // privates
2295
2296 // Get the loc strings for the files-in-use task dialog text.
2297 hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseTitle)", &pLocString);
2286 - ExitOnFailure(hr, "Failed to get FilesInUseTitle loc string.");
2298 + BalExitOnFailure(hr, "Failed to get FilesInUseTitle loc string.");
2299
2300 hr = StrAllocString(&sczTitle, pLocString->wzText, 0);
2289 - ExitOnFailure(hr, "Failed to copy FilesInUseTitle loc string.");
2301 + BalExitOnFailure(hr, "Failed to copy FilesInUseTitle loc string.");
2302
2303 hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseLabel)", &pLocString);
2292 - ExitOnFailure(hr, "Failed to get FilesInUseLabel loc string.");
2304 + BalExitOnFailure(hr, "Failed to get FilesInUseLabel loc string.");
2305
2306 hr = StrAllocString(&sczLabel, pLocString->wzText, 0);
2295 - ExitOnFailure(hr, "Failed to copy FilesInUseLabel loc string.");
2307 + BalExitOnFailure(hr, "Failed to copy FilesInUseLabel loc string.");
2308
2309 hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseCloseRadioButton)", &pLocString);
2298 - ExitOnFailure(hr, "Failed to get FilesInUseCloseRadioButton loc string.");
2310 + BalExitOnFailure(hr, "Failed to get FilesInUseCloseRadioButton loc string.");
2311
2312 hr = StrAllocString(&sczCloseRadioButton, pLocString->wzText, 0);
2301 - ExitOnFailure(hr, "Failed to copy FilesInUseCloseRadioButton loc string.");
2313 + BalExitOnFailure(hr, "Failed to copy FilesInUseCloseRadioButton loc string.");
2314
2315 hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseDontCloseRadioButton)", &pLocString);
2304 - ExitOnFailure(hr, "Failed to get FilesInUseDontCloseRadioButton loc string.");
2316 + BalExitOnFailure(hr, "Failed to get FilesInUseDontCloseRadioButton loc string.");
2317
2318 hr = StrAllocString(&sczDontCloseRadioButton, pLocString->wzText, 0);
2307 - ExitOnFailure(hr, "Failed to copy FilesInUseDontCloseRadioButton loc string.");
2319 + BalExitOnFailure(hr, "Failed to copy FilesInUseDontCloseRadioButton loc string.");
2320
2321 const TASKDIALOG_BUTTON rgRadioButtons[] = {
2322 { IDOK, sczCloseRadioButton },
@@ -2325,12 +2337,208 @@ private: // privates
2337 config.nDefaultRadioButton = IDOK;
2338
2339 hr = ::TaskDialogIndirect(&config, &nButton, &nRadioButton, NULL);
2328 - ExitOnFailure(hr, "Failed to show files-in-use task dialog.");
2340 + BalExitOnFailure(hr, "Failed to show RM files-in-use task dialog.");
2341 +
2342 + *pnResult = IDOK == nButton ? nRadioButton : nButton;
2343 +
2344 +#ifdef DEBUG
2345 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: RMFilesInUse task dialog result: button - %d, radio button - %d, result - %d", nButton, nRadioButton, *pnResult);
2346 +#endif
2347 +
2348 + LExit:
2349 + return hr;
2350 + }
2351 +
2352 +
2353 + int ShowStandardMsiFilesInUseDialog(
2354 + __in_z_opt LPCWSTR sczFilesInUse,
2355 + __out int* pnResult
2356 + )
2357 + {
2358 + HRESULT hr = S_OK;
2359 + TASKDIALOGCONFIG config = { };
2360 + LPWSTR sczTitle = NULL;
2361 + LPWSTR sczLabel = NULL;
2362 + LPWSTR sczRetryButton = NULL;
2363 + LPWSTR sczIgnoreButton = NULL;
2364 + LPWSTR sczExitButton = NULL;
2365 + LOC_STRING* pLocString = NULL;
2366 +
2367 + // Get the loc strings for the files-in-use task dialog text.
2368 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseTitle)", &pLocString);
2369 + BalExitOnFailure(hr, "Failed to get FilesInUseTitle loc string.");
2370 +
2371 + hr = StrAllocString(&sczTitle, pLocString->wzText, 0);
2372 + BalExitOnFailure(hr, "Failed to copy FilesInUseTitle loc string.");
2373 +
2374 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseLabel)", &pLocString);
2375 + BalExitOnFailure(hr, "Failed to get FilesInUseLabel loc string.");
2376 +
2377 + hr = StrAllocString(&sczLabel, pLocString->wzText, 0);
2378 + BalExitOnFailure(hr, "Failed to copy FilesInUseLabel loc string.");
2379 +
2380 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseRetryButton)", &pLocString);
2381 + BalExitOnFailure(hr, "Failed to get FilesInUseRetryButton loc string.");
2382 +
2383 + hr = StrAllocString(&sczRetryButton, pLocString->wzText, 0);
2384 + BalExitOnFailure(hr, "Failed to copy FilesInUseRetryButton loc string.");
2385 +
2386 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseIgnoreButton)", &pLocString);
2387 + BalExitOnFailure(hr, "Failed to get FilesInUseIgnoreButton loc string.");
2388 +
2389 + hr = StrAllocString(&sczIgnoreButton, pLocString->wzText, 0);
2390 + BalExitOnFailure(hr, "Failed to copy FilesInUseIgnoreButton loc string.");
2391 +
2392 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseExitButton)", &pLocString);
2393 + BalExitOnFailure(hr, "Failed to get FilesInUseExitButton loc string.");
2394 +
2395 + hr = StrAllocString(&sczExitButton, pLocString->wzText, 0);
2396 + BalExitOnFailure(hr, "Failed to copy FilesInUseExitButton loc string.");
2397 +
2398 + const TASKDIALOG_BUTTON rgButtons[] = {
2399 + { IDRETRY, sczRetryButton },
2400 + { IDIGNORE, sczIgnoreButton },
2401 + { IDCANCEL, sczExitButton },
2402 + };
2403 +
2404 + config.cbSize = sizeof(config);
2405 + config.hwndParent = m_hWnd;
2406 + config.hInstance = m_hModule;
2407 + config.dwFlags = TDF_SIZE_TO_CONTENT | TDF_POSITION_RELATIVE_TO_WINDOW;
2408 + config.pszWindowTitle = sczTitle;
2409 + config.pszMainInstruction = sczLabel;
2410 + config.pszContent = sczFilesInUse ? sczFilesInUse : L"";
2411 + config.pButtons = rgButtons;
2412 + config.cButtons = countof(rgButtons);
2413 +
2414 + hr = ::TaskDialogIndirect(&config, pnResult, NULL, NULL);
2415 + BalExitOnFailure(hr, "Failed to show files-in-use task dialog.");
2416 +
2417 +#ifdef DEBUG
2418 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: FilesInUse task dialog result: %d", *pnResult);
2419 +#endif
2420 +
2421 + LExit:
2422 + return hr;
2423 + }
2424 +
2425 +
2426 + HRESULT ShowNetfxFilesInUse(
2427 + __in DWORD cFiles,
2428 + __in_ecount_z(cFiles) LPCWSTR* rgwzFiles,
2429 + __inout int* pResult
2430 + )
2431 + {
2432 + HRESULT hr = S_OK;
2433 + LPWSTR sczFilesInUse = NULL;
2434 + DWORD_PTR cchLen = 0;
2435 + int nResult = IDERROR;
2436 +
2437 + // If the user has chosen to ignore on a previously displayed "files in use" page,
2438 + // we will return the same result for other cases. No need to display the page again.
2439 + if (IDNO == m_nLastNetfxFilesInUseResult)
2440 + {
2441 + nResult = m_nLastNetfxFilesInUseResult;
2442 + }
2443 + else if (BOOTSTRAPPER_DISPLAY_FULL != m_command.display) // Only show files in use when using full display mode.
2444 + {
2445 + nResult = IDYES;
2446 + }
2447 + else
2448 + {
2449 + for (DWORD i = 0; i < cFiles; ++i)
2450 + {
2451 + hr = ::StringCchLengthW(rgwzFiles[i], STRSAFE_MAX_CCH, reinterpret_cast<UINT_PTR*>(&cchLen));
2452 + BalExitOnFailure(hr, "Failed to calculate length of string.");
2453 +
2454 + if (cchLen)
2455 + {
2456 + hr = StrAllocConcatFormatted(&sczFilesInUse, L"%ls\r\n", rgwzFiles[i]);
2457 + BalExitOnFailure(hr, "Failed to concat files in use.");
2458 + }
2459 + }
2460 +
2461 + // Show applications using the files.
2462 + hr = ShowNetfxFilesInUseDialog(sczFilesInUse, &nResult);
2463 + ExitOnFailure(hr, "Failed to show Netfx files-in-use dialog.");
2464 + }
2465 +
2466 + LExit:
2467 + ReleaseStr(sczFilesInUse);
2468 +
2469 + // Remember the answer from the user.
2470 + m_nLastNetfxFilesInUseResult = FAILED(hr) ? IDERROR : nResult;
2471 + *pResult = m_nLastNetfxFilesInUseResult;
2472 +
2473 + return hr;
2474 + }
2475 +
2476 +
2477 + int ShowNetfxFilesInUseDialog(
2478 + __in_z_opt LPCWSTR sczFilesInUse,
2479 + __out int* pnResult
2480 + )
2481 + {
2482 + HRESULT hr = S_OK;
2483 + TASKDIALOGCONFIG config = { };
2484 + LPWSTR sczTitle = NULL;
2485 + LPWSTR sczLabel = NULL;
2486 + LPWSTR sczNetfxCloseRadioButton = NULL;
2487 + LPWSTR sczDontCloseRadioButton = NULL;
2488 + LOC_STRING* pLocString = NULL;
2489 + int nButton = 0;
2490 + int nRadioButton = 0;
2491 +
2492 + // Get the loc strings for the files-in-use task dialog text.
2493 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseTitle)", &pLocString);
2494 + BalExitOnFailure(hr, "Failed to get FilesInUseTitle loc string.");
2495 +
2496 + hr = StrAllocString(&sczTitle, pLocString->wzText, 0);
2497 + BalExitOnFailure(hr, "Failed to copy FilesInUseTitle loc string.");
2498 +
2499 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseLabel)", &pLocString);
2500 + BalExitOnFailure(hr, "Failed to get FilesInUseLabel loc string.");
2501 +
2502 + hr = StrAllocString(&sczLabel, pLocString->wzText, 0);
2503 + BalExitOnFailure(hr, "Failed to copy FilesInUseLabel loc string.");
2504 +
2505 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseNetfxCloseRadioButton)", &pLocString);
2506 + BalExitOnFailure(hr, "Failed to get FilesInUseNetfxCloseRadioButton loc string.");
2507 +
2508 + hr = StrAllocString(&sczNetfxCloseRadioButton, pLocString->wzText, 0);
2509 + BalExitOnFailure(hr, "Failed to copy FilesInUseNetfxCloseRadioButton loc string.");
2510 +
2511 + hr = LocGetString(m_pWixLoc, L"#(loc.FilesInUseDontCloseRadioButton)", &pLocString);
2512 + BalExitOnFailure(hr, "Failed to get FilesInUseDontCloseRadioButton loc string.");
2513 +
2514 + hr = StrAllocString(&sczDontCloseRadioButton, pLocString->wzText, 0);
2515 + BalExitOnFailure(hr, "Failed to copy FilesInUseDontCloseRadioButton loc string.");
2516 +
2517 + const TASKDIALOG_BUTTON rgRadioButtons[] = {
2518 + { IDYES, sczNetfxCloseRadioButton },
2519 + { IDNO, sczDontCloseRadioButton },
2520 + };
2521 +
2522 + config.cbSize = sizeof(config);
2523 + config.hwndParent = m_hWnd;
2524 + config.hInstance = m_hModule;
2525 + config.dwFlags = TDF_SIZE_TO_CONTENT | TDF_POSITION_RELATIVE_TO_WINDOW;
2526 + config.dwCommonButtons = TDCBF_RETRY_BUTTON | TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON;
2527 + config.pszWindowTitle = sczTitle;
2528 + config.pszMainInstruction = sczLabel;
2529 + config.pszContent = sczFilesInUse ? sczFilesInUse : L"";
2530 + config.nDefaultButton = IDRETRY;
2531 + config.pRadioButtons = rgRadioButtons;
2532 + config.cRadioButtons = countof(rgRadioButtons);
2533 + config.nDefaultRadioButton = IDYES;
2534 +
2535 + hr = ::TaskDialogIndirect(&config, &nButton, &nRadioButton, NULL);
2536 + BalExitOnFailure(hr, "Failed to show Netfx files-in-use task dialog.");
2537
2538 *pnResult = IDOK == nButton ? nRadioButton : nButton;
2539
2540 #ifdef DEBUG
2333 - BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: FilesInUse task dialog result: button - %d, radio button - %d, result - %d", nButton, nRadioButton, *pnResult);
2541 + BalLog(BOOTSTRAPPER_LOG_LEVEL_STANDARD, "WIXSTDBA: NetfxFilesInUse task dialog result: button - %d, radio button - %d, result - %d", nButton, nRadioButton, *pnResult);
2542 #endif
2543
2544 LExit:
@@ -4423,7 +4631,8 @@ public:
4631 m_fPrereqInstalled = FALSE;
4632 m_fPrereqSkipped = FALSE;
4633
4426 - m_nLastFilesInUseResult = IDNOACTION;
4634 + m_nLastMsiFilesInUseResult = IDNOACTION;
4635 + m_nLastNetfxFilesInUseResult = IDNOACTION;
4636
4637 pEngine->AddRef();
4638 m_pEngine = pEngine;
@@ -4717,7 +4926,8 @@ private:
4926 BOOL m_fShowingInternalUiThisPackage;
4927 BOOL m_fTriedToLaunchElevated;
4928
4720 - int m_nLastFilesInUseResult;
4929 + int m_nLastMsiFilesInUseResult;
4930 + int m_nLastNetfxFilesInUseResult;
4931
4932 HMODULE m_hBAFModule;
4933 PFN_BA_FUNCTIONS_PROC m_pfnBAFunctionsProc;