Coding style (runner) (#1013)

This commit is contained in:
Enrico Giordani 2019-12-26 17:26:11 +01:00 committed by GitHub
parent 9708961654
commit 415a0cdf28
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1414 additions and 1166 deletions

View file

@ -12,351 +12,390 @@
// Helper macros from wix. // Helper macros from wix.
// TODO: use "s" and "..." parameters to report errors from these functions. // TODO: use "s" and "..." parameters to report errors from these functions.
#define ExitOnFailure(x,s,...) if (FAILED(x)) { goto LExit; } #define ExitOnFailure(x, s, ...) \
#define ExitWithLastError(x,s,...) { DWORD Dutil_er = ::GetLastError(); x = HRESULT_FROM_WIN32(Dutil_er); if (!FAILED(x)) { x = E_FAIL; } goto LExit; } if (FAILED(x)) \
#define ExitFunction() { goto LExit; } { \
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_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0' const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
bool enable_auto_start_task_for_this_user()
{
HRESULT hr = S_OK;
bool enable_auto_start_task_for_this_user() { WCHAR username_domain[USERNAME_DOMAIN_LEN];
HRESULT hr = S_OK; WCHAR username[USERNAME_LEN];
WCHAR username_domain[USERNAME_DOMAIN_LEN]; std::wstring wstrTaskName;
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;
ITaskService *pService = NULL; // ------------------------------------------------------
ITaskFolder *pTaskFolder = NULL; // Get the Domain/Username for the trigger.
ITaskDefinition *pTask = NULL; if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
IRegistrationInfo *pRegInfo = NULL; {
ITaskSettings *pSettings = NULL; ExitWithLastError(hr, "Getting username failed: %x", hr);
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 (!GetEnvironmentVariable(L"USERDOMAIN", username_domain, USERNAME_DOMAIN_LEN))
{
// If the task exists, just enable it. ExitWithLastError(hr, "Getting the user's domain failed: %x", hr);
{
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();
}
} }
} wcscat_s(username_domain, L"\\");
wcscat_s(username_domain, username);
// Create the task builder object to create the task. // Task Name.
hr = pService->NewTask(0, &pTask); wstrTaskName = L"Autorun for ";
ExitOnFailure(hr, "Failed to create a task definition: %x", hr); wstrTaskName += username;
// ------------------------------------------------------ // Get the executable path passed to the custom action.
// Get the registration info for setting the identification. WCHAR wszExecutablePath[MAX_PATH];
hr = pTask->get_RegistrationInfo(&pRegInfo); GetModuleFileName(NULL, wszExecutablePath, MAX_PATH);
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 // Create an instance of the Task Service.
hr = pTask->get_Settings(&pSettings); hr = CoCreateInstance(CLSID_TaskScheduler,
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr); NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr);
hr = pSettings->put_StartWhenAvailable(VARIANT_FALSE); // Connect to the task service.
ExitOnFailure(hr, "Cannot put_StartWhenAvailable setting info: %x", hr); hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
hr = pSettings->put_StopIfGoingOnBatteries(VARIANT_FALSE); ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
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. // Get the PowerToys task folder. Creates it if it doesn't exist.
hr = pTask->get_Triggers(&pTriggerCollection); hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
ExitOnFailure(hr, "Cannot get trigger collection: %x", hr); if (FAILED(hr))
{
// Add the logon trigger to the task. // Folder doesn't exist. Get the Root folder and create the PowerToys subfolder.
{ ITaskFolder* pRootFolder = NULL;
ITrigger *pTrigger = NULL; hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
ILogonTrigger *pLogonTrigger = NULL; ExitOnFailure(hr, "Cannot get Root Folder pointer: %x", hr);
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger); hr = pRootFolder->CreateFolder(_bstr_t(L"\\PowerToys"), _variant_t(L""), &pTaskFolder);
ExitOnFailure(hr, "Cannot create the trigger: %x", hr); if (FAILED(hr))
{
hr = pTrigger->QueryInterface( pRootFolder->Release();
IID_ILogonTrigger, (void**)&pLogonTrigger); ExitOnFailure(hr, "Cannot create PowerToys task folder: %x", hr);
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); // If the task exists, just enable it.
} {
// ------------------------------------------------------ IRegisteredTask* pExistingRegisteredTask = NULL;
// Save the task in the PowerToys folder. hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
hr = pTaskFolder->RegisterTaskDefinition( if (SUCCEEDED(hr))
_bstr_t(wstrTaskName.c_str()), {
pTask, // Task exists, try enabling it.
TASK_CREATE_OR_UPDATE, hr = pExistingRegisteredTask->put_Enabled(VARIANT_TRUE);
_variant_t(username_domain), pExistingRegisteredTask->Release();
_variant_t(), if (SUCCEEDED(hr))
TASK_LOGON_INTERACTIVE_TOKEN, {
_variant_t(L""), // Function enable. Sounds like a success.
&pRegisteredTask); ExitFunction();
ExitOnFailure(hr, "Error saving the Task : %x", hr); }
}
}
// 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: LExit:
if (pService) pService->Release(); if (pService)
if (pTaskFolder) pTaskFolder->Release(); pService->Release();
if (pTask) pTask->Release(); if (pTaskFolder)
if (pRegInfo) pRegInfo->Release(); pTaskFolder->Release();
if (pSettings) pSettings->Release(); if (pTask)
if (pTriggerCollection) pTriggerCollection->Release(); pTask->Release();
if (pRegisteredTask) pRegisteredTask->Release(); if (pRegInfo)
pRegInfo->Release();
if (pSettings)
pSettings->Release();
if (pTriggerCollection)
pTriggerCollection->Release();
if (pRegisteredTask)
pRegisteredTask->Release();
return(SUCCEEDED(hr)); return (SUCCEEDED(hr));
} }
bool disable_auto_start_task_for_this_user() { bool disable_auto_start_task_for_this_user()
HRESULT hr = S_OK; {
HRESULT hr = S_OK;
WCHAR username[USERNAME_LEN]; WCHAR username[USERNAME_LEN];
std::wstring wstrTaskName; std::wstring wstrTaskName;
ITaskService *pService = NULL; ITaskService* pService = NULL;
ITaskFolder *pTaskFolder = NULL; ITaskFolder* pTaskFolder = NULL;
// ------------------------------------------------------ // ------------------------------------------------------
// Get the Username for the task. // Get the Username for the task.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
ExitWithLastError(hr, "Getting username failed: %x", hr); {
} ExitWithLastError(hr, "Getting username failed: %x", hr);
}
// Task Name.
wstrTaskName = L"Autorun for "; // Task Name.
wstrTaskName += username; wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// ------------------------------------------------------
// Create an instance of the Task Service. // ------------------------------------------------------
hr = CoCreateInstance(CLSID_TaskScheduler, // Create an instance of the Task Service.
NULL, hr = CoCreateInstance(CLSID_TaskScheduler,
CLSCTX_INPROC_SERVER, NULL,
IID_ITaskService, CLSCTX_INPROC_SERVER,
(void**)&pService); IID_ITaskService,
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); (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(), // Connect to the task service.
_variant_t(), _variant_t()); hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------ // ------------------------------------------------------
// Get the PowerToys task folder. // Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder); hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
if (FAILED(hr)) { if (FAILED(hr))
// Folder doesn't exist. No need to disable a non-existing task. {
hr = S_OK; // Folder doesn't exist. No need to disable a non-existing task.
ExitFunction(); hr = S_OK;
} ExitFunction();
}
// ------------------------------------------------------
// If the task exists, disable. // ------------------------------------------------------
{ // If the task exists, disable.
IRegisteredTask *pExistingRegisteredTask = NULL; {
hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask); IRegisteredTask* pExistingRegisteredTask = NULL;
if (SUCCEEDED(hr)) { hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
// Task exists, try disabling it. if (SUCCEEDED(hr))
hr = pExistingRegisteredTask->put_Enabled(VARIANT_FALSE); {
pExistingRegisteredTask->Release(); // Task exists, try disabling it.
if (SUCCEEDED(hr)) { hr = pExistingRegisteredTask->put_Enabled(VARIANT_FALSE);
// Function disable. Sounds like a success. pExistingRegisteredTask->Release();
ExitFunction(); if (SUCCEEDED(hr))
} {
// Function disable. Sounds like a success.
ExitFunction();
}
}
} }
}
LExit: LExit:
if (pService) pService->Release(); if (pService)
if (pTaskFolder) pTaskFolder->Release(); pService->Release();
if (pTaskFolder)
pTaskFolder->Release();
return(SUCCEEDED(hr)); return (SUCCEEDED(hr));
} }
bool is_auto_start_task_active_for_this_user(){ bool is_auto_start_task_active_for_this_user()
HRESULT hr = S_OK; {
HRESULT hr = S_OK;
WCHAR username[USERNAME_LEN]; WCHAR username[USERNAME_LEN];
std::wstring wstrTaskName; std::wstring wstrTaskName;
ITaskService *pService = NULL; ITaskService* pService = NULL;
ITaskFolder *pTaskFolder = NULL; ITaskFolder* pTaskFolder = NULL;
// ------------------------------------------------------ // ------------------------------------------------------
// Get the Username for the task. // Get the Username for the task.
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN)) { if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
ExitWithLastError(hr, "Getting username failed: %x", hr); {
} ExitWithLastError(hr, "Getting username failed: %x", hr);
}
// Task Name.
wstrTaskName = L"Autorun for "; // Task Name.
wstrTaskName += username; wstrTaskName = L"Autorun for ";
wstrTaskName += username;
// ------------------------------------------------------
// Create an instance of the Task Service. // ------------------------------------------------------
hr = CoCreateInstance(CLSID_TaskScheduler, // Create an instance of the Task Service.
NULL, hr = CoCreateInstance(CLSID_TaskScheduler,
CLSCTX_INPROC_SERVER, NULL,
IID_ITaskService, CLSCTX_INPROC_SERVER,
(void**)&pService); IID_ITaskService,
ExitOnFailure(hr, "Failed to create an instance of ITaskService: %x", hr); (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(), // Connect to the task service.
_variant_t(), _variant_t()); hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr); ExitOnFailure(hr, "ITaskService::Connect failed: %x", hr);
// ------------------------------------------------------ // ------------------------------------------------------
// Get the PowerToys task folder. // Get the PowerToys task folder.
hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder); hr = pService->GetFolder(_bstr_t(L"\\PowerToys"), &pTaskFolder);
ExitOnFailure(hr, "ITaskFolder doesn't exist: %x", hr); ExitOnFailure(hr, "ITaskFolder doesn't exist: %x", hr);
// ------------------------------------------------------ // ------------------------------------------------------
// If the task exists, disable. // If the task exists, disable.
{ {
IRegisteredTask *pExistingRegisteredTask = NULL; IRegisteredTask* pExistingRegisteredTask = NULL;
hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask); hr = pTaskFolder->GetTask(_bstr_t(wstrTaskName.c_str()), &pExistingRegisteredTask);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr))
// Task exists, get its value. {
VARIANT_BOOL is_enabled; // Task exists, get its value.
hr = pExistingRegisteredTask->get_Enabled(&is_enabled); VARIANT_BOOL is_enabled;
pExistingRegisteredTask->Release(); hr = pExistingRegisteredTask->get_Enabled(&is_enabled);
if (SUCCEEDED(hr)) { pExistingRegisteredTask->Release();
// Got the value. Return it. if (SUCCEEDED(hr))
hr = (is_enabled == VARIANT_TRUE) ? S_OK : E_FAIL; // Fake success or fail to return the value. {
ExitFunction(); // Got the value. Return it.
} hr = (is_enabled == VARIANT_TRUE) ? S_OK : E_FAIL; // Fake success or fail to return the value.
ExitFunction();
}
}
} }
}
LExit: LExit:
if (pService) pService->Release(); if (pService)
if (pTaskFolder) pTaskFolder->Release(); pService->Release();
if (pTaskFolder)
return(SUCCEEDED(hr)); pTaskFolder->Release();
return (SUCCEEDED(hr));
} }

