FancyZones and Shortcut Guide initial commit

Co-authored-by: Alexis Campailla <alexis@janeasystems.com>
Co-authored-by: Bret Anderson <bretan@microsoft.com>
Co-authored-by: Enrico Giordani <enrico.giordani@gmail.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Jeff Bogdan <jeffbog@microsoft.com>
Co-authored-by: March Rogers <marchr@microsoft.com>
Co-authored-by: Mike Harsh <mharsh@microsoft.com>
Co-authored-by: Nachum Bundak <Nachum.Bundak@microsoft.com>
Co-authored-by: Oliver Jones <ojones@microsoft.com>
Co-authored-by: Patrick Little <plittle@microsoft.com>
This commit is contained in:
Bartosz Sosnowski
2019-09-04 18:26:26 +02:00
committed by Bartosz Sosnowski
parent 10c5396099
commit 8431b80e48
341 changed files with 54766 additions and 62 deletions

50
src/runner/README.md Normal file
View File

@@ -0,0 +1,50 @@
# PowerToys Runner
# Introduction
The PowerToys Runner contains the project for the PowerToys.exe executable.
It's responsible for:
- Loading the individual PowerToys modules.
- Passing registered events to the PowerToys.
- Showing a system tray icon to manage the PowerToys.
- Bridging between the PowerToys modules and the Settings editor.
![Image of the tray icon](/doc/images/runner/tray.png)
# Code organization
#### [`main.cpp`](./main.cpp)
Contains the executable starting point, initialization code and the list of known PowerToys.
#### [`powertoy_module.h`](./powertoy_module.h) and [`powertoy_module.cpp`](./powertoy_module.cpp)
Contains code for initializing and managing the PowerToy modules.
#### [`powertoys_events.cpp`](./powertoys_events.cpp)
Contains code that handles the various events listeners, and forwards those events to the PowerToys modules.
#### [`lowlevel_keyboard_event.cpp`](./lowlevel_keyboard_event.cpp)
Contains code for registering the low level keyboard event hook that listens for keyboard events.
#### [`win_hook_event.cpp`](./win_hook_event.cpp)
Contains code for registering a Windows event hook through `SetWinEventHook`, that listens for various events raised when a window is interacted with.
#### [`tray_icon.cpp`](./tray_icon.cpp)
Contains code for managing the PowerToys tray icon and its menu commands.
#### [`settings_window.cpp`](./settings_window.cpp)
Contains code for starting the PowerToys settings window and communicating with it.
#### [`general_settings.cpp`](./general_settings.cpp)
Contains code for loading, saving and applying the general setings.
#### [`auto_start_helper.cpp`](./auto_start_helper.cpp)
Contains helper code for registering and unregistering PowerToys to run when the user logs in.
#### [`unhandled_exception_handler.cpp`](./unhandled_exception_handler.cpp)
Contains helper code to get stack traces in builds. Can be used by adding a call to `init_global_error_handlers` in [`WinMain`](./main.cpp).
#### [`trace.cpp`](./trace.cpp)
Contains code for telemetry.
#### [`svgs`](./svgs/)
Contains the SVG assets used by the PowerToys modules.

View File

