[bootstrapper] progress bar UI polishing (#9644)

* Formatting, coding style, variable & function name

* Progress dialog: label position and background
This commit is contained in:
Enrico Giordani
2021-02-11 17:18:19 +01:00
committed by GitHub
parent d190e33313
commit 687b281b47
6 changed files with 146 additions and 89 deletions

View File

@@ -10,21 +10,25 @@ std::optional<RcResource> RcResource::create(int resource_id, const std::wstring
{ {
return std::nullopt; return std::nullopt;
} }
const HGLOBAL memHandle = LoadResource(nullptr, resHandle); const HGLOBAL memHandle = LoadResource(nullptr, resHandle);
if (!memHandle) if (!memHandle)
{ {
return std::nullopt; return std::nullopt;
} }
const size_t resSize = SizeofResource(nullptr, resHandle); const size_t resSize = SizeofResource(nullptr, resHandle);
if (!resSize) if (!resSize)
{ {
return std::nullopt; return std::nullopt;
} }
auto res = static_cast<const std::byte*>(LockResource(memHandle)); auto res = static_cast<const std::byte*>(LockResource(memHandle));
if (!res) if (!res)
{ {
return std::nullopt; return std::nullopt;
} }
return RcResource{ res, resSize }; return RcResource{ res, resSize };
} }
@@ -35,6 +39,7 @@ bool RcResource::saveAsFile(const std::filesystem::path destination)
{ {
return false; return false;
} }
installerFile.write(reinterpret_cast<const char*>(_memory), _size); installerFile.write(reinterpret_cast<const char*>(_memory), _size);
return true; return true;
} }

View File

@@ -59,7 +59,7 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="BOOTSTRAPPER_PROGRESS_TITLE" xml:space="preserve"> <data name="BOOTSTRAPPER_PROGRESS_TITLE" xml:space="preserve">
<value>PowerToys installer</value> <value>PowerToys Installer</value>
</data> </data>
<data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve"> <data name="DOTNET_CORE_DOWNLOAD_FAILURE" xml:space="preserve">
<value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value> <value>Couldn't download .NET Core Desktop Runtime 3.1, please install it manually.</value>

View File