View file

@ -10,110 +10,139 @@
static std::wstring settings_theme = L"system"; static std::wstring settings_theme = L"system";
static bool run_as_elevated = false; static bool run_as_elevated = false;
json::JsonObject load_general_settings() { json::JsonObject load_general_settings()
auto loaded = PTSettingsHelper::load_general_settings(); {
settings_theme = loaded.GetNamedString(L"theme", L"system"); auto loaded = PTSettingsHelper::load_general_settings();
if (settings_theme != L"dark" && settings_theme != L"light") { settings_theme = loaded.GetNamedString(L"theme", L"system");
settings_theme = L"system"; if (settings_theme != L"dark" && settings_theme != L"light")
} {
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false); settings_theme = L"system";
return loaded; }
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false);
return loaded;
} }
json::JsonObject get_general_settings() { json::JsonObject get_general_settings()
json::JsonObject result; {
const bool startup = is_auto_start_task_active_for_this_user(); json::JsonObject result;
result.SetNamedValue(L"startup", json::value(startup)); const bool startup = is_auto_start_task_active_for_this_user();
result.SetNamedValue(L"startup", json::value(startup));
json::JsonObject enabled; json::JsonObject enabled;
for (auto&[name, powertoy] : modules()) { for (auto& [name, powertoy] : modules())
enabled.SetNamedValue(name, json::value(powertoy.is_enabled())); {
} enabled.SetNamedValue(name, json::value(powertoy.is_enabled()));
result.SetNamedValue(L"enabled", std::move(enabled)); }
result.SetNamedValue(L"enabled", std::move(enabled));
bool is_elevated = is_process_elevated(); bool is_elevated = is_process_elevated();
result.SetNamedValue(L"is_elevated", json::value(is_elevated)); result.SetNamedValue(L"is_elevated", json::value(is_elevated));
result.SetNamedValue(L"run_elevated", json::value(run_as_elevated)); result.SetNamedValue(L"run_elevated", json::value(run_as_elevated));
result.SetNamedValue(L"theme", json::value(settings_theme)); result.SetNamedValue(L"theme", json::value(settings_theme));
result.SetNamedValue(L"system_theme", json::value(WindowsColors::is_dark_mode() ? L"dark" : L"light")); result.SetNamedValue(L"system_theme", json::value(WindowsColors::is_dark_mode() ? L"dark" : L"light"));
result.SetNamedValue(L"powertoys_version", json::value(get_product_version())); result.SetNamedValue(L"powertoys_version", json::value(get_product_version()));
return result; return result;
} }
void apply_general_settings(const json::JsonObject& general_configs) { void apply_general_settings(const json::JsonObject& general_configs)
if (json::has(general_configs, L"startup", json::JsonValueType::Boolean)) { {
const bool startup = general_configs.GetNamedBoolean(L"startup"); if (json::has(general_configs, L"startup", json::JsonValueType::Boolean))
const bool current_startup = is_auto_start_task_active_for_this_user(); {
if (current_startup != startup) { const bool startup = general_configs.GetNamedBoolean(L"startup");
if (startup) { const bool current_startup = is_auto_start_task_active_for_this_user();
enable_auto_start_task_for_this_user(); if (current_startup != startup)
} else { {
disable_auto_start_task_for_this_user(); if (startup)
} {
enable_auto_start_task_for_this_user();
}
else
{
disable_auto_start_task_for_this_user();
}
}
} }
} if (json::has(general_configs, L"enabled"))
if (json::has(general_configs, L"enabled")) { {
for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled")) { for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled"))
const auto value = enabled_element.Value(); {
if (value.ValueType() != json::JsonValueType::Boolean) { const auto value = enabled_element.Value();
continue; if (value.ValueType() != json::JsonValueType::Boolean)
} {
const std::wstring name{enabled_element.Key().c_str()}; continue;
const bool found = modules().find(name) != modules().end(); }
if (!found) { const std::wstring name{ enabled_element.Key().c_str() };
continue; const bool found = modules().find(name) != modules().end();
} if (!found)
const bool module_inst_enabled = modules().at(name).is_enabled(); {
const bool target_enabled = value.GetBoolean(); continue;
if (module_inst_enabled == target_enabled) { }
continue; const bool module_inst_enabled = modules().at(name).is_enabled();
} const bool target_enabled = value.GetBoolean();
if (target_enabled) { if (module_inst_enabled == target_enabled)
modules().at(name).enable(); {
} else { continue;
modules().at(name).disable(); }
} if (target_enabled)
{
modules().at(name).enable();
}
else
{
modules().at(name).disable();
}
}
} }
} run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false); if (json::has(general_configs, L"theme", json::JsonValueType::String))
if (json::has(general_configs, L"theme", json::JsonValueType::String)) { {
settings_theme = general_configs.GetNamedString(L"theme"); settings_theme = general_configs.GetNamedString(L"theme");
} }
json::JsonObject save_settings = get_general_settings(); json::JsonObject save_settings = get_general_settings();
PTSettingsHelper::save_general_settings(save_settings); PTSettingsHelper::save_general_settings(save_settings);
} }
void start_initial_powertoys() { void start_initial_powertoys()
bool only_enable_some_powertoys = false; {
bool only_enable_some_powertoys = false;
std::unordered_set<std::wstring> powertoys_to_enable; std::unordered_set<std::wstring> powertoys_to_enable;
json::JsonObject general_settings; json::JsonObject general_settings;
try { try
general_settings = load_general_settings(); {
json::JsonObject enabled = general_settings.GetNamedObject(L"enabled"); general_settings = load_general_settings();
for (const auto & enabled_element : enabled) { json::JsonObject enabled = general_settings.GetNamedObject(L"enabled");
if (enabled_element.Value().GetBoolean()) { for (const auto& enabled_element : enabled)
// Enable this powertoy. {
powertoys_to_enable.emplace(enabled_element.Key()); if (enabled_element.Value().GetBoolean())
} {
// Enable this powertoy.
powertoys_to_enable.emplace(enabled_element.Key());
}
}
only_enable_some_powertoys = true;
} }
only_enable_some_powertoys = true; catch (...)
} {
catch (...) { // Couldn't read the general settings correctly.
// Couldn't read the general settings correctly. // Load all powertoys.
// Load all powertoys. // TODO: notify user about invalid json config
// TODO: notify user about invalid json config only_enable_some_powertoys = false;
only_enable_some_powertoys = false; }
}
for (auto& [name, powertoy] : modules())
for (auto&[name, powertoy] : modules()) { {
if (only_enable_some_powertoys) { if (only_enable_some_powertoys)
if (powertoys_to_enable.find(name)!=powertoys_to_enable.end()) { {
powertoy.enable(); if (powertoys_to_enable.find(name) != powertoys_to_enable.end())
} {
} else { powertoy.enable();
powertoy.enable(); }
}
else
{
powertoy.enable();
}
} }
}
} }