@@ -0,0 +1,362 @@
#include "pch.h"
#include "auto_start_helper.h"
#include <Lmcons.h>
#include <comdef.h>
#include <taskschd.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsupp.lib")
#include <Shlobj.h>
// Helper macros from wix.
// TODO: use "s" and "..." parameters to report errors from these functions.
#define ExitOnFailure(x,s,...) if (FAILED(x)) { goto LExit; }
#define ExitWithLastError(x,s,...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } goto LExit; }
#define ExitFunction() { goto LExit; }
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
bool enable_auto_start_task_for_this_user() {
HRESULT hr = S_OK;
WCHAR username_domain[USERNAME_DOMAIN_LEN];
WCHAR username[USERNAME_LEN];
std::wstring wstrTaskName;
ITaskService *pService = NULL;
ITaskFolder *pTaskFolder = NULL;
ITaskDefinition *pTask = NULL;
IRegistrationInfo *pRegInfo = NULL;
ITaskSettings *pSettings = NULL;
ITriggerCollection *pTriggerCollection = NULL;
IRegisteredTask *pRegisteredTask = NULL;
// ------------------------------------------------------
// Get the Domain/Username for the trigger.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) {
ExitWithLastError(hr, "Getting username failed: %x", hr);
}
if (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN)) {
ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
}
wcscat_s(username_domain, L"\\");
wcscat_s(username_domain, username);
// Task Name.
wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// Get the executable path passed to the custom action.
WCHAR wszExecutablePath[MAX_PATH];
GetModuleFileName(NULL, wszExecutablePath, MAX_PATH);
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder. Creates it if it doesn't exist.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) {
// Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
ITaskFolder *pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
if (FAILED(hr)) {
pRootFolder->Release();
ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
}
}
// If the task exists, just enable it.
{
IRegisteredTask *pExistingRegisteredTask = NULL;
hr=pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
if (SUCCEEDED(hr)) {
// Task exists, try enabling it.
hr = pExistingRegisteredTask->put_Enabled(VARIANT_TRUE);
pExistingRegisteredTask->Release();
if (SUCCEEDED(hr)) {
// Function enable. Sounds like a success.
ExitFunction();
}
}
}
// Create the task builder object to create the task.
hr = pService->NewTask(0, &pTask);
ExitOnFailure(hr, "Failed to create a task definition: %x", hr);
// ------------------------------------------------------
// Get the registration info for setting the identification.
hr = pTask->get_RegistrationInfo(&pRegInfo);
ExitOnFailure(hr, "Cannot get identification pointer: %x", hr);
hr = pRegInfo->put_Author(_bstr_t(username_domain));
ExitOnFailure(hr, "Cannot put identification info: %x", hr);
// ------------------------------------------------------
// Create the settings for the task
hr = pTask->get_Settings(&pSettings);
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr);
hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr);
hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_StopIfGoingOnBatteries setting info: %x", hr);
hr = pSettings->put_ExecutionTimeLimit(_bstr_t(L"PT0S")); //Unlimited
ExitOnFailure(hr, "Cannot put_ExecutionTimeLimit setting info: %x", hr);
hr = pSettings->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
ExitOnFailure(hr, "Cannot put_DisallowStartIfOnBatteries setting info: %x", hr);
// ------------------------------------------------------
// Get the trigger collection to insert the logon trigger.
hr = pTask->get_Triggers(&pTriggerCollection);
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr);
// Add the logon trigger to the task.
{
ITrigger *pTrigger = NULL;
ILogonTrigger *pLogonTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
ExitOnFailure(hr, "Cannot create the trigger: %x", hr);
hr = pTrigger->QueryInterface(
IID_ILogonTrigger, (void**)&pLogonTrigger);
pTrigger->Release();
ExitOnFailure(hr, "QueryInterface call failed for ILogonTrigger: %x", hr);
hr = pLogonTrigger->put_Id(_bstr_t(L"Trigger1"));
// Timing issues may make explorer not be started when the task runs.
// Add a little delay to mitigate this.
hr = pLogonTrigger->put_Delay(_bstr_t(L"PT03S"));
// Define the user. The task will execute when the user logs on.
// The specified user must be a user on this computer.
hr = pLogonTrigger->put_UserId(_bstr_t(username_domain));
pLogonTrigger->Release();
ExitOnFailure(hr, "Cannot add user ID to logon trigger: %x", hr);
}
// ------------------------------------------------------
// Add an Action to the task. This task will execute the path passed to this custom action.
{
IActionCollection *pActionCollection = NULL;
IAction *pAction = NULL;
IExecAction *pExecAction = NULL;
// Get the task action collection pointer.
hr = pTask->get_Actions(&pActionCollection);
ExitOnFailure(hr, "Cannot get Task collection pointer: %x", hr);
// Create the action, specifying that it is an executable action.
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
ExitOnFailure(hr, "Cannot create the action: %x", hr);
// QI for the executable task pointer.
hr = pAction->QueryInterface(
IID_IExecAction, (void**)&pExecAction);
pAction->Release();
ExitOnFailure(hr, "QueryInterface call failed for IExecAction: %x", hr);
// Set the path of the executable to PowerToys (passed as CustomActionData).
hr = pExecAction->put_Path(_bstr_t(wszExecutablePath));
pExecAction->Release();
ExitOnFailure(hr, "Cannot set path of executable: %x", hr);
}
// ------------------------------------------------------
// Create the principal for the task
{
IPrincipal *pPrincipal = NULL;
hr = pTask->get_Principal(&pPrincipal);
ExitOnFailure(hr, "Cannot get principal pointer: %x", hr);
// Set up principal information:
hr = pPrincipal->put_Id(_bstr_t(L"Principal1"));
hr = pPrincipal->put_UserId(_bstr_t(username_domain));
hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
// Run the task with the highest available privileges.
if (IsUserAnAdmin()) {
hr = pPrincipal->put_RunLevel(_TASK_RUNLEVEL::TASK_RUNLEVEL_HIGHEST);
} else {
hr = pPrincipal->put_RunLevel(_TASK_RUNLEVEL::TASK_RUNLEVEL_LUA);
}
pPrincipal->Release();
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
}
// ------------------------------------------------------
// Save the task in the PowerToys folder.
hr = pTaskFolder->RegisterTaskDefinition(
_bstr_t(wstrTaskName.c_str()),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(username_domain),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(L""),
&pRegisteredTask);
ExitOnFailure(hr, "Error saving the Task : %x", hr);
LExit:
if (pService) pService->Release();
if (pTaskFolder) pTaskFolder->Release();
if (pTask) pTask->Release();
if (pRegInfo) pRegInfo->Release();
if (pSettings) pSettings->Release();
if (pTriggerCollection) pTriggerCollection->Release();
if (pRegisteredTask) pRegisteredTask->Release();
return(SUCCEEDED(hr));
}
bool disable_auto_start_task_for_this_user() {
HRESULT hr = S_OK;
WCHAR username[USERNAME_LEN];
std::wstring wstrTaskName;
ITaskService *pService = NULL;
ITaskFolder *pTaskFolder = NULL;
// ------------------------------------------------------
// Get the Username for the task.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) {
ExitWithLastError(hr, "Getting username failed: %x", hr);
}
// Task Name.
wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) {
// Folder doesn't exist. No need to disable a non-existing task.
hr = S_OK;
ExitFunction();
}
// ------------------------------------------------------
// If the task exists, disable.
{
IRegisteredTask *pExistingRegisteredTask = NULL;
hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
if (SUCCEEDED(hr)) {
// Task exists, try disabling it.
hr = pExistingRegisteredTask->put_Enabled(VARIANT_FALSE);
pExistingRegisteredTask->Release();
if (SUCCEEDED(hr)) {
// Function disable. Sounds like a success.
ExitFunction();
}
}
}
LExit:
if (pService) pService->Release();
if (pTaskFolder) pTaskFolder->Release();
return(SUCCEEDED(hr));
}
bool is_auto_start_task_active_for_this_user(){
HRESULT hr = S_OK;
WCHAR username[USERNAME_LEN];
std::wstring wstrTaskName;
ITaskService *pService = NULL;
ITaskFolder *pTaskFolder = NULL;
// ------------------------------------------------------
// Get the Username for the task.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) {
ExitWithLastError(hr, "Getting username failed: %x", hr);
}
// Task Name.
wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// ------------------------------------------------------
// Create an instance of the Task Service.
hr = CoCreateInstance(CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
// Connect to the task service.
hr = pService->Connect(_variant_t(), _variant_t(),
_variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------
// Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
ExitOnFailure(hr, "ITaskFolder doesn't exist: %x", hr);
// ------------------------------------------------------
// If the task exists, disable.
{
IRegisteredTask *pExistingRegisteredTask = NULL;
hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
if (SUCCEEDED(hr)) {
// Task exists, get its value.
VARIANT_BOOL is_enabled;
hr = pExistingRegisteredTask->get_Enabled(&is_enabled);
pExistingRegisteredTask->Release();
if (SUCCEEDED(hr)) {
// Got the value. Return it.
hr = (is_enabled == VARIANT_TRUE) ? S_OK : E_FAIL; // Fake success or fail to return the value.
ExitFunction();
}
}
}
LExit:
if (pService) pService->Release();
if (pTaskFolder) pTaskFolder->Release();
return(SUCCEEDED(hr));
}

View File

@@ -0,0 +1,4 @@
#pragma once
bool is_auto_start_task_active_for_this_user();
bool enable_auto_start_task_for_this_user();
bool disable_auto_start_task_for_this_user();

View File

@@ -0,0 +1,91 @@
#include "pch.h"
#include "general_settings.h"
#include "auto_start_helper.h"
#include <common/settings_helpers.h>
#include "powertoy_module.h"
using namespace web;
web::json::value load_general_settings() {
return PTSettingsHelper::load_general_settings();
}
web::json::value get_general_settings() {
json::value result = json::value::object();
bool startup = is_auto_start_task_active_for_this_user();
result.as_object()[L"startup"] = json::value::boolean(startup);
json::value enabled = json::value::object();
for (auto&[name, powertoy] : modules()) {
enabled.as_object()[name] = json::value::boolean(powertoy.is_enabled());
}
result.as_object()[L"enabled"] = enabled;
return result;
}
void apply_general_settings(const json::value& general_configs) {
bool contains_startup = general_configs.has_boolean_field(L"startup");
if (contains_startup) {
bool startup = general_configs.at(L"startup").as_bool();
bool current_startup = is_auto_start_task_active_for_this_user();
if (current_startup != startup) {
if (startup) {
enable_auto_start_task_for_this_user();
} else {
disable_auto_start_task_for_this_user();
}
}
}
bool contains_enabled = general_configs.has_object_field(L"enabled");
if (contains_enabled) {
for (auto enabled_element : general_configs.at(L"enabled").as_object()) {
if (enabled_element.second.is_boolean() && modules().find(enabled_element.first) != modules().end()) {
bool module_inst_enabled = modules().at(enabled_element.first).is_enabled();
bool target_enabled = enabled_element.second.as_bool();
if (module_inst_enabled != target_enabled) {
if (target_enabled) {
modules().at(enabled_element.first).enable();
} else {
modules().at(enabled_element.first).disable();
}
}
}
}
}
json::value save_settings = get_general_settings();
PTSettingsHelper::save_general_settings(save_settings);
}
void start_initial_powertoys() {
bool only_enable_some_powertoys = false;
std::unordered_set<std::wstring> powertoys_to_enable;
json::value general_settings;
try {
general_settings = load_general_settings();
json::value enabled = general_settings[L"enabled"];
for (auto enabled_element : enabled.as_object()) {
if (enabled_element.second.as_bool()) {
// Enable this powertoy.
powertoys_to_enable.emplace(enabled_element.first);
}
}
only_enable_some_powertoys = true;
}
catch (std::exception ex) {
// Couldn't read the general settings correctly.
// Load all powertoys.
only_enable_some_powertoys = false;
}
for (auto&[name, powertoy] : modules()) {
if (only_enable_some_powertoys) {
if (powertoys_to_enable.find(name)!=powertoys_to_enable.end()) {
powertoy.enable();
}
} else {
powertoy.enable();
}
}
}

View File

@@ -0,0 +1,6 @@
#pragma once
#include <cpprest/json.h>
web::json::value get_general_settings();
void apply_general_settings(const web::json::value& general_configs);
void start_initial_powertoys();

View File

@@ -0,0 +1,36 @@
#include "pch.h"
#include "lowlevel_keyboard_event.h"
#include "powertoys_events.h"
namespace {
HHOOK hook_handle = nullptr;
HHOOK hook_handle_copy = nullptr; // make sure we do use nullptr in CallNextHookEx call
LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam) {
LowlevelKeyboardEvent event;
if (nCode == HC_ACTION) {
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
event.wParam = wParam;
if (powertoys_events().signal_event(ll_keyboard, reinterpret_cast<intptr_t>(&event)) != 0) {
return 1;
}
}
return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
}
}
void start_lowlevel_keyboard_hook() {
if (!hook_handle) {
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
hook_handle_copy = hook_handle;
if (!hook_handle) {
throw std::runtime_error("Cannot install keyboard listener");
}
}
}
void stop_lowlevel_keyboard_hook() {
if (hook_handle) {
UnhookWindowsHookEx(hook_handle);
hook_handle = nullptr;
}
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <interface/lowlevel_keyboard_event_data.h>
void start_lowlevel_keyboard_hook();
void stop_lowlevel_keyboard_hook();

81
src/runner/main.cpp Normal file
View File

@@ -0,0 +1,81 @@
#include "pch.h"
#include <ShellScalingApi.h>
#include <lmcons.h>
#include <filesystem>
#include "tray_icon.h"
#include "powertoy_module.h"
#include "lowlevel_keyboard_event.h"
#include "trace.h"
#include "general_settings.h"
#if _DEBUG && _WIN64
#include "unhandled_exception_handler.h"
#endif
void chdir_current_executable() {
// Change current directory to the path of the executable.
WCHAR executable_path[MAX_PATH];
GetModuleFileName(NULL, executable_path, MAX_PATH);
PathRemoveFileSpec(executable_path);
if(!SetCurrentDirectory(executable_path)) {
show_last_error_message(L"Change Directory to Executable Path", GetLastError());
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WCHAR username[UNLEN + 1];
DWORD username_length = UNLEN + 1;
GetUserNameW(username, &username_length);
auto runner_mutex = CreateMutexW(NULL, TRUE, (std::wstring(L"Local\\PowerToyRunMutex") + username).c_str());
if (runner_mutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) {
// The app is already running
return 0;
}
#if _DEBUG && _WIN64
//Global error handlers to diagnose errors.
//We prefer this not not show any longer until there's a bug to diagnose.
//init_global_error_handlers();
#endif
Trace::RegisterProvider();
winrt::init_apartment();
start_tray_icon();
int result;
try {
// Singletons initialization order needs to be preserved, first events and
// then modules to guarantee the reverse destruction order.
powertoys_events();
modules();
chdir_current_executable();
// Load Powertyos DLLS
// For now only load known DLLs
std::unordered_set<std::wstring> known_dlls = {
L"shortcut_guide.dll",
L"fancyzones.dll"
};
for (auto& file : std::filesystem::directory_iterator(L"modules/")) {
if (file.path().extension() != L".dll")
continue;
if (known_dlls.find(file.path().filename()) == known_dlls.end())
continue;
try {
auto module = load_powertoy(file.path().wstring());
modules().emplace(module.get_name(), std::move(module));
} catch (...) { }
}
// Start initial powertoys
start_initial_powertoys();
Trace::EventLaunch();
result = run_message_loop();
} catch (std::runtime_error err) {
std::string err_what = err.what();
MessageBoxW(NULL, std::wstring(err_what.begin(),err_what.end()).c_str(), L"Error", MB_OK | MB_ICONERROR);
result = -1;
}
Trace::UnregisterProvider();
return result;
}

9
src/runner/pch.cpp Normal file
View File

@@ -0,0 +1,9 @@
#include "pch.h"
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "shcore.lib")
#pragma comment(lib, "windowsapp")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "d3d11")
#pragma comment(lib, "d2d1")
#pragma comment(lib, "dcomp")
#pragma comment(lib, "dwmapi")

26
src/runner/pch.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
#include <winrt/base.h>
#include <Windows.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <string>
#include <algorithm>
#include <chrono>
#include <mutex>
#include <thread>
#include <functional>
#include <condition_variable>
#include <stdexcept>
#include <tuple>
#include <unordered_set>
#include <string>
#include <common/common.h>
#include <ProjectTelemetry.h>

View File

@@ -0,0 +1,24 @@
#include "pch.h"
#include "powertoy_module.h"
#include "lowlevel_keyboard_event.h"
#include <algorithm>
std::unordered_map<std::wstring, PowertoyModule>& modules() {
static std::unordered_map<std::wstring, PowertoyModule> modules;
return modules;
}
PowertoyModule load_powertoy(const std::wstring& filename) {
auto handle = winrt::check_pointer(LoadLibraryW(filename.c_str()));
auto create = reinterpret_cast<powertoy_create_func>(GetProcAddress(handle, "powertoy_create"));
if (!create) {
FreeLibrary(handle);
winrt::throw_last_error();
}
auto module = create();
if (!module) {
FreeLibrary(handle);
winrt::throw_last_error();
}
return PowertoyModule(module, handle);
}

View File

@@ -0,0 +1,83 @@
#pragma once
#include "powertoys_events.h"
#include <interface/powertoy_module_interface.h>
#include <string>
#include <memory>
#include <mutex>
#include <vector>
#include <functional>
class PowertoyModule;
struct PowertoyModuleDeleter {
void operator()(PowertoyModuleIface* module) {
if (module) {
powertoys_events().unregister_receiver(module);
module->disable();
module->destroy();
}
}
};
struct PowertoyModuleDLLDeleter {
using pointer = HMODULE;
void operator()(HMODULE handle) {
FreeLibrary(handle);
}
};
class PowertoyModule {
public:
PowertoyModule(PowertoyModuleIface* module, HMODULE handle) : handle(handle), module(module) {
if (!module)
throw std::runtime_error("Module not initialized");
name = module->get_name();
auto want_signals = module->get_events();
if (want_signals) {
for (; *want_signals; ++want_signals) {
powertoys_events().register_receiver(*want_signals, module);
}
}
}
const std::wstring& get_name() const {
return name;
}
const std::wstring get_config() const {
std::wstring result;
int size = 0;
module->get_config(nullptr, &size);
wchar_t *buffer = new wchar_t[size];
if (module->get_config(buffer, &size)) {
result.assign(buffer);
}
delete[] buffer;
return result;
}
void set_config(const std::wstring& config) {
module->set_config(config.c_str());
}
void call_custom_action(const std::wstring& action) {
module->call_custom_action(action.c_str());
}
intptr_t signal_event(const std::wstring& signal_event, intptr_t data) {
return module->signal_event(signal_event.c_str(), data);
}
bool is_enabled() {
return module->is_enabled();
}
void enable() {
module->enable();
}
void disable() {
module->disable();
}
private:
std::unique_ptr<HMODULE, PowertoyModuleDLLDeleter> handle;
std::unique_ptr<PowertoyModuleIface, PowertoyModuleDeleter> module;
std::wstring name;
};
PowertoyModule load_powertoy(const std::wstring& filename);
std::unordered_map<std::wstring, PowertoyModule>& modules();

View File

@@ -0,0 +1,54 @@
#include "pch.h"
#include "powertoys_events.h"
#include "lowlevel_keyboard_event.h"
#include "win_hook_event.h"
void first_subscribed(const std::wstring& event) {
if (event == ll_keyboard)
start_lowlevel_keyboard_hook();
else if (event == win_hook_event)
start_win_hook_event();
}
void last_unsubscribed(const std::wstring& event) {
if (event == ll_keyboard)
stop_lowlevel_keyboard_hook();
else if (event == win_hook_event)
stop_win_hook_event();
}
PowertoysEvents& powertoys_events() {
static PowertoysEvents powertoys_events;
return powertoys_events;
}
void PowertoysEvents::register_receiver(const std::wstring & event, PowertoyModuleIface* module) {
std::unique_lock lock(mutex);
auto& subscribers = receivers[event];
if (subscribers.empty()) {
first_subscribed(event);
}
subscribers.push_back(module);
}
void PowertoysEvents::unregister_receiver(PowertoyModuleIface* module) {
std::unique_lock lock(mutex);
for (auto&[event, subscribers] : receivers) {
subscribers.erase(remove(begin(subscribers), end(subscribers), module), end(subscribers));
if (subscribers.empty()) {
last_unsubscribed(event);
}
}
}
intptr_t PowertoysEvents::signal_event(const std::wstring & event, intptr_t data) {
intptr_t rvalue = 0;
std::shared_lock lock(mutex);
if (auto it = receivers.find(event); it != end(receivers)) {
for (auto& module : it->second) {
if (module)
rvalue |= module->signal_event(event.c_str(), data);
}
}
return rvalue;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <interface/powertoy_module_interface.h>
#include <string>
class PowertoysEvents {
public:
void register_receiver(const std::wstring& event, PowertoyModuleIface* module);
void unregister_receiver(PowertoyModuleIface* module);
intptr_t signal_event(const std::wstring& event, intptr_t data);
private:
std::shared_mutex mutex;
std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers;
};
PowertoysEvents& powertoys_events();
void first_subscribed(const std::wstring& event);
void last_unsubscribed(const std::wstring& event);

5
src/runner/resource.h Normal file
View File

@@ -0,0 +1,5 @@
#define APPICON 101
#define ID_TRAY_MENU 102
#define ID_EXIT_MENU_COMMAND 40001
#define ID_SETTINGS_MENU_COMMAND 40002
#define ID_ABOUT_MENU_COMMAND 40003

BIN
src/runner/runner.rc Normal file

Binary file not shown.

226
src/runner/runner.vcxproj Normal file
View File

@@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>15.0</VCProjectVersion>
<ProjectGuid>{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}</ProjectGuid>
<RootNamespace>powertoys</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>runner</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>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</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 Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<TargetName>PowerToys</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<TargetName>PowerToys</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\common\inc;..\common\Telemetry;..;..\modules;..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<UACExecutionLevel>HighestAvailable</UACExecutionLevel>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>..\common\inc;..\common\Telemetry;..;..\modules;..\..\deps\cpprestsdk\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<UACExecutionLevel>HighestAvailable</UACExecutionLevel>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="auto_start_helper.cpp" />
<ClCompile Include="general_settings.cpp" />
<ClCompile Include="lowlevel_keyboard_event.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="powertoys_events.cpp" />
<ClCompile Include="powertoy_module.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="settings_window.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="tray_icon.cpp" />
<ClCompile Include="unhandled_exception_handler.cpp" />
<ClCompile Include="win_hook_event.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="auto_start_helper.h" />
<ClInclude Include="general_settings.h" />
<ClInclude Include="lowlevel_keyboard_event.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="powertoys_events.h" />
<ClInclude Include="powertoy_module.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="settings_window.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="tray_icon.h" />
<ClInclude Include="unhandled_exception_handler.h" />
<ClInclude Include="win_hook_event.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="runner.rc" />
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="svgs\0.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\icon.ico">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(OutDir)\svgs</DestinationFolders>
<DestinationFolders Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(OutDir)\svgs</DestinationFolders>
</CopyFileToFolders>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common\common.vcxproj">
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="unhandled_exception_handler.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="lowlevel_keyboard_event.cpp">
<Filter>Events</Filter>
</ClCompile>
<ClCompile Include="tray_icon.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="powertoy_module.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="settings_window.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="auto_start_helper.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="general_settings.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="powertoys_events.cpp">
<Filter>Utils</Filter>
</ClCompile>
<ClCompile Include="win_hook_event.cpp">
<Filter>Events</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="unhandled_exception_handler.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="resource.h" />
<ClInclude Include="lowlevel_keyboard_event.h">
<Filter>Events</Filter>
</ClInclude>
<ClInclude Include="tray_icon.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="powertoy_module.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="settings_window.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="auto_start_helper.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="general_settings.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="powertoys_events.h">
<Filter>Utils</Filter>
</ClInclude>
<ClInclude Include="win_hook_event.h">
<Filter>Events</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Utils">
<UniqueIdentifier>{bc27e9c1-8afa-4d62-8179-789f4651c0b6}</UniqueIdentifier>
</Filter>
<Filter Include="svgs">
<UniqueIdentifier>{1123565f-43ba-4eb8-a6f5-2d5f8f3ae6af}</UniqueIdentifier>
</Filter>
<Filter Include="Events">
<UniqueIdentifier>{d409b25e-c0f1-4913-bb32-da4a21339c76}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="runner.rc" />
</ItemGroup>
<ItemGroup>
<CopyFileToFolders Include="svgs\icon.ico" />
<CopyFileToFolders Include="svgs\0.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\1.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\2.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\3.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\4.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\5.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\6.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\7.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\8.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\9.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\no_active_window.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
<CopyFileToFolders Include="svgs\overlay_portrait.svg">
<Filter>svgs</Filter>
</CopyFileToFolders>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,265 @@
#include "pch.h"
#include <WinSafer.h>
#include <Sddl.h>
#include <sstream>
#include <accctrl.h>
#include <aclapi.h>
#include <cpprest/json.h>
#include "powertoy_module.h"
#include <common/two_way_pipe_message_ipc.h>
#include "tray_icon.h"
#include "general_settings.h"
#define BUFSIZE 1024
using namespace web;
TwoWayPipeMessageIPC* current_settings_ipc = NULL;
json::value get_power_toys_settings() {
json::value result = json::value::object();
for (auto&[name, powertoy] : modules()) {
try {
json::value powertoys_config = json::value::parse(powertoy.get_config());
result.as_object()[name] = powertoys_config;
}
catch (json::json_exception ex) {
//Malformed JSON.
}
}
return result;
}
json::value get_all_settings() {
json::value result = json::value::object();
result.as_object()[L"general"] = get_general_settings();
result.as_object()[L"powertoys"] = get_power_toys_settings();
return result;
}
void dispatch_json_action_to_module(const json::value& powertoys_configs) {
for (auto powertoy_element : powertoys_configs.as_object()) {
std::wstringstream ws;
ws << powertoy_element.second;
if (modules().find(powertoy_element.first) != modules().end()) {
modules().at(powertoy_element.first).call_custom_action(ws.str());
}
}
}
void send_json_config_to_module(const std::wstring& module_key, const std::wstring& settings) {
if (modules().find(module_key) != modules().end()) {
modules().at(module_key).set_config(settings);
}
}
void dispatch_json_config_to_modules(const json::value& powertoys_configs) {
for (auto powertoy_element : powertoys_configs.as_object()) {
std::wstringstream ws;
ws << powertoy_element.second;
send_json_config_to_module(powertoy_element.first, ws.str());
}
};
void dispatch_received_json(const std::wstring &json_to_parse) {
json::value j = json::value::parse(json_to_parse);
for(auto base_element : j.as_object()) {
if (base_element.first == L"general") {
apply_general_settings(base_element.second);
std::wstringstream ws;
ws << get_all_settings();
if (current_settings_ipc != NULL) {
current_settings_ipc->send(ws.str());
}
} else if (base_element.first == L"powertoys") {
dispatch_json_config_to_modules(base_element.second);
std::wstringstream ws;
ws << get_all_settings();
if (current_settings_ipc != NULL) {
current_settings_ipc->send(ws.str());
}
} else if (base_element.first == L"refresh") {
std::wstringstream ws;
ws << get_all_settings();
if (current_settings_ipc != NULL) {
current_settings_ipc->send(ws.str());
}
} else if (base_element.first == L"action") {
dispatch_json_action_to_module(base_element.second);
}
}
return;
}
void dispatch_received_json_callback(PVOID data) {
std::wstring* msg = (std::wstring*)data;
dispatch_received_json(*msg);
delete msg;
}
void receive_json_send_to_main_thread(const std::wstring &msg) {
std::wstring* copy = new std::wstring(msg);
dispatch_run_on_main_ui_thread(dispatch_received_json_callback, copy);
}
DWORD g_settings_process_id = 0;
void run_settings_window() {
STARTUPINFO startup_info = { sizeof(startup_info) };
PROCESS_INFORMATION process_info = { 0 };
HANDLE process = NULL;
HANDLE hToken = NULL;
STARTUPINFOEX siex = { 0 };
PPROC_THREAD_ATTRIBUTE_LIST pptal = NULL;
WCHAR executable_path[MAX_PATH];
GetModuleFileName(NULL, executable_path, MAX_PATH);
PathRemoveFileSpec(executable_path);
wcscat_s(executable_path, L"\\PowerToysSettings.exe");
WCHAR executable_args[MAX_PATH * 3];
// Generate unique names for the pipes, if getting a UUID is possible
std::wstring powertoys_pipe_name(L"\\\\.\\pipe\\powertoys_runner_");
std::wstring settings_pipe_name(L"\\\\.\\pipe\\powertoys_settings_");
SIZE_T size = 0;
UUID temp_uuid;
UuidCreate(&temp_uuid);
wchar_t* uuid_chars;
UuidToString(&temp_uuid, (RPC_WSTR*)&uuid_chars);
if (uuid_chars != NULL) {
powertoys_pipe_name += std::wstring(uuid_chars);
settings_pipe_name += std::wstring(uuid_chars);
RpcStringFree((RPC_WSTR*)&uuid_chars);
uuid_chars = NULL;
}
DWORD powertoys_pid = GetCurrentProcessId();
// Arguments for calling the settings executable:
// C:\powertoys_path\PowerToysSettings.exe powertoys_pipe settings_pipe powertoys_pid
// powertoys_pipe - PowerToys pipe server.
// settings_pipe - Settings pipe server.
// powertoys_pid - PowerToys process pid.
wcscpy_s(executable_args, L"\"");
wcscat_s(executable_args, executable_path);
wcscat_s(executable_args, L"\"");
wcscat_s(executable_args, L" ");
wcscat_s(executable_args, powertoys_pipe_name.c_str());
wcscat_s(executable_args, L" ");
wcscat_s(executable_args, settings_pipe_name.c_str());
wcscat_s(executable_args, L" ");
wcscat_s(executable_args, std::to_wstring(powertoys_pid).c_str());
// Run the Settings process with non-elevated privileges
HWND hwnd = GetShellWindow();
if (!hwnd) {
goto LExit;
}
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
process = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, pid);
if (!process) {
goto LExit;
}
InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
pptal = (PPROC_THREAD_ATTRIBUTE_LIST)new char[size];
if (!pptal) {
goto LExit;
}
if (!InitializeProcThreadAttributeList(pptal, 1, 0, &size)) {
goto LExit;
}
if (!UpdateProcThreadAttribute(pptal,
0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&process,
sizeof(process),
nullptr,
nullptr)) {
goto LExit;
}
siex.lpAttributeList = pptal;
siex.StartupInfo.cb = sizeof(siex);
if (!CreateProcessW(executable_path,
executable_args,
nullptr,
nullptr,
FALSE,
EXTENDED_STARTUPINFO_PRESENT,
nullptr,
nullptr,
&siex.StartupInfo,
&process_info)) {
goto LExit;
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {
goto LExit;
}
current_settings_ipc = new TwoWayPipeMessageIPC(powertoys_pipe_name, settings_pipe_name, receive_json_send_to_main_thread);
current_settings_ipc->start(hToken);
g_settings_process_id = process_info.dwProcessId;
WaitForSingleObject(process_info.hProcess, INFINITE);
if (WaitForSingleObject(process_info.hProcess, INFINITE) != WAIT_OBJECT_0) {
show_last_error_message(L"Couldn't wait on the Settings Window to close.", GetLastError());
}
LExit:
if (process_info.hProcess) {
CloseHandle(process_info.hProcess);
}
if (process_info.hThread) {
CloseHandle(process_info.hThread);
}
if (pptal) {
delete[](char*)pptal;
}
if (process) {
CloseHandle(process);
}
if (current_settings_ipc) {
current_settings_ipc->end();
delete current_settings_ipc;
current_settings_ipc = NULL;
}
if (hToken) {
CloseHandle(hToken);
}
g_settings_process_id = 0;
}
void bring_settings_to_front() {
auto callback = [](HWND hwnd, LPARAM data) -> BOOL
{
DWORD processId;
if (GetWindowThreadProcessId(hwnd, &processId) && processId == g_settings_process_id) {
ShowWindow(hwnd, SW_NORMAL);
SetForegroundWindow(hwnd);
return FALSE;
}
return TRUE;
};
EnumWindows(callback, 0);
}
void open_settings_window() {
if (g_settings_process_id != 0) {
bring_settings_to_front();
} else {
std::thread(run_settings_window).detach();
}
}

View File

@@ -0,0 +1,2 @@
#pragma once
void open_settings_window();

26
src/runner/svgs/0.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>0</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-7.000000, -159.000000)">
<g id="0" transform="translate(7.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M26.5,31.1376953 C24.166655,31.1376953 23,29.4993653 23,26.2226562 C23,24.522778 23.3155893,23.2273808 23.9467773,22.3364258 C24.5779654,21.4454708 25.4928325,21 26.6914062,21 C28.9700635,21 30.109375,22.6656734 30.109375,25.9970703 C30.109375,27.6559328 29.798343,28.9274044 29.1762695,29.8115234 C28.5541961,30.6956424 27.6621152,31.1376953 26.5,31.1376953 Z M26.5957031,22.6474609 C25.6614537,22.6474609 25.1943359,23.8209518 25.1943359,26.1679688 C25.1943359,28.3782663 25.6523392,29.4833984 26.5683594,29.4833984 C27.461593,29.4833984 27.9082031,28.3440869 27.9082031,26.0654297 C27.9082031,23.7867725 27.4707075,22.6474609 26.5957031,22.6474609 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

26
src/runner/svgs/1.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>1</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-67.000000, -159.000000)">
<g id="1" transform="translate(67.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<polygon fill="#FFFFFF" fill-rule="nonzero" points="30.2548828 30.9667969 24.0546875 30.9667969 24.0546875 29.2578125 26.0849609 29.2578125 26.0849609 23.1533203 24 23.6044922 24 21.8544922 28.2382812 21 28.2382812 29.2578125 30.2548828 29.2578125"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