@@ -36,18 +36,19 @@ namespace // Strings in this namespace should not be localized
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::optional<fs::path> extractEmbeddedInstaller(const fs::path extractPath) std::optional<fs::path> ExtractEmbeddedInstaller(const fs::path extractPath)
{ {
auto executableRes = RcResource::create(IDR_BIN_MSIINSTALLER, L"BIN"); auto executableRes = RcResource::create(IDR_BIN_MSIINSTALLER, L"BIN");
if (!executableRes) if (!executableRes)
{ {
return std::nullopt; return std::nullopt;
} }
auto installerPath = extractPath / L"PowerToysBootstrappedInstaller-" PRODUCT_VERSION_STRING L".msi"; auto installerPath = extractPath / L"PowerToysBootstrappedInstaller-" PRODUCT_VERSION_STRING L".msi";
return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt; return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt;
} }
void setup_log(fs::path directory, const spdlog::level::level_enum severity) void SetupLogger(fs::path directory, const spdlog::level::level_enum severity)
{ {
try try
{ {
@@ -65,6 +66,7 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity)
{ {
logger = spdlog::null_logger_mt("null"); logger = spdlog::null_logger_mt("null");
} }
logger->set_pattern("[%L][%d-%m-%C-%T] %v"); logger->set_pattern("[%L][%d-%m-%C-%T] %v");
logger->set_level(severity); logger->set_level(severity);
spdlog::set_default_logger(std::move(logger)); spdlog::set_default_logger(std::move(logger));
@@ -76,7 +78,7 @@ void setup_log(fs::path directory, const spdlog::level::level_enum severity)
} }
} }
void show_error_box(const wchar_t* message, const wchar_t* title) void ShowMessageBoxError(const wchar_t* message, const wchar_t* title)
{ {
MessageBoxW(nullptr, MessageBoxW(nullptr,
message, message,
@@ -84,18 +86,21 @@ void show_error_box(const wchar_t* message, const wchar_t* title)
MB_OK | MB_ICONERROR); MB_OK | MB_ICONERROR);
} }
int bootstrapper(HINSTANCE hInstance) int Bootstrapper(HINSTANCE hInstance)
{ {
winrt::init_apartment(); winrt::init_apartment();
char* programFilesDir = nullptr; char* programFilesDir = nullptr;
size_t size = 0; size_t size = 0;
std::string defaultInstallDir; std::string defaultInstallDir;
if (!_dupenv_s(&programFilesDir, &size, "PROGRAMFILES")) if (!_dupenv_s(&programFilesDir, &size, "PROGRAMFILES"))
{ {
defaultInstallDir += programFilesDir; defaultInstallDir += programFilesDir;
defaultInstallDir += "\\PowerToys"; defaultInstallDir += "\\PowerToys";
} }
cxxopts::Options options{ "PowerToysBootstrapper" }; cxxopts::Options options{ "PowerToysBootstrapper" };
// clang-format off // clang-format off
options.add_options() options.add_options()
("h,help", "Show help") ("h,help", "Show help")
@@ -108,6 +113,7 @@ int bootstrapper(HINSTANCE hInstance)
("install_dir", "Installation directory", cxxopts::value<std::string>()->default_value(defaultInstallDir)) ("install_dir", "Installation directory", cxxopts::value<std::string>()->default_value(defaultInstallDir))
("extract_msi", "Extract MSI to the working directory and exit. Use only if you must access MSI directly."); ("extract_msi", "Extract MSI to the working directory and exit. Use only if you must access MSI directly.");
// clang-format on // clang-format on
cxxopts::ParseResult cmdArgs; cxxopts::ParseResult cmdArgs;
bool showHelp = false; bool showHelp = false;
try try
@@ -178,13 +184,14 @@ int bootstrapper(HINSTANCE hInstance)
{ {
severity = spdlog::level::err; severity = spdlog::level::err;
} }
setup_log(logDir, severity);
spdlog::debug("PowerToys Bootstrapper is launched!\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extract_msi_only); SetupLogger(logDir, severity);
spdlog::debug("PowerToys Bootstrapper is launched\nnoFullUI: {}\nsilent: {}\nno_start_pt: {}\nskip_dotnet_install: {}\nlog_level: {}\ninstall_dir: {}\nextract_msi: {}\n", noFullUI, silent, noStartPT, skipDotnetInstall, logLevel, installDirArg, extract_msi_only);
// If a user requested an MSI -> extract it and exit // If a user requested an MSI -> extract it and exit
if (extract_msi_only) if (extract_msi_only)
{ {
if (const auto installerPath = extractEmbeddedInstaller(fs::current_path())) if (const auto installerPath = ExtractEmbeddedInstaller(fs::current_path()))
{ {
spdlog::info("MSI installer was extracted to {}", installerPath->string()); spdlog::info("MSI installer was extracted to {}", installerPath->string());
} }
@@ -200,6 +207,7 @@ int bootstrapper(HINSTANCE hInstance)
{ {
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr); MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
} }
if (silent) if (silent)
{ {
if (is_process_elevated()) if (is_process_elevated())
@@ -232,12 +240,14 @@ int bootstrapper(HINSTANCE hInstance)
params += L' '; params += L' ';
} }
} }
const auto processHandle = run_elevated(argList[0], params.c_str()); const auto processHandle = run_elevated(argList[0], params.c_str());
if (!processHandle) if (!processHandle)
{ {
spdlog::error("Couldn't restart elevated to enable silent mode! ({})", GetLastError()); spdlog::error("Couldn't restart elevated to enable silent mode! ({})", GetLastError());
return 1; return 1;
} }
if (WaitForSingleObject(processHandle, 3600000) == WAIT_OBJECT_0) if (WaitForSingleObject(processHandle, 3600000) == WAIT_OBJECT_0)
{ {
DWORD exitCode = 0; DWORD exitCode = 0;
@@ -259,6 +269,7 @@ int bootstrapper(HINSTANCE hInstance)
{ {
TerminateProcess(handle.get(), 0); TerminateProcess(handle.get(), 0);
} }
auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME); auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME);
auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME); auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME);
if (!instanceMutex) if (!instanceMutex)
@@ -281,16 +292,18 @@ int bootstrapper(HINSTANCE hInstance)
} }
spdlog::debug("Extracting embedded MSI installer"); spdlog::debug("Extracting embedded MSI installer");
const auto installerPath = extractEmbeddedInstaller(fs::temp_directory_path()); const auto installerPath = ExtractEmbeddedInstaller(fs::temp_directory_path());
if (!installerPath) if (!installerPath)
{ {
if (!silent) if (!silent)
{ {
show_error_box(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); ShowMessageBoxError(GET_RESOURCE_STRING(IDS_INSTALLER_EXTRACT_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
} }
spdlog::error("Couldn't install the MSI installer ({})", GetLastError()); spdlog::error("Couldn't install the MSI installer ({})", GetLastError());
return 1; return 1;
} }
auto removeExtractedInstaller = wil::scope_exit([&] { auto removeExtractedInstaller = wil::scope_exit([&] {
std::error_code _; std::error_code _;
fs::remove(*installerPath, _); fs::remove(*installerPath, _);
@@ -306,18 +319,20 @@ int bootstrapper(HINSTANCE hInstance)
{ {
spdlog::debug("Existing MSI package path not found"); spdlog::debug("Existing MSI package path not found");
} }
if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings)) if (!package_path.empty() && !updating::uninstall_msi_version(package_path, Strings))
{ {
spdlog::error("Couldn't install the existing MSI package ({})", GetLastError()); spdlog::error("Couldn't install the existing MSI package ({})", GetLastError());
if (!silent) if (!silent)
{ {
show_error_box(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); ShowMessageBoxError(GET_RESOURCE_STRING(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
} }
} }
const bool installDotnet = !skipDotnetInstall; const bool installDotnet = !skipDotnetInstall;
if (!silent) if (!silent)
{ {
open_progressbar_window(hInstance, 0, GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(), GET_RESOURCE_STRING(IDS_DOWNLOADING_DOTNET).c_str()); OpenProgressBarDialog(hInstance, 0, GET_RESOURCE_STRING(IDS_BOOTSTRAPPER_PROGRESS_TITLE).c_str(), GET_RESOURCE_STRING(IDS_DOWNLOADING_DOTNET).c_str());
} }
try try
@@ -329,13 +344,13 @@ int bootstrapper(HINSTANCE hInstance)
spdlog::debug("Dotnet is already installed: {}", dotnetInstalled); spdlog::debug("Dotnet is already installed: {}", dotnetInstalled);
if (!dotnetInstalled) if (!dotnetInstalled)
{ {
bool installed_successfully = false; bool installedSuccessfully = false;
if (const auto dotnet_installer_path = updating::download_dotnet()) if (const auto dotnet_installer_path = updating::download_dotnet())
{ {
// Dotnet installer has its own progress bar // Dotnet installer has its own progress bar
close_progressbar_window(); CloseProgressBarDialog();
installed_successfully = updating::install_dotnet(*dotnet_installer_path, silent); installedSuccessfully = updating::install_dotnet(*dotnet_installer_path, silent);
if (!installed_successfully) if (!installedSuccessfully)
{ {
spdlog::error("Couldn't install dotnet"); spdlog::error("Couldn't install dotnet");
} }
@@ -345,11 +360,11 @@ int bootstrapper(HINSTANCE hInstance)
spdlog::error("Couldn't download dotnet"); spdlog::error("Couldn't download dotnet");
} }
if (!installed_successfully) if (!installedSuccessfully)
{ {
if (!silent) if (!silent)
{ {
show_error_box(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE); ShowMessageBoxError(GET_RESOURCE_STRING(IDS_DOTNET_INSTALL_ERROR).c_str(), INSTALLATION_MSGBOX_TITLE);
} }
} }
} }
@@ -362,7 +377,7 @@ int bootstrapper(HINSTANCE hInstance)
} }
// At this point, there's no reason to show progress bar window, since MSI installers have their own // At this point, there's no reason to show progress bar window, since MSI installers have their own
close_progressbar_window(); CloseProgressBarDialog();
const std::wstring msiProps = installFolderProp; const std::wstring msiProps = installFolderProp;
spdlog::debug("Launching MSI installation for new package {}", installerPath->string()); spdlog::debug("Launching MSI installation for new package {}", installerPath->string());
@@ -384,6 +399,7 @@ int bootstrapper(HINSTANCE hInstance)
spdlog::error("Couldn't determine new MSI package install location ({})", GetLastError()); spdlog::error("Couldn't determine new MSI package install location ({})", GetLastError());
return 1; return 1;
} }
*newPTPath += L"\\PowerToys.exe"; *newPTPath += L"\\PowerToys.exe";
SHELLEXECUTEINFOW sei{ sizeof(sei) }; SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE }; sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE };
@@ -399,7 +415,7 @@ int WINAPI WinMain(HINSTANCE hi, HINSTANCE, LPSTR, int)
{ {
try try
{ {
return bootstrapper(hi); return Bootstrapper(hi);
} }
catch (const std::exception& ex) catch (const std::exception& ex)
{ {

View File

@@ -119,7 +119,6 @@
<ClCompile Include="RcResource.cpp" /> <ClCompile Include="RcResource.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\runner\updating.h" />
<ClInclude Include="pch.h" /> <ClInclude Include="pch.h" />
<ClInclude Include="progressbar_window.h" /> <ClInclude Include="progressbar_window.h" />
</ItemGroup> </ItemGroup>

View File

@@ -6,28 +6,29 @@
#include "progressbar_window.h" #include "progressbar_window.h"
#include "Generated Files/resource.h" #include "Generated Files/resource.h"
const int label_height = 20; const int labelHeight = 18;
const int progress_bar_height = 15; const int progressBarHeight = 20;
const int progress_bar_margin = 10; const int margin = 10;
const int window_width = 450; const int windowWidth = 480;
const int title_bar_height = 32; const int titleBarHeight = 32;
const int window_height = progress_bar_margin * 3 + progress_bar_height + label_height + title_bar_height; const int windowHeight = margin * 4 + progressBarHeight + labelHeight + titleBarHeight;
int progressbar_steps = 0; int progressBarSteps = 0;
HWND progress_bar; HWND hDialog = nullptr;
HWND main_window; HWND hLabel = nullptr;
HWND label; HWND hProgressBar = nullptr;
HBRUSH hBrush = nullptr;
std::wstring initial_label; std::wstring labelText;
std::mutex ui_thread_is_running; std::mutex uiThreadIsRunning;
namespace nonlocalized namespace nonlocalized
{ {
const wchar_t window_class[] = L"PTBProgressBarWnd"; const wchar_t windowClass[] = L"PTBProgressBarWnd";
const wchar_t label_class[] = L"static"; const wchar_t labelClass[] = L"static";
} }
#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") #pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
@@ -38,55 +39,87 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{ {
case WM_CREATE: case WM_CREATE:
{ {
ui_thread_is_running.lock(); uiThreadIsRunning.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, hLabel = CreateWindowW(nonlocalized::labelClass,
PROGRESS_CLASS, labelText.c_str(),
nullptr, WS_CHILD | WS_VISIBLE | WS_TABSTOP,
WS_VISIBLE | WS_CHILD | PBS_SMOOTH, margin,
progress_bar_margin, margin,
progress_bar_margin + label_height, windowWidth - (margin * 4),
window_width - progress_bar_margin * 4, labelHeight,
progress_bar_height, hWnd,
hWnd, (HMENU)(501),
(HMENU)(IDR_PROGRESS_BAR), (HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE), nullptr);
(HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
nullptr);
bool filled_on_start = false; hProgressBar = CreateWindowExW(0,
if (progressbar_steps == 0) PROGRESS_CLASS,
nullptr,
WS_VISIBLE | WS_CHILD | PBS_SMOOTH,
margin,
(margin * 2) + labelHeight,
windowWidth - (margin * 4),
progressBarHeight,
hWnd,
(HMENU)(IDR_PROGRESS_BAR),
(HINSTANCE)GetWindowLongPtrW(hWnd, GWLP_HINSTANCE),
nullptr);
bool filledOnStart = false;
if (progressBarSteps == 0)
{ {
progressbar_steps = 1; progressBarSteps = 1;
filled_on_start = true; filledOnStart = true;
} }
SendMessageW(progress_bar, PBM_SETRANGE, 0, MAKELPARAM(0, progressbar_steps));
SendMessageW(progress_bar, PBM_SETSTEP, 1, 0); SendMessageW(hProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, progressBarSteps));
if (filled_on_start) SendMessageW(hProgressBar, PBM_SETSTEP, 1, 0);
if (filledOnStart)
{ {
SendMessageW(progress_bar, PBM_STEPIT, 0, 0); SendMessageW(hProgressBar, PBM_STEPIT, 0, 0);
} }
break; break;
} }
case WM_CTLCOLORSTATIC:
{
if (lParam == (LPARAM)hLabel)
{
if (!hBrush)
{
HDC hdcStatic = (HDC)wParam;
SetTextColor(hdcStatic, RGB(0, 0, 0));
SetBkColor(hdcStatic, RGB(255, 255, 255));
hBrush = CreateSolidBrush(RGB(255, 255, 255));
}
return (LRESULT)hBrush;
}
break;
}
case WM_CLOSE: case WM_CLOSE:
{
DestroyWindow(hWnd); DestroyWindow(hWnd);
PostQuitMessage(0); PostQuitMessage(0);
break; break;
}
default: default:
{
return DefWindowProcW(hWnd, Msg, wParam, lParam); return DefWindowProcW(hWnd, Msg, wParam, lParam);
} }
}
return 0; return 0;
} }
void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label) void OpenProgressBarDialog(HINSTANCE hInstance, const int nProgressbarSteps, const wchar_t* title, const wchar_t* label)
{ {
initial_label = init_label; labelText = label;
progressbar_steps = n_progressbar_steps; progressBarSteps = nProgressbarSteps;
std::wstring window_title{ title }; std::wstring window_title{ title };
std::thread{ std::thread{
[hInstance, window_title = std::move(window_title)] { [hInstance, window_title = std::move(window_title)] {
INITCOMMONCONTROLSEX iccex{ .dwSize = sizeof(iccex), .dwICC = ICC_NATIVEFNTCTL_CLASS | ICC_PROGRESS_CLASS }; INITCOMMONCONTROLSEX iccex{.dwSize = sizeof(iccex), .dwICC = ICC_NATIVEFNTCTL_CLASS | ICC_PROGRESS_CLASS };
InitCommonControlsEx(&iccex); InitCommonControlsEx(&iccex);
WNDCLASSEX wc{}; WNDCLASSEX wc{};
@@ -95,54 +128,58 @@ void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps,
wc.hInstance = hInstance; wc.hInstance = hInstance;
wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON)); wc.hIcon = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON));
wc.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON)); wc.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCE(IDR_BIN_ICON));
wc.lpszClassName = nonlocalized::window_class; wc.lpszClassName = nonlocalized::windowClass;
if (!RegisterClassExW(&wc)) if (!RegisterClassExW(&wc))
{ {
spdlog::warn("Couldn't register main_window class for progress bar."); spdlog::warn("Couldn't register main_window class for progress bar.");
return; return;
} }
RECT rect{}; RECT rect{};
GetClientRect(GetDesktopWindow(), &rect); GetClientRect(GetDesktopWindow(), &rect);
rect.left = rect.right / 2 - window_width / 2; rect.left = rect.right / 2 - windowWidth / 2;
rect.top = rect.bottom / 4 - window_height / 2; rect.top = rect.bottom / 4 - windowHeight / 2;
main_window = CreateWindowExW(WS_EX_CLIENTEDGE, hDialog = CreateWindowExW(WS_EX_CLIENTEDGE,
nonlocalized::window_class, nonlocalized::windowClass,
window_title.c_str(), window_title.c_str(),
WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX, WS_OVERLAPPED | WS_CAPTION | WS_MINIMIZEBOX,
rect.left, rect.left,
rect.top, rect.top,
window_width, windowWidth,
window_height, windowHeight,
nullptr, nullptr,
nullptr, nullptr,
hInstance, hInstance,
nullptr); nullptr);
if (!main_window) if (!hDialog)
{ {
spdlog::warn("Couldn't create progress bar main_window"); spdlog::warn("Couldn't create progress bar main_window");
return; return;
} }
ShowWindow(main_window, SW_SHOW);
UpdateWindow(main_window); ShowWindow(hDialog, SW_SHOW);
UpdateWindow(hDialog);
run_message_loop(); run_message_loop();
ui_thread_is_running.unlock(); uiThreadIsRunning.unlock();
} }
}.detach(); }.detach();
} }
void tick_progressbar_window(const wchar_t* new_status) void UpdateProgressBarDialog(const wchar_t* label)
{ {
SetWindowTextW(label, new_status); SetWindowTextW(hLabel, label);
SendMessageW(progress_bar, PBM_STEPIT, 0, 0); SendMessageW(hProgressBar, PBM_STEPIT, 0, 0);
} }
void close_progressbar_window() void CloseProgressBarDialog()
{ {
SendMessageW(main_window, WM_CLOSE, {}, {}); SendMessageW(hDialog, WM_CLOSE, {}, {});
{ {
std::unique_lock wait_for_ui_to_exit{ui_thread_is_running}; std::unique_lock waitForUIToExit{ uiThreadIsRunning };
} }
// Return focus to the current process, since it was lost due to progress bar closing (?) // Return focus to the current process, since it was lost due to progress bar closing (?)
INPUT i = {INPUT_MOUSE, {}}; INPUT i = {INPUT_MOUSE, {}};
SendInput(1, &i, sizeof(i)); SendInput(1, &i, sizeof(i));

View File

@@ -4,6 +4,6 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
void open_progressbar_window(HINSTANCE hInstance, const int n_progressbar_steps, const wchar_t* title, const wchar_t* init_label); void OpenProgressBarDialog(HINSTANCE hInstance, const int nProgressbarSteps, const wchar_t* title, const wchar_t* label);
void tick_progressbar_window(const wchar_t* new_status); void UpdateProgressBarDialog(const wchar_t* label);
void close_progressbar_window(); void CloseProgressBarDialog();