View file

@ -4,5 +4,5 @@
json::JsonObject load_general_settings(); json::JsonObject load_general_settings();
json::JsonObject get_general_settings(); json::JsonObject get_general_settings();
void apply_general_settings(const json::JsonObject & general_configs); void apply_general_settings(const json::JsonObject& general_configs);
void start_initial_powertoys(); void start_initial_powertoys();

View file

@ -2,44 +2,54 @@
#include "lowlevel_keyboard_event.h" #include "lowlevel_keyboard_event.h"
#include "powertoys_events.h" #include "powertoys_events.h"
namespace { namespace
HHOOK hook_handle = nullptr; {
HHOOK hook_handle_copy = nullptr; // make sure we do use nullptr in CallNextHookEx call HHOOK hook_handle = nullptr;
LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam) { HHOOK hook_handle_copy = nullptr; // make sure we do use nullptr in CallNextHookEx call
LowlevelKeyboardEvent event; LRESULT CALLBACK hook_proc(int nCode, WPARAM wParam, LPARAM lParam)
if (nCode == HC_ACTION) { {
event.lParam = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam); LowlevelKeyboardEvent event;
event.wParam = wParam; if (nCode == HC_ACTION)
if (powertoys_events().signal_event(ll_keyboard, reinterpret_cast<intptr_t>(&event)) != 0) { {
return 1; 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);
} }
return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
}
} }
// Prevent system-wide input lagging while paused in the debugger // Prevent system-wide input lagging while paused in the debugger
//#define DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED //#define DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED
void start_lowlevel_keyboard_hook() { void start_lowlevel_keyboard_hook()
{
#if defined(_DEBUG) && defined(DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED) #if defined(_DEBUG) && defined(DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED)
if(IsDebuggerPresent()) { if (IsDebuggerPresent())
return; {
} return;
}
#endif #endif
if (!hook_handle) { if (!hook_handle)
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL); {
hook_handle_copy = hook_handle; hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
if (!hook_handle) { hook_handle_copy = hook_handle;
throw std::runtime_error("Cannot install keyboard listener"); if (!hook_handle)
{
throw std::runtime_error("Cannot install keyboard listener");
}
} }
}
} }
void stop_lowlevel_keyboard_hook() { void stop_lowlevel_keyboard_hook()
if (hook_handle) { {
UnhookWindowsHookEx(hook_handle); if (hook_handle)
hook_handle = nullptr; {
} UnhookWindowsHookEx(hook_handle);
hook_handle = nullptr;
}
} }

View file

@ -19,107 +19,125 @@
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
void chdir_current_executable()
void chdir_current_executable() { {
// Change current directory to the path of the executable. // Change current directory to the path of the executable.
WCHAR executable_path[MAX_PATH]; WCHAR executable_path[MAX_PATH];
GetModuleFileName(NULL, executable_path, MAX_PATH); GetModuleFileName(NULL, executable_path, MAX_PATH);
PathRemoveFileSpec(executable_path); PathRemoveFileSpec(executable_path);
if(!SetCurrentDirectory(executable_path)) { if (!SetCurrentDirectory(executable_path))
show_last_error_message(L"Change Directory to Executable Path", GetLastError()); {
} show_last_error_message(L"Change Directory to Executable Path", GetLastError());
}
} }
int runner() { int runner()
DPIAware::EnableDPIAwarenessForThisProcess(); {
DPIAware::EnableDPIAwarenessForThisProcess();
#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 {
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",
L"PowerRenameExt.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(get_product_version()); #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
{
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",
L"PowerRenameExt.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();
result = run_message_loop(); Trace::EventLaunch(get_product_version());
} catch (std::runtime_error & err) {
std::string err_what = err.what(); result = run_message_loop();
MessageBoxW(NULL, std::wstring(err_what.begin(), err_what.end()).c_str(), L"Error", MB_OK | MB_ICONERROR); }
result = -1; catch (std::runtime_error& err)
} {
Trace::UnregisterProvider(); std::string err_what = err.what();
return result; MessageBoxW(NULL, std::wstring(err_what.begin(), err_what.end()).c_str(), L"Error", MB_OK | MB_ICONERROR);
result = -1;
}
Trace::UnregisterProvider();
return result;
} }
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
WCHAR username[UNLEN + 1]; {
DWORD username_length = UNLEN + 1; WCHAR username[UNLEN + 1];
GetUserNameW(username, &username_length); DWORD username_length = UNLEN + 1;
auto runner_mutex = CreateMutexW(NULL, TRUE, (std::wstring(L"Local\\PowerToyRunMutex") + username).c_str()); GetUserNameW(username, &username_length);
if (runner_mutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS) { auto runner_mutex = CreateMutexW(NULL, TRUE, (std::wstring(L"Local\\PowerToyRunMutex") + username).c_str());
// The app is already running if (runner_mutex == NULL || GetLastError() == ERROR_ALREADY_EXISTS)
return 0; {
} // The app is already running
int result = 0; return 0;
try { }
// Singletons initialization order needs to be preserved, first events and int result = 0;
// then modules to guarantee the reverse destruction order. try
SystemMenuHelperInstace(); {
powertoys_events(); // Singletons initialization order needs to be preserved, first events and
modules(); // then modules to guarantee the reverse destruction order.
SystemMenuHelperInstace();
powertoys_events();
modules();
auto general_settings = load_general_settings(); auto general_settings = load_general_settings();
int rvalue = 0; int rvalue = 0;
if (is_process_elevated() || if (is_process_elevated() ||
general_settings.GetNamedBoolean(L"run_elevated", false) == false || general_settings.GetNamedBoolean(L"run_elevated", false) == false ||
strcmp(lpCmdLine, "--dont-elevate") == 0) { strcmp(lpCmdLine, "--dont-elevate") == 0)
result = runner(); {
result = runner();
}
else
{
schedule_restart_as_elevated();
result = 0;
}
} }
else { catch (std::runtime_error& err)
schedule_restart_as_elevated(); {
result = 0; std::string err_what = err.what();
MessageBoxW(NULL, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR);
result = -1;
} }
} ReleaseMutex(runner_mutex);
catch (std::runtime_error & err) { CloseHandle(runner_mutex);
std::string err_what = err.what(); if (is_restart_scheduled())
MessageBoxW(NULL, std::wstring(err_what.begin(), err_what.end()).c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR); {
result = -1; if (restart_if_scheduled() == false)
} {
ReleaseMutex(runner_mutex); auto text = is_process_elevated() ? GET_RESOURCE_STRING(IDS_COULDNOT_RESTART_NONELEVATED) :
CloseHandle(runner_mutex); GET_RESOURCE_STRING(IDS_COULDNOT_RESTART_ELEVATED);
if (is_restart_scheduled()) { MessageBoxW(NULL, text.c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR);
if (restart_if_scheduled() == false) { result = -1;
auto text = is_process_elevated() ? GET_RESOURCE_STRING(IDS_COULDNOT_RESTART_NONELEVATED) : }
GET_RESOURCE_STRING(IDS_COULDNOT_RESTART_ELEVATED);
MessageBoxW(NULL, text.c_str(), GET_RESOURCE_STRING(IDS_ERROR).c_str(), MB_OK | MB_ICONERROR);
result = -1;
} }
} stop_tray_icon();
stop_tray_icon(); return result;
return result;
} }

View file