26
src/runner/svgs/2.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>2</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-127.000000, -159.000000)">
<g id="2" transform="translate(127.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M26.4746094,29.1347656 L26.4746094,29.1757812 L30.453125,29.1757812 L30.453125,30.9667969 L24,30.9667969 L24,29.2851562 L26.8095703,26.5986328 C27.3746773,26.0563124 27.772297,25.6017271 28.0024414,25.2348633 C28.2325858,24.8679995 28.3476562,24.4817729 28.3476562,24.0761719 C28.3476562,23.1738236 27.8623095,22.7226562 26.8916016,22.7226562 C26.0484984,22.7226562 25.2418658,23.0576138 24.4716797,23.7275391 L24.4716797,21.8271484 C25.3238975,21.2757134 26.2854764,21 27.3564453,21 C28.3590545,21 29.1417615,21.2517878 29.7045898,21.7553711 C30.2674182,22.2589543 30.5488281,22.9391233 30.5488281,23.7958984 C30.5488281,24.9397844 29.8629626,26.1223897 28.4912109,27.34375 L26.4746094,29.1347656 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

26
src/runner/svgs/3.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>3</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-187.000000, -159.000000)">
<g id="3" transform="translate(187.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24,30.6455078 L24,28.765625 C24.6562533,29.244143 25.4218706,29.4833984 26.296875,29.4833984 C26.84831,29.4833984 27.2778305,29.36491 27.5854492,29.1279297 C27.8930679,28.8909493 28.046875,28.560549 28.046875,28.1367188 C28.046875,27.6992166 27.85661,27.3619804 27.4760742,27.125 C27.0955385,26.8880196 26.5725945,26.7695312 25.9072266,26.7695312 L25.0253906,26.7695312 L25.0253906,25.1152344 L25.8388672,25.1152344 C27.1149152,25.1152344 27.7529297,24.6914105 27.7529297,23.84375 C27.7529297,23.04622 27.2630257,22.6474609 26.2832031,22.6474609 C25.6269498,22.6474609 24.9889354,22.8593729 24.3691406,23.2832031 L24.3691406,21.5195312 C25.0572951,21.1731754 25.8593704,21 26.7753906,21 C27.7779998,21 28.5584282,21.2255837 29.1166992,21.6767578 C29.6749702,22.1279319 29.9541016,22.7135381 29.9541016,23.4335938 C29.9541016,24.7141991 29.304694,25.5162744 28.0058594,25.8398438 L28.0058594,25.8740234 C28.6985712,25.9606124 29.2454407,26.2124003 29.6464844,26.6293945 C30.047528,27.0463888 30.2480469,27.5579397 30.2480469,28.1640625 C30.2480469,29.0800827 29.9130893,29.8046848 29.2431641,30.3378906 C28.5732388,30.8710964 27.6481179,31.1376953 26.4677734,31.1376953 C25.4560496,31.1376953 24.6334667,30.9736345 24,30.6455078 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

