2021-05-21 13:32:34 +03:00
|
|
|
// 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.
|
|
|
|
|
|
2020-02-18 18:11:01 +03:00
|
|
|
#define WIN32_LEAN_AND_MEAN
|
2020-10-22 19:02:59 +03:00
|
|
|
#include "Generated Files/resource.h"
|
|
|
|
|
|
2020-02-18 18:11:01 +03:00
|
|
|
#include <Windows.h>
|
|
|
|
|
#include <shellapi.h>
|
|
|
|
|
|
2020-11-20 11:34:34 +03:00
|
|
|
#include <filesystem>
|
2020-02-18 18:11:01 +03:00
|
|
|
#include <string_view>
|
|
|
|
|
|
2020-04-21 10:30:12 +03:00
|
|
|
#include <common/updating/updating.h>
|
2021-05-21 13:32:34 +03:00
|
|
|
#include <common/updating/updateState.h>
|
2020-11-20 11:34:34 +03:00
|
|
|
#include <common/updating/installer.h>
|
2020-06-23 15:53:02 +03:00
|
|
|
#include <common/updating/http_client.h>
|
2020-07-27 19:53:29 +03:00
|
|
|
#include <common/updating/dotnet_installation.h>
|
2020-02-18 18:11:01 +03:00
|
|
|
|
2020-12-15 15:16:09 +03:00
|
|
|
#include <common/utils/elevation.h>
|
|
|
|
|
#include <common/utils/process_path.h>
|
|
|
|
|
#include <common/utils/resources.h>
|
2021-05-21 13:32:34 +03:00
|
|
|
#include <common/utils/timeutil.h>
|
2020-12-15 15:16:09 +03:00
|
|
|
|
2021-01-11 12:51:13 +02:00
|
|
|
#include <common/SettingsAPI/settings_helpers.h>
|
|
|
|
|
|
|
|
|
|
#include <common/logger/logger.h>
|
|
|
|
|
|
2020-02-18 18:11:01 +03:00
|
|
|
#include <winrt/Windows.ApplicationModel.h>
|
|
|
|
|
#include <winrt/Windows.Storage.h>
|
2020-04-21 10:30:12 +03:00
|
|
|
#include <Msi.h>
|
|
|
|
|
|
|
|
|
|
#include "../runner/tray_icon.h"
|
|
|
|
|
#include "../runner/action_runner_utils.h"
|
2020-02-18 18:11:01 +03:00
|
|
|
|
2020-11-13 15:57:01 +03:00
|
|
|
auto Strings = create_notifications_strings();
|
2020-10-22 19:02:59 +03:00
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
using namespace cmdArg;
|
|
|
|
|
|
|
|
|
|
int UninstallMsiAction()
|
2020-02-18 18:11:01 +03:00
|
|
|
{
|
2020-04-21 10:30:12 +03:00
|
|
|
const auto package_path = updating::get_msi_package_path();
|
2020-02-18 18:11:01 +03:00
|
|
|
if (package_path.empty())
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-10-22 19:02:59 +03:00
|
|
|
if (!updating::uninstall_msi_version(package_path, Strings))
|
2020-02-18 18:11:01 +03:00
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Launch PowerToys again, since it's been terminated by the MSI uninstaller
|
|
|
|
|
std::wstring runner_path{ winrt::Windows::ApplicationModel::Package::Current().InstalledLocation().Path() };
|
|
|
|
|
runner_path += L"\\PowerToys.exe";
|
|
|
|
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
|
|
|
|
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
|
|
|
|
sei.lpFile = runner_path.c_str();
|
|
|
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
|
ShellExecuteExW(&sei);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 10:30:12 +03:00
|
|
|
namespace fs = std::filesystem;
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
std::optional<fs::path> CopySelfToTempDir()
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
|
|
|
|
std::error_code error;
|
2021-05-21 13:32:34 +03:00
|
|
|
auto dst_path = fs::temp_directory_path() / "PowerToys.ActionRunner.exe";
|
2020-04-21 10:30:12 +03:00
|
|
|
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
|
|
|
|
|
if (error)
|
|
|
|
|
{
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-04-21 10:30:12 +03:00
|
|
|
return std::move(dst_path);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
std::optional<fs::path> ObtainInstallerPath()
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
2021-05-21 13:32:34 +03:00
|
|
|
using namespace updating;
|
|
|
|
|
|
|
|
|
|
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_download_info>(*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_download_info>(*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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-18 13:43:09 +03:00
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
bool InstallNewVersionStage1()
|
|
|
|
|
{
|
|
|
|
|
const auto installer = ObtainInstallerPath();
|
|
|
|
|
if (!installer)
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
if (auto copy_in_temp = CopySelfToTempDir())
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
2021-05-21 13:32:34 +03:00
|
|
|
// Detect if PT was running
|
2020-04-21 10:30:12 +03:00
|
|
|
const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
|
2021-05-21 13:32:34 +03:00
|
|
|
const bool launch_powertoys = pt_main_window != nullptr;
|
2020-04-21 10:30:12 +03:00
|
|
|
if (pt_main_window != nullptr)
|
|
|
|
|
{
|
|
|
|
|
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2 };
|
2020-04-21 10:30:12 +03:00
|
|
|
arguments += L" \"";
|
2021-05-21 13:32:34 +03:00
|
|
|
arguments += installer->c_str();
|
2020-04-21 10:30:12 +03:00
|
|
|
arguments += L"\" \"";
|
|
|
|
|
arguments += get_module_folderpath();
|
|
|
|
|
arguments += L"\" ";
|
2021-05-21 13:32:34 +03:00
|
|
|
arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT : UPDATE_STAGE2_DONT_START_PT;
|
2020-04-21 10:30:12 +03:00
|
|
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
|
|
|
|
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
|
|
|
|
sei.lpFile = copy_in_temp->c_str();
|
|
|
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
|
|
|
|
|
|
sei.lpParameters = arguments.c_str();
|
|
|
|
|
return ShellExecuteExW(&sei) == TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
bool InstallNewVersionStage2(std::wstring installer_path, std::wstring_view install_path, bool launch_powertoys)
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
2020-06-18 13:43:09 +03:00
|
|
|
std::transform(begin(installer_path), end(installer_path), begin(installer_path), ::towlower);
|
|
|
|
|
|
|
|
|
|
bool success = true;
|
|
|
|
|
|
|
|
|
|
if (installer_path.ends_with(L".msi"))
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
2020-06-18 13:43:09 +03:00
|
|
|
success = MsiInstallProductW(installer_path.data(), nullptr) == ERROR_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// If it's not .msi, then it's our .exe installer
|
|
|
|
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
2020-08-07 12:00:14 +03:00
|
|
|
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
|
2020-06-18 13:43:09 +03:00
|
|
|
sei.lpFile = installer_path.c_str();
|
|
|
|
|
sei.nShow = SW_SHOWNORMAL;
|
2020-08-07 12:00:14 +03:00
|
|
|
std::wstring parameters = L"--no_full_ui";
|
|
|
|
|
if (launch_powertoys)
|
|
|
|
|
{
|
|
|
|
|
// .exe installer launches the main app by default
|
|
|
|
|
launch_powertoys = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
parameters += L"--no_start_pt";
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-08-07 12:00:14 +03:00
|
|
|
sei.lpParameters = parameters.c_str();
|
2020-06-18 13:43:09 +03:00
|
|
|
|
|
|
|
|
success = ShellExecuteExW(&sei) == TRUE;
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-07-17 11:51:20 +03:00
|
|
|
// Wait for the install completion
|
|
|
|
|
if (success)
|
|
|
|
|
{
|
|
|
|
|
WaitForSingleObject(sei.hProcess, INFINITE);
|
2021-05-21 13:32:34 +03:00
|
|
|
DWORD exitCode = 0;
|
|
|
|
|
GetExitCodeProcess(sei.hProcess, &exitCode);
|
|
|
|
|
success = exitCode == 0;
|
2020-07-17 11:51:20 +03:00
|
|
|
CloseHandle(sei.hProcess);
|
|
|
|
|
}
|
2020-04-21 10:30:12 +03:00
|
|
|
}
|
|
|
|
|
|
2020-06-18 13:43:09 +03:00
|
|
|
if (!success)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
std::error_code _;
|
|
|
|
|
fs::remove(installer_path, _);
|
|
|
|
|
|
|
|
|
|
UpdateState::store([&](UpdateState& state) {
|
|
|
|
|
state = {};
|
|
|
|
|
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
|
|
|
|
|
state.state = UpdateState::upToDate;
|
|
|
|
|
});
|
|
|
|
|
|
2020-04-21 10:30:12 +03:00
|
|
|
if (launch_powertoys)
|
|
|
|
|
{
|
|
|
|
|
std::wstring new_pt_path{ install_path };
|
|
|
|
|
new_pt_path += L"\\PowerToys.exe";
|
|
|
|
|
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
|
|
|
|
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
|
|
|
|
sei.lpFile = new_pt_path.c_str();
|
|
|
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
|
|
|
sei.lpParameters = UPDATE_REPORT_SUCCESS;
|
|
|
|
|
return ShellExecuteExW(&sei) == TRUE;
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-04-21 10:30:12 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-18 18:11:01 +03:00
|
|
|
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
|
|
|
|
{
|
|
|
|
|
int nArgs = 0;
|
|
|
|
|
LPWSTR* args = CommandLineToArgvW(GetCommandLineW(), &nArgs);
|
|
|
|
|
if (!args || nArgs < 2)
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
|
2020-02-18 18:11:01 +03:00
|
|
|
std::wstring_view action{ args[1] };
|
|
|
|
|
|
2021-01-11 12:51:13 +02:00
|
|
|
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());
|
|
|
|
|
|
2021-05-21 13:32:34 +03:00
|
|
|
if (action == RUN_NONELEVATED)
|
2020-05-21 19:44:32 +02:00
|
|
|
{
|
2020-06-08 22:53:40 +02:00
|
|
|
int nextArg = 2;
|
|
|
|
|
|
|
|
|
|
std::wstring_view target;
|
|
|
|
|
std::wstring_view pidFile;
|
2020-06-10 20:58:34 +03:00
|
|
|
std::wstring params;
|
2020-06-08 22:53:40 +02:00
|
|
|
|
|
|
|
|
while (nextArg < nArgs)
|
|
|
|
|
{
|
|
|
|
|
if (std::wstring_view(args[nextArg]) == L"-target" && nextArg + 1 < nArgs)
|
|
|
|
|
{
|
|
|
|
|
target = args[nextArg + 1];
|
|
|
|
|
nextArg += 2;
|
|
|
|
|
}
|
|
|
|
|
else if (std::wstring_view(args[nextArg]) == L"-pidFile" && nextArg + 1 < nArgs)
|
|
|
|
|
{
|
|
|
|
|
pidFile = args[nextArg + 1];
|
|
|
|
|
nextArg += 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-09-21 12:44:16 +02:00
|
|
|
params += args[nextArg];
|
|
|
|
|
params += L' ';
|
2020-06-08 22:53:40 +02:00
|
|
|
nextArg++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE hMapFile = NULL;
|
|
|
|
|
PDWORD pidBuffer = NULL;
|
|
|
|
|
|
|
|
|
|
if (!pidFile.empty())
|
|
|
|
|
{
|
|
|
|
|
hMapFile = OpenFileMappingW(FILE_MAP_WRITE, FALSE, pidFile.data());
|
|
|
|
|
if (hMapFile)
|
|
|
|
|
{
|
|
|
|
|
pidBuffer = reinterpret_cast<PDWORD>(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(DWORD)));
|
|
|
|
|
if (pidBuffer)
|
|
|
|
|
{
|
|
|
|
|
*pidBuffer = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-10 20:58:34 +03:00
|
|
|
run_same_elevation(target.data(), params, pidBuffer);
|
2020-06-18 13:43:09 +03:00
|
|
|
|
2020-06-08 22:53:40 +02:00
|
|
|
if (!pidFile.empty())
|
2020-05-21 19:44:32 +02:00
|
|
|
{
|
2020-05-29 19:02:37 +02:00
|
|
|
if (pidBuffer)
|
|
|
|
|
{
|
|
|
|
|
FlushViewOfFile(pidBuffer, sizeof(DWORD));
|
|
|
|
|
UnmapViewOfFile(pidBuffer);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-08 22:53:40 +02:00
|
|
|
if (hMapFile)
|
|
|
|
|
{
|
|
|
|
|
FlushFileBuffers(hMapFile);
|
|
|
|
|
CloseHandle(hMapFile);
|
|
|
|
|
}
|
2020-05-21 19:44:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
else if (action == UNINSTALL_MSI)
|
2020-02-18 18:11:01 +03:00
|
|
|
{
|
2021-05-21 13:32:34 +03:00
|
|
|
return UninstallMsiAction();
|
2020-02-18 18:11:01 +03:00
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
else if (action == UPDATE_NOW_LAUNCH_STAGE1)
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
2021-05-21 13:32:34 +03:00
|
|
|
const bool failed = !InstallNewVersionStage1();
|
|
|
|
|
if (failed)
|
|
|
|
|
{
|
|
|
|
|
UpdateState::store([&](UpdateState& state) {
|
|
|
|
|
state.downloadedInstallerFilename = {};
|
|
|
|
|
state.githubUpdateLastCheckedDate.emplace(timeutil::now());
|
|
|
|
|
state.state = UpdateState::errorDownloading;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return failed;
|
2020-04-21 10:30:12 +03:00
|
|
|
}
|
2021-05-21 13:32:34 +03:00
|
|
|
else if (action == UPDATE_NOW_LAUNCH_STAGE2)
|
2020-04-21 10:30:12 +03:00
|
|
|
{
|
|
|
|
|
using namespace std::string_view_literals;
|
2021-05-21 13:32:34 +03:00
|
|
|
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;
|
2020-04-21 10:30:12 +03:00
|
|
|
}
|
2020-02-18 18:11:01 +03:00
|
|
|
|
|
|
|
|
return 0;
|
2020-06-18 13:43:09 +03:00
|
|
|
}
|