@ -3,32 +3,37 @@
#include "lowlevel_keyboard_event.h" #include "lowlevel_keyboard_event.h"
#include <algorithm> #include <algorithm>
std::unordered_map<std::wstring, PowertoyModule>& modules() { std::unordered_map<std::wstring, PowertoyModule>& modules()
static std::unordered_map<std::wstring, PowertoyModule> modules; {
return modules; static std::unordered_map<std::wstring, PowertoyModule> modules;
return modules;
} }
PowertoyModule load_powertoy(const std::wstring& filename) { 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")); auto handle = winrt::check_pointer(LoadLibraryW(filename.c_str()));
if (!create) { auto create = reinterpret_cast<powertoy_create_func>(GetProcAddress(handle, "powertoy_create"));
FreeLibrary(handle); if (!create)
winrt::throw_last_error(); {
} FreeLibrary(handle);
auto module = create(); winrt::throw_last_error();
if (!module) { }
FreeLibrary(handle); auto module = create();
winrt::throw_last_error(); if (!module)
} {
module->register_system_menu_helper(&SystemMenuHelperInstace()); FreeLibrary(handle);
return PowertoyModule(module, handle); winrt::throw_last_error();
}
module->register_system_menu_helper(&SystemMenuHelperInstace());
return PowertoyModule(module, handle);
} }
json::JsonObject PowertoyModule::json_config() const { json::JsonObject PowertoyModule::json_config() const
int size = 0; {
module->get_config(nullptr, &size); int size = 0;
std::wstring result; module->get_config(nullptr, &size);
result.resize(size - 1); std::wstring result;
module->get_config(result.data(), &size); result.resize(size - 1);
return json::JsonObject::Parse(result); module->get_config(result.data(), &size);
return json::JsonObject::Parse(result);
} }

View file

@ -11,87 +11,108 @@
class PowertoyModule; class PowertoyModule;
#include <common/json.h> #include <common/json.h>
struct PowertoyModuleDeleter { struct PowertoyModuleDeleter
void operator()(PowertoyModuleIface* module) const { {
if (module) { void operator()(PowertoyModuleIface* module) const
powertoys_events().unregister_system_menu_action(module); {
powertoys_events().unregister_receiver(module); if (module)
module->destroy(); {
powertoys_events().unregister_system_menu_action(module);
powertoys_events().unregister_receiver(module);
module->destroy();
}
} }
}
}; };
struct PowertoyModuleDLLDeleter { struct PowertoyModuleDLLDeleter
using pointer = HMODULE; {
void operator()(HMODULE handle) const { using pointer = HMODULE;
FreeLibrary(handle); void operator()(HMODULE handle) const
} {
FreeLibrary(handle);
}
}; };
class PowertoyModule { class PowertoyModule
{
public: public:
PowertoyModule(PowertoyModuleIface* module, HMODULE handle) : handle(handle), module(module) { PowertoyModule(PowertoyModuleIface* module, HMODULE handle) :
if (!module) { handle(handle), module(module)
throw std::runtime_error("Module not initialized"); {
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);
}
}
if (SystemMenuHelperInstace().HasCustomConfig(module))
{
powertoys_events().register_system_menu_action(module);
}
} }
name = module->get_name();
auto want_signals = module->get_events(); const std::wstring& get_name() const
if (want_signals) { {
for (; *want_signals; ++want_signals) { return name;
powertoys_events().register_receiver(*want_signals, module);
}
} }
if (SystemMenuHelperInstace().HasCustomConfig(module)) {
powertoys_events().register_system_menu_action(module); json::JsonObject json_config() const;
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;
} }
}
const std::wstring& get_name() const { void set_config(const std::wstring& config)
return name; {
} module->set_config(config.c_str());
json::JsonObject json_config() const;
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) { void call_custom_action(const std::wstring& action)
module->set_config(config.c_str()); {
} module->call_custom_action(action.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() { intptr_t signal_event(const std::wstring& signal_event, intptr_t data)
return module->is_enabled(); {
} return module->signal_event(signal_event.c_str(), data);
}
void enable() {
module->enable(); bool is_enabled()
} {
return module->is_enabled();
void disable() { }
module->disable();
} void enable()
{
module->enable();
}
void disable()
{
module->disable();
}
private: private:
std::unique_ptr<HMODULE, PowertoyModuleDLLDeleter> handle; std::unique_ptr<HMODULE, PowertoyModuleDLLDeleter> handle;
std::unique_ptr<PowertoyModuleIface, PowertoyModuleDeleter> module; std::unique_ptr<PowertoyModuleIface, PowertoyModuleDeleter> module;
std::wstring name; std::wstring name;
}; };
PowertoyModule load_powertoy(const std::wstring& filename); PowertoyModule load_powertoy(const std::wstring& filename);

View file

@ -4,83 +4,102 @@
#include "win_hook_event.h" #include "win_hook_event.h"
#include "system_menu_helper.h" #include "system_menu_helper.h"
void first_subscribed(const std::wstring& event) { void first_subscribed(const std::wstring& event)
if (event == ll_keyboard) {
start_lowlevel_keyboard_hook(); if (event == ll_keyboard)
else if (event == win_hook_event) start_lowlevel_keyboard_hook();
start_win_hook_event(); else if (event == win_hook_event)
start_win_hook_event();
} }
void last_unsubscribed(const std::wstring& event) { void last_unsubscribed(const std::wstring& event)
if (event == ll_keyboard) {
stop_lowlevel_keyboard_hook(); if (event == ll_keyboard)
else if (event == win_hook_event) stop_lowlevel_keyboard_hook();
stop_win_hook_event(); else if (event == win_hook_event)
stop_win_hook_event();
} }
PowertoysEvents& powertoys_events() { PowertoysEvents& powertoys_events()
static PowertoysEvents powertoys_events; {
return powertoys_events; static PowertoysEvents powertoys_events;
return powertoys_events;
} }
void PowertoysEvents::register_receiver(const std::wstring & event, PowertoyModuleIface* module) { void PowertoysEvents::register_receiver(const std::wstring& event, PowertoyModuleIface* module)
std::unique_lock lock(mutex); {
auto& subscribers = receivers[event]; std::unique_lock lock(mutex);
if (subscribers.empty()) { auto& subscribers = receivers[event];
first_subscribed(event); if (subscribers.empty())
} {
subscribers.push_back(module); first_subscribed(event);
}
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);
} }
} subscribers.push_back(module);
} }
void PowertoysEvents::register_system_menu_action(PowertoyModuleIface* module) { void PowertoysEvents::unregister_receiver(PowertoyModuleIface* module)
std::unique_lock lock(mutex); {
system_menu_receivers.insert(module); std::unique_lock lock(mutex);
} for (auto& [event, subscribers] : receivers)
{
void PowertoysEvents::unregister_system_menu_action(PowertoyModuleIface* module) { subscribers.erase(remove(begin(subscribers), end(subscribers), module), end(subscribers));
std::unique_lock lock(mutex); if (subscribers.empty())
auto it = system_menu_receivers.find(module); {
if (it != system_menu_receivers.end()) { last_unsubscribed(event);
SystemMenuHelperInstace().Reset(module); }
system_menu_receivers.erase(it);
}
}
void PowertoysEvents::handle_system_menu_action(const WinHookEvent& data) {
if (data.event == EVENT_SYSTEM_MENUSTART) {
for (auto& module : system_menu_receivers) {
SystemMenuHelperInstace().Customize(module, data.hwnd);
} }
}
else if (data.event == EVENT_OBJECT_INVOKED) {
if (PowertoyModuleIface* module{ SystemMenuHelperInstace().ModuleFromItemId(data.idChild) }) {
std::wstring itemName = SystemMenuHelperInstace().ItemNameFromItemId(data.idChild);
// Process event on specified system menu item by responsible module.
module->signal_system_menu_action(itemName.c_str());
// Process event on specified system menu item by system menu helper (check/uncheck if needed).
SystemMenuHelperInstace().ProcessSelectedItem(module, GetForegroundWindow(), itemName.c_str());
}
}
} }
intptr_t PowertoysEvents::signal_event(const std::wstring & event, intptr_t data) { void PowertoysEvents::register_system_menu_action(PowertoyModuleIface* module)
intptr_t rvalue = 0; {
std::shared_lock lock(mutex); std::unique_lock lock(mutex);
if (auto it = receivers.find(event); it != end(receivers)) { system_menu_receivers.insert(module);
for (auto& module : it->second) { }
if (module)
rvalue |= module->signal_event(event.c_str(), data); void PowertoysEvents::unregister_system_menu_action(PowertoyModuleIface* module)
{
std::unique_lock lock(mutex);
auto it = system_menu_receivers.find(module);
if (it != system_menu_receivers.end())
{
SystemMenuHelperInstace().Reset(module);
system_menu_receivers.erase(it);
} }
} }
return rvalue;
void PowertoysEvents::handle_system_menu_action(const WinHookEvent& data)
{
if (data.event == EVENT_SYSTEM_MENUSTART)
{
for (auto& module : system_menu_receivers)
{
SystemMenuHelperInstace().Customize(module, data.hwnd);
}
}
else if (data.event == EVENT_OBJECT_INVOKED)
{
if (PowertoyModuleIface * module{ SystemMenuHelperInstace().ModuleFromItemId(data.idChild) })
{
std::wstring itemName = SystemMenuHelperInstace().ItemNameFromItemId(data.idChild);
// Process event on specified system menu item by responsible module.
module->signal_system_menu_action(itemName.c_str());
// Process event on specified system menu item by system menu helper (check/uncheck if needed).
SystemMenuHelperInstace().ProcessSelectedItem(module, GetForegroundWindow(), itemName.c_str());
}
}
}
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

@ -4,24 +4,25 @@
#include <interface/win_hook_event_data.h> #include <interface/win_hook_event_data.h>
#include <string> #include <string>
class PowertoysEvents { class PowertoysEvents
{
public: public:
void register_receiver(const std::wstring& event, PowertoyModuleIface* module); void register_receiver(const std::wstring& event, PowertoyModuleIface* module);
void unregister_receiver(PowertoyModuleIface* module); void unregister_receiver(PowertoyModuleIface* module);
void register_system_menu_action(PowertoyModuleIface* module); void register_system_menu_action(PowertoyModuleIface* module);
void unregister_system_menu_action(PowertoyModuleIface* module); void unregister_system_menu_action(PowertoyModuleIface* module);
void handle_system_menu_action(const WinHookEvent& data); void handle_system_menu_action(const WinHookEvent& data);
intptr_t signal_event(const std::wstring& event, intptr_t data);
intptr_t signal_event(const std::wstring& event, intptr_t data);
private: private:
std::shared_mutex mutex; std::shared_mutex mutex;
std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers; std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers;
std::unordered_set<PowertoyModuleIface*> system_menu_receivers; std::unordered_set<PowertoyModuleIface*> system_menu_receivers;
}; };
PowertoysEvents& powertoys_events(); PowertoysEvents& powertoys_events();
void first_subscribed(const std::wstring& event); void first_subscribed(const std::wstring& event);
void last_unsubscribed(const std::wstring& event); void last_unsubscribed(const std::wstring& event);

View file

@ -2,36 +2,42 @@
#include "restart_elevated.h" #include "restart_elevated.h"
#include "common/common.h" #include "common/common.h"
enum State { enum State
None, {
RestartAsElevated, None,
RestartAsNonElevated RestartAsElevated,
RestartAsNonElevated
}; };
static State state = None; static State state = None;
void schedule_restart_as_elevated() { void schedule_restart_as_elevated()
state = RestartAsElevated; {
state = RestartAsElevated;
} }
void schedule_restart_as_non_elevated() { void schedule_restart_as_non_elevated()
state = RestartAsNonElevated; {
state = RestartAsNonElevated;
} }
bool is_restart_scheduled() { bool is_restart_scheduled()
return state != None; {
return state != None;
} }
bool restart_if_scheduled() { bool restart_if_scheduled()
// Make sure we have enough room, even for the long (\\?\) paths {
constexpr DWORD exe_path_size = 0xFFFF; // Make sure we have enough room, even for the long (\\?\) paths
auto exe_path = std::make_unique<wchar_t[]>(exe_path_size); constexpr DWORD exe_path_size = 0xFFFF;
GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size); auto exe_path = std::make_unique<wchar_t[]>(exe_path_size);
switch (state) { GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size);
case RestartAsElevated: switch (state)
return run_elevated(exe_path.get(), {}); {
case RestartAsNonElevated: case RestartAsElevated:
return run_non_elevated(exe_path.get(), L"--dont-elevate"); return run_elevated(exe_path.get(), {});
default: case RestartAsNonElevated:
return false; return run_non_elevated(exe_path.get(), L"--dont-elevate");
} default:
return false;
}
} }