26
src/runner/svgs/4.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>4</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-247.000000, -159.000000)">
<g id="4" transform="translate(247.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M30.4169922,28.7177734 L29.2412109,28.7177734 L29.2412109,30.8027344 L27.2724609,30.8027344 L27.2724609,28.7177734 L23,28.7177734 L23,27.34375 L27.1152344,21 L29.2412109,21 L29.2412109,27.1796875 L30.4169922,27.1796875 L30.4169922,28.7177734 Z M27.2998047,23.1601562 L27.2587891,23.1601562 C27.1949867,23.3242196 27.071941,23.5794254 26.8896484,23.9257812 L24.7910156,27.1796875 L27.2724609,27.1796875 L27.2724609,24.0966797 C27.2724609,23.8232408 27.2815754,23.5110695 27.2998047,23.1601562 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

26
src/runner/svgs/5.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>5</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-307.000000, -159.000000)">
<g id="5" transform="translate(307.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24,30.5498047 L24,28.7041016 C24.6699252,29.1142599 25.3831342,29.3193359 26.1396484,29.3193359 C26.7093127,29.3193359 27.1547835,29.1837579 27.4760742,28.9125977 C27.7973649,28.6414374 27.9580078,28.2734398 27.9580078,27.8085938 C27.9580078,26.8378858 27.2721423,26.3525391 25.9003906,26.3525391 C25.4537738,26.3525391 24.9137401,26.3935543 24.2802734,26.4755859 L24.2802734,21 L29.7080078,21 L29.7080078,22.7636719 L26.1259766,22.7636719 L26.1259766,24.7255859 C26.4130874,24.6982421 26.6842435,24.6845703 26.9394531,24.6845703 C27.9466196,24.6845703 28.7350232,24.9488906 29.3046875,25.4775391 C29.8743518,26.0061875 30.1591797,26.7171179 30.1591797,27.6103516 C30.1591797,28.5992888 29.8196649,29.4070607 29.140625,30.0336914 C28.4615851,30.6603221 27.5410214,30.9736328 26.3789062,30.9736328 C25.4355422,30.9736328 24.6425813,30.8323582 24,30.5498047 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

