diff --git a/.pipelines/pipeline.user.windows.yml b/.pipelines/pipeline.user.windows.yml
index 0e72e144eb..b435e4258d 100644
--- a/.pipelines/pipeline.user.windows.yml
+++ b/.pipelines/pipeline.user.windows.yml
@@ -64,7 +64,7 @@ build:
- from: 'x64/Release'
to: 'Build_Output'
include:
- - 'action_runner.exe'
+ - 'PowerToys.ActionRunner.exe'
- 'BugReportTool\BugReportTool.exe'
- 'modules\ColorPicker\ColorPicker.dll'
- 'modules\ColorPicker\ColorPickerUI.dll'
diff --git a/PowerToys.sln b/PowerToys.sln
index cd31c81bc7..c0799f590d 100644
--- a/PowerToys.sln
+++ b/PowerToys.sln
@@ -100,7 +100,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImageResizerExt", "src\modu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ImageResizerUITest", "src\modules\imageresizer\tests\ImageResizerUITest.csproj", "{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action_runner\action_runner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ActionRunner", "src\ActionRunner\ActionRunner.vcxproj", "{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}"
ProjectSection(ProjectDependencies) = postProject
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
EndProjectSection
diff --git a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx
index 0687da7399..ce29162af4 100644
--- a/installer/PowerToysBootstrapper/bootstrapper/Resources.resx
+++ b/installer/PowerToysBootstrapper/bootstrapper/Resources.resx
@@ -70,54 +70,18 @@
An update to PowerToys is available.
-
- PowerToys download started.
-
-
- An update to PowerToys is ready to install.
-
-
- Error: couldn't download PowerToys installer. Visit our GitHub page to update.
-
Update now
-
- At next launch
-
Error: please uninstall the previous version of PowerToys manually.
An update to PowerToys is available. Visit our GitHub page to update.
-
- PowerToys is up to date.
-
-
- Visit
-
More info...
-
- Abort
-
-
- Click Snooze to be reminded in:
-
-
- 1 day
-
-
- 5 days
-
-
- Downloading...
-
-
- Download complete
-
PowerToys Update
@@ -144,16 +108,6 @@
Couldn't install new PowerToys version.
-
-
- Snooze
-
-
- Updating from a local build is not supported.
- User cannot autoupdate from a locally-built PowerToys version
-
-
- Failed to connect to the server. Check your network connection or retry later.
A newer version is already installed.
diff --git a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp
index f2d996c30e..02eb38102f 100644
--- a/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp
+++ b/installer/PowerToysBootstrapper/bootstrapper/bootstrapper.cpp
@@ -207,12 +207,8 @@ int Bootstrapper(HINSTANCE hInstance)
{
}
- spdlog::level::level_enum severity = spdlog::level::off;
- if (logLevel == "debug")
- {
- severity = spdlog::level::debug;
- }
- else if (logLevel == "error")
+ spdlog::level::level_enum severity = spdlog::level::debug;
+ if (logLevel == "error")
{
severity = spdlog::level::err;
}
@@ -359,6 +355,7 @@ int Bootstrapper(HINSTANCE hInstance)
{
spdlog::error("Couldn't install the existing MSI package ({})", GetLastError());
ShowMessageBoxError(IDS_UNINSTALL_PREVIOUS_VERSION_ERROR);
+ return 1;
}
const bool installDotnet = !skipDotnetInstall;
diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs
index b706692f8e..9c45963e4f 100644
--- a/installer/PowerToysSetup/Product.wxs
+++ b/installer/PowerToysSetup/Product.wxs
@@ -71,7 +71,7 @@
-
+
@@ -324,8 +324,8 @@
-
-
+
+
@@ -792,7 +792,7 @@
-
+
diff --git a/src/action_runner/LocProject.json b/src/ActionRunner/LocProject.json
similarity index 100%
rename from src/action_runner/LocProject.json
rename to src/ActionRunner/LocProject.json
diff --git a/src/action_runner/Resources.resx b/src/ActionRunner/Resources.resx
similarity index 68%
rename from src/action_runner/Resources.resx
rename to src/ActionRunner/Resources.resx
index 5662ceb353..7e2feb1971 100644
--- a/src/action_runner/Resources.resx
+++ b/src/ActionRunner/Resources.resx
@@ -67,54 +67,18 @@
An update to PowerToys is available.
-
- PowerToys download started.
-
-
- An update to PowerToys is ready to install.
-
-
- Error: couldn't download PowerToys installer. Visit our GitHub page to update.
-
Update now
-
- At next launch
-
Error: please uninstall the previous version of PowerToys manually.
An update to PowerToys is available. Visit our GitHub page to update.
-
- PowerToys is up to date.
-
-
- Visit
-
More info...
-
- Abort
-
-
- Click Snooze to be reminded in:
-
-
- 1 day
-
-
- 5 days
-
-
- Downloading...
-
-
- Download complete
-
PowerToys Update
@@ -124,14 +88,4 @@
PowerToys: uninstall previous version?
-
- Snooze
-
-
- Updating from a local build is not supported.
- User cannot autoupdate from a locally-built PowerToys version
-
-
- Failed to connect to the server. Check your network connection or retry later.
-
diff --git a/src/action_runner/action_runner.base.rc b/src/ActionRunner/actionRunner.base.rc
similarity index 100%
rename from src/action_runner/action_runner.base.rc
rename to src/ActionRunner/actionRunner.base.rc
diff --git a/src/action_runner/action_runner.cpp b/src/ActionRunner/actionRunner.cpp
similarity index 62%
rename from src/action_runner/action_runner.cpp
rename to src/ActionRunner/actionRunner.cpp
index 144d57b3e8..bfd54f816b 100644
--- a/src/action_runner/action_runner.cpp
+++ b/src/ActionRunner/actionRunner.cpp
@@ -1,3 +1,7 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
#define WIN32_LEAN_AND_MEAN
#include "Generated Files/resource.h"
@@ -8,6 +12,7 @@
#include
#include
+#include
#include
#include
#include
@@ -15,6 +20,7 @@
#include
#include
#include
+#include
#include
@@ -29,13 +35,16 @@
auto Strings = create_notifications_strings();
-int uninstall_msi_action()
+using namespace cmdArg;
+
+int UninstallMsiAction()
{
const auto package_path = updating::get_msi_package_path();
if (package_path.empty())
{
return 0;
}
+
if (!updating::uninstall_msi_version(package_path, Strings))
{
return -1;
@@ -55,44 +64,92 @@ int uninstall_msi_action()
namespace fs = std::filesystem;
-std::optional copy_self_to_temp_dir()
+std::optional CopySelfToTempDir()
{
std::error_code error;
- auto dst_path = fs::temp_directory_path() / "action_runner.exe";
+ auto dst_path = fs::temp_directory_path() / "PowerToys.ActionRunner.exe";
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
if (error)
{
return std::nullopt;
}
+
return std::move(dst_path);
}
-bool install_new_version_stage_1(const std::wstring_view installer_filename, const bool must_restart = false)
+std::optional ObtainInstallerPath()
{
- const fs::path installer{ updating::get_pending_updates_path() / installer_filename };
+ using namespace updating;
- if (!fs::is_regular_file(installer))
+ auto state = UpdateState::read();
+ if (state.state == UpdateState::readyToDownload || state.state == UpdateState::errorDownloading)
+ {
+ const auto new_version_info = get_github_version_info_async(Strings).get();
+ if (!new_version_info)
+ {
+ Logger::error(L"Couldn't obtain github version info: {}", new_version_info.error());
+ return std::nullopt;
+ }
+
+ if (!std::holds_alternative(*new_version_info))
+ {
+ Logger::error("Invoked with -update_now argument, but no update was available");
+ return std::nullopt;
+ }
+
+ auto downloaded_installer = download_new_version(std::get(*new_version_info)).get();
+ if (!downloaded_installer)
+ {
+ Logger::error("Couldn't download new installer");
+ }
+
+ return downloaded_installer;
+ }
+ else if (state.state == UpdateState::readyToInstall)
+ {
+ fs::path installer{ get_pending_updates_path() / state.downloadedInstallerFilename };
+ if (fs::is_regular_file(installer))
+ {
+ return std::move(installer);
+ }
+ else
+ {
+ Logger::error(L"Couldn't find a downloaded installer {}", installer.native());
+ return std::nullopt;
+ }
+ }
+ else
+ {
+ Logger::error("Invoked with -update_now argument, but update state was invalid");
+ return std::nullopt;
+ }
+}
+
+bool InstallNewVersionStage1()
+{
+ const auto installer = ObtainInstallerPath();
+ if (!installer)
{
return false;
}
- if (auto copy_in_temp = copy_self_to_temp_dir())
+ if (auto copy_in_temp = CopySelfToTempDir())
{
- // detect if PT was running
+ // Detect if PT was running
const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
- const bool launch_powertoys = must_restart || pt_main_window != nullptr;
+ const bool launch_powertoys = pt_main_window != nullptr;
if (pt_main_window != nullptr)
{
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
}
- std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2_CMDARG };
+ std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 };
arguments += L" \"";
- arguments += installer.c_str();
+ arguments += installer->c_str();
arguments += L"\" \"";
arguments += get_module_folderpath();
arguments += L"\" ";
- arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT_CMDARG : UPDATE_STAGE2_DONT_START_PT_CMDARG;
+ arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT : UPDATE_STAGE2_DONT_START_PT;
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = copy_in_temp->c_str();
@@ -107,7 +164,7 @@ bool install_new_version_stage_1(const std::wstring_view installer_filename, con
}
}
-bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view install_path, bool launch_powertoys)
+bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view install_path, bool launch_powertoys)
{
std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower);
@@ -134,25 +191,36 @@ bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view
{
parameters += L"--no_start_pt";
}
+
sei.lpParameters = parameters.c_str();
success = ShellExecuteExW(&sei) == TRUE;
+
// Wait for the install completion
if (success)
{
WaitForSingleObject(sei.hProcess, INFINITE);
+ DWORD exitCode = 0;
+ GetExitCodeProcess(sei.hProcess, &exitCode);
+ success = exitCode == 0;
CloseHandle(sei.hProcess);
}
}
- std::error_code _;
- fs::remove(installer_path, _);
-
if (!success)
{
return false;
}
+ std::error_code _;
+ fs::remove(installer_path, _);
+
+ UpdateState::store([&](UpdateState& state) {
+ state = {};
+ state.githubUpdateLastCheckedDate.emplace(timeutil::now());
+ state.state = UpdateState::upToDate;
+ });
+
if (launch_powertoys)
{
std::wstring new_pt_path{ install_path };
@@ -164,6 +232,7 @@ bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view
sei.lpParameters = UPDATE_REPORT_SUCCESS;
return ShellExecuteExW(&sei) == TRUE;
}
+
return true;
}
@@ -175,13 +244,14 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
return 1;
}
+
std::wstring_view action{ args[1] };
std::filesystem::path logFilePath(PTSettingsHelper::get_root_save_folder_location());
logFilePath.append(LogSettings::actionRunnerLogPath);
Logger::init(LogSettings::actionRunnerLoggerName, logFilePath.wstring(), PTSettingsHelper::get_log_settings_file_location());
- if (action == RUN_NONELEVATED_CMDARG)
+ if (action == RUN_NONELEVATED)
{
int nextArg = 2;
@@ -227,7 +297,6 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
run_same_elevation(target.data(), params, pidBuffer);
- // cleanup
if (!pidFile.empty())
{
if (pidBuffer)
@@ -243,24 +312,36 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
}
}
}
- else if (action == UNINSTALL_MSI_CMDARG)
+ else if (action == UNINSTALL_MSI)
{
- return uninstall_msi_action();
+ return UninstallMsiAction();
}
- else if (action == UPDATE_NOW_LAUNCH_STAGE1_CMDARG)
+ else if (action == UPDATE_NOW_LAUNCH_STAGE1)
{
- std::wstring_view installerFilename{ args[2] };
- return !install_new_version_stage_1(installerFilename);
+ const bool failed = !InstallNewVersionStage1();
+ if (failed)
+ {
+ UpdateState::store([&](UpdateState& state) {
+ state.downloadedInstallerFilename = {};
+ state.githubUpdateLastCheckedDate.emplace(timeutil::now());
+ state.state = UpdateState::errorDownloading;
+ });
+ }
+ return failed;
}
- else if (action == UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG)
- {
- std::wstring_view installerFilename{ args[2] };
- return !install_new_version_stage_1(installerFilename, true);
- }
- else if (action == UPDATE_NOW_LAUNCH_STAGE2_CMDARG)
+ else if (action == UPDATE_NOW_LAUNCH_STAGE2)
{
using namespace std::string_view_literals;
- return !install_new_version_stage_2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT_CMDARG });
+ const bool failed = !InstallNewVersionStage2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT });
+ if (failed)
+ {
+ UpdateState::store([&](UpdateState& state) {
+ state.downloadedInstallerFilename = {};
+ state.githubUpdateLastCheckedDate.emplace(timeutil::now());
+ state.state = UpdateState::errorDownloading;
+ });
+ }
+ return failed;
}
return 0;
diff --git a/src/action_runner/action_runner.vcxproj b/src/ActionRunner/actionRunner.vcxproj
similarity index 74%
rename from src/action_runner/action_runner.vcxproj
rename to src/ActionRunner/actionRunner.vcxproj
index 89133a9928..126508090e 100644
--- a/src/action_runner/action_runner.vcxproj
+++ b/src/ActionRunner/actionRunner.vcxproj
@@ -2,13 +2,13 @@
-
+
16.0
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}
- action_runner
- action_runner
+ actionRunner
+ PowerToys.ActionRunner
@@ -20,16 +20,7 @@
-
-
-
-
-
-
-
-
-
-
+
@@ -44,7 +35,7 @@
-
+
@@ -61,12 +52,11 @@
-
-
-
+
+
@@ -83,4 +73,4 @@
-
+
\ No newline at end of file
diff --git a/src/action_runner/loc/cs/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/cs/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/cs/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/cs/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/de/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/de/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/de/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/de/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/es/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/es/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/es/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/es/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/fr/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/fr/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/fr/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/fr/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/hu/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/hu/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/hu/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/hu/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/it/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/it/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/it/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/it/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/ja/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/ja/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/ja/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/ja/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/ko/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/ko/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/ko/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/ko/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/nl/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/nl/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/nl/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/nl/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/pl/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/pl/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/pl/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/pl/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/pt-BR/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/pt-BR/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/pt-BR/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/pt-BR/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/pt-PT/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/pt-PT/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/pt-PT/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/pt-PT/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/ru/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/ru/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/ru/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/ru/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/sv/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/sv/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/sv/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/sv/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/tr/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/tr/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/tr/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/tr/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/zh-Hans/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/zh-Hans/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/zh-Hans/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/zh-Hans/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/loc/zh-Hant/src/action_runner/Resources.resx.lcl b/src/ActionRunner/loc/zh-Hant/src/action_runner/Resources.resx.lcl
similarity index 100%
rename from src/action_runner/loc/zh-Hant/src/action_runner/Resources.resx.lcl
rename to src/ActionRunner/loc/zh-Hant/src/action_runner/Resources.resx.lcl
diff --git a/src/action_runner/packages.config b/src/ActionRunner/packages.config
similarity index 100%
rename from src/action_runner/packages.config
rename to src/ActionRunner/packages.config
diff --git a/src/action_runner/resource.base.h b/src/ActionRunner/resource.base.h
similarity index 55%
rename from src/action_runner/resource.base.h
rename to src/ActionRunner/resource.base.h
index cadb2738ed..2d2c6d7cd6 100644
--- a/src/action_runner/resource.base.h
+++ b/src/ActionRunner/resource.base.h
@@ -1,11 +1,11 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
-// Used by action_runner.rc
+// Used by PowerToys.ActionRunner.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys ActionRunner"
-#define INTERNAL_NAME "action_runner"
-#define ORIGINAL_FILENAME "action_runner.exe"
+#define INTERNAL_NAME "PowerToys.ActionRunner"
+#define ORIGINAL_FILENAME "PowerToys.ActionRunner.exe"
diff --git a/src/common/notifications/dont_show_again.cpp b/src/common/notifications/dont_show_again.cpp
index 4d9d8d91a8..64f927f646 100644
--- a/src/common/notifications/dont_show_again.cpp
+++ b/src/common/notifications/dont_show_again.cpp
@@ -57,6 +57,5 @@ namespace notifications
}
RegCloseKey(key);
return timeutil::diff::in_days(timeutil::now(), last_disabled_time) < disable_interval_in_days;
- return false;
}
}
\ No newline at end of file
diff --git a/src/common/updating/installer.cpp b/src/common/updating/installer.cpp
index f7b4a673a3..91572d0dde 100644
--- a/src/common/updating/installer.cpp
+++ b/src/common/updating/installer.cpp
@@ -81,7 +81,7 @@ namespace updating
}
catch (...)
{
- updating::notifications::show_uninstallation_error(strings);
+ MessageBoxW(nullptr, strings.UNINSTALLATION_UNKNOWN_ERROR.c_str(), strings.NOTIFICATION_TITLE.c_str(), MB_OK | MB_ICONERROR);
}
}
return false;
diff --git a/src/common/updating/notifications.cpp b/src/common/updating/notifications.cpp
index 853fc4f130..7a08a1031f 100644
--- a/src/common/updating/notifications.cpp
+++ b/src/common/updating/notifications.cpp
@@ -14,7 +14,7 @@ namespace updating
namespace notifications
{
using namespace ::notifications;
- std::wstring current_version_to_next_version(const updating::new_version_download_info& info)
+ std::wstring current_version_to_next_version(const new_version_download_info& info)
{
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
@@ -22,15 +22,7 @@ namespace updating
return current_version_to_next_version;
}
- void show_unavailable(const notifications::strings& strings, std::wstring reason)
- {
- remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
-
- toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
- show_toast(std::move(reason), strings.TOAST_TITLE, std::move(toast_params));
- }
-
- void show_available(const updating::new_version_download_info& info, const notifications::strings& strings)
+ void show_new_version_available(const new_version_download_info& info, const strings& strings)
{
remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
@@ -40,106 +32,30 @@ namespace updating
contents += current_version_to_next_version(info);
show_toast_with_activations(std::move(contents),
- strings.TOAST_TITLE,
+ strings.NOTIFICATION_TITLE,
{},
{ link_button{ strings.GITHUB_NEW_VERSION_UPDATE_NOW,
- L"powertoys://download_and_install_update/" },
+ L"powertoys://update_now/" },
link_button{ strings.GITHUB_NEW_VERSION_MORE_INFO,
- info.release_page_uri.ToString().c_str() } },
+ L"powertoys://open_settings/" } },
std::move(toast_params));
}
- void show_download_start(const updating::new_version_download_info& info, const notifications::strings& strings)
- {
- remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
-
- progress_bar_params progress_bar_params;
- std::wstring progress_title{ info.version.toWstring() };
- progress_title += L' ';
- progress_title += strings.DOWNLOAD_IN_PROGRESS;
-
- progress_bar_params.progress_title = progress_title;
- progress_bar_params.progress = .0f;
- toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false, std::move(progress_bar_params) };
- show_toast_with_activations(strings.GITHUB_NEW_VERSION_DOWNLOAD_STARTED,
- strings.TOAST_TITLE,
- {},
- {},
- std::move(toast_params));
- }
-
- void show_visit_github(const updating::new_version_download_info& info, const notifications::strings& strings)
+ void show_open_settings_for_update(const strings& strings)
{
remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
- std::wstring contents = strings.GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
- contents += L'\n';
- contents += current_version_to_next_version(info);
- show_toast_with_activations(std::move(contents),
- strings.TOAST_TITLE,
+
+ std::vector actions = {
+ link_button{ strings.GITHUB_NEW_VERSION_MORE_INFO,
+ L"powertoys://open_settings/" },
+ };
+ show_toast_with_activations(strings.GITHUB_NEW_VERSION_AVAILABLE,
+ strings.NOTIFICATION_TITLE,
{},
- { link_button{ strings.GITHUB_NEW_VERSION_VISIT,
- info.release_page_uri.ToString().c_str() } },
+ std::move(actions),
std::move(toast_params));
}
-
- void show_install_error(const updating::new_version_download_info& info, const notifications::strings& strings)
- {
- remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
-
- toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
- std::wstring contents = strings.GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
- contents += L'\n';
- contents += current_version_to_next_version(info);
- show_toast_with_activations(std::move(contents),
- strings.TOAST_TITLE,
- {},
- { link_button{ strings.GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
- std::move(toast_params));
- }
-
- void show_version_ready(const updating::new_version_download_info& info, const notifications::strings& strings)
- {
- remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
-
- toast_params toast_params{ UPDATING_PROCESS_TOAST_TAG, false };
- std::wstring new_version_ready{ strings.GITHUB_NEW_VERSION_READY_TO_INSTALL };
- new_version_ready += L'\n';
- new_version_ready += current_version_to_next_version(info);
-
- show_toast_with_activations(std::move(new_version_ready),
- strings.TOAST_TITLE,
- {},
- { link_button{ strings.GITHUB_NEW_VERSION_UPDATE_NOW,
- L"powertoys://update_now/" + info.installer_filename },
- link_button{ strings.GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART,
- L"powertoys://schedule_update/" + info.installer_filename },
- snooze_button{
- strings.GITHUB_NEW_VERSION_SNOOZE_TITLE,
- { { strings.GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 },
- { strings.GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } },
- strings.SNOOZE_BUTTON } },
- std::move(toast_params));
- }
-
- void show_uninstallation_error(const notifications::strings& strings)
- {
- remove_toasts_by_tag(UPDATING_PROCESS_TOAST_TAG);
-
- show_toast(strings.UNINSTALLATION_UNKNOWN_ERROR, strings.TOAST_TITLE);
- }
-
- void update_download_progress(const updating::new_version_download_info& info, float progress, const notifications::strings& strings)
- {
- progress_bar_params progress_bar_params;
-
- std::wstring progress_title{ info.version.toWstring() };
- progress_title += L' ';
- progress_title += progress < 1 ? strings.DOWNLOAD_IN_PROGRESS : strings.DOWNLOAD_COMPLETE;
- progress_bar_params.progress_title = progress_title;
- progress_bar_params.progress = progress;
- update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params);
- }
}
}
diff --git a/src/common/updating/notifications.h b/src/common/updating/notifications.h
index 1862671d9a..421a32d716 100644
--- a/src/common/updating/notifications.h
+++ b/src/common/updating/notifications.h
@@ -10,71 +10,31 @@ namespace updating
{
struct strings
{
- std::wstring DOWNLOAD_COMPLETE;
- std::wstring DOWNLOAD_IN_PROGRESS;
-
- std::wstring GITHUB_NEW_VERSION_ABORT;
std::wstring GITHUB_NEW_VERSION_AVAILABLE;
- std::wstring GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
- std::wstring GITHUB_NEW_VERSION_CHECK_ERROR;
- std::wstring GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
- std::wstring GITHUB_NEW_VERSION_DOWNLOAD_STARTED;
std::wstring GITHUB_NEW_VERSION_MORE_INFO;
- std::wstring GITHUB_NEW_VERSION_READY_TO_INSTALL;
- std::wstring GITHUB_NEW_VERSION_SNOOZE_TITLE;
- std::wstring GITHUB_NEW_VERSION_UP_TO_DATE;
std::wstring GITHUB_NEW_VERSION_UPDATE_NOW;
- std::wstring GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART;
- std::wstring GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D;
- std::wstring GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D;
- std::wstring GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR;
- std::wstring GITHUB_NEW_VERSION_VISIT;
std::wstring OFFER_UNINSTALL_MSI;
std::wstring OFFER_UNINSTALL_MSI_TITLE;
- std::wstring SNOOZE_BUTTON;
- std::wstring TOAST_TITLE;
+ std::wstring NOTIFICATION_TITLE;
std::wstring UNINSTALLATION_UNKNOWN_ERROR;
};
- void show_unavailable(const notifications::strings& strings, std::wstring reason);
- void show_available(const updating::new_version_download_info& info, const strings&);
- void show_download_start(const updating::new_version_download_info& info, const strings&);
- void show_visit_github(const updating::new_version_download_info& info, const strings&);
- void show_install_error(const updating::new_version_download_info& info, const strings&);
- void show_version_ready(const updating::new_version_download_info& info, const strings&);
- void show_uninstallation_error(const notifications::strings& strings);
-
- void update_download_progress(const updating::new_version_download_info& info, float progress, const notifications::strings& strings);
+ void show_new_version_available(const new_version_download_info& info, const strings& strings);
+ void show_open_settings_for_update(const strings& strings);
}
}
#define create_notifications_strings() \
::updating::notifications::strings \
{ \
- .DOWNLOAD_COMPLETE = GET_RESOURCE_STRING(IDS_DOWNLOAD_COMPLETE), \
- .DOWNLOAD_IN_PROGRESS = GET_RESOURCE_STRING(IDS_DOWNLOAD_IN_PROGRESS), \
- .GITHUB_NEW_VERSION_ABORT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_ABORT), \
.GITHUB_NEW_VERSION_AVAILABLE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_AVAILABLE), \
- .GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT), \
- .GITHUB_NEW_VERSION_CHECK_ERROR = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_CHECK_ERROR), \
- .GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR), \
- .GITHUB_NEW_VERSION_DOWNLOAD_STARTED = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_DOWNLOAD_STARTED), \
.GITHUB_NEW_VERSION_MORE_INFO = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_MORE_INFO), \
- .GITHUB_NEW_VERSION_READY_TO_INSTALL = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_READY_TO_INSTALL), \
- .GITHUB_NEW_VERSION_SNOOZE_TITLE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_SNOOZE_TITLE), \
- .GITHUB_NEW_VERSION_UP_TO_DATE = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UP_TO_DATE), \
.GITHUB_NEW_VERSION_UPDATE_NOW = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_NOW), \
- .GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART), \
- .GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D), \
- .GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D), \
- .GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR), \
- .GITHUB_NEW_VERSION_VISIT = GET_RESOURCE_STRING(IDS_GITHUB_NEW_VERSION_VISIT), \
.OFFER_UNINSTALL_MSI = GET_RESOURCE_STRING(IDS_OFFER_UNINSTALL_MSI), \
.OFFER_UNINSTALL_MSI_TITLE = GET_RESOURCE_STRING(IDS_OFFER_UNINSTALL_MSI_TITLE), \
- .SNOOZE_BUTTON = GET_RESOURCE_STRING(IDS_SNOOZE_BUTTON), \
- .TOAST_TITLE = GET_RESOURCE_STRING(IDS_TOAST_TITLE), \
+ .NOTIFICATION_TITLE = GET_RESOURCE_STRING(IDS_TOAST_TITLE), \
.UNINSTALLATION_UNKNOWN_ERROR = GET_RESOURCE_STRING(IDS_UNINSTALLATION_UNKNOWN_ERROR) \
}
diff --git a/src/common/updating/pch.h b/src/common/updating/pch.h
index 3e2b83fbff..6b484c4eea 100644
--- a/src/common/updating/pch.h
+++ b/src/common/updating/pch.h
@@ -27,9 +27,11 @@
#include
#include
-#include
#include
#include
+#include
+
+#include
#endif //PCH_H
diff --git a/src/common/updating/updateState.cpp b/src/common/updating/updateState.cpp
new file mode 100644
index 0000000000..40e6330576
--- /dev/null
+++ b/src/common/updating/updateState.cpp
@@ -0,0 +1,70 @@
+#include "pch.h"
+#include "updateState.h"
+
+#include
+#include
+#include
+
+namespace
+{
+ const wchar_t PERSISTENT_STATE_FILENAME[] = L"\\UpdateState.json";
+ const wchar_t UPDATE_STATE_MUTEX[] = L"Local\\PowerToysRunnerUpdateStateMutex";
+}
+
+UpdateState deserialize(const json::JsonObject& json)
+{
+ UpdateState result;
+
+ result.state = static_cast(json.GetNamedNumber(L"state", UpdateState::upToDate));
+ result.releasePageUrl = json.GetNamedString(L"releasePageUrl", L"");
+ result.githubUpdateLastCheckedDate = timeutil::from_string(json.GetNamedString(L"githubUpdateLastCheckedDate", L"invalid").c_str());
+ result.downloadedInstallerFilename = json.GetNamedString(L"downloadedInstallerFilename", L"");
+ return result;
+}
+
+json::JsonObject serialize(const UpdateState& state)
+{
+ json::JsonObject json;
+
+ if (state.githubUpdateLastCheckedDate.has_value())
+ {
+ json.SetNamedValue(L"githubUpdateLastCheckedDate", json::value(timeutil::to_string(*state.githubUpdateLastCheckedDate)));
+ }
+ json.SetNamedValue(L"releasePageUrl", json::value(state.releasePageUrl));
+ json.SetNamedValue(L"state", json::value(static_cast(state.state)));
+ json.SetNamedValue(L"downloadedInstallerFilename", json::value(state.downloadedInstallerFilename));
+
+ return json;
+}
+
+UpdateState UpdateState::read()
+{
+ const auto filename = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
+ std::optional json;
+ {
+ wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
+ auto lock = mutex.acquire();
+ json = json::from_file(filename);
+ }
+ return json ? deserialize(*json) : UpdateState{};
+}
+
+void UpdateState::store(std::function stateModifier)
+{
+ const auto filename = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
+
+ std::optional json;
+ {
+ wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
+ auto lock = mutex.acquire();
+ json = json::from_file(filename);
+ UpdateState state;
+ if (json)
+ {
+ state = deserialize(*json);
+ }
+ stateModifier(state);
+ json.emplace(serialize(state));
+ json::to_file(filename, *json);
+ }
+}
diff --git a/src/common/updating/updateState.h b/src/common/updating/updateState.h
new file mode 100644
index 0000000000..09cb0c2f41
--- /dev/null
+++ b/src/common/updating/updateState.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include
+#include
+#include
+
+// All fields must be default-initialized
+struct UpdateState
+{
+ enum State
+ {
+ upToDate = 0,
+ errorDownloading = 1,
+ readyToDownload = 2,
+ readyToInstall = 3
+ } state = upToDate;
+ std::wstring releasePageUrl;
+ std::optional githubUpdateLastCheckedDate;
+ std::wstring downloadedInstallerFilename;
+
+ // To prevent concurrent modification of the file, we enforce this interface, which locks the file while
+ // the state_modifier is active.
+ static void store(std::function stateModifier);
+ static UpdateState read();
+};
\ No newline at end of file
diff --git a/src/common/updating/updating.cpp b/src/common/updating/updating.cpp
index 2c478ae582..a80017ed4d 100644
--- a/src/common/updating/updating.cpp
+++ b/src/common/updating/updating.cpp
@@ -15,12 +15,27 @@ namespace // Strings in this namespace should not be localized
{
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t ALL_RELEASES_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases";
+
+ const wchar_t LOCAL_BUILD_ERROR[] = L"Local build cannot be updated";
+ const wchar_t NETWORK_ERROR[] = L"Network error";
const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
}
namespace updating
{
+ Uri extract_release_page_url(const json::JsonObject& release_object)
+ {
+ try
+ {
+ return Uri{ release_object.GetNamedString(L"html_url") };
+ }
+ catch (...)
+ {
+ }
+ return nullptr;
+ }
+
std::optional extract_version_from_release_object(const json::JsonObject& release_object)
{
try
@@ -66,7 +81,7 @@ namespace updating
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
if (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
{
- co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_USING_LOCAL_BUILD_ERROR);
+ co_return nonstd::make_unexpected(LOCAL_BUILD_ERROR);
}
try
@@ -109,24 +124,16 @@ namespace updating
co_return version_up_to_date{};
}
- Uri release_page_url{ release_object.GetNamedString(L"html_url") };
- auto installer_download_url = extract_installer_asset_download_info(release_object);
- co_return new_version_download_info{ std::move(release_page_url),
+ auto [installer_download_url, installer_filename] = extract_installer_asset_download_info(release_object);
+ co_return new_version_download_info{ extract_release_page_url(release_object),
std::move(github_version),
- std::move(installer_download_url.first),
- std::move(installer_download_url.second) };
+ std::move(installer_download_url),
+ std::move(installer_filename) };
}
catch (...)
{
}
- co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_CHECK_ERROR);
- }
-
- bool could_be_costly_connection()
- {
- using namespace winrt::Windows::Networking::Connectivity;
- ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile();
- return internetConnectionProfile.IsWwanConnectionProfile();
+ co_return nonstd::make_unexpected(NETWORK_ERROR);
}
std::filesystem::path get_pending_updates_path()
@@ -136,86 +143,40 @@ namespace updating
return { std::move(path_str) };
}
- std::filesystem::path create_download_path()
+ std::optional create_download_path()
{
- auto installer_download_dst = get_pending_updates_path();
- std::error_code _;
- std::filesystem::create_directories(installer_download_dst, _);
- return installer_download_dst;
+ auto installer_download_path = get_pending_updates_path();
+ std::error_code ec;
+ std::filesystem::create_directories(installer_download_path, ec);
+ return !ec ? std::optional{ installer_download_path } : std::nullopt;
}
- std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings& strings)
+ std::future> download_new_version(const new_version_download_info& new_version)
{
- const auto version_check_result = co_await get_github_version_info_async(strings);
- if (!version_check_result)
+ auto installer_download_path = create_download_path();
+ if (!installer_download_path)
{
- co_return false;
+ co_return std::nullopt;
}
- if (std::holds_alternative(*version_check_result))
- {
- co_return true;
- }
- const auto new_version = std::get(*version_check_result);
- if (download_updates_automatically && !could_be_costly_connection())
+ *installer_download_path /= new_version.installer_filename;
+
+ bool download_success = false;
+ for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
{
- auto installer_download_dst = create_download_path() / new_version.installer_filename;
- bool download_success = false;
- for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
+ try
{
- try
- {
- http::HttpClient client;
- co_await client.download(new_version.installer_download_url, installer_download_dst);
- download_success = true;
- break;
- }
- catch (...)
- {
- // reattempt to download or do nothing
- }
+ http::HttpClient client;
+ co_await client.download(new_version.installer_download_url, *installer_download_path);
+ download_success = true;
+ break;
}
- if (!download_success)
+ catch (...)
{
- updating::notifications::show_install_error(new_version, strings);
- co_return false;
+ // reattempt to download or do nothing
}
-
- updating::notifications::show_version_ready(new_version, strings);
}
- else
- {
- updating::notifications::show_visit_github(new_version, strings);
- }
- co_return true;
+ co_return download_success ? installer_download_path : std::nullopt;
}
- std::future download_update(const notifications::strings& strings)
- {
- const auto version_check_result = co_await get_github_version_info_async(strings);
- if (!version_check_result || std::holds_alternative(*version_check_result))
- {
- co_return L"";
- }
- const auto new_version = std::get(*version_check_result);
- auto installer_download_dst = create_download_path() / new_version.installer_filename;
- updating::notifications::show_download_start(new_version, strings);
-
- try
- {
- auto progressUpdateHandle = [&](float progress) {
- updating::notifications::update_download_progress(new_version, progress, strings);
- };
-
- http::HttpClient client;
- co_await client.download(new_version.installer_download_url, installer_download_dst, progressUpdateHandle);
- }
- catch (...)
- {
- updating::notifications::show_install_error(new_version, strings);
- co_return L"";
- }
-
- co_return new_version.installer_filename;
- }
}
diff --git a/src/common/updating/updating.h b/src/common/updating/updating.h
index dcde36eb7c..1aac92d712 100644
--- a/src/common/updating/updating.h
+++ b/src/common/updating/updating.h
@@ -14,9 +14,9 @@
namespace updating
{
using winrt::Windows::Foundation::Uri;
- struct version_up_to_date {};
- using github_version_info = std::variant;
-
+ struct version_up_to_date
+ {
+ };
struct new_version_download_info
{
Uri release_page_uri = nullptr;
@@ -24,11 +24,10 @@ namespace updating
Uri installer_download_url = nullptr;
std::wstring installer_filename;
};
+ using github_version_info = std::variant;
- // Returns whether the update check has succeeded
- std::future try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
+ std::future> download_new_version(const new_version_download_info& new_version);
std::filesystem::path get_pending_updates_path();
- std::future download_update(const notifications::strings&);
std::future> get_github_version_info_async(const notifications::strings& strings, const bool prerelease = false);
// non-localized
diff --git a/src/common/updating/updating.vcxproj b/src/common/updating/updating.vcxproj
index 5f0935569f..c9caaba5c4 100644
--- a/src/common/updating/updating.vcxproj
+++ b/src/common/updating/updating.vcxproj
@@ -40,6 +40,7 @@
+
@@ -49,6 +50,7 @@
+
Create
diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp
index 4524ff2557..430dbecb22 100644
--- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp
+++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp
@@ -225,7 +225,7 @@ public:
params += L" -powerToysPid " + std::to_wstring(powertoys_pid) + L" ";
params += L"--centralized-kb-hook ";
- action_runner_path += L"\\action_runner.exe";
+ action_runner_path += L"\\PowerToys.ActionRunner.exe";
// Set up the shared file from which to retrieve the PID of PowerLauncher
HANDLE hMapFile = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(DWORD), POWER_LAUNCHER_PID_SHARED_FILE);
if (!hMapFile)
diff --git a/src/runner/Resources.resx b/src/runner/Resources.resx
index bc0c6d1130..801a308b3f 100644
--- a/src/runner/Resources.resx
+++ b/src/runner/Resources.resx
@@ -94,54 +94,18 @@
An update to PowerToys is available.
-
- PowerToys download started.
-
-
- An update to PowerToys is ready to install.
-
-
- Error: couldn't download PowerToys installer. Visit our GitHub page to update.
-
Update now
-
- At next launch
-
Error: please uninstall the previous version of PowerToys manually.
An update to PowerToys is available. Visit our GitHub page to update.
-
- PowerToys is up to date.
-
-
- Visit
-
More info...
-
- Abort
-
-
- Click Snooze to be reminded in:
-
-
- 1 day
-
-
- 5 days
-
-
- Downloading...
-
-
- Download complete
-
PowerToys Update
@@ -151,9 +115,6 @@
PowerToys: uninstall previous version?
-
- Snooze
-
Settings
@@ -167,11 +128,4 @@
Bug report .zip file has been created on your Desktop.
-
- Updating from a local build is not supported.
- User cannot autoupdate from a locally-built PowerToys version
-
-
- Failed to connect to the server. Check your network connection or retry later.
-
diff --git a/src/runner/action_runner_utils.cpp b/src/runner/action_runner_utils.cpp
index 64f3ee0400..e4d7344166 100644
--- a/src/runner/action_runner_utils.cpp
+++ b/src/runner/action_runner_utils.cpp
@@ -17,7 +17,7 @@ SHELLEXECUTEINFOW launch_action_runner(const wchar_t* cmdline)
action_runner_path = get_module_folderpath();
}
- action_runner_path += L"\\action_runner.exe";
+ action_runner_path += L"\\PowerToys.ActionRunner.exe";
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS };
sei.lpFile = action_runner_path.c_str();
diff --git a/src/runner/action_runner_utils.h b/src/runner/action_runner_utils.h
index 13e5353869..c1b5b795a8 100644
--- a/src/runner/action_runner_utils.h
+++ b/src/runner/action_runner_utils.h
@@ -5,15 +5,19 @@
SHELLEXECUTEINFOW launch_action_runner(const wchar_t* cmdline);
-const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG = L"-update_now_and_start_pt";
-const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1_CMDARG = L"-update_now";
+namespace cmdArg
+{
+ // Starts first stage of the PowerToys auto-update process, which involves copying action runner to a temp path and
+ // restarting it from there, so it doesn't interfere with the installation process.
+ const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE1 = L"-update_now";
+ // Stage 2 consists of starting the installer and optionally launching newly installed PowerToys binary.
+ // That's indicated by the following 2 flags.
+ const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE2 = L"-update_now_stage_2";
+ const inline wchar_t* UPDATE_STAGE2_RESTART_PT = L"restart";
+ const inline wchar_t* UPDATE_STAGE2_DONT_START_PT = L"dont_start";
-const inline wchar_t* UPDATE_NOW_LAUNCH_STAGE2_CMDARG = L"-update_now_stage_2";
-const inline wchar_t* UPDATE_STAGE2_RESTART_PT_CMDARG = L"restart";
-const inline wchar_t* UPDATE_STAGE2_DONT_START_PT_CMDARG = L"dont_start";
-
-const inline wchar_t * UNINSTALL_MSI_CMDARG = L"-uninstall_msi";
-const inline wchar_t * RUN_NONELEVATED_CMDARG = L"-run-non-elevated";
-
-const inline wchar_t* UPDATE_REPORT_SUCCESS = L"-report_update_success";
+ const inline wchar_t* UNINSTALL_MSI = L"-uninstall_msi";
+ const inline wchar_t* RUN_NONELEVATED = L"-run-non-elevated";
+ const inline wchar_t* UPDATE_REPORT_SUCCESS = L"-report_update_success";
+}
diff --git a/src/runner/main.cpp b/src/runner/main.cpp
index 5c9b34fe3b..bcf37d72c5 100644
--- a/src/runner/main.cpp
+++ b/src/runner/main.cpp
@@ -17,13 +17,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
-#include "update_state.h"
#include "update_utils.h"
#include "action_runner_utils.h"
@@ -96,7 +96,7 @@ void debug_verify_launcher_assets()
const auto assetPath = powertoysRoot / asset;
if (!fs::is_regular_file(assetPath))
{
- Logger::error("{} couldn't be found.", assetPath.string());
+ Logger::error("{} couldn't be found.", assetPath.string());
}
}
}
@@ -125,7 +125,7 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
debug_verify_launcher_assets();
std::thread{ [] {
- github_update_worker();
+ periodic_update_worker();
} }.detach();
if (winstore::running_as_packaged())
@@ -227,7 +227,7 @@ SpecialMode should_run_in_special_mode(const int n_cmd_args, LPWSTR* cmd_arg_lis
{
return SpecialMode::ToastNotificationHandler;
}
- else if (n_cmd_args == 2 && !wcscmp(UPDATE_REPORT_SUCCESS, cmd_arg_list[i]))
+ else if (n_cmd_args == 2 && !wcscmp(cmdArg::UPDATE_REPORT_SUCCESS, cmd_arg_list[i]))
{
return SpecialMode::ReportSuccessfulUpdate;
}
@@ -252,9 +252,7 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
{
const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/";
const std::wstring_view couldnt_toggle_powerpreview_modules_disable = L"couldnt_toggle_powerpreview_modules_disable/";
- const std::wstring_view download_and_install_update = L"download_and_install_update/";
const std::wstring_view open_settings = L"open_settings/";
- const std::wstring_view schedule_update = L"schedule_update/";
const std::wstring_view update_now = L"update_now/";
if (param == cant_drag_elevated_disable)
@@ -263,46 +261,10 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
}
else if (param.starts_with(update_now))
{
- std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG };
- const auto installerFilename = param.data() + size(update_now);
- args += L' ';
- args += installerFilename;
+ std::wstring args{ cmdArg::UPDATE_NOW_LAUNCH_STAGE1 };
launch_action_runner(args.c_str());
return toast_notification_handler_result::exit_success;
}
- else if (param.starts_with(schedule_update))
- {
- const auto installerFilename = param.data() + size(schedule_update);
- UpdateState::store([=](UpdateState& state) {
- state.pending_update = true;
- state.pending_installer_filename = installerFilename;
- });
-
- return toast_notification_handler_result::exit_success;
- }
- else if (param.starts_with(download_and_install_update))
- {
- try
- {
- std::wstring installer_filename = updating::download_update(Strings).get();
-
- std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG };
- args += L' ';
- args += installer_filename;
- launch_action_runner(args.c_str());
-
- return toast_notification_handler_result::exit_success;
- }
- catch (...)
- {
- MessageBoxW(nullptr,
- GET_RESOURCE_STRING(IDS_DOWNLOAD_UPDATE_ERROR).c_str(),
- L"PowerToys",
- MB_ICONWARNING | MB_OK);
-
- return toast_notification_handler_result::exit_error;
- }
- }
else if (param == couldnt_toggle_powerpreview_modules_disable)
{
return notifications::disable_toast(notifications::PreviewModulesDontShowAgainRegistryPath) ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error;
@@ -334,11 +296,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
L"(ML;;NX;;;LW)"; // Integrity label on No execute up for Low mandatory level
initializeCOMSecurity(securityDescriptor);
- if (launch_pending_update())
- {
- return 0;
- }
-
int n_cmd_args = 0;
LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args);
switch (should_run_in_special_mode(n_cmd_args, cmd_arg_list))
diff --git a/src/runner/pch.h b/src/runner/pch.h
index 907a4901a2..06d67b3c8f 100644
--- a/src/runner/pch.h
+++ b/src/runner/pch.h
@@ -28,6 +28,7 @@
#include
#include
+#include
#include
#include
diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj
index 9d5ffe7b49..052fe88b61 100644
--- a/src/runner/runner.vcxproj
+++ b/src/runner/runner.vcxproj
@@ -63,7 +63,6 @@
-
@@ -74,7 +73,6 @@
-
diff --git a/src/runner/runner.vcxproj.filters b/src/runner/runner.vcxproj.filters
index d532e62952..65337bf1d0 100644
--- a/src/runner/runner.vcxproj.filters
+++ b/src/runner/runner.vcxproj.filters
@@ -27,9 +27,6 @@
Utils
-
- Utils
-
Utils
@@ -75,9 +72,6 @@
Utils
-
- Utils
-
Utils
diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp
index 685b078b5d..84d99cdb59 100644
--- a/src/runner/settings_window.cpp
+++ b/src/runner/settings_window.cpp
@@ -8,9 +8,7 @@
#include
#include "tray_icon.h"
#include "general_settings.h"
-#include
#include "restart_elevated.h"
-#include "update_state.h"
#include "update_utils.h"
#include "centralized_kb_hook.h"
@@ -23,6 +21,8 @@
#include
#include
#include
+#include
+#include
#define BUFSIZE 1024
@@ -84,34 +84,16 @@ std::optional dispatch_json_action_to_module(const json::JsonObjec
}
else if (action == L"check_for_updates")
{
- if (auto update_check_result = check_for_updates())
- {
- VersionHelper latestVersion{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION };
- bool isVersionLatest = true;
- if (auto new_version = std::get_if(&*update_check_result))
- {
- latestVersion = new_version->version;
- isVersionLatest = false;
- }
- json::JsonObject json;
- json.SetNamedValue(L"version", json::value(latestVersion.toWstring()));
- json.SetNamedValue(L"isVersionLatest", json::value(isVersionLatest));
-
- result.emplace(json.Stringify());
-
- UpdateState::store([](UpdateState& state) {
- state.github_update_last_checked_date.emplace(timeutil::now());
- });
- }
+ check_for_updates_settings_callback();
}
else if (action == L"request_update_state_date")
{
json::JsonObject json;
auto update_state = UpdateState::read();
- if (update_state.github_update_last_checked_date)
+ if (update_state.githubUpdateLastCheckedDate)
{
- const time_t date = *update_state.github_update_last_checked_date;
+ const time_t date = *update_state.githubUpdateLastCheckedDate;
json.SetNamedValue(L"updateStateDate", json::value(std::to_wstring(date)));
}
@@ -336,7 +318,7 @@ void run_settings_window(bool showOobeWindow)
// Arg 6: elevated status
bool isElevated{ get_general_settings().isElevated };
std::wstring settings_elevatedStatus = isElevated ? L"true" : L"false";
-
+
// Arg 7: is user an admin
bool isAdmin{ get_general_settings().isAdmin };
std::wstring settings_isUserAnAdmin = isAdmin ? L"true" : L"false";
@@ -364,14 +346,14 @@ void run_settings_window(bool showOobeWindow)
executable_args.append(settings_isUserAnAdmin);
executable_args.append(L" ");
executable_args.append(settings_showOobe);
-
+
BOOL process_created = false;
if (is_process_elevated())
{
// TODO: Revisit this after switching to .NET 5
// Due to a bug in .NET, running the Settings process as non-elevated
- // from an elevated process sometimes results in a crash.
+ // from an elevated process sometimes results in a crash.
// process_created = run_settings_non_elevated(executable_path.c_str(), executable_args.data(), &process_info);
}
diff --git a/src/runner/update_state.cpp b/src/runner/update_state.cpp
deleted file mode 100644
index d4ea01aaff..0000000000
--- a/src/runner/update_state.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#include "pch.h"
-#include "update_state.h"
-
-#include
-#include
-#include
-
-namespace
-{
- const wchar_t PERSISTENT_STATE_FILENAME[] = L"\\update_state.json";
- const wchar_t UPDATE_STATE_MUTEX[] = L"Local\\PowerToys_Runner_UpdateStateMutex";
-}
-
-UpdateState deserialize(const json::JsonObject& json)
-{
- UpdateState result;
-
- result.github_update_last_checked_date = timeutil::from_string(json.GetNamedString(L"github_update_last_checked_date", L"invalid").c_str());
- result.pending_update = json.GetNamedBoolean(L"pending_update", false);
- result.pending_installer_filename = json.GetNamedString(L"pending_installer_filename", L"");
-
- return result;
-}
-
-json::JsonObject serialize(const UpdateState& state)
-{
- json::JsonObject json;
- if (state.github_update_last_checked_date.has_value())
- {
- json.SetNamedValue(L"github_update_last_checked_date", json::value(timeutil::to_string(*state.github_update_last_checked_date)));
- }
- json.SetNamedValue(L"pending_update", json::value(state.pending_update));
- json.SetNamedValue(L"pending_installer_filename", json::value(state.pending_installer_filename));
-
- return json;
-}
-
-UpdateState UpdateState::read()
-{
- const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
- std::optional json;
- {
- wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
- auto lock = mutex.acquire();
- json = json::from_file(file_name);
- }
- return json ? deserialize(*json) : UpdateState{};
-}
-
-void UpdateState::store(std::function state_modifier)
-{
- const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
-
- std::optional json;
- {
- wil::unique_mutex_nothrow mutex{ CreateMutexW(nullptr, FALSE, UPDATE_STATE_MUTEX) };
- auto lock = mutex.acquire();
- json = json::from_file(file_name);
- UpdateState state;
- if (json)
- {
- state = deserialize(*json);
- }
- state_modifier(state);
- json.emplace(serialize(state));
- json::to_file(file_name, *json);
- }
-}
diff --git a/src/runner/update_state.h b/src/runner/update_state.h
deleted file mode 100644
index 8f24de3a7c..0000000000
--- a/src/runner/update_state.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include
-#include
-#include
-
-// All fields must be default-initialized
-struct UpdateState
-{
- std::optional github_update_last_checked_date;
- bool pending_update = false;
- std::wstring pending_installer_filename;
-
- // To prevent concurrent modification of the file, we enforce this interface, which locks the file while
- // the state_modifier is active.
- static void store(std::function state_modifier);
- static UpdateState read();
-};
\ No newline at end of file
diff --git a/src/runner/update_utils.cpp b/src/runner/update_utils.cpp
index 5a5cec9c54..4c49592dde 100644
--- a/src/runner/update_utils.cpp
+++ b/src/runner/update_utils.cpp
@@ -3,14 +3,16 @@
#include "Generated Files/resource.h"
#include "action_runner_utils.h"
-#include "update_state.h"
+#include "general_settings.h"
#include "update_utils.h"
+#include
#include
+#include
#include
+#include
#include
#include
-#include
auto Strings = create_notifications_strings();
@@ -44,15 +46,80 @@ bool start_msi_uninstallation_sequence()
return exit_code == 0;
}
-void github_update_worker()
+using namespace updating;
+
+bool could_be_costly_connection()
+{
+ using namespace winrt::Windows::Networking::Connectivity;
+ ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile();
+ return internetConnectionProfile && internetConnectionProfile.IsWwanConnectionProfile();
+}
+
+void process_new_version_info(const github_version_info& version_info,
+ UpdateState& state,
+ const bool download_update,
+ const bool show_notifications)
+{
+ state.githubUpdateLastCheckedDate.emplace(timeutil::now());
+ if (std::holds_alternative(version_info))
+ {
+ state.state = UpdateState::upToDate;
+ state.releasePageUrl = {};
+ state.downloadedInstallerFilename = {};
+ Logger::trace(L"Version is up to date");
+ return;
+ }
+ const auto new_version_info = std::get(version_info);
+ state.releasePageUrl = new_version_info.release_page_uri.ToString().c_str();
+ Logger::trace(L"Discovered new version {}", new_version_info.version.toWstring());
+
+ const bool already_downloaded = state.state == UpdateState::readyToInstall && state.downloadedInstallerFilename == new_version_info.installer_filename;
+ if (already_downloaded)
+ {
+ Logger::trace(L"New version is already downloaded");
+ return;
+ }
+
+ if (download_update)
+ {
+ Logger::trace(L"Downloading installer for a new version");
+ if (download_new_version(new_version_info).get())
+ {
+ state.state = UpdateState::readyToInstall;
+ state.downloadedInstallerFilename = new_version_info.installer_filename;
+ if (show_notifications)
+ {
+ notifications::show_new_version_available(new_version_info, Strings);
+ }
+ }
+ else
+ {
+ state.state = UpdateState::errorDownloading;
+ state.downloadedInstallerFilename = {};
+ Logger::error("Couldn't download new installer");
+ }
+ }
+ else
+ {
+ Logger::trace(L"New version is ready to download, showing notification");
+ state.state = UpdateState::readyToDownload;
+ state.downloadedInstallerFilename = {};
+ if (show_notifications)
+ {
+ notifications::show_open_settings_for_update(Strings);
+ }
+ }
+}
+
+void periodic_update_worker()
{
for (;;)
{
auto state = UpdateState::read();
int64_t sleep_minutes_till_next_update = 0;
- if (state.github_update_last_checked_date.has_value())
+ if (state.githubUpdateLastCheckedDate.has_value())
{
- int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date);
+ int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.githubUpdateLastCheckedDate);
if (last_checked_minutes_ago < 0)
{
last_checked_minutes_ago = UPDATE_CHECK_INTERVAL_MINUTES;
@@ -61,22 +128,31 @@ void github_update_worker()
}
std::this_thread::sleep_for(std::chrono::minutes{ sleep_minutes_till_next_update });
- const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically;
- bool update_check_ok = false;
+
+ const bool download_update = !could_be_costly_connection() && get_general_settings().downloadUpdatesAutomatically;
+ bool version_info_obtained = false;
try
{
- update_check_ok = updating::try_autoupdate(download_updates_automatically, Strings).get();
+ const auto new_version_info = get_github_version_info_async(Strings).get();
+ if (new_version_info.has_value())
+ {
+ version_info_obtained = true;
+ process_new_version_info(*new_version_info, state, download_update, true);
+ }
+ else
+ {
+ Logger::error(L"Couldn't obtain version info from github: {}", new_version_info.error());
+ }
}
catch (...)
{
- // Couldn't autoupdate
- update_check_ok = false;
+ Logger::error("periodic_update_worker: error while processing version info");
}
- if (update_check_ok)
+ if (version_info_obtained)
{
- UpdateState::store([](UpdateState& state) {
- state.github_update_last_checked_date.emplace(timeutil::now());
+ UpdateState::store([&](UpdateState& v) {
+ v = std::move(state);
});
}
else
@@ -86,55 +162,27 @@ void github_update_worker()
}
}
-std::optional check_for_updates()
+void check_for_updates_settings_callback()
{
+ Logger::trace(L"Check for updates callback invoked");
+ auto state = UpdateState::read();
try
{
- auto version_check_result = updating::get_github_version_info_async(Strings).get();
- if (!version_check_result)
+ auto new_version_info = get_github_version_info_async(Strings).get();
+ if (!new_version_info)
{
- updating::notifications::show_unavailable(Strings, std::move(version_check_result.error()));
- return std::nullopt;
+ // If we couldn't get a new version from github for some reason, assume we're up to date, but also log error
+ new_version_info = version_up_to_date{};
+ Logger::error(L"Couldn't obtain version info from github: {}", new_version_info.error());
}
-
- if (std::holds_alternative(*version_check_result))
- {
- updating::notifications::show_unavailable(Strings, Strings.GITHUB_NEW_VERSION_UP_TO_DATE);
- return std::move(*version_check_result);
- }
-
- auto new_version = std::get(*version_check_result);
- updating::notifications::show_available(new_version, Strings);
- return std::move(new_version);
+ const bool download_update = !could_be_costly_connection() && get_general_settings().downloadUpdatesAutomatically;
+ process_new_version_info(*new_version_info, state, download_update, false);
+ UpdateState::store([&](UpdateState& v) {
+ v = std::move(state);
+ });
}
catch (...)
{
- // Couldn't autoupdate
+ Logger::error("check_for_updates_settings_callback: error while processing version info");
}
- return std::nullopt;
-}
-
-bool launch_pending_update()
-{
- try
- {
- auto update_state = UpdateState::read();
- if (update_state.pending_update)
- {
- UpdateState::store([](UpdateState& state) {
- state.pending_update = false;
- state.pending_installer_filename = {};
- });
- std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG };
- args += L' ';
- args += update_state.pending_installer_filename;
-
- launch_action_runner(args.c_str());
- return true;
- }
- }
- catch (...)
- {
- }
- return false;
}
diff --git a/src/runner/update_utils.h b/src/runner/update_utils.h
index 72e4119853..e1632c7b21 100644
--- a/src/runner/update_utils.h
+++ b/src/runner/update_utils.h
@@ -3,6 +3,5 @@
#include
bool start_msi_uninstallation_sequence();
-void github_update_worker();
-std::optional check_for_updates();
-bool launch_pending_update();
\ No newline at end of file
+void periodic_update_worker();
+void check_for_updates_settings_callback();
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/UpdatingSettings.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/UpdatingSettings.cs
new file mode 100644
index 0000000000..585bbb972d
--- /dev/null
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/UpdatingSettings.cs
@@ -0,0 +1,126 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.IO.Abstractions;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+
+namespace Microsoft.PowerToys.Settings.UI.Library
+{
+ public class UpdatingSettings
+ {
+ public enum UpdatingState
+ {
+ UpToDate = 0,
+ ErrorDownloading,
+ ReadyToDownload,
+ ReadyToInstall,
+ }
+
+ // Gets or sets a value of the updating state
+ [JsonPropertyName("state")]
+ public UpdatingState State { get; set; }
+
+ // Gets or sets a value of the release page url
+ [JsonPropertyName("releasePageUrl")]
+ public string ReleasePageLink { get; set; }
+
+ // Gets or sets a value of the github last checked date
+ [JsonPropertyName("githubUpdateLastCheckedDate")]
+ public string LastCheckedDate { get; set; }
+
+ // Gets or sets a value of the updating state
+ [JsonPropertyName("downloadedInstallerFilename")]
+ public string DownloadedInstallerFilename { get; set; }
+
+ // Non-localizable strings: Files
+ public const string SettingsFilePath = "\\Microsoft\\PowerToys\\";
+ public const string SettingsFile = "UpdateState.json";
+
+ public string NewVersion
+ {
+ get
+ {
+ if (ReleasePageLink == null)
+ {
+ return string.Empty;
+ }
+
+ try
+ {
+ string version = ReleasePageLink.Substring(ReleasePageLink.LastIndexOf('/') + 1);
+ return version.Trim();
+ }
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception)
+#pragma warning restore CA1031 // Do not catch general exception types
+ {
+ }
+
+ return string.Empty;
+ }
+ }
+
+ public string LastCheckedDateLocalized
+ {
+ get
+ {
+ try
+ {
+ long seconds = long.Parse(LastCheckedDate, CultureInfo.CurrentCulture);
+ var date = DateTimeOffset.FromUnixTimeSeconds(seconds).UtcDateTime;
+ return date.ToLocalTime().ToString(CultureInfo.CurrentCulture);
+ }
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception)
+#pragma warning restore CA1031 // Do not catch general exception types
+ {
+ }
+
+ return string.Empty;
+ }
+ }
+
+ public UpdatingSettings()
+ {
+ State = UpdatingState.UpToDate;
+ }
+
+ public static UpdatingSettings LoadSettings()
+ {
+ FileSystem fileSystem = new FileSystem();
+ var localAppDataDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+ var file = localAppDataDir + SettingsFilePath + SettingsFile;
+
+ if (fileSystem.File.Exists(file))
+ {
+ try
+ {
+ Stream inputStream = fileSystem.File.Open(file, FileMode.Open);
+ StreamReader reader = new StreamReader(inputStream);
+ string data = reader.ReadToEnd();
+ inputStream.Close();
+ reader.Dispose();
+
+ return JsonSerializer.Deserialize(data);
+ }
+#pragma warning disable CA1031 // Do not catch general exception types
+ catch (Exception)
+#pragma warning restore CA1031 // Do not catch general exception types
+ {
+ }
+ }
+
+ return null;
+ }
+
+ public string ToJsonString()
+ {
+ return JsonSerializer.Serialize(this);
+ }
+ }
+}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs
index 22b2964592..b577dc2102 100644
--- a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/Utilities/Helper.cs
@@ -75,6 +75,12 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Utilities
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
}
+ public static string GetPowerToysInstallationFolder()
+ {
+ var settingsPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
+ return Directory.GetParent(settingsPath).FullName;
+ }
+
private static readonly interop.LayoutMapManaged LayoutMap = new interop.LayoutMapManaged();
public static string GetKeyName(uint key)
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs
index 62a6b6133c..bbe68746c4 100644
--- a/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI.Library/ViewModels/GeneralViewModel.cs
@@ -1,9 +1,11 @@
-// Copyright (c) Microsoft Corporation
+// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.IO.Abstractions;
using System.Runtime.CompilerServices;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
@@ -16,10 +18,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
{
private GeneralSettings GeneralSettingsConfig { get; set; }
+ private UpdatingSettings UpdatingSettingsConfig { get; set; }
+
public ButtonClickCommand CheckForUpdatesEventHandler { get; set; }
public ButtonClickCommand RestartElevatedButtonEventHandler { get; set; }
+ public ButtonClickCommand UpdateNowButtonEventHandler { get; set; }
+
public Func UpdateUIThemeCallBack { get; }
public Func SendConfigMSG { get; }
@@ -34,10 +40,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private string _settingsConfigFileFolder = string.Empty;
- public GeneralViewModel(ISettingsRepository settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "")
+ private IFileSystemWatcher _fileWatcher;
+
+ public GeneralViewModel(ISettingsRepository settingsRepository, string runAsAdminText, string runAsUserText, bool isElevated, bool isAdmin, Func updateTheme, Func ipcMSGCallBackFunc, Func ipcMSGRestartAsAdminMSGCallBackFunc, Func ipcMSGCheckForUpdatesCallBackFunc, string configFileSubfolder = "", Action dispatcherAction = null)
{
CheckForUpdatesEventHandler = new ButtonClickCommand(CheckForUpdatesClick);
RestartElevatedButtonEventHandler = new ButtonClickCommand(RestartElevated);
+ UpdateNowButtonEventHandler = new ButtonClickCommand(UpdateNowClick);
// To obtain the general settings configuration of PowerToys if it exists, else to create a new file and return the default configurations.
if (settingsRepository == null)
@@ -46,6 +55,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
+ UpdatingSettingsConfig = UpdatingSettings.LoadSettings();
+ if (UpdatingSettingsConfig == null)
+ {
+ UpdatingSettingsConfig = new UpdatingSettings();
+ }
// set the callback functions value to hangle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
@@ -86,6 +100,16 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
RunningAsAdminDefaultText = runAsAdminText;
_isAdmin = isAdmin;
+
+ _updatingState = UpdatingSettingsConfig.State;
+ _newAvailableVersion = UpdatingSettingsConfig.NewVersion;
+ _newAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
+ _updateCheckedDate = UpdatingSettingsConfig.LastCheckedDateLocalized;
+
+ if (dispatcherAction != null)
+ {
+ _fileWatcher = Helper.GetFileWatcher(string.Empty, UpdatingSettings.SettingsFile, dispatcherAction);
+ }
}
private bool _packaged;
@@ -98,9 +122,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
private bool _isSystemThemeRadioButtonChecked;
private bool _autoDownloadUpdates;
- private string _latestAvailableVersion = string.Empty;
+ private UpdatingSettings.UpdatingState _updatingState = UpdatingSettings.UpdatingState.UpToDate;
+ private string _newAvailableVersion = string.Empty;
+ private string _newAvailableVersionLink = string.Empty;
private string _updateCheckedDate = string.Empty;
+ private bool _isNewVersionDownloading;
+ private bool _isNewVersionChecked;
+
// Gets or sets a value indicating whether packaged.
public bool Packaged
{
@@ -360,24 +389,90 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
- // Temp string. Appears when a user clicks "Check for updates" button and shows latest version available on the Github.
- public string LatestAvailableVersion
+ public UpdatingSettings.UpdatingState PowerToysUpdatingState
{
get
{
- return _latestAvailableVersion;
+ return _updatingState;
+ }
+
+ private set
+ {
+ if (value != _updatingState)
+ {
+ _updatingState = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ public string PowerToysNewAvailableVersion
+ {
+ get
+ {
+ return _newAvailableVersion;
+ }
+
+ private set
+ {
+ if (value != _newAvailableVersion)
+ {
+ _newAvailableVersion = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ public string PowerToysNewAvailableVersionLink
+ {
+ get
+ {
+ return _newAvailableVersionLink;
+ }
+
+ private set
+ {
+ if (value != _newAvailableVersionLink)
+ {
+ _newAvailableVersionLink = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+
+ public bool IsNewVersionDownloading
+ {
+ get
+ {
+ return _isNewVersionDownloading;
}
set
{
- if (_latestAvailableVersion != value)
+ if (value != _isNewVersionDownloading)
{
- _latestAvailableVersion = value;
+ _isNewVersionDownloading = value;
NotifyPropertyChanged();
}
}
}
+ public bool IsNewVersionCheckedAndUpToDate
+ {
+ get
+ {
+ return _isNewVersionChecked;
+ }
+ }
+
+ public bool IsDownloadAllowed
+ {
+ get
+ {
+ return AutoUpdatesEnabled && !IsNewVersionDownloading;
+ }
+ }
+
public void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
// Notify UI of property change
@@ -390,6 +485,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
// callback function to launch the URL to check for updates.
private void CheckForUpdatesClick()
{
+ IsNewVersionDownloading = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
+ NotifyPropertyChanged(nameof(IsDownloadAllowed));
+
+ if (_isNewVersionChecked)
+ {
+ _isNewVersionChecked = !IsNewVersionDownloading;
+ NotifyPropertyChanged(nameof(IsNewVersionCheckedAndUpToDate));
+ }
+
GeneralSettingsConfig.CustomActionName = "check_for_updates";
OutGoingGeneralSettings outsettings = new OutGoingGeneralSettings(GeneralSettingsConfig);
@@ -398,6 +502,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
SendCheckForUpdatesConfigMSG(customaction.ToString());
}
+ private void UpdateNowClick()
+ {
+ IsNewVersionDownloading = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
+ NotifyPropertyChanged(nameof(IsDownloadAllowed));
+
+ Process.Start(new ProcessStartInfo(Helper.GetPowerToysInstallationFolder() + "\\PowerToys.exe") { Arguments = "powertoys://update_now/" });
+ }
+
public void RequestUpdateCheckedDate()
{
GeneralSettingsConfig.CustomActionName = "request_update_state_date";
@@ -417,5 +529,45 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
SendRestartAsAdminConfigMSG(customaction.ToString());
}
+
+ public void RefreshUpdatingState()
+ {
+ var config = UpdatingSettings.LoadSettings();
+
+ // Retry loading if failed
+ for (int i = 0; i < 3 && config == null; i++)
+ {
+ System.Threading.Thread.Sleep(100);
+ config = UpdatingSettings.LoadSettings();
+ }
+
+ if (config == null || config.ToJsonString() == UpdatingSettingsConfig.ToJsonString())
+ {
+ return;
+ }
+
+ UpdatingSettingsConfig = config;
+
+ if (PowerToysUpdatingState != config.State)
+ {
+ IsNewVersionDownloading = false;
+ }
+ else
+ {
+ bool dateChanged = UpdateCheckedDate == UpdatingSettingsConfig.LastCheckedDateLocalized;
+ bool fileDownloaded = string.IsNullOrEmpty(UpdatingSettingsConfig.DownloadedInstallerFilename);
+ IsNewVersionDownloading = !(dateChanged || fileDownloaded);
+ }
+
+ PowerToysUpdatingState = UpdatingSettingsConfig.State;
+ PowerToysNewAvailableVersion = UpdatingSettingsConfig.NewVersion;
+ PowerToysNewAvailableVersionLink = UpdatingSettingsConfig.ReleasePageLink;
+ UpdateCheckedDate = UpdatingSettingsConfig.LastCheckedDateLocalized;
+
+ _isNewVersionChecked = PowerToysUpdatingState == UpdatingSettings.UpdatingState.UpToDate && !IsNewVersionDownloading;
+ NotifyPropertyChanged(nameof(IsNewVersionCheckedAndUpToDate));
+
+ NotifyPropertyChanged(nameof(IsDownloadAllowed));
+ }
}
}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateCannotDownloadToVisibilityConverter.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateCannotDownloadToVisibilityConverter.cs
new file mode 100644
index 0000000000..26748b6a3f
--- /dev/null
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateCannotDownloadToVisibilityConverter.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Data;
+
+namespace Microsoft.PowerToys.Settings.UI.Converters
+{
+ public sealed class UpdatingStateCannotDownloadToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return (UpdatingSettings.UpdatingState)value == UpdatingSettings.UpdatingState.ErrorDownloading ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return value;
+ }
+ }
+}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToDownloadToVisibilityConverter.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToDownloadToVisibilityConverter.cs
new file mode 100644
index 0000000000..d809338119
--- /dev/null
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToDownloadToVisibilityConverter.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Data;
+
+namespace Microsoft.PowerToys.Settings.UI.Converters
+{
+ public sealed class UpdatingStateReadyToDownloadToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return (UpdatingSettings.UpdatingState)value == UpdatingSettings.UpdatingState.ReadyToDownload ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return value;
+ }
+ }
+}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToInstallToVisibilityConverter.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToInstallToVisibilityConverter.cs
new file mode 100644
index 0000000000..c9ab831811
--- /dev/null
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateReadyToInstallToVisibilityConverter.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Data;
+
+namespace Microsoft.PowerToys.Settings.UI.Converters
+{
+ public sealed class UpdatingStateReadyToInstallToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return (UpdatingSettings.UpdatingState)value == UpdatingSettings.UpdatingState.ReadyToInstall ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return value;
+ }
+ }
+}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateUpToDateToVisibilityConverter.cs b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateUpToDateToVisibilityConverter.cs
new file mode 100644
index 0000000000..c60e261d7a
--- /dev/null
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Converters/UpdatingStateUpToDateToVisibilityConverter.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using Microsoft.PowerToys.Settings.UI.Library;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Data;
+
+namespace Microsoft.PowerToys.Settings.UI.Converters
+{
+ public sealed class UpdatingStateUpToDateToVisibilityConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ return (UpdatingSettings.UpdatingState)value == UpdatingSettings.UpdatingState.UpToDate ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ return value;
+ }
+ }
+}
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj
index 2d865ca040..f2d65009ad 100644
--- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Microsoft.PowerToys.Settings.UI.csproj
@@ -106,6 +106,10 @@
ShortcutVisualControl.xaml
+
+
+
+
Code
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
index 0e29cf5da1..e70452007d 100644
--- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Strings/en-us/Resources.resw
@@ -400,6 +400,9 @@
Check for updates
+
+ Update now
+
Privacy statement
@@ -706,10 +709,10 @@
Updates
- Version:
+ Installed version:
- Last successfully checked:
+ Last checked:
Version
@@ -1194,4 +1197,34 @@ From there, simply click on a Markdown file or SVG icon in the File Explorer and
Open Shortcut Guide
+
+ An error occurred trying to update to
+
+
+ Install now
+
+
+ Read more
+
+
+ A new version is available:
+
+
+ Downloading...
+
+
+ Try again to download and install
+
+
+ Checking for updates...
+
+
+ A new version is ready to install:
+
+
+ PowerToys is up to date
+
+
+ Download and install
+
\ No newline at end of file
diff --git a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml
index 0103991272..5e83eac8b2 100644
--- a/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml
+++ b/src/settings-ui/Microsoft.PowerToys.Settings.UI/Views/GeneralPage.xaml
@@ -6,6 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
+ xmlns:localConverters="using:Microsoft.PowerToys.Settings.UI.Converters"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
@@ -14,6 +15,10 @@
+
+
+
+
@@ -62,7 +67,7 @@
AutomationProperties.HeadingLevel="Level2"
Style="{StaticResource SubtitleTextBlockStyle}"/>
-
@@ -116,45 +121,176 @@
IsOn="{Binding Mode=TwoWay, Path=Startup}"/>
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ {
+ await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ViewModel.RefreshUpdatingState);
+ };
+
ViewModel = new GeneralViewModel(
SettingsRepository.GetInstance(settingsUtils),
loader.GetString("GeneralSettings_RunningAsAdminText"),
@@ -47,52 +53,9 @@ namespace Microsoft.PowerToys.Settings.UI.Views
UpdateUIThemeMethod,
ShellPage.SendDefaultIPCMessage,
ShellPage.SendRestartAdminIPCMessage,
- ShellPage.SendCheckForUpdatesIPCMessage);
-
- ShellPage.ShellHandler.IPCResponseHandleList.Add((JsonObject json) =>
- {
- try
- {
- string version = json.GetNamedString("version", string.Empty);
- bool isLatest = json.GetNamedBoolean("isVersionLatest", false);
-
- if (json.ContainsKey("version"))
- {
- ViewModel.RequestUpdateCheckedDate();
- }
-
- var str = string.Empty;
- if (isLatest)
- {
- str = ResourceLoader.GetForCurrentView().GetString("GeneralSettings_VersionIsLatest");
- }
- else if (!string.IsNullOrEmpty(version))
- {
- str = ResourceLoader.GetForCurrentView().GetString("GeneralSettings_NewVersionIsAvailable");
- if (!string.IsNullOrEmpty(str))
- {
- str += ": " + version;
- }
- }
-
- // Using CurrentCulture since this is user-facing
- if (!string.IsNullOrEmpty(str))
- {
- ViewModel.LatestAvailableVersion = string.Format(CultureInfo.CurrentCulture, str);
- }
-
- string updateStateDate = json.GetNamedString("updateStateDate", string.Empty);
- if (!string.IsNullOrEmpty(updateStateDate) && long.TryParse(updateStateDate, out var uTCTime))
- {
- var localTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(uTCTime).ToLocalTime();
- ViewModel.UpdateCheckedDate = localTime.ToString(CultureInfo.CurrentCulture);
- }
- }
- catch (Exception e)
- {
- Logger.LogError("Exception encountered when reading the version.", e);
- }
- });
+ ShellPage.SendCheckForUpdatesIPCMessage,
+ string.Empty,
+ stateUpdatingAction);
DataContext = ViewModel;
}
diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp
index 5b45fd68c1..603748630a 100644
--- a/tools/BugReportTool/BugReportTool/Main.cpp
+++ b/tools/BugReportTool/BugReportTool/Main.cpp
@@ -142,7 +142,7 @@ void HideUserPrivateInfo(const filesystem::path& dir)
HideForFile(dir, it.first);
}
- // delete files
+ // Delete files
for (auto it : filesToDelete)
{
auto path = dir;
@@ -243,6 +243,26 @@ void ReportDotNetInstallationInfo(const filesystem::path& tmpDir)
}
}
+void ReportBootstrapperLog(const filesystem::path& targetDir)
+{
+ for (const auto entry : filesystem::directory_iterator{temp_directory_path()})
+ {
+ if (!entry.is_regular_file() || !entry.path().has_filename())
+ {
+ continue;
+ }
+
+ const std::wstring filename = entry.path().filename().native();
+ if (!filename.starts_with(L"powertoys-bootstrapper-") || !filename.ends_with(L".log"))
+ {
+ continue;
+ }
+
+ std::error_code _;
+ copy(entry.path(), targetDir, _);
+ }
+}
+
int wmain(int argc, wchar_t* argv[], wchar_t*)
{
// Get path to save zip
@@ -280,6 +300,7 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
try
{
copy(settingsRootPath, tmpDir, copy_options::recursive);
+
// Remove updates folder contents
DeleteFolder(tmpDir / "Updates");
}
@@ -296,7 +317,7 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
ReportWindowsSettings(tmpDir);
// Write monitors info to the temporary folder
- reportMonitorInfo(tmpDir);
+ ReportMonitorInfo(tmpDir);
// Write windows version info to the temporary folder
ReportWindowsVersion(tmpDir);
@@ -310,6 +331,8 @@ int wmain(int argc, wchar_t* argv[], wchar_t*)
// Write compatibility tab info to the temporary folder
ReportCompatibilityTab(tmpDir);
+ ReportBootstrapperLog(tmpDir);
+
// Zip folder
auto zipPath = path::path(saveZipPath);
std::string reportFilename{"PowerToysReport_"};
diff --git a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
index e788000950..63c093db0c 100644
--- a/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
+++ b/tools/BugReportTool/BugReportTool/RegistryUtils.cpp
@@ -246,6 +246,7 @@ void ReportRegistry(const filesystem::path& tmpDir)
{
registryReport << "ERROR " << result << "\n";
}
+
registryReport << "\n";
}
@@ -288,12 +289,14 @@ void ReportRegistry(const filesystem::path& tmpDir)
registryReport << "ERROR " << result << "\n";
}
}
+
RegCloseKey(rootKey);
}
else
{
registryReport << "ERROR " << result << "\n";
}
+
registryReport << "\n";
}
}
diff --git a/tools/BugReportTool/BugReportTool/RegistryUtils.h b/tools/BugReportTool/BugReportTool/RegistryUtils.h
index dd2d20c6b2..40dd364e47 100644
--- a/tools/BugReportTool/BugReportTool/RegistryUtils.h
+++ b/tools/BugReportTool/BugReportTool/RegistryUtils.h
@@ -8,4 +8,4 @@
#include
void ReportRegistry(const std::filesystem::path& tmpDir);
-void ReportCompatibilityTab(const std::filesystem::path& tmpDir);
\ No newline at end of file
+void ReportCompatibilityTab(const std::filesystem::path& tmpDir);
diff --git a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
index 5028257578..96e14ac3f6 100644
--- a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
+++ b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.cpp
@@ -7,7 +7,7 @@ using namespace std;
namespace
{
- int buildMonitorInfoReport(std::wostream& os)
+ int BuildMonitorInfoReport(std::wostream& os)
{
struct capture
{
@@ -45,6 +45,7 @@ namespace
}
return TRUE;
};
+
capture c;
c.os = &os;
if (EnumDisplayMonitors(nullptr, nullptr, callback, (LPARAM)&c))
@@ -60,7 +61,7 @@ namespace
}
}
-void reportMonitorInfo(const filesystem::path& tmpDir)
+void ReportMonitorInfo(const filesystem::path& tmpDir)
{
auto monitorReportPath = tmpDir;
monitorReportPath.append("monitor-report-info.txt");
@@ -69,7 +70,7 @@ void reportMonitorInfo(const filesystem::path& tmpDir)
{
wofstream monitorReport(monitorReportPath);
monitorReport << "GetSystemMetrics = " << GetSystemMetrics(SM_CMONITORS) << '\n';
- buildMonitorInfoReport(monitorReport);
+ BuildMonitorInfoReport(monitorReport);
}
catch (std::exception& ex)
{
diff --git a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.h b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.h
index d1dcd26ab7..ea18e37ca9 100644
--- a/tools/BugReportTool/BugReportTool/ReportMonitorInfo.h
+++ b/tools/BugReportTool/BugReportTool/ReportMonitorInfo.h
@@ -1,4 +1,4 @@
#pragma once
#include
-void reportMonitorInfo(const std::filesystem::path& tmpDir);
+void ReportMonitorInfo(const std::filesystem::path& tmpDir);