View file

@ -68,7 +68,7 @@ void dispatch_json_action_to_module(const json::JsonObject& powertoys_configs)
else if (modules().find(name) != modules().end()) else if (modules().find(name) != modules().end())
{ {
const auto element = powertoy_element.Value().Stringify(); const auto element = powertoy_element.Value().Stringify();
modules().at(name).call_custom_action(element.c_str()); modules().at(name).call_custom_action(element.c_str());
} }
} }
} }
@ -145,7 +145,6 @@ void receive_json_send_to_main_thread(const std::wstring& msg)
dispatch_run_on_main_ui_thread(dispatch_received_json_callback, copy); dispatch_run_on_main_ui_thread(dispatch_received_json_callback, copy);
} }
// Try to run the Settings process with non-elevated privileges. // Try to run the Settings process with non-elevated privileges.
BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, PROCESS_INFORMATION* process_info) BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args, PROCESS_INFORMATION* process_info)
{ {
@ -166,7 +165,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args,
SIZE_T size = 0; SIZE_T size = 0;
InitializeProcThreadAttributeList(nullptr, 1, 0, &size); InitializeProcThreadAttributeList(nullptr, 1, 0, &size);
auto pproc_buffer = std::unique_ptr<char[]>{new (std::nothrow)char[size]}; auto pproc_buffer = std::unique_ptr<char[]>{ new (std::nothrow) char[size] };
auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get()); auto pptal = reinterpret_cast<PPROC_THREAD_ATTRIBUTE_LIST>(pproc_buffer.get());
if (!pptal) if (!pptal)
{ {

View file

@ -3,127 +3,158 @@
#include <interface/powertoy_module_interface.h> #include <interface/powertoy_module_interface.h>
namespace { namespace
constexpr int KSeparatorPos = 1; {
constexpr int KNewItemPos = 2; constexpr int KSeparatorPos = 1;
constexpr int KNewItemPos = 2;
unsigned int GenerateItemId() { unsigned int GenerateItemId()
static unsigned int generator = 0x70777479; {
return ++generator; static unsigned int generator = 0x70777479;
} return ++generator;
}
} }
SystemMenuHelper& SystemMenuHelperInstace() { SystemMenuHelper& SystemMenuHelperInstace()
static SystemMenuHelper instance; {
return instance; static SystemMenuHelper instance;
return instance;
} }
void SystemMenuHelper::SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) { void SystemMenuHelper::SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config)
Reset(module); {
Configurations[module] = config; Reset(module);
for (auto& [window, modules] : ProcessedModules) { Configurations[module] = config;
// Unregister module. After system menu is opened again, new configuration will be applied. for (auto& [window, modules] : ProcessedModules)
modules.erase(std::remove(std::begin(modules), std::end(modules), module), std::end(modules)); {
} // Unregister module. After system menu is opened again, new configuration will be applied.
modules.erase(std::remove(std::begin(modules), std::end(modules), module), std::end(modules));
}
} }
void SystemMenuHelper::ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) { void SystemMenuHelper::ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName)
for (const auto& item : Configurations[module]) { {
if (itemName == item.name && item.checkBox) { for (const auto& item : Configurations[module])
// Handle check/uncheck action only if specified by module configuration. {
for (const auto& [id, data] : IdMappings) { if (itemName == item.name && item.checkBox)
if (data.second == itemName) { {
HMENU systemMenu = GetSystemMenu(window, false); // Handle check/uncheck action only if specified by module configuration.
int state = (GetMenuState(systemMenu, id, MF_BYCOMMAND) == MF_CHECKED) ? MF_UNCHECKED : MF_CHECKED; for (const auto& [id, data] : IdMappings)
CheckMenuItem(systemMenu, id, MF_BYCOMMAND | state); {
break; if (data.second == itemName)
{
HMENU systemMenu = GetSystemMenu(window, false);
int state = (GetMenuState(systemMenu, id, MF_BYCOMMAND) == MF_CHECKED) ? MF_UNCHECKED : MF_CHECKED;
CheckMenuItem(systemMenu, id, MF_BYCOMMAND | state);
break;
}
}
break;
} }
}
break;
} }
}
} }
bool SystemMenuHelper::Customize(PowertoyModuleIface* module, HWND window) { bool SystemMenuHelper::Customize(PowertoyModuleIface* module, HWND window)
auto& modules = ProcessedModules[window]; {
for (const auto& m : modules) { auto& modules = ProcessedModules[window];
if (module == m) { for (const auto& m : modules)
return false; {
} if (module == m)
} {
AddSeparator(module, window); return false;
for (const auto& info : Configurations[module]) {
AddItem(module, window, info.name, info.enable);
}
modules.push_back(module);
return true;
}
void SystemMenuHelper::Reset(PowertoyModuleIface* module) {
for (auto& [window, modules] : ProcessedModules) {
if (HMENU systemMenu{ GetSystemMenu(window, false) }) {
for (auto& [id, data] : IdMappings) {
if (data.first == module) {
DeleteMenu(systemMenu, id, MF_BYCOMMAND);
} }
}
} }
} AddSeparator(module, window);
} for (const auto& info : Configurations[module])
{
bool SystemMenuHelper::HasCustomConfig(PowertoyModuleIface* module) { AddItem(module, window, info.name, info.enable);
return Configurations.find(module) != Configurations.end();
}
bool SystemMenuHelper::AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable) {
if (HMENU systemMenu{ GetSystemMenu(window, false) }) {
MENUITEMINFO item;
item.cbSize = sizeof(item);
item.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
item.fState = MF_UNCHECKED | MF_DISABLED; // Item is disabled by default.
item.wID = GenerateItemId();
item.dwTypeData = const_cast<WCHAR*>(name.c_str());
item.cch = (UINT)name.size() + 1;
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KNewItemPos, true, &item)) {
IdMappings[item.wID] = { module, name };
if (enable) {
EnableMenuItem(systemMenu, item.wID, MF_BYCOMMAND | MF_ENABLED);
}
return true;
} }
} modules.push_back(module);
return false; return true;
} }
bool SystemMenuHelper::AddSeparator(PowertoyModuleIface* module, HWND window) { void SystemMenuHelper::Reset(PowertoyModuleIface* module)
if (HMENU systemMenu{ GetSystemMenu(window, false) }) { {
MENUITEMINFO separator; for (auto& [window, modules] : ProcessedModules)
separator.cbSize = sizeof(separator); {
separator.fMask = MIIM_ID | MIIM_FTYPE; if (HMENU systemMenu{ GetSystemMenu(window, false) })
separator.fType = MFT_SEPARATOR; {
separator.wID = GenerateItemId(); for (auto& [id, data] : IdMappings)
{
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KSeparatorPos, true, &separator)) { if (data.first == module)
IdMappings[separator.wID] = { module, L"sepparator_dummy_name" }; {
return true; DeleteMenu(systemMenu, id, MF_BYCOMMAND);
}
}
}
} }
}
return false;
} }
PowertoyModuleIface* SystemMenuHelper::ModuleFromItemId(const int& id) { bool SystemMenuHelper::HasCustomConfig(PowertoyModuleIface* module)
auto it = IdMappings.find(id); {
if (it != IdMappings.end()) { return Configurations.find(module) != Configurations.end();
return it->second.first;
}
return nullptr;
} }
const std::wstring SystemMenuHelper::ItemNameFromItemId(const int& id) { bool SystemMenuHelper::AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable)
auto itemIt = IdMappings.find(id); {
if (itemIt != IdMappings.end()) { if (HMENU systemMenu{ GetSystemMenu(window, false) })
return itemIt->second.second; {
} MENUITEMINFO item;
return std::wstring{}; item.cbSize = sizeof(item);
item.fMask = MIIM_ID | MIIM_STRING | MIIM_STATE;
item.fState = MF_UNCHECKED | MF_DISABLED; // Item is disabled by default.
item.wID = GenerateItemId();
item.dwTypeData = const_cast<WCHAR*>(name.c_str());
item.cch = (UINT)name.size() + 1;
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KNewItemPos, true, &item))
{
IdMappings[item.wID] = { module, name };
if (enable)
{
EnableMenuItem(systemMenu, item.wID, MF_BYCOMMAND | MF_ENABLED);
}
return true;
}
}
return false;
}
bool SystemMenuHelper::AddSeparator(PowertoyModuleIface* module, HWND window)
{
if (HMENU systemMenu{ GetSystemMenu(window, false) })
{
MENUITEMINFO separator;
separator.cbSize = sizeof(separator);
separator.fMask = MIIM_ID | MIIM_FTYPE;
separator.fType = MFT_SEPARATOR;
separator.wID = GenerateItemId();
if (InsertMenuItem(systemMenu, GetMenuItemCount(systemMenu) - KSeparatorPos, true, &separator))
{
IdMappings[separator.wID] = { module, L"sepparator_dummy_name" };
return true;
}
}
return false;
}
PowertoyModuleIface* SystemMenuHelper::ModuleFromItemId(const int& id)
{
auto it = IdMappings.find(id);
if (it != IdMappings.end())
{
return it->second.first;
}
return nullptr;
}
const std::wstring SystemMenuHelper::ItemNameFromItemId(const int& id)
{
auto itemIt = IdMappings.find(id);
if (itemIt != IdMappings.end())
{
return itemIt->second.second;
}
return std::wstring{};
} }