26
src/runner/svgs/6.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>6</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-367.000000, -159.000000)">
<g id="6" transform="translate(367.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M30.2070312,21.2666016 L30.2070312,23.0712891 C29.710284,22.7887356 29.1679717,22.6474609 28.5800781,22.6474609 C27.8554651,22.6474609 27.2709983,22.9243136 26.8266602,23.4780273 C26.382322,24.031741 26.1510418,24.7734329 26.1328125,25.703125 L26.1738281,25.703125 C26.6432315,25.0104132 27.3290971,24.6640625 28.2314453,24.6640625 C29.0517619,24.6640625 29.7023088,24.9420545 30.1831055,25.4980469 C30.6639021,26.0540392 30.9042969,26.7900345 30.9042969,27.7060547 C30.9042969,28.6904346 30.5852896,29.5084603 29.9472656,30.1601562 C29.3092416,30.8118522 28.4980518,31.1376953 27.5136719,31.1376953 C26.4108018,31.1376953 25.5494823,30.7389363 24.9296875,29.9414062 C24.3098927,29.1438762 24,28.0250723 24,26.5849609 C24,24.88964 24.398759,23.5349986 25.1962891,22.5209961 C25.9938191,21.5069936 27.076165,21 28.4433594,21 C29.1998736,21 29.7877583,21.0888663 30.2070312,21.2666016 Z M27.5,26.2636719 C27.1035136,26.2636719 26.7879244,26.4026679 26.5532227,26.6806641 C26.318521,26.9586602 26.2011719,27.3141254 26.2011719,27.7470703 C26.2011719,28.2255883 26.3207996,28.6346012 26.5600586,28.9741211 C26.7993176,29.313641 27.1171855,29.4833984 27.5136719,29.4833984 C27.9010436,29.4833984 28.2109363,29.33187 28.4433594,29.0288086 C28.6757824,28.7257472 28.7919922,28.3304061 28.7919922,27.8427734 C28.7919922,26.7900338 28.3613324,26.2636719 27.5,26.2636719 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

