mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
Autoupdate: implement updating bootstrapper utility (#5204)
This commit is contained in:
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>
|
||||
Reference in New Issue
Block a user