View file

@ -10,33 +10,34 @@
class PowertoyModuleIface; class PowertoyModuleIface;
class SystemMenuHelper : public PowertoySystemMenuIface { class SystemMenuHelper : public PowertoySystemMenuIface
{
public: public:
// PowertoySystemMenuIface // PowertoySystemMenuIface
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) override; virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) override;
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override; virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override;
bool Customize(PowertoyModuleIface* module, HWND window); bool Customize(PowertoyModuleIface* module, HWND window);
void Reset(PowertoyModuleIface* module); void Reset(PowertoyModuleIface* module);
bool HasCustomConfig(PowertoyModuleIface* module); bool HasCustomConfig(PowertoyModuleIface* module);
PowertoyModuleIface* ModuleFromItemId(const int& id); PowertoyModuleIface* ModuleFromItemId(const int& id);
const std::wstring ItemNameFromItemId(const int& id); const std::wstring ItemNameFromItemId(const int& id);
private: private:
bool AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable); bool AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable);
bool AddSeparator(PowertoyModuleIface* module, HWND window); bool AddSeparator(PowertoyModuleIface* module, HWND window);
// Store processed modules per window to avoid handling it multiple times. // Store processed modules per window to avoid handling it multiple times.
std::unordered_map<HWND, std::vector<PowertoyModuleIface*>> ProcessedModules{}; std::unordered_map<HWND, std::vector<PowertoyModuleIface*>> ProcessedModules{};
// Keep mappings form item id to the module who created it and item name for faster processing later. // Keep mappings form item id to the module who created it and item name for faster processing later.
std::unordered_map<int, std::pair<PowertoyModuleIface*, std::wstring>> IdMappings{}; std::unordered_map<int, std::pair<PowertoyModuleIface*, std::wstring>> IdMappings{};
// Store configurations provided by module. // Store configurations provided by module.
// This will be used to create custom system menu items and to handle updates. // This will be used to create custom system menu items and to handle updates.
std::unordered_map<PowertoyModuleIface*, std::vector<ItemInfo>> Configurations{}; std::unordered_map<PowertoyModuleIface*, std::vector<ItemInfo>> Configurations{};
}; };
SystemMenuHelper& SystemMenuHelperInstace(); SystemMenuHelper& SystemMenuHelperInstace();

View file