26
src/runner/svgs/7.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>7</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-427.000000, -159.000000)">
<g id="7" transform="translate(427.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<polygon fill="#FFFFFF" fill-rule="nonzero" points="30.8222656 21.9980469 27.3632812 30.8027344 25.0800781 30.8027344 28.5664062 22.7636719 24 22.7636719 24 21 30.8222656 21"></polygon>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

26
src/runner/svgs/8.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>8</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-487.000000, -159.000000)">
<g id="8" transform="translate(487.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M25.9755859,25.8740234 L25.9755859,25.8398438 C24.8544866,25.379555 24.2939453,24.6344453 24.2939453,23.6044922 C24.2939453,22.847978 24.5947236,22.2247746 25.1962891,21.7348633 C25.7978546,21.244952 26.574865,21 27.5273438,21 C28.4980517,21 29.2727836,21.2290016 29.8515625,21.6870117 C30.4303414,22.1450218 30.7197266,22.7363245 30.7197266,23.4609375 C30.7197266,24.5319064 30.0999411,25.32031 28.8603516,25.8261719 L28.8603516,25.8535156 C29.5257195,26.0494801 30.0418276,26.3719053 30.4086914,26.8208008 C30.7755552,27.2696963 30.9589844,27.7744113 30.9589844,28.3349609 C30.9589844,29.2008507 30.6388378,29.8844376 29.9985352,30.3857422 C29.3582325,30.8870468 28.4638729,31.1376953 27.3154297,31.1376953 C26.3219351,31.1376953 25.5209991,30.8927433 24.9125977,30.402832 C24.3041962,29.9129207 24,29.2646525 24,28.4580078 C24,27.286778 24.6585221,26.4254585 25.9755859,25.8740234 Z M28.6894531,23.6455078 C28.6894531,23.3128239 28.5834972,23.0473643 28.371582,22.8491211 C28.1596669,22.6508779 27.878257,22.5517578 27.5273438,22.5517578 C27.1855452,22.5517578 26.9018566,22.6554352 26.6762695,22.862793 C26.4506825,23.0701508 26.3378906,23.3356104 26.3378906,23.6591797 C26.3378906,24.2698598 26.7298138,24.7324203 27.5136719,25.046875 C28.29753,24.7233057 28.6894531,24.2561879 28.6894531,23.6455078 Z M27.4179688,26.6943359 C26.5019485,27.0543638 26.0439453,27.6057906 26.0439453,28.3486328 C26.0439453,28.7041033 26.1840806,28.9980457 26.4643555,29.2304688 C26.7446303,29.4628918 27.0852845,29.5791016 27.4863281,29.5791016 C27.919273,29.5791016 28.2679023,29.4651704 28.5322266,29.2373047 C28.7965508,29.009439 28.9287109,28.7086607 28.9287109,28.3349609 C28.9287109,27.5875614 28.4251352,27.0406918 27.4179688,26.6943359 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

