mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 02:36:19 +02:00
runner: initial automatic update (#2141)
This commit is contained in:
28
src/runner/action_runner_utils.cpp
Normal file
28
src/runner/action_runner_utils.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "action_runner_utils.h"
|
||||
|
||||
#include <common/common.h>
|
||||
#include <common/winstore.h>
|
||||
|
||||
SHELLEXECUTEINFOW launch_action_runner(const wchar_t* cmdline)
|
||||
{
|
||||
std::wstring action_runner_path;
|
||||
if (winstore::running_as_packaged())
|
||||
{
|
||||
action_runner_path = winrt::Windows::ApplicationModel::Package::Current().InstalledLocation().Path();
|
||||
}
|
||||
else
|
||||
{
|
||||
action_runner_path = get_module_folderpath();
|
||||
}
|
||||
|
||||
action_runner_path += L"\\action_runner.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();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = cmdline;
|
||||
ShellExecuteExW(&sei);
|
||||
return sei;
|
||||
}
|
||||
15
src/runner/action_runner_utils.h
Normal file
15
src/runner/action_runner_utils.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
|
||||
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";
|
||||
|
||||
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* UPDATE_REPORT_SUCCESS = L"-report_update_success";
|
||||
@@ -10,8 +10,10 @@
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
// TODO: would be nice to get rid of these globals, since they're basically cached json settings
|
||||
static std::wstring settings_theme = L"system";
|
||||
static bool run_as_elevated = false;
|
||||
static bool download_updates_automatically = true;
|
||||
|
||||
// TODO: add resource.rc for settings project and localize
|
||||
namespace localized_strings
|
||||
@@ -40,6 +42,7 @@ json::JsonObject GeneralSettings::to_json()
|
||||
|
||||
result.SetNamedValue(L"is_elevated", json::value(isElevated));
|
||||
result.SetNamedValue(L"run_elevated", json::value(isRunElevated));
|
||||
result.SetNamedValue(L"download_updates_automatically", json::value(downloadUpdatesAutomatically));
|
||||
result.SetNamedValue(L"is_admin", json::value(isAdmin));
|
||||
result.SetNamedValue(L"theme", json::value(theme));
|
||||
result.SetNamedValue(L"system_theme", json::value(systemTheme));
|
||||
@@ -57,19 +60,22 @@ json::JsonObject load_general_settings()
|
||||
settings_theme = L"system";
|
||||
}
|
||||
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false);
|
||||
download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true);
|
||||
|
||||
return loaded;
|
||||
}
|
||||
|
||||
GeneralSettings get_settings()
|
||||
GeneralSettings get_general_settings()
|
||||
{
|
||||
GeneralSettings settings{
|
||||
.isPackaged = winstore::running_as_packaged(),
|
||||
.isElevated = is_process_elevated(),
|
||||
.isRunElevated = run_as_elevated,
|
||||
.isAdmin = check_user_is_admin(),
|
||||
.downloadUpdatesAutomatically = download_updates_automatically,
|
||||
.theme = settings_theme,
|
||||
.systemTheme = WindowsColors::is_dark_mode() ? L"dark" : L"light",
|
||||
.powerToysVersion = get_product_version(),
|
||||
.powerToysVersion = get_product_version()
|
||||
};
|
||||
|
||||
if (winstore::running_as_packaged())
|
||||
@@ -107,16 +113,12 @@ GeneralSettings get_settings()
|
||||
return settings;
|
||||
}
|
||||
|
||||
json::JsonObject get_general_settings()
|
||||
{
|
||||
auto settings = get_settings();
|
||||
return settings.to_json();
|
||||
}
|
||||
|
||||
void apply_general_settings(const json::JsonObject& general_configs)
|
||||
{
|
||||
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
|
||||
|
||||
download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
|
||||
|
||||
if (json::has(general_configs, L"startup", json::JsonValueType::Boolean))
|
||||
{
|
||||
const bool startup = general_configs.GetNamedBoolean(L"startup");
|
||||
@@ -192,7 +194,7 @@ void apply_general_settings(const json::JsonObject& general_configs)
|
||||
settings_theme = general_configs.GetNamedString(L"theme");
|
||||
}
|
||||
|
||||
GeneralSettings save_settings = get_settings();
|
||||
GeneralSettings save_settings = get_general_settings();
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
Trace::SettingsChanged(save_settings);
|
||||
}
|
||||
@@ -217,7 +219,9 @@ void start_initial_powertoys()
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { }
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
if (powertoys_to_disable.empty())
|
||||
{
|
||||
|
||||
@@ -11,6 +11,7 @@ struct GeneralSettings
|
||||
bool isElevated;
|
||||
bool isRunElevated;
|
||||
bool isAdmin;
|
||||
bool downloadUpdatesAutomatically;
|
||||
std::wstring theme;
|
||||
std::wstring systemTheme;
|
||||
std::wstring powerToysVersion;
|
||||
@@ -19,6 +20,6 @@ struct GeneralSettings
|
||||
};
|
||||
|
||||
json::JsonObject load_general_settings();
|
||||
json::JsonObject get_general_settings();
|
||||
GeneralSettings get_general_settings();
|
||||
void apply_general_settings(const json::JsonObject& general_configs);
|
||||
void start_initial_powertoys();
|
||||
|
||||
@@ -13,12 +13,14 @@
|
||||
#include <common/common.h>
|
||||
#include <common/dpi_aware.h>
|
||||
|
||||
#include <common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h>
|
||||
#include <common/winstore.h>
|
||||
#include <common/notifications.h>
|
||||
#include <common/timeutil.h>
|
||||
|
||||
#include <common/updating/updating.h>
|
||||
|
||||
#include "update_state.h"
|
||||
#include "update_utils.h"
|
||||
#include "action_runner_utils.h"
|
||||
|
||||
#include <winrt/Windows.System.h>
|
||||
|
||||
@@ -33,9 +35,6 @@ namespace localized_strings
|
||||
{
|
||||
const wchar_t MSI_VERSION_IS_ALREADY_RUNNING[] = L"An older version of PowerToys is already running.";
|
||||
const wchar_t OLDER_MSIX_UNINSTALLED[] = L"An older MSIX version of PowerToys was uninstalled.";
|
||||
|
||||
const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to get ";
|
||||
const wchar_t GITHUB_NEW_VERSION_AGREE[] = L"Visit";
|
||||
}
|
||||
|
||||
namespace
|
||||
@@ -78,78 +77,6 @@ wil::unique_mutex_nothrow create_msix_mutex()
|
||||
return create_runner_mutex(true);
|
||||
}
|
||||
|
||||
bool start_msi_uninstallation_sequence()
|
||||
{
|
||||
const auto package_path = get_msi_package_path();
|
||||
|
||||
if (package_path.empty())
|
||||
{
|
||||
// No MSI version detected
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!offer_msi_uninstallation())
|
||||
{
|
||||
// User declined to uninstall or opted for "Don't show again"
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring action_runner_path{ winrt::Windows::ApplicationModel::Package::Current().InstalledLocation().Path() };
|
||||
action_runner_path += L"\\action_runner.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();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = L"-uninstall_msi";
|
||||
ShellExecuteExW(&sei);
|
||||
WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exit_code = 0;
|
||||
GetExitCodeProcess(sei.hProcess, &exit_code);
|
||||
CloseHandle(sei.hProcess);
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
std::future<void> check_github_updates()
|
||||
{
|
||||
const auto new_version = co_await check_for_new_github_release_async();
|
||||
if (!new_version)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
using namespace localized_strings;
|
||||
|
||||
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
|
||||
contents += new_version->version_string;
|
||||
contents += L'.';
|
||||
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } });
|
||||
}
|
||||
|
||||
void github_update_checking_worker()
|
||||
{
|
||||
const int64_t update_check_period_minutes = 60 * 24;
|
||||
|
||||
auto state = UpdateState::load();
|
||||
for (;;)
|
||||
{
|
||||
int64_t sleep_minutes_till_next_update = 0;
|
||||
if (state.github_update_last_checked_date.has_value())
|
||||
{
|
||||
int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date);
|
||||
if (last_checked_minutes_ago < 0)
|
||||
{
|
||||
last_checked_minutes_ago = update_check_period_minutes;
|
||||
}
|
||||
sleep_minutes_till_next_update = max(0, update_check_period_minutes - last_checked_minutes_ago);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::minutes(sleep_minutes_till_next_update));
|
||||
|
||||
check_github_updates().get();
|
||||
state.github_update_last_checked_date.emplace(timeutil::now());
|
||||
state.save();
|
||||
}
|
||||
}
|
||||
|
||||
void open_menu_from_another_instance()
|
||||
{
|
||||
HWND hwnd_main = FindWindow(L"PToyTrayIconWindow", NULL);
|
||||
@@ -172,7 +99,7 @@ int runner(bool isProcessElevated)
|
||||
try
|
||||
{
|
||||
std::thread{ [] {
|
||||
github_update_checking_worker();
|
||||
github_update_worker();
|
||||
} }.detach();
|
||||
|
||||
if (winstore::running_as_packaged())
|
||||
@@ -183,13 +110,12 @@ int runner(bool isProcessElevated)
|
||||
}
|
||||
else
|
||||
{
|
||||
std::thread{[] {
|
||||
if(uninstall_previous_msix_version_async().get())
|
||||
std::thread{ [] {
|
||||
if (updating::uninstall_previous_msix_version_async().get())
|
||||
{
|
||||
notifications::show_toast(localized_strings::OLDER_MSIX_UNINSTALLED);
|
||||
}
|
||||
}}.detach();
|
||||
|
||||
} }.detach();
|
||||
}
|
||||
|
||||
notifications::register_background_toast_handler();
|
||||
@@ -243,7 +169,8 @@ enum class SpecialMode
|
||||
{
|
||||
None,
|
||||
Win32ToastNotificationCOMServer,
|
||||
ToastNotificationHandler
|
||||
ToastNotificationHandler,
|
||||
ReportSuccessfulUpdate
|
||||
};
|
||||
|
||||
SpecialMode should_run_in_special_mode(const int n_cmd_args, LPWSTR* cmd_arg_list)
|
||||
@@ -258,6 +185,10 @@ 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]))
|
||||
{
|
||||
return SpecialMode::ReportSuccessfulUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
return SpecialMode::None;
|
||||
@@ -281,6 +212,19 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
|
||||
{
|
||||
return disable_cant_drag_elevated_warning() ? toast_notification_handler_result::exit_success : toast_notification_handler_result::exit_error;
|
||||
}
|
||||
else if (param == L"update_now/")
|
||||
{
|
||||
launch_action_runner(UPDATE_NOW_LAUNCH_STAGE1_CMDARG);
|
||||
return toast_notification_handler_result::exit_success;
|
||||
}
|
||||
else if (param == L"schedule_update/")
|
||||
{
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.pending_update = true;
|
||||
});
|
||||
|
||||
return toast_notification_handler_result::exit_success;
|
||||
}
|
||||
else
|
||||
{
|
||||
return toast_notification_handler_result::exit_error;
|
||||
@@ -291,6 +235,11 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
{
|
||||
winrt::init_apartment();
|
||||
|
||||
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))
|
||||
@@ -305,6 +254,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
case toast_notification_handler_result::exit_success:
|
||||
return 0;
|
||||
}
|
||||
case SpecialMode::ReportSuccessfulUpdate:
|
||||
notifications::show_toast(GET_RESOURCE_STRING(IDS_AUTOUPDATE_SUCCESS));
|
||||
break;
|
||||
|
||||
case SpecialMode::None:
|
||||
// continue as usual
|
||||
break;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define IDS_COULDNOT_RESTART_NONELEVATED 105
|
||||
#define IDS_COULDNOT_RESTART_ELEVATED 106
|
||||
#define IDS_ANOTHER_INSTANCE_RUNNING 107
|
||||
#define IDS_AUTOUPDATE_SUCCESS 108
|
||||
|
||||
#define ID_EXIT_MENU_COMMAND 40001
|
||||
#define ID_SETTINGS_MENU_COMMAND 40002
|
||||
|
||||
Binary file not shown.
@@ -105,6 +105,7 @@
|
||||
</Manifest>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="action_runner_utils.cpp" />
|
||||
<ClCompile Include="auto_start_helper.cpp" />
|
||||
<ClCompile Include="general_settings.cpp" />
|
||||
<ClCompile Include="lowlevel_keyboard_event.cpp" />
|
||||
@@ -121,14 +122,17 @@
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="tray_icon.cpp" />
|
||||
<ClCompile Include="unhandled_exception_handler.cpp" />
|
||||
<ClCompile Include="update_utils.cpp" />
|
||||
<ClCompile Include="update_state.cpp" />
|
||||
<ClCompile Include="win_hook_event.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="action_runner_utils.h" />
|
||||
<ClInclude Include="auto_start_helper.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="lowlevel_keyboard_event.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="update_utils.h" />
|
||||
<ClInclude Include="update_state.h" />
|
||||
<ClInclude Include="powertoys_events.h" />
|
||||
<ClInclude Include="powertoy_module.h" />
|
||||
@@ -234,7 +238,7 @@
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj">
|
||||
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
||||
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
<ClCompile Include="update_state.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="update_utils.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="action_runner_utils.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -85,6 +91,12 @@
|
||||
<ClInclude Include="update_state.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="update_utils.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="action_runner_utils.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Utils">
|
||||
|
||||
@@ -40,7 +40,7 @@ json::JsonObject get_all_settings()
|
||||
{
|
||||
json::JsonObject result;
|
||||
|
||||
result.SetNamedValue(L"general", get_general_settings());
|
||||
result.SetNamedValue(L"general", get_general_settings().to_json());
|
||||
result.SetNamedValue(L"powertoys", get_power_toys_settings());
|
||||
return result;
|
||||
}
|
||||
@@ -243,7 +243,7 @@ void run_settings_window()
|
||||
DWORD powertoys_pid = GetCurrentProcessId();
|
||||
|
||||
// Arg 4: settings theme.
|
||||
const std::wstring settings_theme_setting{ get_general_settings().GetNamedString(L"theme").c_str() };
|
||||
const std::wstring settings_theme_setting{ get_general_settings().theme };
|
||||
std::wstring settings_theme;
|
||||
if (settings_theme_setting == L"dark" || (settings_theme_setting == L"system" && WindowsColors::is_dark_mode()))
|
||||
{
|
||||
|
||||
@@ -55,6 +55,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings)
|
||||
TraceLoggingWideString(settings.startupDisabledReason.c_str(), "StartupDisabledReason"),
|
||||
TraceLoggingWideString(enabledModules.c_str(), "ModulesEnabled"),
|
||||
TraceLoggingBoolean(settings.isRunElevated, "AlwaysRunElevated"),
|
||||
TraceLoggingBoolean(settings.downloadUpdatesAutomatically, "DownloadUpdatesAutomatically"),
|
||||
TraceLoggingWideString(settings.theme.c_str(), "Theme"),
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
|
||||
@@ -164,17 +164,16 @@ void start_tray_icon()
|
||||
{
|
||||
UINT id_tray_icon = wm_icon_notify = RegisterWindowMessageW(L"WM_PowerToysIconNotify");
|
||||
|
||||
static LPCWSTR class_name = L"PToyTrayIconWindow";
|
||||
WNDCLASS wc = {};
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hInstance = h_instance;
|
||||
wc.lpszClassName = class_name;
|
||||
wc.lpszClassName = pt_tray_icon_window_class;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = tray_icon_window_proc;
|
||||
wc.hIcon = icon;
|
||||
RegisterClass(&wc);
|
||||
auto hwnd = CreateWindowW(wc.lpszClassName,
|
||||
L"PToyTrayIconWindow",
|
||||
pt_tray_icon_window_class,
|
||||
WS_OVERLAPPEDWINDOW | WS_POPUP,
|
||||
CW_USEDEFAULT,
|
||||
CW_USEDEFAULT,
|
||||
|
||||
@@ -9,3 +9,5 @@ void open_settings_window();
|
||||
typedef void (*main_loop_callback_function)(PVOID);
|
||||
// Calls a callback in _callback
|
||||
bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data);
|
||||
|
||||
const inline wchar_t* pt_tray_icon_window_class = L"PToyTrayIconWindow";
|
||||
@@ -8,31 +8,59 @@
|
||||
namespace
|
||||
{
|
||||
const wchar_t PERSISTENT_STATE_FILENAME[] = L"\\update_state.json";
|
||||
const wchar_t UPDATE_STATE_MUTEX[] = L"PTUpdateStateMutex";
|
||||
}
|
||||
|
||||
UpdateState UpdateState::load()
|
||||
UpdateState deserialize(const json::JsonObject& json)
|
||||
{
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
auto json = json::from_file(file_name);
|
||||
UpdateState state;
|
||||
UpdateState result;
|
||||
|
||||
if (!json)
|
||||
{
|
||||
return state;
|
||||
}
|
||||
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);
|
||||
|
||||
state.github_update_last_checked_date = timeutil::from_string(json->GetNamedString(L"github_update_last_checked_date", L"invalid").c_str());
|
||||
|
||||
return state;
|
||||
return result;
|
||||
}
|
||||
|
||||
void UpdateState::save()
|
||||
json::JsonObject serialize(const UpdateState& state)
|
||||
{
|
||||
json::JsonObject json;
|
||||
if (github_update_last_checked_date.has_value())
|
||||
if (state.github_update_last_checked_date.has_value())
|
||||
{
|
||||
json.SetNamedValue(L"github_update_last_checked_date", json::value(timeutil::to_string(*github_update_last_checked_date)));
|
||||
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));
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
UpdateState UpdateState::read()
|
||||
{
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
std::optional<json::JsonObject> 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<void(UpdateState&)> state_modifier)
|
||||
{
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
|
||||
std::optional<json::JsonObject> 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);
|
||||
}
|
||||
const auto file_name = PTSettingsHelper::get_root_save_folder_location() + PERSISTENT_STATE_FILENAME;
|
||||
json::to_file(file_name, json);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
#include <ctime>
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
|
||||
// All fields must be default-initialized
|
||||
struct UpdateState
|
||||
{
|
||||
std::optional<std::time_t> github_update_last_checked_date;
|
||||
bool pending_update = false;
|
||||
|
||||
static UpdateState load();
|
||||
void save();
|
||||
// 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<void(UpdateState&)> state_modifier);
|
||||
static UpdateState read();
|
||||
};
|
||||
90
src/runner/update_utils.cpp
Normal file
90
src/runner/update_utils.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "action_runner_utils.h"
|
||||
#include "update_state.h"
|
||||
#include "update_utils.h"
|
||||
|
||||
#include <common/timeutil.h>
|
||||
#include <common/updating/updating.h>
|
||||
#include <runner/general_settings.h>
|
||||
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
#include <winrt/Windows.Web.Http.Headers.h>
|
||||
|
||||
bool start_msi_uninstallation_sequence()
|
||||
{
|
||||
const auto package_path = updating::get_msi_package_path();
|
||||
|
||||
if (package_path.empty())
|
||||
{
|
||||
// No MSI version detected
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!updating::offer_msi_uninstallation())
|
||||
{
|
||||
// User declined to uninstall or opted for "Don't show again"
|
||||
return false;
|
||||
}
|
||||
auto sei = launch_action_runner(L"-uninstall_msi");
|
||||
|
||||
WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
DWORD exit_code = 0;
|
||||
GetExitCodeProcess(sei.hProcess, &exit_code);
|
||||
CloseHandle(sei.hProcess);
|
||||
return exit_code == 0;
|
||||
}
|
||||
|
||||
void github_update_worker()
|
||||
{
|
||||
const int64_t update_check_period_minutes = 60 * 24;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto state = UpdateState::read();
|
||||
int64_t sleep_minutes_till_next_update = 0;
|
||||
if (state.github_update_last_checked_date.has_value())
|
||||
{
|
||||
int64_t last_checked_minutes_ago = timeutil::diff::in_minutes(timeutil::now(), *state.github_update_last_checked_date);
|
||||
if (last_checked_minutes_ago < 0)
|
||||
{
|
||||
last_checked_minutes_ago = update_check_period_minutes;
|
||||
}
|
||||
sleep_minutes_till_next_update = max(0, update_check_period_minutes - last_checked_minutes_ago);
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::minutes(sleep_minutes_till_next_update));
|
||||
const bool download_updates_automatically = get_general_settings().downloadUpdatesAutomatically;
|
||||
try
|
||||
{
|
||||
updating::try_autoupdate(download_updates_automatically).get();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Couldn't autoupdate
|
||||
}
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.github_update_last_checked_date.emplace(timeutil::now());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool launch_pending_update()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto update_state = UpdateState::read();
|
||||
if (update_state.pending_update)
|
||||
{
|
||||
UpdateState::store([](UpdateState& state) {
|
||||
state.pending_update = false;
|
||||
});
|
||||
launch_action_runner(UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
return false;
|
||||
}
|
||||
5
src/runner/update_utils.h
Normal file
5
src/runner/update_utils.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
bool start_msi_uninstallation_sequence();
|
||||
void github_update_worker();
|
||||
bool launch_pending_update();
|
||||
Reference in New Issue
Block a user