@ -2,26 +2,29 @@
#include "trace.h" #include "trace.h"
TRACELOGGING_DEFINE_PROVIDER( 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(const std::wstring& versionNumber) {
TraceLoggingWrite(
g_hProvider, g_hProvider,
"Runner_Launch", "Microsoft.PowerToys",
TraceLoggingWideString(versionNumber.c_str(), "Version"), // {38e8889b-9731-53f5-e901-e8a7c1753074}
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), TraceLoggingOptionProjectTelemetry());
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
void Trace::RegisterProvider()
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider()
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::EventLaunch(const std::wstring& versionNumber)
{
TraceLoggingWrite(
g_hProvider,
"Runner_Launch",
TraceLoggingWideString(versionNumber.c_str(), "Version"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
} }

View file

@ -1,8 +1,9 @@
#pragma once #pragma once
class Trace { class Trace
{
public: public:
static void RegisterProvider(); static void RegisterProvider();
static void UnregisterProvider(); static void UnregisterProvider();
static void EventLaunch(const std::wstring& versionNumber); static void EventLaunch(const std::wstring& versionNumber);
}; };

View file

@ -7,177 +7,199 @@
extern "C" IMAGE_DOS_HEADER __ImageBase; extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace { namespace
HWND tray_icon_hwnd = NULL; {
HWND tray_icon_hwnd = NULL;
// Message code that Windows will use for tray icon notifications. // Message code that Windows will use for tray icon notifications.
UINT wm_icon_notify = 0; UINT wm_icon_notify = 0;
// Contains the Windows Message for taskbar creation. // Contains the Windows Message for taskbar creation.
UINT wm_taskbar_restart = 0; UINT wm_taskbar_restart = 0;
UINT wm_run_on_main_ui_thread = 0; UINT wm_run_on_main_ui_thread = 0;
NOTIFYICONDATAW tray_icon_data; NOTIFYICONDATAW tray_icon_data;
bool tray_icon_created = false; bool tray_icon_created = false;
bool about_box_shown = false; bool about_box_shown = false;
HMENU h_menu = nullptr; HMENU h_menu = nullptr;
HMENU h_sub_menu = nullptr; HMENU h_sub_menu = nullptr;
} }
// Struct to fill with callback and the data. The window_proc is responsible for cleaning it. // Struct to fill with callback and the data. The window_proc is responsible for cleaning it.
struct run_on_main_ui_thread_msg { struct run_on_main_ui_thread_msg
main_loop_callback_function _callback; {
PVOID data; main_loop_callback_function _callback;
PVOID data;
}; };
bool dispatch_run_on_main_ui_thread(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; if (tray_icon_hwnd == NULL)
} {
struct run_on_main_ui_thread_msg *wnd_msg = new struct run_on_main_ui_thread_msg(); return false;
wnd_msg->_callback = _callback; }
wnd_msg->data = data; 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); PostMessage(tray_icon_hwnd, wm_run_on_main_ui_thread, 0, (LPARAM)wnd_msg);
return true; return true;
} }
LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
switch (message) { {
case WM_CREATE: switch (message)
if (wm_taskbar_restart == 0) { {
tray_icon_hwnd = window; case WM_CREATE:
wm_taskbar_restart = RegisterWindowMessageW(L"TaskbarCreated"); if (wm_taskbar_restart == 0)
wm_run_on_main_ui_thread = RegisterWindowMessage(L"RunOnMainThreadCallback"); {
} tray_icon_hwnd = window;
break; wm_taskbar_restart = RegisterWindowMessageW(L"TaskbarCreated");
case WM_DESTROY: wm_run_on_main_ui_thread = RegisterWindowMessage(L"RunOnMainThreadCallback");
if (tray_icon_created) {
Shell_NotifyIcon(NIM_DELETE, &tray_icon_data);
tray_icon_created = false;
}
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);
} }
break;
case WM_DESTROY:
if (tray_icon_created)
{
Shell_NotifyIcon(NIM_DELETE, &tray_icon_data);
tray_icon_created = false;
}
PostQuitMessage(0);
break;
case WM_CLOSE:
DestroyWindow(window); DestroyWindow(window);
break; break;
case ID_ABOUT_MENU_COMMAND: case WM_COMMAND:
if (!about_box_shown) { switch (wparam)
about_box_shown = true; {
std::wstring about_msg = L"PowerToys\nVersion " + get_product_version() + L"\n\xa9 2019 Microsoft Corporation"; case ID_SETTINGS_MENU_COMMAND:
MessageBox(nullptr, about_msg.c_str(), L"About PowerToys", MB_OK); open_settings_window();
about_box_shown = false; 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;
std::wstring about_msg = L"PowerToys\nVersion " + get_product_version() + L"\n\xa9 2019 Microsoft Corporation";
MessageBox(nullptr, about_msg.c_str(), L"About PowerToys", MB_OK);
about_box_shown = false;
}
break;
}
break;
// Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it.
// We'll also never receive wm_taskbar_restart message if the first call to Shell_NotifyIcon failed, so we use
// WM_WINDOWPOSCHANGING which is always received on explorer startup sequence.
case WM_WINDOWPOSCHANGING: {
if (!tray_icon_created)
{
tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE;
} }
break; break;
} }
break; default:
// Shell_NotifyIcon can fail when we invoke it during the time explorer.exe isn't present/ready to handle it. if (message == wm_icon_notify)
// We'll also never receive wm_taskbar_restart message if the first call to Shell_NotifyIcon failed, so we use
// WM_WINDOWPOSCHANGING which is always received on explorer startup sequence.
case WM_WINDOWPOSCHANGING:
{
if(!tray_icon_created) {
tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE;
}
break;
}
default:
if (message == wm_icon_notify) {
switch(lparam) {
case WM_LBUTTONUP:
{ {
open_settings_window(); switch (lparam)
break; {
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;
}
} }
case WM_RBUTTONUP: else if (message == wm_run_on_main_ui_thread)
case WM_CONTEXTMENU:
{ {
if (!h_menu) { if (lparam != NULL)
h_menu = LoadMenu(reinterpret_cast<HINSTANCE>(&__ImageBase), MAKEINTRESOURCE(ID_TRAY_MENU)); {
} struct run_on_main_ui_thread_msg* msg = (struct run_on_main_ui_thread_msg*)lparam;
if (!h_sub_menu) { msg->_callback(msg->data);
h_sub_menu = GetSubMenu(h_menu, 0); delete msg;
} lparam = NULL;
POINT mouse_pointer; }
GetCursorPos(&mouse_pointer); break;
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); else if (message == wm_taskbar_restart)
{
tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE;
break;
} }
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) {
tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE;
break;
} }
} return DefWindowProc(window, message, wparam, lparam);
return DefWindowProc(window, message, wparam, lparam);
} }
void start_tray_icon() { void start_tray_icon()
auto h_instance = reinterpret_cast<HINSTANCE>(&__ImageBase); {
auto icon = LoadIcon(h_instance, MAKEINTRESOURCE(APPICON)); auto h_instance = reinterpret_cast<HINSTANCE>(&__ImageBase);
if (icon) { auto icon = LoadIcon(h_instance, MAKEINTRESOURCE(APPICON));
UINT id_tray_icon = wm_icon_notify = RegisterWindowMessageW(L"WM_PowerToysIconNotify"); if (icon)
{
UINT id_tray_icon = wm_icon_notify = RegisterWindowMessageW(L"WM_PowerToysIconNotify");
static LPCWSTR class_name = L"PToyTrayIconWindow"; static LPCWSTR class_name = L"PToyTrayIconWindow";
WNDCLASS wc = {}; WNDCLASS wc = {};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW); wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = h_instance; wc.hInstance = h_instance;
wc.lpszClassName = class_name; wc.lpszClassName = class_name;
wc.style = CS_HREDRAW | CS_VREDRAW; wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = tray_icon_window_proc; wc.lpfnWndProc = tray_icon_window_proc;
wc.hIcon = icon; wc.hIcon = icon;
RegisterClass(&wc); RegisterClass(&wc);
auto hwnd = CreateWindowW(wc.lpszClassName, auto hwnd = CreateWindowW(wc.lpszClassName,
L"PToyTrayIconWindow", L"PToyTrayIconWindow",
WS_OVERLAPPEDWINDOW | WS_POPUP, WS_OVERLAPPEDWINDOW | WS_POPUP,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
nullptr, nullptr,
nullptr, nullptr,
wc.hInstance, wc.hInstance,
nullptr); nullptr);
WINRT_VERIFY(hwnd); WINRT_VERIFY(hwnd);
memset(&tray_icon_data, 0, sizeof(tray_icon_data)); memset(&tray_icon_data, 0, sizeof(tray_icon_data));
tray_icon_data.cbSize = sizeof(tray_icon_data); tray_icon_data.cbSize = sizeof(tray_icon_data);
tray_icon_data.hIcon = icon; tray_icon_data.hIcon = icon;
tray_icon_data.hWnd = hwnd; tray_icon_data.hWnd = hwnd;
tray_icon_data.uID = id_tray_icon; tray_icon_data.uID = id_tray_icon;
tray_icon_data.uCallbackMessage = wm_icon_notify; tray_icon_data.uCallbackMessage = wm_icon_notify;
wcscpy_s(tray_icon_data.szTip, sizeof(tray_icon_data.szTip) / sizeof(WCHAR), L"PowerToys"); 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; tray_icon_data.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE; tray_icon_created = Shell_NotifyIcon(NIM_ADD, &tray_icon_data) == TRUE;
} }
} }
void stop_tray_icon() { void stop_tray_icon()
if (tray_icon_created) { {
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0); if (tray_icon_created)
} {
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
}
} }

View file

@ -6,6 +6,6 @@ void stop_tray_icon();
// Open the Settings Window // Open the Settings Window
void open_settings_window(); void open_settings_window();
// Callback type to be called by the tray icon loop // Callback type to be called by the tray icon loop
typedef void(*main_loop_callback_function)(PVOID); typedef void (*main_loop_callback_function)(PVOID);
// Calls a callback in _callback // Calls a callback in _callback
bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data); bool dispatch_run_on_main_ui_thread(main_loop_callback_function _callback, PVOID data);

View file