26
src/runner/svgs/9.svg Normal file
View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="53px" height="52px" viewBox="0 0 53 52" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 54.1 (76490) - https://sketchapp.com -->
<title>9</title>
<desc>Created with Sketch.</desc>
<g id="Devices" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Artboard" transform="translate(-547.000000, -159.000000)">
<g id="9" transform="translate(547.000000, 159.000000)">
<rect id="Rectangle" fill="#000000" x="7" y="6" width="40" height="40"></rect>
<g id="left" transform="translate(0.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="bottom" transform="translate(15.000000, 28.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="right" transform="translate(29.000000, 14.000000)" fill="#000000">
<rect id="carat" transform="translate(12.000000, 12.000000) rotate(-314.000000) translate(-12.000000, -12.000000) " x="4" y="4" width="16" height="16"></rect>
</g>
<g id="top" transform="translate(16.000000, 0.000000)" fill="#000000">
<rect id="carat" transform="translate(11.311985, 12.000000) rotate(-314.000000) translate(-11.311985, -12.000000) " x="3.31198537" y="4" width="16" height="16"></rect>
</g>
<path d="M24.5605469,30.7548828 L24.5605469,28.9775391 C25.0755234,29.3147803 25.6953089,29.4833984 26.4199219,29.4833984 C27.1764361,29.4833984 27.7609029,29.2270533 28.1733398,28.7143555 C28.5857768,28.2016576 28.7942708,27.4736375 28.7988281,26.5302734 L28.7578125,26.5166016 C28.3248676,27.1591829 27.6663456,27.4804688 26.7822266,27.4804688 C25.9801392,27.4804688 25.3159206,27.2001981 24.7895508,26.6396484 C24.263181,26.0790988 24,25.352218 24,24.4589844 C24,23.4153594 24.3212858,22.5779654 24.9638672,21.9467773 C25.6064485,21.3155893 26.4381459,21 27.4589844,21 C28.5071667,21 29.342282,21.3964804 29.9643555,22.1894531 C30.5864289,22.9824258 30.8974609,24.1171801 30.8974609,25.59375 C30.8974609,27.3437587 30.5226274,28.705236 29.7729492,29.6782227 C29.023271,30.6512093 27.9762437,31.1376953 26.6318359,31.1376953 C25.8343059,31.1376953 25.1438831,31.0100924 24.5605469,30.7548828 Z M27.3974609,22.6474609 C27.0237612,22.6474609 26.716147,22.7989894 26.4746094,23.1020508 C26.2330717,23.4051122 26.1123047,23.795896 26.1123047,24.2744141 C26.1123047,24.7711613 26.2319324,25.1619452 26.4711914,25.4467773 C26.7104504,25.7316095 27.0351542,25.8740234 27.4453125,25.8740234 C27.8190123,25.8740234 28.1243478,25.7384454 28.3613281,25.4672852 C28.5983085,25.1961249 28.7167969,24.8554708 28.7167969,24.4453125 C28.7167969,23.930336 28.5926119,23.5019548 28.3442383,23.1601562 C28.0958646,22.8183577 27.7802754,22.6474609 27.3974609,22.6474609 Z" fill="#FFFFFF" fill-rule="nonzero"></path>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/runner/svgs/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

207
src/runner/svgs/overlay.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 362 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 368 KiB

26
src/runner/trace.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() {
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() {
TraceLoggingUnregister(g_hProvider);
}
void Trace::EventLaunch() {
TraceLoggingWrite(
g_hProvider,
"Runner::Event::Launch",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

8
src/runner/trace.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
class Trace {
public:
static void RegisterProvider();
static void UnregisterProvider();
static void EventLaunch();
};

158
src/runner/tray_icon.cpp Normal file
View File

@@ -0,0 +1,158 @@
#include "pch.h"
#include "resource.h"
#include "settings_window.h"
#include "tray_icon.h"
#include <Windows.h>
extern "C" IMAGE_DOS_HEADER __ImageBase;
HWND tray_icon_hwnd = NULL;
// Message code that Windows will use for tray icon notifications.
UINT wm_icon_notify = 0;
// Contains the Windows Message for taskbar creation.
UINT wm_taskbar_restart = 0;
UINT wm_run_on_main_ui_thread = 0;
NOTIFYICONDATAW tray_icon_data;
static bool about_box_shown = false;
HMENU h_menu = nullptr;
HMENU h_sub_menu = nullptr;
// Struct to fill with callback and the data. The window_proc is responsible for cleaning it.
struct run_on_main_ui_thread_msg {
main_loop_callback_function _callback;
PVOID data;
};
bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data) {
if (tray_icon_hwnd == NULL) {
return false;
}
struct run_on_main_ui_thread_msg *wnd_msg = new struct run_on_main_ui_thread_msg();
wnd_msg->_callback = _callback;
wnd_msg->data = data;
PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, (LPARAM)wnd_msg);
return true;
}
LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) {
switch (message) {
case WM_CREATE:
if (wm_taskbar_restart == 0) {
tray_icon_hwnd = window;
wm_taskbar_restart = RegisterWindowMessageW(L"TaskbarCreated");
wm_run_on_main_ui_thread = RegisterWindowMessage(L"RunOnMainThreadCallback");
}
break;
case WM_DESTROY:
Shell_NotifyIcon(NIM_DELETE, &tray_icon_data);
PostQuitMessage(0);
break;
case WM_CLOSE:
DestroyWindow(window);
break;
case WM_COMMAND:
switch(wparam) {
case ID_SETTINGS_MENU_COMMAND:
open_settings_window();
break;
case ID_EXIT_MENU_COMMAND:
if (h_menu) {
DestroyMenu(h_menu);
}
DestroyWindow(window);
break;
case ID_ABOUT_MENU_COMMAND:
if (!about_box_shown) {
about_box_shown = true;
MessageBox(nullptr, L"PowerToys\nVersion 0.11.0\n\xa9 2019 Microsoft Corporation", L"About PowerToys", MB_OK);
about_box_shown = false;
}
break;
}
break;
default:
if (message == wm_icon_notify) {
switch(lparam) {
case WM_LBUTTONUP:
{
open_settings_window();
break;
}
case WM_RBUTTONUP:
case WM_CONTEXTMENU:
{
if (!h_menu) {
h_menu = LoadMenu(reinterpret_cast<HINSTANCE>(&__ImageBase), MAKEINTRESOURCE(ID_TRAY_MENU));
}
if (!h_sub_menu) {
h_sub_menu = GetSubMenu(h_menu, 0);
}
POINT mouse_pointer;
GetCursorPos(&mouse_pointer);
SetForegroundWindow(window); // Needed for the context menu to disappear.
TrackPopupMenu(h_sub_menu, TPM_CENTERALIGN|TPM_BOTTOMALIGN, mouse_pointer.x, mouse_pointer.y, 0, window, nullptr);
}
break;
}
} else if (message == wm_run_on_main_ui_thread) {
if (lparam != NULL) {
struct run_on_main_ui_thread_msg *msg = (struct run_on_main_ui_thread_msg *)lparam;
msg->_callback(msg->data);
delete msg;
lparam = NULL;
}
break;
} else if (message == wm_taskbar_restart) {
Shell_NotifyIcon(NIM_ADD, &tray_icon_data);
break;
}
}
return DefWindowProc(window, message, wparam, lparam);
}
void start_tray_icon() {
auto h_instance = reinterpret_cast<HINSTANCE>(&__ImageBase);
auto icon = LoadIcon(h_instance, MAKEINTRESOURCE(APPICON));
if (icon) {
UINT id_tray_icon = wm_icon_notify = RegisterWindowMessageW(L"WM_PowerToysIconNotify");
static LPCWSTR class_name = L"PToyTrayIconWindow";
WNDCLASS wc = {};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = h_instance;
wc.lpszClassName = class_name;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = tray_icon_window_proc;
wc.hIcon = icon;
RegisterClass(&wc);
auto hwnd = CreateWindowW(wc.lpszClassName,
L"PToyTrayIconWindow",
WS_OVERLAPPEDWINDOW | WS_POPUP,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
nullptr,
nullptr,
wc.hInstance,
nullptr);
WINRT_VERIFY(hwnd);
memset(&tray_icon_data, 0, sizeof(tray_icon_data));
tray_icon_data.cbSize = sizeof(tray_icon_data);
tray_icon_data.hIcon = icon;
tray_icon_data.hWnd = hwnd;
tray_icon_data.uID = id_tray_icon;
tray_icon_data.uCallbackMessage = wm_icon_notify;
wcscpy_s(tray_icon_data.szTip, sizeof(tray_icon_data.szTip) / sizeof(WCHAR), L"PowerToys");
tray_icon_data.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
Shell_NotifyIcon(NIM_ADD, &tray_icon_data);
}
}

9
src/runner/tray_icon.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
// Start the Tray Icon
void start_tray_icon();
// Open the Settings Window
void open_settings_window();
// Callback type to be called by the tray icon loop
typedef void(*main_loop_callback_function)(PVOID);
// Calls a callback in _callback
bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data);

