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">&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 &applications.</String>
69
<String Id="FilesInUseCloseRadioButton">Close the &applications and attempt to restart them.</String>
70
<String Id="FilesInUseDontCloseRadioButton">&Do not close applications. A reboot will be required.</String>
71
+ <String Id="FilesInUseRetryButton">&Retry</String>
72
+ <String Id="FilesInUseIgnoreButton">&Ignore</String>
73
+ <String Id="FilesInUseExitButton">E&xit</String>
74
</WixLocalization>
src/ext/Bal/wixstdba/Resources/RtfTheme.wxl
+4
@@ -62,6 +62,10 @@
62
<String Id="FailureCloseButton">&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 &applications.</String>
66
<String Id="FilesInUseCloseRadioButton">Close the &applications and attempt to restart them.</String>
67
<String Id="FilesInUseDontCloseRadioButton">&Do not close applications. A reboot will be required.</String>
68
+ <String Id="FilesInUseRetryButton">&Retry</String>
69
+ <String Id="FilesInUseIgnoreButton">&Ignore</String>
70
+ <String Id="FilesInUseExitButton">E&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;