mirror of
https://github.com/microsoft/PowerToys.git
synced 2024-11-24 04:12:32 +08:00
Autoupdate: implement updating bootstrapper utility (#5204)
This commit is contained in:
parent
5a48376a77
commit
3796a5ef97
10
.pipelines/build-bootstrapper.cmd
Normal file
10
.pipelines/build-bootstrapper.cmd
Normal file
@ -0,0 +1,10 @@
|
||||
cd /D "%~dp0"
|
||||
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0
|
||||
pushd .
|
||||
cd ..
|
||||
set SolutionDir=%cd%
|
||||
popd
|
||||
SET IsPipeline=1
|
||||
call msbuild ../src/bootstrapper/bootstrapper.vcxproj /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true /p:BuildProjectReferences=false /p:SolutionDir=%SolutionDir%\ || exit /b 1
|
||||
|
@ -132,6 +132,17 @@ build:
|
||||
- 'PowerToysSetup-*.msi'
|
||||
signing_options:
|
||||
sign_inline: true # This does signing a soon as this command completes
|
||||
- !!buildcommand
|
||||
name: 'Build Power Toys Bootstrapper'
|
||||
command: '.pipelines\build-bootstrapper.cmd'
|
||||
artifacts:
|
||||
- from: 'x64/Release'
|
||||
to: 'Build_Installer_Output'
|
||||
include:
|
||||
- 'PowerToysSetup-*.exe'
|
||||
signing_options:
|
||||
sign_inline: true # This does signing a soon as this command completes
|
||||
|
||||
|
||||
#package:
|
||||
# commands:
|
||||
|
@ -263,6 +263,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorPickerUI", "src\module
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "colorpicker", "colorpicker", "{1D78B84B-CA39-406C-98F4-71F7EC266CC0}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bootstrapper", "src\bootstrapper\bootstrapper.vcxproj", "{D194E3AA-F824-4CA9-9A58-034DD6B7D022}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@ -521,6 +523,8 @@ Global
|
||||
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Debug|x64.Build.0 = Debug|x64
|
||||
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Release|x64.ActiveCfg = Release|x64
|
||||
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Release|x64.Build.0 = Release|x64
|
||||
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D194E3AA-F824-4CA9-9A58-034DD6B7D022}.Release|x64.ActiveCfg = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <ProjectTelemetry.h>
|
||||
|
||||
#include "../../src/common/updating/updating.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
@ -14,7 +16,6 @@ TRACELOGGING_DEFINE_PROVIDER(
|
||||
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
|
||||
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
|
||||
|
||||
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
||||
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
||||
|
||||
// Creates a Scheduled Task to run at logon for the current user.
|
||||
@ -563,52 +564,6 @@ LExit:
|
||||
return WcaFinalize(er);
|
||||
}
|
||||
|
||||
std::optional<std::wstring> getMsiPackageInstalledPath(const wchar_t* product_upgrade_code, const wchar_t* file_component)
|
||||
{
|
||||
constexpr size_t guid_length = 39;
|
||||
wchar_t product_ID[guid_length];
|
||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(product_upgrade_code, 0, 0, product_ID); !found)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(product_ID); !installed)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DWORD buf_size = MAX_PATH;
|
||||
wchar_t buf[MAX_PATH];
|
||||
if (ERROR_SUCCESS == MsiGetProductInfoW(product_ID, INSTALLPROPERTY_INSTALLLOCATION, buf, &buf_size) && buf_size)
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
DWORD package_path_size = 0;
|
||||
|
||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
std::wstring package_path(++package_path_size, L'\0');
|
||||
|
||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||
|
||||
wchar_t path[256];
|
||||
DWORD path_size = 256;
|
||||
MsiGetComponentPathW(product_ID, file_component, path, &path_size);
|
||||
if (!path_size)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
PathCchRemoveFileSpec(path, path_size);
|
||||
return path;
|
||||
}
|
||||
|
||||
UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
@ -617,7 +572,7 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
|
||||
|
||||
try
|
||||
{
|
||||
if (auto install_path = getMsiPackageInstalledPath(POWERTOYS_UPGRADE_CODE, POWERTOYS_EXE_COMPONENT))
|
||||
if (auto install_path = updating::get_msi_package_installed_path())
|
||||
{
|
||||
MsiSetPropertyW(hInstall, L"INSTALLFOLDER", install_path->data());
|
||||
}
|
||||
|
@ -60,7 +60,7 @@
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -82,7 +82,7 @@
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@ -93,12 +93,12 @@
|
||||
<UACExecutionLevel>HighestAvailable</UACExecutionLevel>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(CIBuild)'!='true'">
|
||||
<ItemDefinitionGroup Condition="'$(CIBuild)'!='true'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CustomAction.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <common/common.h>
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/updating/http_client.h>
|
||||
#include <common/updating/dotnet_installation.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
@ -114,6 +115,7 @@ bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE};
|
||||
sei.lpFile = installer_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = L"-silent";
|
||||
|
||||
success = ShellExecuteExW(&sei) == TRUE;
|
||||
// Wait for the install completion
|
||||
@ -146,57 +148,6 @@ bool install_new_version_stage_2(std::wstring installer_path, std::wstring_view
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dotnet_is_installed()
|
||||
{
|
||||
auto runtimes = exec_and_read_output(LR"(dotnet --list-runtimes)");
|
||||
if (!runtimes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const char DESKTOP_DOTNET_RUNTIME_STRING[] = "Microsoft.WindowsDesktop.App 3.1.";
|
||||
return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos;
|
||||
}
|
||||
|
||||
bool install_dotnet()
|
||||
{
|
||||
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/d8cf1fe3-21c2-4baf-988f-f0152996135e/0c00b94713ee93e7ad5b4f82e2b86607/windowsdesktop-runtime-3.1.4-win-x64.exe";
|
||||
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime-3.1.4-win-x64.exe";
|
||||
|
||||
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
|
||||
winrt::Windows::Foundation::Uri download_link{ DOTNET_DESKTOP_DOWNLOAD_LINK };
|
||||
|
||||
const size_t max_attempts = 3;
|
||||
bool download_success = false;
|
||||
for (size_t i = 0; i < max_attempts; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
http::HttpClient client;
|
||||
client.download(download_link, dotnet_download_path).wait();
|
||||
download_success = true;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// couldn't download
|
||||
}
|
||||
}
|
||||
if (!download_success)
|
||||
{
|
||||
MessageBoxW(nullptr,
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE).c_str(),
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE).c_str(),
|
||||
MB_OK | MB_ICONERROR);
|
||||
return false;
|
||||
}
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOASYNC };
|
||||
sei.lpFile = dotnet_download_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = L"/install /passive";
|
||||
return ShellExecuteExW(&sei) == TRUE;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
int nArgs = 0;
|
||||
@ -270,11 +221,18 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
}
|
||||
else if (action == L"-install_dotnet")
|
||||
{
|
||||
if (dotnet_is_installed())
|
||||
if (updating::dotnet_is_installed())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return !install_dotnet();
|
||||
const bool success = updating::install_dotnet();
|
||||
|
||||
MessageBoxW(nullptr,
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE).c_str(),
|
||||
GET_RESOURCE_STRING(IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE).c_str(),
|
||||
MB_OK | MB_ICONERROR);
|
||||
|
||||
return !success;
|
||||
}
|
||||
else if (action == L"-uninstall_msi")
|
||||
{
|
||||
|
@ -1,27 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="action_runner.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\runner\msi_to_msix_upgrade.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
208
src/bootstrapper/bootstrapper.cpp
Normal file
208
src/bootstrapper/bootstrapper.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
|
||||
#include <common/common.h>
|
||||
#include <common/notifications.h>
|
||||
#include <common/RcResource.h>
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/updating/dotnet_installation.h>
|
||||
#include <common/version.h>
|
||||
#include <common/appMutex.h>
|
||||
#include <common/processApi.h>
|
||||
|
||||
#include <wil/resource.h>
|
||||
|
||||
#include <winrt/base.h>
|
||||
#include <span>
|
||||
#include "resource.h"
|
||||
#include <Msi.h>
|
||||
|
||||
#include <runner/action_runner_utils.h>
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t APPLICATION_ID[] = L"PowerToysInstaller";
|
||||
const wchar_t TOAST_TAG[] = L"PowerToysInstallerProgress";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
const wchar_t INSTALLER_EXTRACT_ERROR[] = L"Couldn't extract MSI installer!";
|
||||
const wchar_t TOAST_TITLE[] = L"PowerToys Installation";
|
||||
const wchar_t EXTRACTING_INSTALLER[] = L"Extracting PowerToys MSI...";
|
||||
const wchar_t UNINSTALLING_PREVIOUS_VERSION[] = L"Uninstalling previous PowerToys version...";
|
||||
const wchar_t UNINSTALL_PREVIOUS_VERSION_ERROR[] = L"Couldn't uninstall previous PowerToys version!";
|
||||
const wchar_t INSTALLING_DOTNET[] = L"Installing dotnet...";
|
||||
const wchar_t DOTNET_INSTALL_ERROR[] = L"Couldn't install dotnet!";
|
||||
const wchar_t INSTALLING_NEW_VERSION[] = L"Installing new PowerToys version...";
|
||||
const wchar_t NEW_VERSION_INSTALLATION_DONE[] = L"PowerToys installation complete!";
|
||||
const wchar_t NEW_VERSION_INSTALLATION_ERROR[] = L"Couldn't install new PowerToys version.";
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::optional<fs::path> extractEmbeddedInstaller()
|
||||
{
|
||||
auto executableRes = RcResource::create(IDR_BIN_MSIINSTALLER, L"BIN");
|
||||
if (!executableRes)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto installerPath = fs::temp_directory_path() / L"PowerToysBootstrappedInstaller-" PRODUCT_VERSION_STRING L".msi";
|
||||
return executableRes->saveAsFile(installerPath) ? std::make_optional(std::move(installerPath)) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<fs::path> extractIcon()
|
||||
{
|
||||
auto iconRes = RcResource::create(IDR_BIN_ICON, L"BIN");
|
||||
if (!iconRes)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto icoPath = fs::temp_directory_path() / L"PowerToysBootstrappedInstaller.ico";
|
||||
return iconRes->saveAsFile(icoPath) ? std::make_optional(std::move(icoPath)) : std::nullopt;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
using namespace localized_strings;
|
||||
winrt::init_apartment();
|
||||
|
||||
// Try killing PowerToys and prevent future processes launch
|
||||
for (auto& handle : getProcessHandlesByName(L"PowerToys.exe", PROCESS_TERMINATE))
|
||||
{
|
||||
TerminateProcess(handle.get(), 0);
|
||||
}
|
||||
auto powerToysMutex = createAppMutex(POWERTOYS_MSI_MUTEX_NAME);
|
||||
|
||||
int n_cmd_args = 0;
|
||||
LPWSTR* cmd_arg_list = CommandLineToArgvW(GetCommandLineW(), &n_cmd_args);
|
||||
const bool silent = n_cmd_args > 1 && std::wstring_view{ L"-silent" } == cmd_arg_list[1];
|
||||
|
||||
auto instanceMutex = createAppMutex(POWERTOYS_BOOTSTRAPPER_MUTEX_NAME);
|
||||
if (!instanceMutex)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
notifications::set_application_id(APPLICATION_ID);
|
||||
|
||||
fs::path iconPath{ L"C:\\" };
|
||||
if (auto extractedIcon = extractIcon())
|
||||
{
|
||||
iconPath = std::move(*extractedIcon);
|
||||
}
|
||||
|
||||
notifications::register_application_id(TOAST_TITLE, iconPath.c_str());
|
||||
|
||||
auto removeShortcut = wil::scope_exit([&] {
|
||||
notifications::unregister_application_id();
|
||||
});
|
||||
|
||||
// Check if there's a newer version installed, and launch its installer if so.
|
||||
const VersionHelper myVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
if (const auto installedVersion = updating::get_installed_powertoys_version(); installedVersion && *installedVersion >= myVersion)
|
||||
{
|
||||
auto msi_path = updating::get_msi_package_path();
|
||||
if (!msi_path.empty())
|
||||
{
|
||||
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
|
||||
MsiInstallProductW(msi_path.c_str(), nullptr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex progressLock;
|
||||
notifications::progress_bar_params progressParams;
|
||||
progressParams.progress = 0.0f;
|
||||
progressParams.progress_title = EXTRACTING_INSTALLER;
|
||||
notifications::toast_params params{ TOAST_TAG, false, std::move(progressParams) };
|
||||
notifications::show_toast_with_activations({}, TOAST_TITLE, {}, {}, std::move(params));
|
||||
|
||||
auto processToasts = wil::scope_exit([&] {
|
||||
run_message_loop(true, 2);
|
||||
});
|
||||
|
||||
// Worker thread to periodically increase progress and keep the progress toast from losing focus
|
||||
std::thread{ [&] {
|
||||
for (;; Sleep(3000))
|
||||
{
|
||||
std::scoped_lock lock{ progressLock };
|
||||
if (progressParams.progress == 1.f)
|
||||
{
|
||||
break;
|
||||
}
|
||||
progressParams.progress = min(0.99f, progressParams.progress + 0.001f);
|
||||
notifications::update_progress_bar_toast(TOAST_TAG, progressParams);
|
||||
}
|
||||
} }.detach();
|
||||
|
||||
auto updateProgressBar = [&](const float value, const wchar_t* title) {
|
||||
std::scoped_lock lock{ progressLock };
|
||||
progressParams.progress = value;
|
||||
progressParams.progress_title = title;
|
||||
notifications::update_progress_bar_toast(TOAST_TAG, progressParams);
|
||||
};
|
||||
|
||||
const auto installerPath = extractEmbeddedInstaller();
|
||||
if (!installerPath)
|
||||
{
|
||||
notifications::show_toast(INSTALLER_EXTRACT_ERROR, TOAST_TITLE);
|
||||
return 1;
|
||||
}
|
||||
auto removeExtractedInstaller = wil::scope_exit([&] {
|
||||
std::error_code _;
|
||||
fs::remove(*installerPath, _);
|
||||
});
|
||||
|
||||
updateProgressBar(.25f, UNINSTALLING_PREVIOUS_VERSION);
|
||||
const auto package_path = updating::get_msi_package_path();
|
||||
if (!package_path.empty() && !updating::uninstall_msi_version(package_path))
|
||||
{
|
||||
notifications::show_toast(UNINSTALL_PREVIOUS_VERSION_ERROR, TOAST_TITLE);
|
||||
}
|
||||
|
||||
updateProgressBar(.5f, INSTALLING_DOTNET);
|
||||
if (!updating::dotnet_is_installed() && !updating::install_dotnet())
|
||||
{
|
||||
notifications::show_toast(DOTNET_INSTALL_ERROR, TOAST_TITLE);
|
||||
}
|
||||
|
||||
updateProgressBar(.75f, INSTALLING_NEW_VERSION);
|
||||
if (!silent)
|
||||
{
|
||||
MsiSetInternalUI(INSTALLUILEVEL_FULL, nullptr);
|
||||
}
|
||||
const bool installationDone = MsiInstallProductW(installerPath->c_str(), nullptr) == ERROR_SUCCESS;
|
||||
|
||||
updateProgressBar(1.f, installationDone ? NEW_VERSION_INSTALLATION_DONE : NEW_VERSION_INSTALLATION_ERROR);
|
||||
|
||||
if (!installationDone)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
auto newPTPath = updating::get_msi_package_installed_path();
|
||||
if (!newPTPath)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
// Do not launch PowerToys, if we're launched from the action_runner
|
||||
if (!silent)
|
||||
{
|
||||
*newPTPath += L"\\PowerToys.exe";
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NO_CONSOLE };
|
||||
sei.lpFile = newPTPath->c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = UPDATE_REPORT_SUCCESS;
|
||||
ShellExecuteExW(&sei);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
48
src/bootstrapper/bootstrapper.rc
Normal file
48
src/bootstrapper/bootstrapper.rc
Normal file
@ -0,0 +1,48 @@
|
||||
#include <windows.h>
|
||||
#include "resource.h"
|
||||
#include "../common/version.h"
|
||||
|
||||
MAINICON ICON "../runner/svgs/icon.ico"
|
||||
IDR_BIN_ICON BIN "../runner/svgs/icon.ico"
|
||||
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_DOTNET_CORE_DOWNLOAD_FAILURE "Couldn't download .NET Core Desktop Runtime 3.1.3, please install it manually."
|
||||
IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE "PowerToys installation error"
|
||||
END
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
|
||||
END
|
||||
END
|
||||
|
||||
#include "Generated Files\installer_resource.rc"
|
139
src/bootstrapper/bootstrapper.vcxproj
Normal file
139
src/bootstrapper/bootstrapper.vcxproj
Normal file
@ -0,0 +1,139 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Import Project="..\..\installer\Version.props" />
|
||||
<Target Name="Generate Resource file" BeforeTargets="PrepareForBuild">
|
||||
<ItemGroup>
|
||||
<RCLines Include="IDR_BIN_MSIINSTALLER BIN "..\\..\\installer\\PowerToysSetup\\$(PlatformShortName)\\$(Configuration)\\PowerToysSetup-$(Version)-$(PlatformShortName).msi"" />
|
||||
</ItemGroup>
|
||||
<WriteLinesToFile File="Generated Files\installer_resource.rc" Lines="@(RCLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
|
||||
</Target>
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{D194E3AA-F824-4CA9-9A58-034DD6B7D022}</ProjectGuid>
|
||||
<RootNamespace>bootstrapper</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>bootstrapper</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>Spectre</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup>
|
||||
<TargetName>PowerToysSetup-$(Version)-$(PlatformShortName)</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>../;$(IncludePath)</IncludePath>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>WindowsApp.lib;Msi.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="bootstrapper.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
||||
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\runner\updating.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="bootstrapper.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="..\runner\svgs\icon.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
5
src/bootstrapper/packages.config
Normal file
5
src/bootstrapper/packages.config
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200703.9" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200519.2" targetFramework="native" />
|
||||
</packages>
|
19
src/bootstrapper/resource.h
Normal file
19
src/bootstrapper/resource.h
Normal file
@ -0,0 +1,19 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by bootstrapper.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys Bootstrapper"
|
||||
#define INTERNAL_NAME "bootstrapper"
|
||||
#define ORIGINAL_FILENAME "bootstrapper.exe"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
|
||||
#define IDS_DOTNET_CORE_DOWNLOAD_FAILURE 101
|
||||
#define IDS_DOTNET_CORE_DOWNLOAD_FAILURE_TITLE 102
|
||||
|
||||
#define IDR_BIN_MSIINSTALLER 103
|
||||
#define IDR_BIN_ICON 104
|
40
src/common/RcResource.cpp
Normal file
40
src/common/RcResource.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "pch.h"
|
||||
#include "RcResource.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
std::optional<RcResource> RcResource::create(int resource_id, const std::wstring_view resource_class)
|
||||
{
|
||||
const HRSRC resHandle = FindResourceW(nullptr, MAKEINTRESOURCEW(resource_id), resource_class.data());
|
||||
if (!resHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const HGLOBAL memHandle = LoadResource(nullptr, resHandle);
|
||||
if (!memHandle)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
const size_t resSize = SizeofResource(nullptr, resHandle);
|
||||
if (!resSize)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
auto res = static_cast<const std::byte*>(LockResource(memHandle));
|
||||
if (!res)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return RcResource{ { res, resSize } };
|
||||
}
|
||||
|
||||
bool RcResource::saveAsFile(const std::filesystem::path destination)
|
||||
{
|
||||
std::fstream installerFile{ destination, std::ios_base::binary | std::ios_base::out | std::ios_base::trunc };
|
||||
if (!installerFile.is_open())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
installerFile.write(reinterpret_cast<const char*>(_memory.data()), _memory.size());
|
||||
return true;
|
||||
}
|
22
src/common/RcResource.h
Normal file
22
src/common/RcResource.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <filesystem>
|
||||
|
||||
class RcResource
|
||||
{
|
||||
public:
|
||||
std::span<const std::byte> _memory;
|
||||
|
||||
static std::optional<RcResource> create(int resource_id, const std::wstring_view resource_class);
|
||||
bool saveAsFile(const std::filesystem::path destination);
|
||||
|
||||
private:
|
||||
RcResource() = delete;
|
||||
RcResource(std::span<const std::byte> memory) :
|
||||
_memory{ std::move(memory) }
|
||||
{
|
||||
}
|
||||
};
|
@ -4,45 +4,7 @@
|
||||
#include <RestartManager.h>
|
||||
#include <Psapi.h>
|
||||
|
||||
std::vector<RM_UNIQUE_PROCESS> GetProcessInfoByName(const std::wstring& processName)
|
||||
{
|
||||
DWORD bytesReturned{};
|
||||
std::vector<DWORD> processIds{};
|
||||
processIds.resize(1024);
|
||||
DWORD processIdSize{ (DWORD)processIds.size() * sizeof(DWORD) };
|
||||
EnumProcesses(processIds.data(), processIdSize, &bytesReturned);
|
||||
while (bytesReturned == processIdSize)
|
||||
{
|
||||
processIdSize *= 2;
|
||||
processIds.resize(processIdSize / sizeof(DWORD));
|
||||
EnumProcesses(processIds.data(), processIdSize, &bytesReturned);
|
||||
}
|
||||
std::vector<RM_UNIQUE_PROCESS> pInfos{};
|
||||
for (const DWORD& processId : processIds)
|
||||
{
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||
if (hProcess)
|
||||
{
|
||||
wchar_t name[MAX_PATH];
|
||||
if (GetProcessImageFileName(hProcess, name, MAX_PATH) > 0)
|
||||
{
|
||||
if (processName == PathFindFileName(name))
|
||||
{
|
||||
FILETIME creationTime{};
|
||||
FILETIME exitTime{};
|
||||
FILETIME kernelTime{};
|
||||
FILETIME userTime{};
|
||||
if (GetProcessTimes(hProcess, &creationTime, &exitTime, &kernelTime, &userTime))
|
||||
{
|
||||
pInfos.push_back({ processId, creationTime });
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
return pInfos;
|
||||
}
|
||||
#include "processApi.h"
|
||||
|
||||
void RestartProcess(const std::wstring& processName)
|
||||
{
|
||||
@ -52,7 +14,18 @@ void RestartProcess(const std::wstring& processName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::vector<RM_UNIQUE_PROCESS> pInfo = GetProcessInfoByName(processName);
|
||||
auto processHandles = getProcessHandlesByName(processName, PROCESS_QUERY_INFORMATION);
|
||||
std::vector<RM_UNIQUE_PROCESS> pInfo;
|
||||
for (const auto& hProcess : processHandles)
|
||||
{
|
||||
FILETIME creationTime{};
|
||||
FILETIME _{};
|
||||
if (GetProcessTimes(hProcess.get(), &creationTime, &_, &_, &_))
|
||||
{
|
||||
pInfo.emplace_back(RM_UNIQUE_PROCESS{ GetProcessId(hProcess.get()), creationTime });
|
||||
}
|
||||
}
|
||||
|
||||
if (pInfo.empty() ||
|
||||
RmRegisterResources(sessionHandle, 0, nullptr, sizeof(pInfo), pInfo.data(), 0, nullptr) != ERROR_SUCCESS)
|
||||
{
|
||||
|
27
src/common/appMutex.h
Normal file
27
src/common/appMutex.h
Normal file
@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
|
||||
#include "wil/resource.h"
|
||||
#include <lmcons.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr inline wchar_t POWERTOYS_MSI_MUTEX_NAME[] = L"Local\\PowerToyRunMutex";
|
||||
constexpr inline wchar_t POWERTOYS_MSIX_MUTEX_NAME[] = L"Local\\PowerToyMSIXRunMutex";
|
||||
constexpr inline wchar_t POWERTOYS_BOOTSTRAPPER_MUTEX_NAME[] = L"PowerToysBootstrapperMutex";
|
||||
}
|
||||
|
||||
inline wil::unique_mutex_nothrow createAppMutex(std::wstring mutexName)
|
||||
{
|
||||
wchar_t username[UNLEN + 1];
|
||||
DWORD username_length = UNLEN + 1;
|
||||
GetUserNameW(username, &username_length);
|
||||
mutexName += username;
|
||||
wil::unique_mutex_nothrow result{ CreateMutexW(nullptr, TRUE, mutexName.c_str()) };
|
||||
|
||||
return GetLastError() == ERROR_ALREADY_EXISTS ? wil::unique_mutex_nothrow{} : std::move(result);
|
||||
}
|
@ -268,13 +268,26 @@ RECT keep_rect_inside_rect(const RECT& small_rect, const RECT& big_rect)
|
||||
return result;
|
||||
}
|
||||
|
||||
int run_message_loop()
|
||||
int run_message_loop(const bool until_idle, const std::optional<uint32_t> timeout_seconds)
|
||||
{
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
bool stop = false;
|
||||
UINT_PTR timerId = 0;
|
||||
if (timeout_seconds.has_value())
|
||||
{
|
||||
timerId = SetTimer(nullptr, 0, *timeout_seconds * 1000, nullptr);
|
||||
}
|
||||
|
||||
while (!stop && GetMessageW(&msg, nullptr, 0, 0))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
stop = until_idle && !PeekMessageW(&msg, nullptr, 0, 0, PM_NOREMOVE);
|
||||
stop = stop || (msg.message == WM_TIMER && msg.wParam == timerId);
|
||||
}
|
||||
if (timeout_seconds.has_value())
|
||||
{
|
||||
KillTimer(nullptr, timerId);
|
||||
}
|
||||
return static_cast<int>(msg.wParam);
|
||||
}
|
||||
@ -537,7 +550,7 @@ bool run_non_elevated(const std::wstring& file, const std::wstring& params, DWOR
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
@ -548,7 +561,7 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params, DW
|
||||
{
|
||||
executable_args += L" " + params;
|
||||
}
|
||||
|
||||
|
||||
STARTUPINFO si = { 0 };
|
||||
PROCESS_INFORMATION pi = { 0 };
|
||||
auto succeeded = CreateProcessW(file.c_str(),
|
||||
@ -656,7 +669,7 @@ std::wstring get_module_filename(HMODULE mod)
|
||||
return { buffer, actual_length };
|
||||
}
|
||||
|
||||
std::wstring get_module_folderpath(HMODULE mod)
|
||||
std::wstring get_module_folderpath(HMODULE mod, const bool removeFilename)
|
||||
{
|
||||
wchar_t buffer[MAX_PATH + 1];
|
||||
DWORD actual_length = GetModuleFileNameW(mod, buffer, MAX_PATH);
|
||||
@ -671,7 +684,10 @@ std::wstring get_module_folderpath(HMODULE mod)
|
||||
return long_filename;
|
||||
}
|
||||
|
||||
PathRemoveFileSpecW(buffer);
|
||||
if (removeFilename)
|
||||
{
|
||||
PathRemoveFileSpecW(buffer);
|
||||
}
|
||||
return { buffer, (UINT)lstrlenW(buffer) };
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,7 @@ bool operator<(const RECT& lhs, const RECT& rhs);
|
||||
// Moves and/or resizes small_rect to fit inside big_rect.
|
||||
RECT keep_rect_inside_rect(const RECT& small_rect, const RECT& big_rect);
|
||||
// Initializes and runs windows message loop
|
||||
int run_message_loop();
|
||||
int run_message_loop(const bool until_idle = false, const std::optional<uint32_t> timeout_seconds = {});
|
||||
|
||||
std::optional<std::wstring> get_last_error_message(const DWORD dw);
|
||||
void show_last_error_message(LPCWSTR lpszFunction, DWORD dw);
|
||||
@ -89,7 +89,7 @@ std::wstring get_process_path(HWND hwnd) noexcept;
|
||||
std::wstring get_product_version();
|
||||
|
||||
std::wstring get_module_filename(HMODULE mod = nullptr);
|
||||
std::wstring get_module_folderpath(HMODULE mod = nullptr);
|
||||
std::wstring get_module_folderpath(HMODULE mod = nullptr, const bool removeFilename = true);
|
||||
|
||||
// Get a string from the resource file
|
||||
std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, const wchar_t* fallback);
|
||||
@ -135,6 +135,6 @@ struct overloaded : Ts...
|
||||
using Ts::operator()...;
|
||||
};
|
||||
template<class... Ts>
|
||||
overloaded(Ts...)->overloaded<Ts...>;
|
||||
overloaded(Ts...) -> overloaded<Ts...>;
|
||||
|
||||
#define POWER_LAUNCHER_PID_SHARED_FILE L"Local\\3cbfbad4-199b-4e2c-9825-942d5d3d3c74"
|
||||
|
@ -116,6 +116,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="animation.h" />
|
||||
<ClInclude Include="appMutex.h" />
|
||||
<ClInclude Include="async_message_queue.h" />
|
||||
<ClInclude Include="d2d_svg.h" />
|
||||
<ClInclude Include="d2d_text.h" />
|
||||
@ -126,6 +127,8 @@
|
||||
<ClInclude Include="keyboard_layout.h" />
|
||||
<ClInclude Include="keyboard_layout_impl.h" />
|
||||
<ClInclude Include="notifications.h" />
|
||||
<ClInclude Include="processApi.h" />
|
||||
<ClInclude Include="RcResource.h" />
|
||||
<ClInclude Include="os-detect.h" />
|
||||
<ClInclude Include="RestartManagement.h" />
|
||||
<ClInclude Include="shared_constants.h" />
|
||||
@ -165,6 +168,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RcResource.cpp" />
|
||||
<ClCompile Include="RestartManagement.cpp" />
|
||||
<ClCompile Include="settings_helpers.cpp" />
|
||||
<ClCompile Include="settings_objects.cpp" />
|
||||
@ -195,4 +199,4 @@
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.CppWinRT.2.0.200703.9\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
@ -120,6 +120,15 @@
|
||||
<ClInclude Include="debug_control.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="RcResource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="appMutex.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="processApi.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="d2d_svg.cpp">
|
||||
@ -195,6 +204,9 @@
|
||||
<ClCompile Include="RestartManagement.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="RcResource.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "common.h"
|
||||
#include "com_object_factory.h"
|
||||
#include "notifications.h"
|
||||
#include "winstore.h"
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/base.h>
|
||||
@ -11,35 +12,37 @@
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.UI.Notifications.h>
|
||||
#include <winrt/Windows.ApplicationModel.Background.h>
|
||||
|
||||
#include "winstore.h"
|
||||
#include <wil/com.h>
|
||||
#include <propvarutil.h>
|
||||
#include <propkey.h>
|
||||
#include <Shobjidl.h>
|
||||
|
||||
#include <winerror.h>
|
||||
#include <NotificationActivationCallback.h>
|
||||
|
||||
#include "notifications_winrt/handler_functions.h"
|
||||
#include <filesystem>
|
||||
|
||||
using namespace winrt::Windows::ApplicationModel::Background;
|
||||
using winrt::Windows::Data::Xml::Dom::XmlDocument;
|
||||
using winrt::Windows::UI::Notifications::ToastNotification;
|
||||
using winrt::Windows::UI::Notifications::ToastNotificationManager;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr std::wstring_view TASK_NAME = L"PowerToysBackgroundNotificationsHandler";
|
||||
constexpr std::wstring_view TASK_ENTRYPOINT = L"PowerToysNotifications.BackgroundHandler";
|
||||
constexpr std::wstring_view APPLICATION_ID = L"PowerToys";
|
||||
constexpr std::wstring_view PACKAGED_APPLICATION_ID = L"PowerToys";
|
||||
constexpr std::wstring_view APPIDS_REGISTRY = LR"(Software\Classes\AppUserModelId\)";
|
||||
|
||||
constexpr std::wstring_view WIN32_AUMID = L"Microsoft.PowerToysWin32";
|
||||
std::wstring APPLICATION_ID;
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
namespace localized_strings
|
||||
{
|
||||
constexpr std::wstring_view SNOOZE_BUTTON = L"Snooze";
|
||||
|
||||
constexpr std::wstring_view PT_UPDATE = L"PowerToys update";
|
||||
constexpr std::wstring_view DOWNLOAD_IN_PROGRESS = L"Downloading...";
|
||||
constexpr std::wstring_view DOWNLOAD_COMPLETE = L"Download complete";
|
||||
}
|
||||
|
||||
static DWORD loop_thread_id()
|
||||
@ -119,6 +122,74 @@ void notifications::run_desktop_app_activator_loop()
|
||||
CoRevokeClassObject(token);
|
||||
}
|
||||
|
||||
bool notifications::register_application_id(const std::wstring_view appName, const std::wstring_view iconPath)
|
||||
{
|
||||
std::wstring aumidPath{ APPIDS_REGISTRY };
|
||||
aumidPath += APPLICATION_ID;
|
||||
wil::unique_hkey aumidKey;
|
||||
if (FAILED(RegCreateKeyW(HKEY_CURRENT_USER, aumidPath.c_str(), &aumidKey)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (FAILED(RegSetKeyValueW(aumidKey.get(),
|
||||
nullptr,
|
||||
L"DisplayName",
|
||||
REG_SZ,
|
||||
appName.data(),
|
||||
static_cast<DWORD>((size(appName) + 1) * sizeof(wchar_t)))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FAILED(RegSetKeyValueW(aumidKey.get(),
|
||||
nullptr,
|
||||
L"IconUri",
|
||||
REG_SZ,
|
||||
iconPath.data(),
|
||||
static_cast<DWORD>((size(iconPath) + 1) * sizeof(wchar_t)))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::wstring_view iconColor = L"FFDDDDDD";
|
||||
if (FAILED(RegSetKeyValueW(aumidKey.get(),
|
||||
nullptr,
|
||||
L"IconBackgroundColor",
|
||||
REG_SZ,
|
||||
iconColor.data(),
|
||||
static_cast<DWORD>((size(iconColor) + 1) * sizeof(wchar_t)))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void notifications::unregister_application_id()
|
||||
{
|
||||
std::wstring aumidPath{ APPIDS_REGISTRY };
|
||||
aumidPath += APPLICATION_ID;
|
||||
wil::unique_hkey registryRoot;
|
||||
RegOpenKeyW(HKEY_CURRENT_USER, aumidPath.c_str(), ®istryRoot);
|
||||
if (!registryRoot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
RegDeleteTreeW(registryRoot.get(), nullptr);
|
||||
registryRoot.reset();
|
||||
RegOpenKeyW(HKEY_CURRENT_USER, APPIDS_REGISTRY.data(), ®istryRoot);
|
||||
if (!registryRoot)
|
||||
{
|
||||
return;
|
||||
}
|
||||
RegDeleteKeyW(registryRoot.get(), APPLICATION_ID.data());
|
||||
}
|
||||
|
||||
void notifications::set_application_id(const std::wstring_view appID)
|
||||
{
|
||||
APPLICATION_ID = appID;
|
||||
SetCurrentProcessExplicitAppUserModelID(APPLICATION_ID.c_str());
|
||||
}
|
||||
|
||||
void notifications::register_background_toast_handler()
|
||||
{
|
||||
if (!winstore::running_as_packaged())
|
||||
@ -133,7 +204,7 @@ void notifications::register_background_toast_handler()
|
||||
BackgroundExecutionManager::RequestAccessAsync().get();
|
||||
|
||||
BackgroundTaskBuilder builder;
|
||||
ToastNotificationActionTrigger trigger{ APPLICATION_ID };
|
||||
ToastNotificationActionTrigger trigger{ PACKAGED_APPLICATION_ID };
|
||||
builder.SetTrigger(trigger);
|
||||
builder.TaskEntryPoint(TASK_ENTRYPOINT);
|
||||
builder.Name(TASK_NAME);
|
||||
@ -154,10 +225,10 @@ void notifications::register_background_toast_handler()
|
||||
}
|
||||
}
|
||||
|
||||
void notifications::show_toast(std::wstring message, toast_params params)
|
||||
void notifications::show_toast(std::wstring message, std::wstring title, toast_params params)
|
||||
{
|
||||
// The toast won't be actually activated in the background, since it doesn't have any buttons
|
||||
show_toast_with_activations(std::move(message), {}, {}, std::move(params));
|
||||
show_toast_with_activations(std::move(message), std::move(title), {}, {}, std::move(params));
|
||||
}
|
||||
|
||||
inline void xml_escape(std::wstring data)
|
||||
@ -191,34 +262,26 @@ inline void xml_escape(std::wstring data)
|
||||
data.swap(buffer);
|
||||
}
|
||||
|
||||
void notifications::show_toast_with_activations(std::wstring message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params)
|
||||
void notifications::show_toast_with_activations(std::wstring message,
|
||||
std::wstring title,
|
||||
std::wstring_view background_handler_id,
|
||||
std::vector<action_t> actions,
|
||||
toast_params params)
|
||||
{
|
||||
// DO NOT LOCALIZE any string in this function, because they're XML tags and a subject to
|
||||
// https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/toast-xml-schema
|
||||
|
||||
std::wstring toast_xml;
|
||||
toast_xml.reserve(2048);
|
||||
std::wstring title{ L"PowerToys" };
|
||||
if (winstore::running_as_packaged())
|
||||
{
|
||||
title += L" (Experimental)";
|
||||
}
|
||||
|
||||
toast_xml += LR"(<?xml version="1.0"?><toast><visual><binding template="ToastGeneric"><text>)";
|
||||
toast_xml += LR"(<?xml version="1.0"?><toast><visual><binding template="ToastGeneric"><text id="1">)";
|
||||
toast_xml += title;
|
||||
toast_xml += L"</text><text>";
|
||||
toast_xml += LR"(</text><text id="2">)";
|
||||
toast_xml += message;
|
||||
toast_xml += L"</text>";
|
||||
if (params.progress)
|
||||
if (params.progress_bar.has_value())
|
||||
{
|
||||
toast_xml += LR"(<progress title=")";
|
||||
toast_xml += localized_strings::PT_UPDATE;
|
||||
if (params.subtitle)
|
||||
{
|
||||
toast_xml += L" ";
|
||||
toast_xml += *params.subtitle;
|
||||
}
|
||||
toast_xml += LR"(" value="{progressValue}" valueStringOverride="{progressValueString}" status="{progressStatus}"/>)";
|
||||
toast_xml += LR"(<progress title="{progressTitle}" value="{progressValue}" valueStringOverride="{progressValueString}" status="" />)";
|
||||
}
|
||||
toast_xml += L"</binding></visual><actions>";
|
||||
for (size_t i = 0; i < size(actions); ++i)
|
||||
@ -310,19 +373,19 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
|
||||
toast_xml_doc.LoadXml(toast_xml);
|
||||
ToastNotification notification{ toast_xml_doc };
|
||||
|
||||
if (params.progress)
|
||||
if (params.progress_bar.has_value())
|
||||
{
|
||||
float progress = std::clamp(params.progress.value(), 0.0f, 1.0f);
|
||||
float progress = std::clamp(params.progress_bar->progress, 0.0f, 1.0f);
|
||||
winrt::Windows::Foundation::Collections::StringMap map;
|
||||
map.Insert(L"progressValue", std::to_wstring(progress));
|
||||
map.Insert(L"progressValueString", std::to_wstring(static_cast<int>(progress * 100)) + std::wstring(L"%"));
|
||||
map.Insert(L"progressStatus", localized_strings::DOWNLOAD_IN_PROGRESS);
|
||||
map.Insert(L"progressTitle", params.progress_bar->progress_title);
|
||||
winrt::Windows::UI::Notifications::NotificationData data(map);
|
||||
notification.Data(data);
|
||||
}
|
||||
|
||||
const auto notifier = winstore::running_as_packaged() ? ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(WIN32_AUMID);
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(APPLICATION_ID);
|
||||
|
||||
// Set a tag-related params if it has a valid length
|
||||
if (params.tag.has_value() && params.tag->length() < 64)
|
||||
@ -343,29 +406,18 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
|
||||
notifier.Show(notification);
|
||||
}
|
||||
|
||||
void notifications::update_progress_bar_toast(std::wstring plaintext_message, toast_params params)
|
||||
void notifications::update_progress_bar_toast(std::wstring_view tag, progress_bar_params params)
|
||||
{
|
||||
if (!params.progress.has_value())
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto notifier = winstore::running_as_packaged() ?
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(APPLICATION_ID);
|
||||
|
||||
const auto notifier = winstore::running_as_packaged() ? ToastNotificationManager::ToastNotificationManager::CreateToastNotifier() :
|
||||
ToastNotificationManager::ToastNotificationManager::CreateToastNotifier(WIN32_AUMID);
|
||||
|
||||
float progress = std::clamp(params.progress.value(), 0.0f, 1.0f);
|
||||
float progress = std::clamp(params.progress, 0.0f, 1.0f);
|
||||
winrt::Windows::Foundation::Collections::StringMap map;
|
||||
map.Insert(L"progressValue", std::to_wstring(progress));
|
||||
map.Insert(L"progressValueString", std::to_wstring(static_cast<int>(progress * 100)) + std::wstring(L"%"));
|
||||
map.Insert(L"progressStatus", progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE);
|
||||
map.Insert(L"progressTitle", params.progress_title);
|
||||
|
||||
|
||||
winrt::Windows::UI::Notifications::NotificationData data(map);
|
||||
std::wstring tag = L"";
|
||||
if (params.tag.has_value() && params.tag->length() < 64)
|
||||
{
|
||||
tag = *params.tag;
|
||||
}
|
||||
|
||||
winrt::Windows::UI::Notifications::NotificationUpdateResult res = notifier.Update(data, tag);
|
||||
}
|
||||
|
@ -10,10 +10,13 @@ namespace notifications
|
||||
{
|
||||
constexpr inline const wchar_t TOAST_ACTIVATED_LAUNCH_ARG[] = L"-ToastActivated";
|
||||
|
||||
void set_application_id(const std::wstring_view appID);
|
||||
void register_background_toast_handler();
|
||||
|
||||
void run_desktop_app_activator_loop();
|
||||
|
||||
bool register_application_id(const std::wstring_view appName, const std::wstring_view iconPath);
|
||||
void unregister_application_id();
|
||||
|
||||
struct snooze_duration
|
||||
{
|
||||
std::wstring label;
|
||||
@ -39,17 +42,22 @@ namespace notifications
|
||||
bool context_menu = false;
|
||||
};
|
||||
|
||||
struct progress_bar_params
|
||||
{
|
||||
std::wstring_view progress_title;
|
||||
float progress = 0.f;
|
||||
};
|
||||
|
||||
struct toast_params
|
||||
{
|
||||
std::optional<std::wstring_view> tag;
|
||||
bool resend_if_scheduled = true;
|
||||
std::optional<float> progress;
|
||||
std::optional<std::wstring_view> subtitle;
|
||||
std::optional<progress_bar_params> progress_bar;
|
||||
};
|
||||
|
||||
using action_t = std::variant<link_button, background_activated_button, snooze_button>;
|
||||
|
||||
void show_toast(std::wstring plaintext_message, toast_params params = {});
|
||||
void show_toast_with_activations(std::wstring plaintext_message, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
|
||||
void update_progress_bar_toast(std::wstring plaintext_message, toast_params params);
|
||||
void show_toast(std::wstring plaintext_message, std::wstring title, toast_params params = {});
|
||||
void show_toast_with_activations(std::wstring plaintext_message, std::wstring title, std::wstring_view background_handler_id, std::vector<action_t> actions, toast_params params = {});
|
||||
void update_progress_bar_toast(std::wstring_view tag, progress_bar_params params);
|
||||
}
|
||||
|
42
src/common/processApi.h
Normal file
42
src/common/processApi.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <wil/resource.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <Psapi.h>
|
||||
#include <string_view>
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
inline std::vector<wil::unique_process_handle> getProcessHandlesByName(const std::wstring_view processName, DWORD handleAccess)
|
||||
{
|
||||
std::vector<wil::unique_process_handle> result;
|
||||
DWORD bytesRequired;
|
||||
std::vector<DWORD> processIds;
|
||||
processIds.resize(4096 / sizeof(processIds[0]));
|
||||
auto processIdSize = static_cast<DWORD>(size(processIds) * sizeof(processIds[0]));
|
||||
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
||||
while (bytesRequired == processIdSize)
|
||||
{
|
||||
processIdSize *= 2;
|
||||
processIds.resize(processIdSize / sizeof(processIds[0]));
|
||||
EnumProcesses(processIds.data(), processIdSize, &bytesRequired);
|
||||
}
|
||||
processIds.resize(bytesRequired / sizeof(processIds[0]));
|
||||
|
||||
handleAccess |= PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ;
|
||||
for (const DWORD processId : processIds)
|
||||
{
|
||||
wil::unique_process_handle hProcess{ OpenProcess(handleAccess, FALSE, processId) };
|
||||
wchar_t name[MAX_PATH + 1];
|
||||
if (!hProcess || !GetProcessImageFileNameW(hProcess.get(), name, MAX_PATH))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (processName == PathFindFileNameW(name))
|
||||
{
|
||||
result.push_back(std::move(hProcess));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
64
src/common/updating/dotnet_installation.cpp
Normal file
64
src/common/updating/dotnet_installation.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include <common/common.h>
|
||||
|
||||
#include "http_client.h"
|
||||
#include "dotnet_installation.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace updating
|
||||
{
|
||||
bool dotnet_is_installed()
|
||||
{
|
||||
auto runtimes = exec_and_read_output(LR"(dotnet --list-runtimes)");
|
||||
if (!runtimes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const char DESKTOP_DOTNET_RUNTIME_STRING[] = "Microsoft.WindowsDesktop.App 3.1.";
|
||||
return runtimes->find(DESKTOP_DOTNET_RUNTIME_STRING) != std::string::npos;
|
||||
}
|
||||
|
||||
bool install_dotnet()
|
||||
{
|
||||
const wchar_t DOTNET_DESKTOP_DOWNLOAD_LINK[] = L"https://download.visualstudio.microsoft.com/download/pr/3eb7efa1-96c6-4e97-bb9f-563ecf595f8a/7efd9c1cdd74df8fb0a34c288138a84f/windowsdesktop-runtime-3.1.6-win-x64.exe";
|
||||
const wchar_t DOTNET_DESKTOP_FILENAME[] = L"windowsdesktop-runtime.exe";
|
||||
|
||||
auto dotnet_download_path = fs::temp_directory_path() / DOTNET_DESKTOP_FILENAME;
|
||||
winrt::Windows::Foundation::Uri download_link{ DOTNET_DESKTOP_DOWNLOAD_LINK };
|
||||
|
||||
const size_t max_attempts = 3;
|
||||
bool download_success = false;
|
||||
for (size_t i = 0; i < max_attempts; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
http::HttpClient client;
|
||||
client.download(download_link, dotnet_download_path).wait();
|
||||
download_success = true;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// couldn't download
|
||||
}
|
||||
}
|
||||
if (!download_success)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE };
|
||||
sei.lpFile = dotnet_download_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = L"/install /passive";
|
||||
if (ShellExecuteExW(&sei) != TRUE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
WaitForSingleObject(sei.hProcess, INFINITE);
|
||||
CloseHandle(sei.hProcess);
|
||||
return true;
|
||||
}
|
||||
}
|
7
src/common/updating/dotnet_installation.h
Normal file
7
src/common/updating/dotnet_installation.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
namespace updating
|
||||
{
|
||||
bool dotnet_is_installed();
|
||||
bool install_dotnet();
|
||||
}
|
@ -13,5 +13,7 @@
|
||||
#include <Shobjidl.h>
|
||||
#include <Knownfolders.h>
|
||||
#include <ShlObj_core.h>
|
||||
#include <shellapi.h>
|
||||
#include <filesystem>
|
||||
|
||||
#endif //PCH_H
|
||||
|
@ -13,6 +13,7 @@ namespace
|
||||
{
|
||||
const wchar_t UPDATE_NOTIFY_TOAST_TAG[] = L"PTUpdateNotifyTag";
|
||||
const wchar_t UPDATE_READY_TOAST_TAG[] = L"PTUpdateReadyTag";
|
||||
const wchar_t TOAST_TITLE[] = L"PowerToys Update";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
@ -23,7 +24,7 @@ namespace localized_strings
|
||||
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
|
||||
|
||||
|
||||
const wchar_t UNINSTALLATION_SUCCESS[] = L"Previous version of PowerToys was uninstalled successfully.";
|
||||
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
|
||||
|
||||
@ -35,6 +36,9 @@ namespace localized_strings
|
||||
const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
|
||||
const wchar_t DOWNLOAD_IN_PROGRESS[] = L"Downloading...";
|
||||
const wchar_t DOWNLOAD_COMPLETE[] = L"Download complete";
|
||||
|
||||
}
|
||||
|
||||
namespace updating
|
||||
@ -55,7 +59,7 @@ namespace updating
|
||||
{
|
||||
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
|
||||
std::wstring contents = GITHUB_NEW_VERSION_UNAVAILABLE;
|
||||
::notifications::show_toast(std::move(contents), std::move(toast_params));
|
||||
::notifications::show_toast(std::move(contents), TOAST_TITLE, std::move(toast_params));
|
||||
}
|
||||
|
||||
void show_available(const updating::new_version_download_info& info)
|
||||
@ -64,18 +68,28 @@ namespace updating
|
||||
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE;
|
||||
contents += current_version_to_next_version(info);
|
||||
|
||||
::notifications::show_toast_with_activations(std::move(contents), {},
|
||||
{
|
||||
::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" },
|
||||
::notifications::link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_uri.ToString().c_str() }
|
||||
},
|
||||
std::move(toast_params));
|
||||
::notifications::show_toast_with_activations(std::move(contents),
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://download_and_install_update/" }, ::notifications::link_button{ GITHUB_NEW_VERSION_MORE_INFO, info.release_page_uri.ToString().c_str() } },
|
||||
std::move(toast_params));
|
||||
}
|
||||
|
||||
void show_download_start(const updating::new_version_download_info& info)
|
||||
{
|
||||
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false, 0.0f, info.version_string };
|
||||
::notifications::show_toast_with_activations(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED, {}, {}, std::move(toast_params));
|
||||
::notifications::progress_bar_params progress_bar_params;
|
||||
std::wstring progress_title{ info.version_string };
|
||||
progress_title += L' ';
|
||||
progress_title += localized_strings::DOWNLOAD_IN_PROGRESS;
|
||||
|
||||
progress_bar_params.progress_title = progress_title;
|
||||
progress_bar_params.progress = .0f;
|
||||
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false, std::move(progress_bar_params) };
|
||||
::notifications::show_toast_with_activations(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED,
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
{},
|
||||
std::move(toast_params));
|
||||
}
|
||||
|
||||
void show_visit_github(const updating::new_version_download_info& info)
|
||||
@ -83,7 +97,11 @@ namespace updating
|
||||
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
|
||||
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
|
||||
contents += current_version_to_next_version(info);
|
||||
::notifications::show_toast_with_activations(std::move(contents), {}, { ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } }, std::move(toast_params));
|
||||
::notifications::show_toast_with_activations(std::move(contents),
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
{ ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } },
|
||||
std::move(toast_params));
|
||||
}
|
||||
|
||||
void show_install_error(const updating::new_version_download_info& info)
|
||||
@ -91,7 +109,11 @@ namespace updating
|
||||
::notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
|
||||
std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
|
||||
contents += current_version_to_next_version(info);
|
||||
::notifications::show_toast_with_activations(std::move(contents), {}, { ::notifications::link_button{ GITHUB_NEW_VERSION_VISIT, info.release_page_uri.ToString().c_str() } }, std::move(toast_params));
|
||||
::notifications::show_toast_with_activations(std::move(contents),
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
{ ::notifications::link_button{ 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)
|
||||
@ -101,27 +123,34 @@ namespace updating
|
||||
new_version_ready += current_version_to_next_version(info);
|
||||
|
||||
::notifications::show_toast_with_activations(std::move(new_version_ready),
|
||||
{},
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
{ ::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" + info.installer_filename },
|
||||
::notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" + info.installer_filename },
|
||||
::notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
|
||||
std::move(toast_params));
|
||||
std::move(toast_params));
|
||||
}
|
||||
|
||||
void show_uninstallation_success()
|
||||
{
|
||||
::notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS);
|
||||
::notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS, TOAST_TITLE);
|
||||
}
|
||||
|
||||
void show_uninstallation_error()
|
||||
{
|
||||
::notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR);
|
||||
::notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR, TOAST_TITLE);
|
||||
}
|
||||
|
||||
void update_download_progress(float progress)
|
||||
void update_download_progress(const updating::new_version_download_info& info, float progress)
|
||||
{
|
||||
::notifications::toast_params toast_params { UPDATE_NOTIFY_TOAST_TAG, false, progress };
|
||||
::notifications::update_progress_bar_toast(localized_strings::GITHUB_NEW_VERSION_DOWNLOAD_STARTED, std::move(toast_params));
|
||||
::notifications::progress_bar_params progress_bar_params;
|
||||
|
||||
std::wstring progress_title{ info.version_string };
|
||||
progress_title += L' ';
|
||||
progress_title += progress < 1 ? localized_strings::DOWNLOAD_IN_PROGRESS : localized_strings::DOWNLOAD_COMPLETE;
|
||||
progress_bar_params.progress_title = progress_title;
|
||||
progress_bar_params.progress = progress;
|
||||
::notifications::update_progress_bar_toast(UPDATE_NOTIFY_TOAST_TAG, progress_bar_params);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,6 @@ namespace updating
|
||||
void show_uninstallation_success();
|
||||
void show_uninstallation_error();
|
||||
|
||||
void update_download_progress(float progress);
|
||||
void update_download_progress(const updating::new_version_download_info& info, float progress);
|
||||
}
|
||||
}
|
@ -17,16 +17,19 @@
|
||||
#include <winrt/Windows.Networking.Connectivity.h>
|
||||
|
||||
#include "VersionHelper.h"
|
||||
#include <PathCch.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
||||
const wchar_t POWERTOYS_EXE_COMPONENT[] = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
|
||||
const wchar_t DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH[] = L"delete_previous_powertoys_confirm";
|
||||
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
|
||||
const wchar_t MSIX_PACKAGE_NAME[] = L"Microsoft.PowerToys";
|
||||
const wchar_t MSIX_PACKAGE_PUBLISHER[] = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
|
||||
|
||||
const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
|
||||
const wchar_t TOAST_TITLE[] = L"PowerToys";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
@ -88,7 +91,7 @@ namespace updating
|
||||
{
|
||||
try
|
||||
{
|
||||
::notifications::show_toast(*system_message);
|
||||
::notifications::show_toast(*system_message, TOAST_TITLE);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -114,7 +117,7 @@ namespace updating
|
||||
if (github_version > current_version)
|
||||
{
|
||||
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
|
||||
constexpr const std::wstring_view required_filename_pattern = updating::installer_filename_pattern;
|
||||
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
|
||||
// Desc-sorted by its priority
|
||||
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
|
||||
for (const auto asset_extension : asset_extensions)
|
||||
@ -202,7 +205,7 @@ namespace updating
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
|
||||
if (download_updates_automatically && !could_be_costly_connection())
|
||||
{
|
||||
auto installer_download_dst = create_download_path() / new_version->installer_filename;
|
||||
@ -260,8 +263,8 @@ namespace updating
|
||||
|
||||
try
|
||||
{
|
||||
auto progressUpdateHandle = [](float progress) {
|
||||
updating::notifications::update_download_progress(progress);
|
||||
auto progressUpdateHandle = [&](float progress) {
|
||||
updating::notifications::update_download_progress(new_version.value(), progress);
|
||||
};
|
||||
|
||||
http::HttpClient client;
|
||||
@ -275,4 +278,84 @@ namespace updating
|
||||
|
||||
co_return new_version->installer_filename;
|
||||
}
|
||||
|
||||
std::optional<std::wstring> get_msi_package_installed_path()
|
||||
{
|
||||
constexpr size_t guid_length = 39;
|
||||
wchar_t product_ID[guid_length];
|
||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, product_ID); !found)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(product_ID); !installed)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DWORD buf_size = MAX_PATH;
|
||||
wchar_t buf[MAX_PATH];
|
||||
if (ERROR_SUCCESS == MsiGetProductInfoW(product_ID, INSTALLPROPERTY_INSTALLLOCATION, buf, &buf_size) && buf_size)
|
||||
{
|
||||
return buf;
|
||||
}
|
||||
|
||||
DWORD package_path_size = 0;
|
||||
|
||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
std::wstring package_path(++package_path_size, L'\0');
|
||||
|
||||
if (ERROR_SUCCESS != MsiGetProductInfoW(product_ID, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||
|
||||
wchar_t path[MAX_PATH];
|
||||
DWORD path_size = MAX_PATH;
|
||||
MsiGetComponentPathW(product_ID, POWERTOYS_EXE_COMPONENT, path, &path_size);
|
||||
if (!path_size)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
PathCchRemoveFileSpec(path, path_size);
|
||||
return path;
|
||||
}
|
||||
|
||||
std::optional<VersionHelper> get_installed_powertoys_version()
|
||||
{
|
||||
auto installed_path = get_msi_package_installed_path();
|
||||
if (!installed_path)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
*installed_path += L"\\PowerToys.exe";
|
||||
|
||||
// Get the version information for the file requested
|
||||
const DWORD fvSize = GetFileVersionInfoSizeW(installed_path->c_str(), nullptr);
|
||||
if (!fvSize)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto pbVersionInfo = std::make_unique<BYTE[]>(fvSize);
|
||||
|
||||
if (!GetFileVersionInfoW(installed_path->c_str(), 0, fvSize, pbVersionInfo.get()))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
VS_FIXEDFILEINFO* fileInfo = nullptr;
|
||||
UINT fileInfoLen = 0;
|
||||
if (!VerQueryValueW(pbVersionInfo.get(), L"\\", reinterpret_cast<LPVOID*>(&fileInfo), &fileInfoLen))
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return VersionHelper{ (fileInfo->dwFileVersionMS >> 16) & 0xffff,
|
||||
(fileInfo->dwFileVersionMS >> 0) & 0xffff,
|
||||
(fileInfo->dwFileVersionLS >> 16) & 0xffff };
|
||||
}
|
||||
}
|
@ -4,14 +4,17 @@
|
||||
#include <string>
|
||||
#include <future>
|
||||
#include <filesystem>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
#include "../VersionHelper.h"
|
||||
|
||||
namespace updating
|
||||
{
|
||||
std::wstring get_msi_package_path();
|
||||
bool uninstall_msi_version(const std::wstring& package_path);
|
||||
bool offer_msi_uninstallation();
|
||||
std::optional<std::wstring> get_msi_package_installed_path();
|
||||
std::optional<VersionHelper> get_installed_powertoys_version();
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async();
|
||||
|
||||
@ -30,5 +33,6 @@ namespace updating
|
||||
std::future<void> check_new_version_available();
|
||||
std::future<std::wstring> download_update();
|
||||
|
||||
constexpr inline std::wstring_view installer_filename_pattern = L"powertoyssetup";
|
||||
// non-localized
|
||||
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";
|
||||
}
|
@ -118,6 +118,7 @@
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>Pathcch.lib;Version.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
@ -150,6 +151,7 @@
|
||||
<Lib>
|
||||
<AdditionalLibraryDirectories>
|
||||
</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>Pathcch.lib;Version.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
@ -169,12 +171,14 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="dotnet_installation.h" />
|
||||
<ClInclude Include="http_client.h" />
|
||||
<ClInclude Include="toast_notifications_helper.h" />
|
||||
<ClInclude Include="updating.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dotnet_installation.cpp" />
|
||||
<ClCompile Include="http_client.cpp" />
|
||||
<ClCompile Include="toast_notifications_helper.cpp" />
|
||||
<ClCompile Include="updating.cpp" />
|
||||
|
@ -27,6 +27,9 @@
|
||||
<ClInclude Include="http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dotnet_installation.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@ -41,9 +44,11 @@
|
||||
<ClCompile Include="http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dotnet_installation.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="..\UnitTests-CommonLib\packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -16,6 +16,11 @@
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t TOAST_TITLE[] = L"FancyZones";
|
||||
}
|
||||
|
||||
namespace WindowMoveHandlerUtils
|
||||
{
|
||||
bool IsCursorTypeIndicatingSizeEvent()
|
||||
@ -399,7 +404,10 @@ void WindowMoveHandlerPrivate::WarnIfElevationIsRequired(HWND window) noexcept
|
||||
notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_LEARN_MORE), L"https://aka.ms/powertoysDetectedElevatedHelp" },
|
||||
notifications::link_button{ GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED_DIALOG_DONT_SHOW_AGAIN), L"powertoys://cant_drag_elevated_disable/" }
|
||||
};
|
||||
notifications::show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED), {}, std::move(actions));
|
||||
notifications::show_toast_with_activations(GET_RESOURCE_STRING(IDS_CANT_DRAG_ELEVATED),
|
||||
TOAST_TITLE,
|
||||
{},
|
||||
std::move(actions));
|
||||
warning_shown = true;
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <common/notifications.h>
|
||||
#include <common/updating/updating.h>
|
||||
#include <common/RestartManagement.h>
|
||||
#include <common/appMutex.h>
|
||||
|
||||
#include "update_state.h"
|
||||
#include "update_utils.h"
|
||||
@ -39,16 +40,13 @@ 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 PT_UPDATE_MESSAGE_BOX_TITLE[] = L"PowerToys";
|
||||
const wchar_t PT_UPDATE_MESSAGE_BOX_TEXT[] = L"PowerToys was updated and some components require Windows Explorer to restart. Do you want to restart Windows Explorer now?";
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t MSI_VERSION_MUTEX_NAME[] = L"Local\\PowerToyRunMutex";
|
||||
const wchar_t MSIX_VERSION_MUTEX_NAME[] = L"Local\\PowerToyMSIXRunMutex";
|
||||
const wchar_t PT_URI_PROTOCOL_SCHEME[] = L"powertoys://";
|
||||
const wchar_t APPLICATION_ID[] = L"Microsoft.PowerToysWin32";
|
||||
}
|
||||
|
||||
void chdir_current_executable()
|
||||
@ -63,30 +61,20 @@ void chdir_current_executable()
|
||||
}
|
||||
}
|
||||
|
||||
wil::unique_mutex_nothrow create_runner_mutex(const bool msix_version)
|
||||
inline wil::unique_mutex_nothrow create_msi_mutex()
|
||||
{
|
||||
wchar_t username[UNLEN + 1];
|
||||
DWORD username_length = UNLEN + 1;
|
||||
GetUserNameW(username, &username_length);
|
||||
wil::unique_mutex_nothrow result{ CreateMutexW(nullptr, TRUE, (std::wstring(msix_version ? MSIX_VERSION_MUTEX_NAME : MSI_VERSION_MUTEX_NAME) + username).c_str()) };
|
||||
|
||||
return GetLastError() == ERROR_ALREADY_EXISTS ? wil::unique_mutex_nothrow{} : std::move(result);
|
||||
return createAppMutex(POWERTOYS_MSI_MUTEX_NAME);
|
||||
}
|
||||
|
||||
wil::unique_mutex_nothrow create_msi_mutex()
|
||||
inline wil::unique_mutex_nothrow create_msix_mutex()
|
||||
{
|
||||
return create_runner_mutex(false);
|
||||
}
|
||||
|
||||
wil::unique_mutex_nothrow create_msix_mutex()
|
||||
{
|
||||
return create_runner_mutex(true);
|
||||
return createAppMutex(POWERTOYS_MSIX_MUTEX_NAME);
|
||||
}
|
||||
|
||||
void open_menu_from_another_instance()
|
||||
{
|
||||
HWND hwnd_main = FindWindow(L"PToyTrayIconWindow", NULL);
|
||||
PostMessage(hwnd_main, WM_COMMAND, ID_SETTINGS_MENU_COMMAND, NULL);
|
||||
const HWND hwnd_main = FindWindowW(L"PToyTrayIconWindow", nullptr);
|
||||
PostMessageW(hwnd_main, WM_COMMAND, ID_SETTINGS_MENU_COMMAND, 0);
|
||||
}
|
||||
|
||||
int runner(bool isProcessElevated)
|
||||
@ -119,11 +107,12 @@ int runner(bool isProcessElevated)
|
||||
std::thread{ [] {
|
||||
if (updating::uninstall_previous_msix_version_async().get())
|
||||
{
|
||||
notifications::show_toast(localized_strings::OLDER_MSIX_UNINSTALLED);
|
||||
notifications::show_toast(localized_strings::OLDER_MSIX_UNINSTALLED, L"PowerToys");
|
||||
}
|
||||
} }.detach();
|
||||
}
|
||||
|
||||
notifications::set_application_id(APPLICATION_ID);
|
||||
notifications::register_background_toast_handler();
|
||||
|
||||
chdir_current_executable();
|
||||
@ -140,7 +129,7 @@ int runner(bool isProcessElevated)
|
||||
L"modules/ColorPicker/ColorPicker.dll",
|
||||
};
|
||||
|
||||
for (const auto & moduleSubdir : knownModules)
|
||||
for (const auto& moduleSubdir : knownModules)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -211,7 +200,6 @@ enum class toast_notification_handler_result
|
||||
exit_error
|
||||
};
|
||||
|
||||
|
||||
toast_notification_handler_result toast_notification_handler(const std::wstring_view param)
|
||||
{
|
||||
const std::wstring_view cant_drag_elevated_disable = L"cant_drag_elevated_disable/";
|
||||
@ -245,7 +233,7 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
|
||||
else if (param.starts_with(download_and_install_update))
|
||||
{
|
||||
std::wstring installer_filename = updating::download_update().get();
|
||||
|
||||
|
||||
std::wstring args{ UPDATE_NOW_LAUNCH_STAGE1_CMDARG };
|
||||
args += L' ';
|
||||
args += installer_filename;
|
||||
@ -261,10 +249,10 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
|
||||
|
||||
void RequestExplorerRestart()
|
||||
{
|
||||
if (MessageBox(nullptr,
|
||||
localized_strings::PT_UPDATE_MESSAGE_BOX_TEXT,
|
||||
localized_strings::PT_UPDATE_MESSAGE_BOX_TITLE,
|
||||
MB_ICONINFORMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES)
|
||||
if (MessageBoxW(nullptr,
|
||||
localized_strings::PT_UPDATE_MESSAGE_BOX_TEXT,
|
||||
L"PowerToys",
|
||||
MB_ICONINFORMATION | MB_YESNO | MB_DEFBUTTON1) == IDYES)
|
||||
{
|
||||
RestartProcess(EXPLORER_PROCESS_NAME);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user