View File

@@ -0,0 +1,134 @@
#include "pch.h"
#if _DEBUG && _WIN64
#include "unhandled_exception_handler.h"
#include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib")
#include <string>
#include <sstream>
#include <signal.h>
static IMAGEHLP_SYMBOL64* p_symbol = (IMAGEHLP_SYMBOL64*)malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH * sizeof(WCHAR));
static IMAGEHLP_LINE64 line;
static bool processing_exception = false;
static WCHAR module_path[MAX_PATH];
static LPTOP_LEVEL_EXCEPTION_FILTER default_top_level_exception_handler = NULL;
static const WCHAR* exception_description(const DWORD& code) {
switch (code) {
case EXCEPTION_ACCESS_VIOLATION: return L"EXCEPTION_ACCESS_VIOLATION";
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_BREAKPOINT: return L"EXCEPTION_BREAKPOINT";
case EXCEPTION_DATATYPE_MISALIGNMENT: return L"EXCEPTION_DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_DENORMAL_OPERAND: return L"EXCEPTION_FLT_DENORMAL_OPERAND";
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return L"EXCEPTION_FLT_DIVIDE_BY_ZERO";
case EXCEPTION_FLT_INEXACT_RESULT: return L"EXCEPTION_FLT_INEXACT_RESULT";
case EXCEPTION_FLT_INVALID_OPERATION: return L"EXCEPTION_FLT_INVALID_OPERATION";
case EXCEPTION_FLT_OVERFLOW: return L"EXCEPTION_FLT_OVERFLOW";
case EXCEPTION_FLT_STACK_CHECK: return L"EXCEPTION_FLT_STACK_CHECK";
case EXCEPTION_FLT_UNDERFLOW: return L"EXCEPTION_FLT_UNDERFLOW";
case EXCEPTION_ILLEGAL_INSTRUCTION: return L"EXCEPTION_ILLEGAL_INSTRUCTION";
case EXCEPTION_IN_PAGE_ERROR: return L"EXCEPTION_IN_PAGE_ERROR";
case EXCEPTION_INT_DIVIDE_BY_ZERO: return L"EXCEPTION_INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW: return L"EXCEPTION_INT_OVERFLOW";
case EXCEPTION_INVALID_DISPOSITION: return L"EXCEPTION_INVALID_DISPOSITION";
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return L"EXCEPTION_NONCONTINUABLE_EXCEPTION";
case EXCEPTION_PRIV_INSTRUCTION: return L"EXCEPTION_PRIV_INSTRUCTION";
case EXCEPTION_SINGLE_STEP: return L"EXCEPTION_SINGLE_STEP";
case EXCEPTION_STACK_OVERFLOW: return L"EXCEPTION_STACK_OVERFLOW";
default: return L"UNKNOWN EXCEPTION";
}
}
void init_symbols() {
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
auto process = GetCurrentProcess();
SymInitialize(process, NULL, TRUE);
}
void log_stack_trace(std::wstring& generalErrorDescription) {
memset(p_symbol, '\0', sizeof(*p_symbol) + MAX_PATH);
memset(&module_path[0], '\0', sizeof(module_path));
line.LineNumber = 0;
CONTEXT context;
RtlCaptureContext(&context);
auto process = GetCurrentProcess();
auto thread = GetCurrentThread();
STACKFRAME64 stack;
memset(&stack, 0, sizeof(STACKFRAME64));
stack.AddrPC.Offset = context.Rip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Rsp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Rbp;
stack.AddrFrame.Mode = AddrModeFlat;
std::wstringstream ss;
ss << generalErrorDescription << std::endl;
for (ULONG frame = 0;; frame++) {
auto result = StackWalk64(IMAGE_FILE_MACHINE_AMD64,
process,
thread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL);
p_symbol->MaxNameLength = MAX_PATH;
p_symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
DWORD64 dw64Displacement;
SymGetSymFromAddr64(process, stack.AddrPC.Offset, &dw64Displacement, p_symbol);
DWORD dwDisplacement;
SymGetLineFromAddr64(process, stack.AddrPC.Offset, &dwDisplacement, &line);
auto module_base = SymGetModuleBase64(process, stack.AddrPC.Offset);
if (module_base) {
GetModuleFileName((HINSTANCE)module_base, module_path, MAX_PATH);
}
ss << module_path << "!"
<< p_symbol->Name
<< "(" << line.FileName << ":" << line.LineNumber << ")\n";
if (!result) {
break;
}
}
auto errorString = ss.str();
MessageBoxW(NULL, errorString.c_str(), L"Unhandled Error", MB_OK | MB_ICONERROR);
}
LONG WINAPI unhandled_exceptiont_handler(PEXCEPTION_POINTERS info) {
if (!processing_exception) {
processing_exception = true;
try {
init_symbols();
std::wstring ex_description = L"Exception code not available";
if (info != NULL && info->ExceptionRecord != NULL && info->ExceptionRecord->ExceptionCode != NULL) {
ex_description = exception_description(info->ExceptionRecord->ExceptionCode);
}
log_stack_trace(ex_description);
}
catch (...) {}
if (default_top_level_exception_handler != NULL && info != NULL) {
default_top_level_exception_handler(info);
}
processing_exception = false;
}
return EXCEPTION_CONTINUE_SEARCH;
}
extern "C" void AbortHandler(int signal_number) {
init_symbols();
std::wstring ex_description = L"SIGABRT was raised.";
log_stack_trace(ex_description);
}
void init_global_error_handlers() {
default_top_level_exception_handler = SetUnhandledExceptionFilter(unhandled_exceptiont_handler);
signal(SIGABRT, &AbortHandler);
}
#endif

View File

@@ -0,0 +1,4 @@
#pragma once
#if _DEBUG && _WIN64
void init_global_error_handlers();
#endif

View File

@@ -0,0 +1,72 @@
#include "pch.h"
#include "win_hook_event.h"
#include "powertoy_module.h"
#include <mutex>
#include <deque>
#include <thread>
static std::mutex mutex;
static std::deque<WinHookEvent> hook_events;
static std::condition_variable dispatch_cv;
static void CALLBACK win_hook_event_proc(HWINEVENTHOOK winEventHook,
DWORD event,
HWND window,
LONG object,
LONG child,
DWORD eventThread,
DWORD eventTime) {
std::unique_lock lock(mutex);
hook_events.push_back({ event,
window,
object,
child,
eventThread,
eventTime });
lock.unlock();
dispatch_cv.notify_one();
}
static bool running = false;
static std::thread dispatch_thread;
static void dispatch_thread_proc() {
std::unique_lock lock(mutex);
while (running) {
dispatch_cv.wait(lock, []{ return !running || !hook_events.empty(); });
if (!running)
return;
while (!hook_events.empty()) {
auto event = hook_events.front();
hook_events.pop_front();
lock.unlock();
powertoys_events().signal_event(win_hook_event, reinterpret_cast<intptr_t>(&event));
lock.lock();
}
}
}
static HWINEVENTHOOK hook_handle;
void start_win_hook_event() {
std::lock_guard lock(mutex);
if (running)
return;
running = true;
dispatch_thread = std::thread(dispatch_thread_proc);
hook_handle = SetWinEventHook(EVENT_MIN, EVENT_MAX, nullptr, win_hook_event_proc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
}
void stop_win_hook_event() {
std::unique_lock lock(mutex);
if (!running)
return;
running = false;
UnhookWinEvent(hook_handle);
lock.unlock();
dispatch_cv.notify_one();
dispatch_thread.join();
lock.lock();
hook_events.clear();
hook_events.shrink_to_fit();
}

View File

@@ -0,0 +1,7 @@
#pragma once
#include <interface/win_hook_event_data.h>
void start_win_hook_event();
void stop_win_hook_event();