@ -1,5 +1,5 @@
#include "pch.h" #include "pch.h"
#if _DEBUG && _WIN64 #if _DEBUG && _WIN64
#include "unhandled_exception_handler.h" #include "unhandled_exception_handler.h"
#include <DbgHelp.h> #include <DbgHelp.h>
#pragma comment(lib, "DbgHelp.lib") #pragma comment(lib, "DbgHelp.lib")
@ -13,122 +13,158 @@ static bool processing_exception = false;
static WCHAR module_path[MAX_PATH]; static WCHAR module_path[MAX_PATH];
static LPTOP_LEVEL_EXCEPTION_FILTER default_top_level_exception_handler = NULL; static LPTOP_LEVEL_EXCEPTION_FILTER default_top_level_exception_handler = NULL;
static const WCHAR* exception_description(const DWORD& code) { static const WCHAR* exception_description(const DWORD& code)
switch (code) { {
case EXCEPTION_ACCESS_VIOLATION: return L"EXCEPTION_ACCESS_VIOLATION"; switch (code)
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; {
case EXCEPTION_BREAKPOINT: return L"EXCEPTION_BREAKPOINT"; case EXCEPTION_ACCESS_VIOLATION:
case EXCEPTION_DATATYPE_MISALIGNMENT: return L"EXCEPTION_DATATYPE_MISALIGNMENT"; return L"EXCEPTION_ACCESS_VIOLATION";
case EXCEPTION_FLT_DENORMAL_OPERAND: return L"EXCEPTION_FLT_DENORMAL_OPERAND"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
case EXCEPTION_FLT_DIVIDE_BY_ZERO: return L"EXCEPTION_FLT_DIVIDE_BY_ZERO"; return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
case EXCEPTION_FLT_INEXACT_RESULT: return L"EXCEPTION_FLT_INEXACT_RESULT"; case EXCEPTION_BREAKPOINT:
case EXCEPTION_FLT_INVALID_OPERATION: return L"EXCEPTION_FLT_INVALID_OPERATION"; return L"EXCEPTION_BREAKPOINT";
case EXCEPTION_FLT_OVERFLOW: return L"EXCEPTION_FLT_OVERFLOW"; case EXCEPTION_DATATYPE_MISALIGNMENT:
case EXCEPTION_FLT_STACK_CHECK: return L"EXCEPTION_FLT_STACK_CHECK"; return L"EXCEPTION_DATATYPE_MISALIGNMENT";
case EXCEPTION_FLT_UNDERFLOW: return L"EXCEPTION_FLT_UNDERFLOW"; case EXCEPTION_FLT_DENORMAL_OPERAND:
case EXCEPTION_ILLEGAL_INSTRUCTION: return L"EXCEPTION_ILLEGAL_INSTRUCTION"; return L"EXCEPTION_FLT_DENORMAL_OPERAND";
case EXCEPTION_IN_PAGE_ERROR: return L"EXCEPTION_IN_PAGE_ERROR"; case EXCEPTION_FLT_DIVIDE_BY_ZERO:
case EXCEPTION_INT_DIVIDE_BY_ZERO: return L"EXCEPTION_INT_DIVIDE_BY_ZERO"; return L"EXCEPTION_FLT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW: return L"EXCEPTION_INT_OVERFLOW"; case EXCEPTION_FLT_INEXACT_RESULT:
case EXCEPTION_INVALID_DISPOSITION: return L"EXCEPTION_INVALID_DISPOSITION"; return L"EXCEPTION_FLT_INEXACT_RESULT";
case EXCEPTION_NONCONTINUABLE_EXCEPTION: return L"EXCEPTION_NONCONTINUABLE_EXCEPTION"; case EXCEPTION_FLT_INVALID_OPERATION:
case EXCEPTION_PRIV_INSTRUCTION: return L"EXCEPTION_PRIV_INSTRUCTION"; return L"EXCEPTION_FLT_INVALID_OPERATION";
case EXCEPTION_SINGLE_STEP: return L"EXCEPTION_SINGLE_STEP"; case EXCEPTION_FLT_OVERFLOW:
case EXCEPTION_STACK_OVERFLOW: return L"EXCEPTION_STACK_OVERFLOW"; return L"EXCEPTION_FLT_OVERFLOW";
default: return L"UNKNOWN EXCEPTION"; case EXCEPTION_FLT_STACK_CHECK:
} return L"EXCEPTION_FLT_STACK_CHECK";
} case EXCEPTION_FLT_UNDERFLOW:
return L"EXCEPTION_FLT_UNDERFLOW";
void init_symbols() { case EXCEPTION_ILLEGAL_INSTRUCTION:
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME); return L"EXCEPTION_ILLEGAL_INSTRUCTION";
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); case EXCEPTION_IN_PAGE_ERROR:
auto process = GetCurrentProcess(); return L"EXCEPTION_IN_PAGE_ERROR";
SymInitialize(process, NULL, TRUE); case EXCEPTION_INT_DIVIDE_BY_ZERO:
} return L"EXCEPTION_INT_DIVIDE_BY_ZERO";
case EXCEPTION_INT_OVERFLOW:
void log_stack_trace(std::wstring& generalErrorDescription) { return L"EXCEPTION_INT_OVERFLOW";
memset(p_symbol, '\0', sizeof(*p_symbol) + MAX_PATH); case EXCEPTION_INVALID_DISPOSITION:
memset(&module_path[0], '\0', sizeof(module_path)); return L"EXCEPTION_INVALID_DISPOSITION";
line.LineNumber = 0; case EXCEPTION_NONCONTINUABLE_EXCEPTION:
return L"EXCEPTION_NONCONTINUABLE_EXCEPTION";
CONTEXT context; case EXCEPTION_PRIV_INSTRUCTION:
RtlCaptureContext(&context); return L"EXCEPTION_PRIV_INSTRUCTION";
auto process = GetCurrentProcess(); case EXCEPTION_SINGLE_STEP:
auto thread = GetCurrentThread(); return L"EXCEPTION_SINGLE_STEP";
STACKFRAME64 stack; case EXCEPTION_STACK_OVERFLOW:
memset(&stack, 0, sizeof(STACKFRAME64)); return L"EXCEPTION_STACK_OVERFLOW";
stack.AddrPC.Offset = context.Rip; default:
stack.AddrPC.Mode = AddrModeFlat; return L"UNKNOWN EXCEPTION";
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) { void init_symbols()
if (!processing_exception) { {
processing_exception = true; SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
try { line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
init_symbols(); auto process = GetCurrentProcess();
std::wstring ex_description = L"Exception code not available"; SymInitialize(process, NULL, TRUE);
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) { void log_stack_trace(std::wstring& generalErrorDescription)
init_symbols(); {
std::wstring ex_description = L"SIGABRT was raised."; memset(p_symbol, '\0', sizeof(*p_symbol) + MAX_PATH);
log_stack_trace(ex_description); 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);
} }
void init_global_error_handlers() { LONG WINAPI unhandled_exceptiont_handler(PEXCEPTION_POINTERS info)
default_top_level_exception_handler = SetUnhandledExceptionFilter(unhandled_exceptiont_handler); {
signal(SIGABRT, &AbortHandler); 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 #endif

View file

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

View file

@ -17,66 +17,74 @@ static void CALLBACK win_hook_event_proc(HWINEVENTHOOK winEventHook,
LONG object, LONG object,
LONG child, LONG child,
DWORD eventThread, DWORD eventThread,
DWORD eventTime) { DWORD eventTime)
std::unique_lock lock(mutex); {
hook_events.push_back({ event, std::unique_lock lock(mutex);
window, hook_events.push_back({ event,
object, window,
child, object,
eventThread, child,
eventTime }); eventThread,
lock.unlock(); eventTime });
dispatch_cv.notify_one(); lock.unlock();
dispatch_cv.notify_one();
} }
static bool running = false; static bool running = false;
static std::thread dispatch_thread; static std::thread dispatch_thread;
static void dispatch_thread_proc() { static void dispatch_thread_proc()
std::unique_lock lock(mutex); {
while (running) { std::unique_lock lock(mutex);
dispatch_cv.wait(lock, []{ return !running || !hook_events.empty(); }); while (running)
if (!running) {
return; dispatch_cv.wait(lock, [] { return !running || !hook_events.empty(); });
while (!hook_events.empty()) { if (!running)
auto event = hook_events.front(); return;
hook_events.pop_front(); while (!hook_events.empty())
lock.unlock(); {
intptr_t data = reinterpret_cast<intptr_t>(&event); auto event = hook_events.front();
intercept_system_menu_action(data); hook_events.pop_front();
powertoys_events().signal_event(win_hook_event, data); lock.unlock();
lock.lock(); intptr_t data = reinterpret_cast<intptr_t>(&event);
intercept_system_menu_action(data);
powertoys_events().signal_event(win_hook_event, data);
lock.lock();
}
} }
}
} }
static HWINEVENTHOOK hook_handle; static HWINEVENTHOOK hook_handle;
void start_win_hook_event() { void start_win_hook_event()
std::lock_guard lock(mutex); {
if (running) std::lock_guard lock(mutex);
return; if (running)
running = true; return;
dispatch_thread = std::thread(dispatch_thread_proc); running = true;
hook_handle = SetWinEventHook(EVENT_MIN, EVENT_MAX, nullptr, win_hook_event_proc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS); 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() { void stop_win_hook_event()
std::unique_lock lock(mutex); {
if (!running) std::unique_lock lock(mutex);
return; if (!running)
running = false; return;
UnhookWinEvent(hook_handle); running = false;
lock.unlock(); UnhookWinEvent(hook_handle);
dispatch_cv.notify_one(); lock.unlock();
dispatch_thread.join(); dispatch_cv.notify_one();
lock.lock(); dispatch_thread.join();
hook_events.clear(); lock.lock();
hook_events.shrink_to_fit(); hook_events.clear();
hook_events.shrink_to_fit();
} }
void intercept_system_menu_action(intptr_t data) { void intercept_system_menu_action(intptr_t data)
WinHookEvent* evt = reinterpret_cast<WinHookEvent*>(data); {
if (evt->event == EVENT_SYSTEM_MENUSTART || evt->event == EVENT_OBJECT_INVOKED) { WinHookEvent* evt = reinterpret_cast<WinHookEvent*>(data);
powertoys_events().handle_system_menu_action(*evt); if (evt->event == EVENT_SYSTEM_MENUSTART || evt->event == EVENT_OBJECT_INVOKED)
} {
powertoys_events().handle_system_menu_action(*evt);
}
} }

View file

@ -4,4 +4,3 @@
void start_win_hook_event(); void start_win_hook_event();
void stop_win_hook_event(); void stop_win_hook_event();