From 9735459552527f8c401e5fac2f41ac412a30d715 Mon Sep 17 00:00:00 2001 From: Andrey Nekrasov Date: Fri, 27 Nov 2020 00:31:00 +0300 Subject: [PATCH] =?UTF-8?q?bootstrapper:=20use=20WinAPI=20progress=20bar?= =?UTF-8?q?=20window=20instead=20of=20toast=20notific=E2=80=A6=20(#8210)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bootstrapper: use WinAPI progress bar window instead of toast notifications * remove obsolete msi action --- .github/actions/spell-check/expect.txt | 10 ++ .../bootstrapper/Resources.resx | 18 +-- .../bootstrapper/bootstrapper.cpp | 136 ++++++---------- .../bootstrapper/bootstrapper.vcxproj | 6 +- .../PowerToysBootstrapper/bootstrapper/pch.h | 2 + .../bootstrapper/progressbar_window.cpp | 150 ++++++++++++++++++ .../bootstrapper/progressbar_window.h | 9 ++ .../bootstrapper/resource.base.h | 1 + installer/PowerToysSetup/Product.wxs | 12 -- src/action_runner/action_runner.cpp | 15 -- src/common/updating/dotnet_installation.cpp | 13 +- src/common/updating/dotnet_installation.h | 7 +- src/common/updating/pch.h | 2 + 13 files changed, 246 insertions(+), 135 deletions(-) create mode 100644 installer/PowerToysBootstrapper/bootstrapper/progressbar_window.cpp create mode 100644 installer/PowerToysBootstrapper/bootstrapper/progressbar_window.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index c0fe2f2d42..8143e72b72 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -16,6 +16,7 @@ Acceleratorkeys ACCEPTFILES accessibile accessibilityinsights +acf Acl aclapi AColumn @@ -154,6 +155,7 @@ bc bcc bck Bcl +bdaa bddac BEGINLABELEDIT betadele @@ -955,6 +957,7 @@ IBackground IBeam IBind icase +iccex IClass ico ICollection @@ -1045,6 +1048,7 @@ Infotip ingbuffer inheritdoc ini +INITCOMMONCONTROLSEX INITDIALOG INITGUID inl @@ -1328,6 +1332,7 @@ makeappx MAKEINTRESOURCE MAKEINTRESOURCEW MAKELANGID +MAKELPARAM makepri malloc manifestdependency @@ -1424,6 +1429,7 @@ MSIRESTARTMANAGERCONTROL msix msixbundle MSIXVERSION +MSGBOX MSLLHOOKSTRUCT Mso msp @@ -1450,6 +1456,7 @@ NAMECHANGE nameof NAMEONLY namespace +NATIVEFNTCTL nc NCACTIVATE ncc @@ -1979,8 +1986,10 @@ SETICON setings setlocal setnt +SETRANGE Setrect SETREDRAW +SETSTEP SETTEXT SETTINGCHANGE settingsheader @@ -2130,6 +2139,7 @@ STDMETHODIMP stdout Stdout stefan +STEPIT stgm STGMEDIUM stoi diff --git a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx index 62fa30db44..7d5dc48c2a 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx +++ b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx @@ -58,6 +58,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + PowerToys installer + Couldn't download .NET Core Desktop Runtime 3.1, please install it manually. @@ -130,24 +133,15 @@ Extracting PowerToys MSI... - - Uninstalling previous PowerToys version... - Couldn't uninstall previous PowerToys version! - - Installing dotnet... + + Downloading .NET Core Runtime... Couldn't install dotnet! - - Installing new PowerToys version... - - - PowerToys installation complete! - Couldn't install new PowerToys version. @@ -160,5 +154,5 @@ Failed to connect to the server. Check your network connection or retry later. - + diff --git a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp index daffb571a4..e2af68fcb1 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp +++ b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp @@ -2,7 +2,6 @@ #include "Generated Files/resource.h" #include -#include #include #include #include @@ -12,6 +11,8 @@ #include +#include "progressbar_window.h" + extern "C" IMAGE_DOS_HEADER __ImageBase; auto Strings = create_notifications_strings(); @@ -22,7 +23,7 @@ auto Strings = create_notifications_strings(); namespace // Strings in this namespace should not be localized { const wchar_t APPLICATION_ID[] = L"PowerToysInstaller"; - const wchar_t INSTALLATION_TOAST_TITLE[] = L"PowerToys Installation"; + const wchar_t INSTALLATION_MSGBOX_TITLE[] = L"PowerToys Installation"; const wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress"; const char LOG_FILENAME[] = "powertoys-bootstrapper-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log"; const char MSI_LOG_FILENAME[] = "powertoys-bootstrapper-msi-" STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_REVISION) ".log"; @@ -44,17 +45,6 @@ std::optional extractEmbeddedInstaller() return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt; } -std::optional extractIcon() -{ - auto iconRes = RcResource::create(IDR_BIN_ICON, L"BIN"); - if (!iconRes) - { - return std::nullopt; - } - auto icoPath = fs::temp_directory_path() / L"PowerToysBootstrappedInstaller.ico"; - return iconRes->saveAsFile(icoPath) ? std::make_optional(std::move(icoPath)) : std::nullopt; -} - void setup_log(fs::path directory, const spdlog::level::level_enum severity) { try @@ -84,7 +74,15 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity) } } -int bootstrapper() +void show_error_box(const wchar_t* message, const wchar_t* title) +{ + MessageBoxW(nullptr, + message, + title, + MB_OK | MB_ICONERROR); +} + +int bootstrapper(HINSTANCE hInstance) { winrt::init_apartment(); cxxopts::Options options{ "PowerToysBootstrapper" }; @@ -233,19 +231,6 @@ int bootstrapper() spdlog::error("Couldn't acquire PowerToys global mutex. That means setup couldn't kill PowerToys.exe process"); return 1; } - notifications::override_application_id(APPLICATION_ID); - spdlog::debug("Extracting icon for toast notifications"); - fs::path iconPath{ L"C:\\" }; - if (auto extractedIcon = extractIcon()) - { - iconPath = std::move(*extractedIcon); - } - spdlog::debug("Registering app id for toast notifications"); - notifications::register_application_id(INSTALLATION_TOAST_TITLE, iconPath.c_str()); - - auto removeShortcut = wil::scope_exit([&] { - notifications::unregister_application_id(); - }); // Check if there's a newer version installed, and launch its installer if so. const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION); @@ -260,58 +245,13 @@ int bootstrapper() } } - std::mutex progressLock; - notifications::progress_bar_params progressParams; - progressParams.progress = 0.0f; - progressParams.progress_title = GET_RESOURCE_STRING(IDS_EXTRACTING_INSTALLER); - notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) }; - if (!silent) - { - spdlog::debug("Launching progress toast notification"); - notifications::show_toast_with_activations({}, INSTALLATION_TOAST_TITLE, {}, {}, std::move(params)); - } - - auto processToasts = wil::scope_exit([&] { - spdlog::debug("Processing HWND messages for 2s so toast have time to show up"); - run_message_loop(true, 2); - }); - - if (!silent) - { - // Worker thread to periodically increase progress and keep the progress toast from losing focus - std::thread{ [&] { - spdlog::debug("Started worker thread for progress bar update"); - for (;; Sleep(3000)) - { - std::scoped_lock lock{ progressLock }; - if (progressParams.progress == 1.f) - { - break; - } - progressParams.progress = std::min(0.99f, progressParams.progress + 0.001f); - notifications::update_toast_progress_bar(TOAST_TAG, progressParams); - } - } }.detach(); - } - - auto updateProgressBar = [&](const float value, const wchar_t* title) { - if (silent) - { - return; - } - std::scoped_lock lock{ progressLock }; - progressParams.progress = value; - progressParams.progress_title = title; - notifications::update_toast_progress_bar(TOAST_TAG, progressParams); - }; - spdlog::debug("Extracting embedded MSI installer"); const auto installerPath = extractEmbeddedInstaller(); if (!installerPath) { if (!silent) { - notifications::show_toast(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR), INSTALLATION_TOAST_TITLE); + show_error_box(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); } spdlog::error("Couldn't install the MSI installer ({})", GetLastError()); return 1; @@ -321,7 +261,6 @@ int bootstrapper() fs::remove(*installerPath, _); }); - updateProgressBar(.25f, GET_RESOURCE_STRING(IDS_UNINSTALLING_PREVIOUS_VERSION).c_str()); spdlog::debug("Acquiring existing MSI package path"); const auto package_path = updating::get_msi_package_path(); if (!package_path.empty()) @@ -332,15 +271,18 @@ int bootstrapper() { spdlog::debug("Existing MSI package path not found"); } - if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings) && !silent) + if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings)) { spdlog::error("Couldn't install the existing MSI package ({})", GetLastError()); - notifications::show_toast(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR), INSTALLATION_TOAST_TITLE); + if (!silent) + { + show_error_box(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); + } } const bool installDotnet = !skipDotnetInstall; - if (installDotnet) + if (!silent) { - updateProgressBar(.5f, GET_RESOURCE_STRING(IDS_INSTALLING_DOTNET).c_str()); + open_progressbar_window(hInstance, 0, GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(), GET_RESOURCE_STRING(IDS_DOWNLOADING_DOTNET).c_str()); } try @@ -350,11 +292,31 @@ int bootstrapper() spdlog::debug("Detecting if dotnet is installed"); const bool dotnetInstalled = updating::dotnet_is_installed(); spdlog::debug("Dotnet is installed: {}", dotnetInstalled); - if (!dotnetInstalled && - !updating::install_dotnet(silent) && - !silent) + if (!dotnetInstalled) { - notifications::show_toast(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR), INSTALLATION_TOAST_TITLE); + bool installed_successfully = false; + if (const auto dotnet_installer_path = updating::download_dotnet()) + { + // Dotnet installer has its own progress bar + close_progressbar_window(); + installed_successfully = updating::install_dotnet(*dotnet_installer_path, silent); + if (!installed_successfully) + { + spdlog::error("Couldn't install dotnet"); + } + } + else + { + spdlog::error("Couldn't download dotnet"); + } + + if (!installed_successfully) + { + if (!silent) + { + show_error_box(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); + } + } } } } @@ -364,19 +326,19 @@ int bootstrapper() MessageBoxW(nullptr, L".NET Core installation", L"Unknown exception encountered!", MB_OK | MB_ICONERROR); } - updateProgressBar(.75f, GET_RESOURCE_STRING(IDS_INSTALLING_NEW_VERSION).c_str()); + // At this point, there's no reason to show progress bar window, since MSI installers have their own + close_progressbar_window(); // Always skip dotnet install, because we should've installed it from here earlier std::wstring msiProps = L"SKIPDOTNETINSTALL=1 "; spdlog::debug("Launching MSI installation for new package {}", installerPath->string()); const bool installationDone = MsiInstallProductW(installerPath->c_str(), msiProps.c_str()) == ERROR_SUCCESS; - updateProgressBar(1.f, - installationDone ? GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_DONE).c_str() : GET_RESOURCE_STRING(IDS_NEW_VERSION_INSTALLATION_ERROR).c_str()); if (!installationDone) { spdlog::error("Couldn't install new MSI package ({})", GetLastError()); return 1; } + spdlog::debug("Installation completed"); if (!noStartPT && !silent) @@ -399,11 +361,11 @@ int bootstrapper() return 0; } -int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) +int WINAPI WinMain(HINSTANCE hi, HINSTANCE, LPSTR, int) { try { - return bootstrapper(); + return bootstrapper(hi); } catch (const std::exception& ex) { diff --git a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.vcxproj b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.vcxproj index f03a56aa49..8b2aa64603 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.vcxproj +++ b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.vcxproj @@ -90,7 +90,7 @@ true true true - WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies) + WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;%(AdditionalDependencies) @@ -110,7 +110,7 @@ Windows true - WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies) + WindowsApp.lib;Msi.lib;Shlwapi.lib;Comctl32.lib;%(AdditionalDependencies) @@ -119,10 +119,12 @@ Create Create + + diff --git a/installer/PowerToysBootstrapper/bootstrapper/pch.h b/installer/PowerToysBootstrapper/bootstrapper/pch.h index 84ffdb89ed..80934f7d21 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/pch.h +++ b/installer/PowerToysBootstrapper/bootstrapper/pch.h @@ -4,6 +4,7 @@ #define WIN32_LEAN_AND_MEAN #include #include +#include #include #include @@ -12,6 +13,7 @@ #include #include +#include #include #include diff --git a/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.cpp b/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.cpp new file mode 100644 index 0000000000..f8ddafed21 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.cpp @@ -0,0 +1,150 @@ +#include "pch.h" + +#include + +#include "progressbar_window.h" +#include "Generated Files/resource.h" + +const int label_height = 20; + +const int progress_bar_height = 15; +const int progress_bar_margin = 10; + +const int window_width = 450; +const int title_bar_height = 32; +const int window_height = progress_bar_margin * 3 + progress_bar_height + label_height + title_bar_height; + +int progressbar_steps = 0; + +HWND progress_bar; +HWND main_window; +HWND label; + +std::wstring initial_label; +std::mutex ui_thread_is_running; + +namespace nonlocalized +{ + const wchar_t window_class[] = L"PTBProgressBarWnd"; + const wchar_t label_class[] = L"static"; +} + +#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + switch (Msg) + { + case WM_CREATE: + { + ui_thread_is_running.lock(); + label = CreateWindowW(nonlocalized::label_class, initial_label.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP, progress_bar_margin, 0, window_width - progress_bar_margin * 4, label_height, hWnd, (HMENU)(501), (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE), nullptr); + + progress_bar = CreateWindowExW(0, + PROGRESS_CLASS, + nullptr, + WS_VISIBLE | WS_CHILD | PBS_SMOOTH, + progress_bar_margin, + progress_bar_margin + label_height, + window_width - progress_bar_margin * 4, + progress_bar_height, + hWnd, + (HMENU)(IDR_PROGRESS_BAR), + (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE), + nullptr); + + bool filled_on_start = false; + if (progressbar_steps == 0) + { + progressbar_steps = 1; + filled_on_start = true; + } + SendMessageW(progress_bar, PBM_SETRANGE, 0, MAKELPARAM(0, progressbar_steps)); + SendMessageW(progress_bar, PBM_SETSTEP, 1, 0); + if (filled_on_start) + { + SendMessageW(progress_bar, PBM_STEPIT, 0, 0); + } + + break; + } + case WM_CLOSE: + DestroyWindow(hWnd); + PostQuitMessage(0); + break; + default: + return DefWindowProcW(hWnd, Msg, wParam, lParam); + } + return 0; +} + +void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label) +{ + initial_label = init_label; + progressbar_steps = n_progressbar_steps; + std::wstring window_title{ title }; + std::thread{ + [hInstance, window_title = std::move(window_title)] { + INITCOMMONCONTROLSEX iccex{ .dwSize = sizeof(iccex), .dwICC = ICC_NATIVEFNTCTL_CLASS | ICC_PROGRESS_CLASS }; + InitCommonControlsEx(&iccex); + + WNDCLASSEX wc{}; + wc.cbSize = sizeof(WNDCLASSEX); + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON)); + wc.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON)); + wc.lpszClassName = nonlocalized::window_class; + if (!RegisterClassExW(&wc)) + { + spdlog::warn("Couldn't register main_window class for progress bar."); + return; + } + RECT rect{}; + GetClientRect(GetDesktopWindow(), &rect); + rect.left = rect.right / 2 - window_width / 2; + rect.top = rect.bottom / 4 - window_height / 2; + main_window = CreateWindowExW(WS_EX_CLIENTEDGE, + nonlocalized::window_class, + window_title.c_str(), + WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX, + rect.left, + rect.top, + window_width, + window_height, + nullptr, + nullptr, + hInstance, + nullptr); + + if (!main_window) + { + spdlog::warn("Couldn't create progress bar main_window"); + return; + } + ShowWindow(main_window, SW_SHOW); + UpdateWindow(main_window); + run_message_loop(); + ui_thread_is_running.unlock(); + } + }.detach(); +} + +void tick_progressbar_window(const wchar_t* new_status) +{ + SetWindowTextW(label, new_status); + SendMessageW(progress_bar, PBM_STEPIT, 0, 0); +} + +void close_progressbar_window() +{ + SendMessageW(main_window, WM_CLOSE, {}, {}); + { + std::unique_lock wait_for_ui_to_exit{ui_thread_is_running}; + } + // Return focus to the current process, since it was lost due to progress bar closing (?) + INPUT i = {INPUT_MOUSE, {}}; + SendInput(1, &i, sizeof(i)); + SetForegroundWindow(GetActiveWindow()); + +} \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.h b/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.h new file mode 100644 index 0000000000..6b24aa2b21 --- /dev/null +++ b/installer/PowerToysBootstrapper/bootstrapper/progressbar_window.h @@ -0,0 +1,9 @@ +#pragma once + +#define NOMINMAX +#define WIN32_LEAN_AND_MEAN +#include + +void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label); +void tick_progressbar_window(const wchar_t* new_status); +void close_progressbar_window(); \ No newline at end of file diff --git a/installer/PowerToysBootstrapper/bootstrapper/resource.base.h b/installer/PowerToysBootstrapper/bootstrapper/resource.base.h index 8af5951145..49b371996c 100644 --- a/installer/PowerToysBootstrapper/bootstrapper/resource.base.h +++ b/installer/PowerToysBootstrapper/bootstrapper/resource.base.h @@ -14,3 +14,4 @@ #define IDR_BIN_MSIINSTALLER 103 #define IDR_BIN_ICON 104 +#define IDR_PROGRESS_BAR 105 diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 8f00bcb3a3..5285cd7c07 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -96,10 +96,6 @@ Installed and (NOT UPGRADINGPRODUCTCODE) AND (REMOVE="ALL") - - NOT Installed and (SKIPDOTNETINSTALL = 0) - - @@ -112,14 +108,6 @@ BinaryKey="PTCustomActions" DllEntry="TerminateProcessesCA" /> - - diff --git a/src/action_runner/action_runner.cpp b/src/action_runner/action_runner.cpp index 073e513496..5e5fdb2262 100644 --- a/src/action_runner/action_runner.cpp +++ b/src/action_runner/action_runner.cpp @@ -234,21 +234,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) } } } - else if (action == L"-install_dotnet") - { - if (updating::dotnet_is_installed()) - { - return 0; - } - const bool success = updating::install_dotnet(); - - MessageBoxW(nullptr, - GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE).c_str(), - GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE).c_str(), - MB_OK | MB_ICONERROR); - - return !success; - } else if (action == L"-uninstall_msi") { return uninstall_msi_action(); diff --git a/src/common/updating/dotnet_installation.cpp b/src/common/updating/dotnet_installation.cpp index bed2c26b7c..fd8d197ad8 100644 --- a/src/common/updating/dotnet_installation.cpp +++ b/src/common/updating/dotnet_installation.cpp @@ -20,9 +20,9 @@ namespace updating return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos; } - bool install_dotnet(const bool silent) + std::optional download_dotnet() { - const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/3eb7efa1-96c6-4e97-bb9f-563ecf595f8a/7efd9c1cdd74df8fb0a34c288138a84f/windowsdesktop-runtime-3.1.6-win-x64.exe"; + const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/513acf37-8da2-497d-bdaa-84d6e33c1fee/eb7b010350df712c752f4ec4b615f89d/windowsdesktop-runtime-3.1.10-win-x64.exe"; const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe"; auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME; @@ -44,10 +44,11 @@ namespace updating // couldn't download } } - if (!download_success) - { - return false; - } + return download_success ? std::make_optional(dotnet_download_path) : std::nullopt; + } + + bool install_dotnet(fs::path dotnet_download_path, const bool silent = false) + { SHELLEXECUTEINFOW sei{ sizeof(sei) }; sei.fMask = { SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE }; sei.lpFile = dotnet_download_path.c_str(); diff --git a/src/common/updating/dotnet_installation.h b/src/common/updating/dotnet_installation.h index e67d9b7d33..67ff6ad3b9 100644 --- a/src/common/updating/dotnet_installation.h +++ b/src/common/updating/dotnet_installation.h @@ -1,7 +1,12 @@ #pragma once +#include +#include + +namespace fs = std::filesystem; namespace updating { bool dotnet_is_installed(); - bool install_dotnet(const bool silent = false); + std::optional download_dotnet(); + bool install_dotnet(fs::path dotnet_download_path, const bool silent); } \ No newline at end of file diff --git a/src/common/updating/pch.h b/src/common/updating/pch.h index 66ebc3d655..d51d5df04c 100644 --- a/src/common/updating/pch.h +++ b/src/common/updating/pch.h @@ -17,4 +17,6 @@ #include #include +#include + #endif //PCH_H