mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 14:41:21 +08:00
Coding style (runner) (#1013)
This commit is contained in:
parent
9708961654
commit
415a0cdf28
@ -12,351 +12,390 @@
|
||||
|
||||
// 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; }
|
||||
|
||||
#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;
|
||||
|
||||
bool enable_auto_start_task_for_this_user() {
|
||||
HRESULT hr = S_OK;
|
||||
WCHAR username_domain[USERNAME_DOMAIN_LEN];
|
||||
WCHAR username[USERNAME_LEN];
|
||||
|
||||
WCHAR username_domain[USERNAME_DOMAIN_LEN];
|
||||
WCHAR username[USERNAME_LEN];
|
||||
std::wstring wstrTaskName;
|
||||
|
||||
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;
|
||||
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);
|
||||
// ------------------------------------------------------
|
||||
// Get the Domain/Username for the trigger.
|
||||
if (!GetEnvironmentVariable(L"USERNAME", username, USERNAME_LEN))
|
||||
{
|
||||
ExitWithLastError(hr, "Getting username failed: %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();
|
||||
}
|
||||
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);
|
||||
|
||||
// Create the task builder object to create the task.
|
||||
hr = pService->NewTask(0, &pTask);
|
||||
ExitOnFailure(hr, "Failed to create a task definition: %x", hr);
|
||||
// Task Name.
|
||||
wstrTaskName = L"Autorun for ";
|
||||
wstrTaskName += username;
|
||||
|
||||
// ------------------------------------------------------
|
||||
// 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);
|
||||
// Get the executable path passed to the custom action.
|
||||
WCHAR wszExecutablePath[MAX_PATH];
|
||||
GetModuleFileName(NULL, wszExecutablePath, MAX_PATH);
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Create the settings for the task
|
||||
hr = pTask->get_Settings(&pSettings);
|
||||
ExitOnFailure(hr, "Cannot get settings pointer: %x", hr);
|
||||
// ------------------------------------------------------
|
||||
// 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);
|
||||
|
||||
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);
|
||||
// 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 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);
|
||||
// ------------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
// 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();
|
||||
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));
|
||||
return (SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
bool disable_auto_start_task_for_this_user() {
|
||||
HRESULT hr = S_OK;
|
||||
bool disable_auto_start_task_for_this_user()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
WCHAR username[USERNAME_LEN];
|
||||
std::wstring wstrTaskName;
|
||||
WCHAR username[USERNAME_LEN];
|
||||
std::wstring wstrTaskName;
|
||||
|
||||
ITaskService *pService = NULL;
|
||||
ITaskFolder *pTaskFolder = NULL;
|
||||
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();
|
||||
}
|
||||
// ------------------------------------------------------
|
||||
// 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();
|
||||
if (pService)
|
||||
pService->Release();
|
||||
if (pTaskFolder)
|
||||
pTaskFolder->Release();
|
||||
|
||||
return(SUCCEEDED(hr));
|
||||
return (SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
bool is_auto_start_task_active_for_this_user(){
|
||||
HRESULT hr = S_OK;
|
||||
bool is_auto_start_task_active_for_this_user()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
WCHAR username[USERNAME_LEN];
|
||||
std::wstring wstrTaskName;
|
||||
WCHAR username[USERNAME_LEN];
|
||||
std::wstring wstrTaskName;
|
||||
|
||||
ITaskService *pService = NULL;
|
||||
ITaskFolder *pTaskFolder = NULL;
|
||||
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();
|
||||
}
|
||||
// ------------------------------------------------------
|
||||
// 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));
|
||||
if (pService)
|
||||
pService->Release();
|
||||
if (pTaskFolder)
|
||||
pTaskFolder->Release();
|
||||
|
||||
return (SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
|
@ -10,110 +10,139 @@
|
||||
static std::wstring settings_theme = L"system";
|
||||
static bool run_as_elevated = false;
|
||||
|
||||
json::JsonObject load_general_settings() {
|
||||
auto loaded = PTSettingsHelper::load_general_settings();
|
||||
settings_theme = loaded.GetNamedString(L"theme", L"system");
|
||||
if (settings_theme != L"dark" && settings_theme != L"light") {
|
||||
settings_theme = L"system";
|
||||
}
|
||||
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false);
|
||||
return loaded;
|
||||
json::JsonObject load_general_settings()
|
||||
{
|
||||
auto loaded = PTSettingsHelper::load_general_settings();
|
||||
settings_theme = loaded.GetNamedString(L"theme", L"system");
|
||||
if (settings_theme != L"dark" && settings_theme != L"light")
|
||||
{
|
||||
settings_theme = L"system";
|
||||
}
|
||||
run_as_elevated = loaded.GetNamedBoolean(L"run_elevated", false);
|
||||
return loaded;
|
||||
}
|
||||
|
||||
json::JsonObject get_general_settings() {
|
||||
json::JsonObject result;
|
||||
const bool startup = is_auto_start_task_active_for_this_user();
|
||||
result.SetNamedValue(L"startup", json::value(startup));
|
||||
json::JsonObject get_general_settings()
|
||||
{
|
||||
json::JsonObject result;
|
||||
const bool startup = is_auto_start_task_active_for_this_user();
|
||||
result.SetNamedValue(L"startup", json::value(startup));
|
||||
|
||||
json::JsonObject enabled;
|
||||
for (auto&[name, powertoy] : modules()) {
|
||||
enabled.SetNamedValue(name, json::value(powertoy.is_enabled()));
|
||||
}
|
||||
result.SetNamedValue(L"enabled", std::move(enabled));
|
||||
json::JsonObject enabled;
|
||||
for (auto& [name, powertoy] : modules())
|
||||
{
|
||||
enabled.SetNamedValue(name, json::value(powertoy.is_enabled()));
|
||||
}
|
||||
result.SetNamedValue(L"enabled", std::move(enabled));
|
||||
|
||||
bool is_elevated = is_process_elevated();
|
||||
result.SetNamedValue(L"is_elevated", json::value(is_elevated));
|
||||
result.SetNamedValue(L"run_elevated", json::value(run_as_elevated));
|
||||
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"powertoys_version", json::value(get_product_version()));
|
||||
return result;
|
||||
bool is_elevated = is_process_elevated();
|
||||
result.SetNamedValue(L"is_elevated", json::value(is_elevated));
|
||||
result.SetNamedValue(L"run_elevated", json::value(run_as_elevated));
|
||||
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"powertoys_version", json::value(get_product_version()));
|
||||
return result;
|
||||
}
|
||||
|
||||
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");
|
||||
const 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();
|
||||
}
|
||||
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");
|
||||
const 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (json::has(general_configs, L"enabled")) {
|
||||
for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled")) {
|
||||
const auto value = enabled_element.Value();
|
||||
if (value.ValueType() != json::JsonValueType::Boolean) {
|
||||
continue;
|
||||
}
|
||||
const std::wstring name{enabled_element.Key().c_str()};
|
||||
const bool found = modules().find(name) != modules().end();
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
const bool module_inst_enabled = modules().at(name).is_enabled();
|
||||
const bool target_enabled = value.GetBoolean();
|
||||
if (module_inst_enabled == target_enabled) {
|
||||
continue;
|
||||
}
|
||||
if (target_enabled) {
|
||||
modules().at(name).enable();
|
||||
} else {
|
||||
modules().at(name).disable();
|
||||
}
|
||||
if (json::has(general_configs, L"enabled"))
|
||||
{
|
||||
for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled"))
|
||||
{
|
||||
const auto value = enabled_element.Value();
|
||||
if (value.ValueType() != json::JsonValueType::Boolean)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const std::wstring name{ enabled_element.Key().c_str() };
|
||||
const bool found = modules().find(name) != modules().end();
|
||||
if (!found)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const bool module_inst_enabled = modules().at(name).is_enabled();
|
||||
const bool target_enabled = value.GetBoolean();
|
||||
if (module_inst_enabled == target_enabled)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (target_enabled)
|
||||
{
|
||||
modules().at(name).enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
modules().at(name).disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
|
||||
if (json::has(general_configs, L"theme", json::JsonValueType::String)) {
|
||||
settings_theme = general_configs.GetNamedString(L"theme");
|
||||
}
|
||||
json::JsonObject save_settings = get_general_settings();
|
||||
PTSettingsHelper::save_general_settings(save_settings);
|
||||
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
|
||||
if (json::has(general_configs, L"theme", json::JsonValueType::String))
|
||||
{
|
||||
settings_theme = general_configs.GetNamedString(L"theme");
|
||||
}
|
||||
json::JsonObject save_settings = get_general_settings();
|
||||
PTSettingsHelper::save_general_settings(save_settings);
|
||||
}
|
||||
|
||||
void start_initial_powertoys() {
|
||||
bool only_enable_some_powertoys = false;
|
||||
void start_initial_powertoys()
|
||||
{
|
||||
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;
|
||||
try {
|
||||
general_settings = load_general_settings();
|
||||
json::JsonObject enabled = general_settings.GetNamedObject(L"enabled");
|
||||
for (const auto & enabled_element : enabled) {
|
||||
if (enabled_element.Value().GetBoolean()) {
|
||||
// Enable this powertoy.
|
||||
powertoys_to_enable.emplace(enabled_element.Key());
|
||||
}
|
||||
json::JsonObject general_settings;
|
||||
try
|
||||
{
|
||||
general_settings = load_general_settings();
|
||||
json::JsonObject enabled = general_settings.GetNamedObject(L"enabled");
|
||||
for (const auto& enabled_element : enabled)
|
||||
{
|
||||
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 (...) {
|
||||
// Couldn't read the general settings correctly.
|
||||
// Load all powertoys.
|
||||
// TODO: notify user about invalid json config
|
||||
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();
|
||||
catch (...)
|
||||
{
|
||||
// Couldn't read the general settings correctly.
|
||||
// Load all powertoys.
|
||||
// TODO: notify user about invalid json config
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,5 +4,5 @@
|
||||
|
||||
json::JsonObject load_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();
|
||||
|
@ -2,44 +2,54 @@
|
||||
#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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
return CallNextHookEx(hook_handle_copy, nCode, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent system-wide input lagging while paused in the debugger
|
||||
//#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(IsDebuggerPresent()) {
|
||||
return;
|
||||
}
|
||||
if (IsDebuggerPresent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
void stop_lowlevel_keyboard_hook()
|
||||
{
|
||||
if (hook_handle)
|
||||
{
|
||||
UnhookWindowsHookEx(hook_handle);
|
||||
hook_handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -19,107 +19,125 @@
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
|
||||
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());
|
||||
}
|
||||
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 runner() {
|
||||
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();
|
||||
int runner()
|
||||
{
|
||||
DPIAware::EnableDPIAwarenessForThisProcess();
|
||||
|
||||
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();
|
||||
} 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;
|
||||
Trace::EventLaunch(get_product_version());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
int result = 0;
|
||||
try {
|
||||
// Singletons initialization order needs to be preserved, first events and
|
||||
// then modules to guarantee the reverse destruction order.
|
||||
SystemMenuHelperInstace();
|
||||
powertoys_events();
|
||||
modules();
|
||||
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;
|
||||
}
|
||||
int result = 0;
|
||||
try
|
||||
{
|
||||
// Singletons initialization order needs to be preserved, first events and
|
||||
// then modules to guarantee the reverse destruction order.
|
||||
SystemMenuHelperInstace();
|
||||
powertoys_events();
|
||||
modules();
|
||||
|
||||
auto general_settings = load_general_settings();
|
||||
int rvalue = 0;
|
||||
if (is_process_elevated() ||
|
||||
general_settings.GetNamedBoolean(L"run_elevated", false) == false ||
|
||||
strcmp(lpCmdLine, "--dont-elevate") == 0) {
|
||||
result = runner();
|
||||
auto general_settings = load_general_settings();
|
||||
int rvalue = 0;
|
||||
if (is_process_elevated() ||
|
||||
general_settings.GetNamedBoolean(L"run_elevated", false) == false ||
|
||||
strcmp(lpCmdLine, "--dont-elevate") == 0)
|
||||
{
|
||||
result = runner();
|
||||
}
|
||||
else
|
||||
{
|
||||
schedule_restart_as_elevated();
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
schedule_restart_as_elevated();
|
||||
result = 0;
|
||||
catch (std::runtime_error& err)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error & err) {
|
||||
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);
|
||||
CloseHandle(runner_mutex);
|
||||
if (is_restart_scheduled()) {
|
||||
if (restart_if_scheduled() == false) {
|
||||
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;
|
||||
ReleaseMutex(runner_mutex);
|
||||
CloseHandle(runner_mutex);
|
||||
if (is_restart_scheduled())
|
||||
{
|
||||
if (restart_if_scheduled() == false)
|
||||
{
|
||||
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();
|
||||
return result;
|
||||
stop_tray_icon();
|
||||
return result;
|
||||
}
|
||||
|
@ -3,32 +3,37 @@
|
||||
#include "lowlevel_keyboard_event.h"
|
||||
#include <algorithm>
|
||||
|
||||
std::unordered_map<std::wstring, PowertoyModule>& modules() {
|
||||
static std::unordered_map<std::wstring, PowertoyModule> modules;
|
||||
return modules;
|
||||
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();
|
||||
}
|
||||
module->register_system_menu_helper(&SystemMenuHelperInstace());
|
||||
return PowertoyModule(module, handle);
|
||||
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();
|
||||
}
|
||||
module->register_system_menu_helper(&SystemMenuHelperInstace());
|
||||
return PowertoyModule(module, handle);
|
||||
}
|
||||
|
||||
json::JsonObject PowertoyModule::json_config() const {
|
||||
int size = 0;
|
||||
module->get_config(nullptr, &size);
|
||||
std::wstring result;
|
||||
result.resize(size - 1);
|
||||
module->get_config(result.data(), &size);
|
||||
return json::JsonObject::Parse(result);
|
||||
json::JsonObject PowertoyModule::json_config() const
|
||||
{
|
||||
int size = 0;
|
||||
module->get_config(nullptr, &size);
|
||||
std::wstring result;
|
||||
result.resize(size - 1);
|
||||
module->get_config(result.data(), &size);
|
||||
return json::JsonObject::Parse(result);
|
||||
}
|
||||
|
@ -11,87 +11,108 @@
|
||||
class PowertoyModule;
|
||||
#include <common/json.h>
|
||||
|
||||
struct PowertoyModuleDeleter {
|
||||
void operator()(PowertoyModuleIface* module) const {
|
||||
if (module) {
|
||||
powertoys_events().unregister_system_menu_action(module);
|
||||
powertoys_events().unregister_receiver(module);
|
||||
module->destroy();
|
||||
struct PowertoyModuleDeleter
|
||||
{
|
||||
void operator()(PowertoyModuleIface* module) const
|
||||
{
|
||||
if (module)
|
||||
{
|
||||
powertoys_events().unregister_system_menu_action(module);
|
||||
powertoys_events().unregister_receiver(module);
|
||||
module->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct PowertoyModuleDLLDeleter {
|
||||
using pointer = HMODULE;
|
||||
void operator()(HMODULE handle) const {
|
||||
FreeLibrary(handle);
|
||||
}
|
||||
struct PowertoyModuleDLLDeleter
|
||||
{
|
||||
using pointer = HMODULE;
|
||||
void operator()(HMODULE handle) const
|
||||
{
|
||||
FreeLibrary(handle);
|
||||
}
|
||||
};
|
||||
|
||||
class PowertoyModule {
|
||||
class PowertoyModule
|
||||
{
|
||||
public:
|
||||
PowertoyModule(PowertoyModuleIface* module, HMODULE handle) : handle(handle), module(module) {
|
||||
if (!module) {
|
||||
throw std::runtime_error("Module not initialized");
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (SystemMenuHelperInstace().HasCustomConfig(module))
|
||||
{
|
||||
powertoys_events().register_system_menu_action(module);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
return name;
|
||||
}
|
||||
|
||||
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);
|
||||
void set_config(const std::wstring& config)
|
||||
{
|
||||
module->set_config(config.c_str());
|
||||
}
|
||||
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);
|
||||
}
|
||||
void call_custom_action(const std::wstring& action)
|
||||
{
|
||||
module->call_custom_action(action.c_str());
|
||||
}
|
||||
|
||||
bool is_enabled() {
|
||||
return module->is_enabled();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
module->enable();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
module->disable();
|
||||
}
|
||||
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;
|
||||
std::unique_ptr<HMODULE, PowertoyModuleDLLDeleter> handle;
|
||||
std::unique_ptr<PowertoyModuleIface, PowertoyModuleDeleter> module;
|
||||
std::wstring name;
|
||||
};
|
||||
|
||||
PowertoyModule load_powertoy(const std::wstring& filename);
|
||||
|
@ -4,83 +4,102 @@
|
||||
#include "win_hook_event.h"
|
||||
#include "system_menu_helper.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 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();
|
||||
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;
|
||||
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);
|
||||
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::register_system_menu_action(PowertoyModuleIface* module) {
|
||||
std::unique_lock lock(mutex);
|
||||
system_menu_receivers.insert(module);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
void PowertoysEvents::register_system_menu_action(PowertoyModuleIface* module)
|
||||
{
|
||||
std::unique_lock lock(mutex);
|
||||
system_menu_receivers.insert(module);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
@ -4,24 +4,25 @@
|
||||
#include <interface/win_hook_event_data.h>
|
||||
#include <string>
|
||||
|
||||
class PowertoysEvents {
|
||||
class PowertoysEvents
|
||||
{
|
||||
public:
|
||||
void register_receiver(const std::wstring& event, PowertoyModuleIface* module);
|
||||
void unregister_receiver(PowertoyModuleIface* module);
|
||||
void register_receiver(const std::wstring& event, PowertoyModuleIface* module);
|
||||
void unregister_receiver(PowertoyModuleIface* module);
|
||||
|
||||
void register_system_menu_action(PowertoyModuleIface* module);
|
||||
void unregister_system_menu_action(PowertoyModuleIface* module);
|
||||
void handle_system_menu_action(const WinHookEvent& data);
|
||||
void register_system_menu_action(PowertoyModuleIface* module);
|
||||
void unregister_system_menu_action(PowertoyModuleIface* module);
|
||||
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:
|
||||
std::shared_mutex mutex;
|
||||
std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers;
|
||||
std::unordered_set<PowertoyModuleIface*> system_menu_receivers;
|
||||
std::shared_mutex mutex;
|
||||
std::unordered_map<std::wstring, std::vector<PowertoyModuleIface*>> receivers;
|
||||
std::unordered_set<PowertoyModuleIface*> system_menu_receivers;
|
||||
};
|
||||
|
||||
PowertoysEvents& powertoys_events();
|
||||
|
||||
void first_subscribed(const std::wstring& event);
|
||||
void last_unsubscribed(const std::wstring& event);
|
||||
|
||||
|
@ -2,36 +2,42 @@
|
||||
#include "restart_elevated.h"
|
||||
#include "common/common.h"
|
||||
|
||||
enum State {
|
||||
None,
|
||||
RestartAsElevated,
|
||||
RestartAsNonElevated
|
||||
enum State
|
||||
{
|
||||
None,
|
||||
RestartAsElevated,
|
||||
RestartAsNonElevated
|
||||
};
|
||||
static State state = None;
|
||||
|
||||
void schedule_restart_as_elevated() {
|
||||
state = RestartAsElevated;
|
||||
void schedule_restart_as_elevated()
|
||||
{
|
||||
state = RestartAsElevated;
|
||||
}
|
||||
|
||||
void schedule_restart_as_non_elevated() {
|
||||
state = RestartAsNonElevated;
|
||||
void schedule_restart_as_non_elevated()
|
||||
{
|
||||
state = RestartAsNonElevated;
|
||||
}
|
||||
|
||||
bool is_restart_scheduled() {
|
||||
return state != None;
|
||||
bool is_restart_scheduled()
|
||||
{
|
||||
return state != None;
|
||||
}
|
||||
|
||||
bool restart_if_scheduled() {
|
||||
// Make sure we have enough room, even for the long (\\?\) paths
|
||||
constexpr DWORD exe_path_size = 0xFFFF;
|
||||
auto exe_path = std::make_unique<wchar_t[]>(exe_path_size);
|
||||
GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size);
|
||||
switch (state) {
|
||||
case RestartAsElevated:
|
||||
return run_elevated(exe_path.get(), {});
|
||||
case RestartAsNonElevated:
|
||||
return run_non_elevated(exe_path.get(), L"--dont-elevate");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
bool restart_if_scheduled()
|
||||
{
|
||||
// Make sure we have enough room, even for the long (\\?\) paths
|
||||
constexpr DWORD exe_path_size = 0xFFFF;
|
||||
auto exe_path = std::make_unique<wchar_t[]>(exe_path_size);
|
||||
GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size);
|
||||
switch (state)
|
||||
{
|
||||
case RestartAsElevated:
|
||||
return run_elevated(exe_path.get(), {});
|
||||
case RestartAsNonElevated:
|
||||
return run_non_elevated(exe_path.get(), L"--dont-elevate");
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ void dispatch_json_action_to_module(const json::JsonObject& powertoys_configs)
|
||||
else if (modules().find(name) != modules().end())
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
@ -166,7 +165,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args,
|
||||
|
||||
SIZE_T size = 0;
|
||||
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());
|
||||
if (!pptal)
|
||||
{
|
||||
|
@ -3,127 +3,158 @@
|
||||
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
|
||||
namespace {
|
||||
constexpr int KSeparatorPos = 1;
|
||||
constexpr int KNewItemPos = 2;
|
||||
namespace
|
||||
{
|
||||
constexpr int KSeparatorPos = 1;
|
||||
constexpr int KNewItemPos = 2;
|
||||
|
||||
unsigned int GenerateItemId() {
|
||||
static unsigned int generator = 0x70777479;
|
||||
return ++generator;
|
||||
}
|
||||
unsigned int GenerateItemId()
|
||||
{
|
||||
static unsigned int generator = 0x70777479;
|
||||
return ++generator;
|
||||
}
|
||||
}
|
||||
|
||||
SystemMenuHelper& SystemMenuHelperInstace() {
|
||||
static SystemMenuHelper instance;
|
||||
return instance;
|
||||
SystemMenuHelper& SystemMenuHelperInstace()
|
||||
{
|
||||
static SystemMenuHelper instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void SystemMenuHelper::SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) {
|
||||
Reset(module);
|
||||
Configurations[module] = config;
|
||||
for (auto& [window, modules] : ProcessedModules) {
|
||||
// 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::SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config)
|
||||
{
|
||||
Reset(module);
|
||||
Configurations[module] = config;
|
||||
for (auto& [window, modules] : ProcessedModules)
|
||||
{
|
||||
// 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) {
|
||||
for (const auto& item : Configurations[module]) {
|
||||
if (itemName == item.name && item.checkBox) {
|
||||
// Handle check/uncheck action only if specified by module configuration.
|
||||
for (const auto& [id, data] : IdMappings) {
|
||||
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;
|
||||
void SystemMenuHelper::ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName)
|
||||
{
|
||||
for (const auto& item : Configurations[module])
|
||||
{
|
||||
if (itemName == item.name && item.checkBox)
|
||||
{
|
||||
// Handle check/uncheck action only if specified by module configuration.
|
||||
for (const auto& [id, data] : IdMappings)
|
||||
{
|
||||
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) {
|
||||
auto& modules = ProcessedModules[window];
|
||||
for (const auto& m : modules) {
|
||||
if (module == m) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
AddSeparator(module, window);
|
||||
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);
|
||||
bool SystemMenuHelper::Customize(PowertoyModuleIface* module, HWND window)
|
||||
{
|
||||
auto& modules = ProcessedModules[window];
|
||||
for (const auto& m : modules)
|
||||
{
|
||||
if (module == m)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SystemMenuHelper::HasCustomConfig(PowertoyModuleIface* module) {
|
||||
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;
|
||||
AddSeparator(module, window);
|
||||
for (const auto& info : Configurations[module])
|
||||
{
|
||||
AddItem(module, window, info.name, info.enable);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
modules.push_back(module);
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
PowertoyModuleIface* SystemMenuHelper::ModuleFromItemId(const int& id) {
|
||||
auto it = IdMappings.find(id);
|
||||
if (it != IdMappings.end()) {
|
||||
return it->second.first;
|
||||
}
|
||||
return nullptr;
|
||||
bool SystemMenuHelper::HasCustomConfig(PowertoyModuleIface* module)
|
||||
{
|
||||
return Configurations.find(module) != Configurations.end();
|
||||
}
|
||||
|
||||
const std::wstring SystemMenuHelper::ItemNameFromItemId(const int& id) {
|
||||
auto itemIt = IdMappings.find(id);
|
||||
if (itemIt != IdMappings.end()) {
|
||||
return itemIt->second.second;
|
||||
}
|
||||
return std::wstring{};
|
||||
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;
|
||||
}
|
||||
}
|
||||
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{};
|
||||
}
|
||||
|
@ -10,33 +10,34 @@
|
||||
|
||||
class PowertoyModuleIface;
|
||||
|
||||
class SystemMenuHelper : public PowertoySystemMenuIface {
|
||||
class SystemMenuHelper : public PowertoySystemMenuIface
|
||||
{
|
||||
public:
|
||||
// PowertoySystemMenuIface
|
||||
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) override;
|
||||
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override;
|
||||
// PowertoySystemMenuIface
|
||||
virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector<ItemInfo>& config) override;
|
||||
virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override;
|
||||
|
||||
bool Customize(PowertoyModuleIface* module, HWND window);
|
||||
void Reset(PowertoyModuleIface* module);
|
||||
bool Customize(PowertoyModuleIface* module, HWND window);
|
||||
void Reset(PowertoyModuleIface* module);
|
||||
|
||||
bool HasCustomConfig(PowertoyModuleIface* module);
|
||||
bool HasCustomConfig(PowertoyModuleIface* module);
|
||||
|
||||
PowertoyModuleIface* ModuleFromItemId(const int& id);
|
||||
const std::wstring ItemNameFromItemId(const int& id);
|
||||
PowertoyModuleIface* ModuleFromItemId(const int& id);
|
||||
const std::wstring ItemNameFromItemId(const int& id);
|
||||
|
||||
private:
|
||||
bool AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable);
|
||||
bool AddSeparator(PowertoyModuleIface* module, HWND window);
|
||||
bool AddItem(PowertoyModuleIface* module, HWND window, const std::wstring& name, const bool enable);
|
||||
bool AddSeparator(PowertoyModuleIface* module, HWND window);
|
||||
|
||||
// Store processed modules per window to avoid handling it multiple times.
|
||||
std::unordered_map<HWND, std::vector<PowertoyModuleIface*>> ProcessedModules{};
|
||||
// Store processed modules per window to avoid handling it multiple times.
|
||||
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.
|
||||
std::unordered_map<int, std::pair<PowertoyModuleIface*, std::wstring>> IdMappings{};
|
||||
// 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{};
|
||||
|
||||
// Store configurations provided by module.
|
||||
// This will be used to create custom system menu items and to handle updates.
|
||||
std::unordered_map<PowertoyModuleIface*, std::vector<ItemInfo>> Configurations{};
|
||||
// Store configurations provided by module.
|
||||
// This will be used to create custom system menu items and to handle updates.
|
||||
std::unordered_map<PowertoyModuleIface*, std::vector<ItemInfo>> Configurations{};
|
||||
};
|
||||
|
||||
SystemMenuHelper& SystemMenuHelperInstace();
|
||||
|
@ -2,26 +2,29 @@
|
||||
#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(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));
|
||||
"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,
|
||||
"Runner_Launch",
|
||||
TraceLoggingWideString(versionNumber.c_str(), "Version"),
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
class Trace {
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void EventLaunch(const std::wstring& versionNumber);
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void EventLaunch(const std::wstring& versionNumber);
|
||||
};
|
||||
|
@ -7,177 +7,199 @@
|
||||
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
namespace {
|
||||
HWND tray_icon_hwnd = NULL;
|
||||
namespace
|
||||
{
|
||||
HWND tray_icon_hwnd = NULL;
|
||||
|
||||
// Message code that Windows will use for tray icon notifications.
|
||||
UINT wm_icon_notify = 0;
|
||||
// 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;
|
||||
// Contains the Windows Message for taskbar creation.
|
||||
UINT wm_taskbar_restart = 0;
|
||||
UINT wm_run_on_main_ui_thread = 0;
|
||||
|
||||
NOTIFYICONDATAW tray_icon_data;
|
||||
bool tray_icon_created = false;
|
||||
NOTIFYICONDATAW tray_icon_data;
|
||||
bool tray_icon_created = false;
|
||||
|
||||
bool about_box_shown = false;
|
||||
bool about_box_shown = false;
|
||||
|
||||
HMENU h_menu = nullptr;
|
||||
HMENU h_sub_menu = nullptr;
|
||||
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;
|
||||
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;
|
||||
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);
|
||||
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) {
|
||||
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:
|
||||
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);
|
||||
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:
|
||||
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 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;
|
||||
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;
|
||||
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;
|
||||
// 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;
|
||||
}
|
||||
default:
|
||||
if (message == wm_icon_notify) {
|
||||
switch(lparam) {
|
||||
case WM_LBUTTONUP:
|
||||
default:
|
||||
if (message == wm_icon_notify)
|
||||
{
|
||||
open_settings_window();
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
case WM_RBUTTONUP:
|
||||
case WM_CONTEXTMENU:
|
||||
else if (message == wm_run_on_main_ui_thread)
|
||||
{
|
||||
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);
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
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");
|
||||
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);
|
||||
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;
|
||||
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;
|
||||
|
||||
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() {
|
||||
if (tray_icon_created) {
|
||||
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
void stop_tray_icon()
|
||||
{
|
||||
if (tray_icon_created)
|
||||
{
|
||||
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,6 @@ void stop_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);
|
||||
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);
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "pch.h"
|
||||
#if _DEBUG && _WIN64
|
||||
#if _DEBUG && _WIN64
|
||||
#include "unhandled_exception_handler.h"
|
||||
#include <DbgHelp.h>
|
||||
#pragma comment(lib, "DbgHelp.lib")
|
||||
@ -13,122 +13,158 @@ 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);
|
||||
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";
|
||||
}
|
||||
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;
|
||||
void init_symbols()
|
||||
{
|
||||
SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
|
||||
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
||||
auto process = GetCurrentProcess();
|
||||
SymInitialize(process, NULL, TRUE);
|
||||
}
|
||||
|
||||
extern "C" void AbortHandler(int signal_number) {
|
||||
init_symbols();
|
||||
std::wstring ex_description = L"SIGABRT was raised.";
|
||||
log_stack_trace(ex_description);
|
||||
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);
|
||||
}
|
||||
|
||||
void init_global_error_handlers() {
|
||||
default_top_level_exception_handler = SetUnhandledExceptionFilter(unhandled_exceptiont_handler);
|
||||
signal(SIGABRT, &AbortHandler);
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#if _DEBUG && _WIN64
|
||||
#if _DEBUG && _WIN64
|
||||
void init_global_error_handlers();
|
||||
#endif
|
||||
|
@ -17,66 +17,74 @@ static void CALLBACK win_hook_event_proc(HWINEVENTHOOK winEventHook,
|
||||
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();
|
||||
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();
|
||||
intptr_t data = reinterpret_cast<intptr_t>(&event);
|
||||
intercept_system_menu_action(data);
|
||||
powertoys_events().signal_event(win_hook_event, data);
|
||||
lock.lock();
|
||||
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();
|
||||
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;
|
||||
|
||||
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 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();
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
powertoys_events().handle_system_menu_action(*evt);
|
||||
}
|
||||
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)
|
||||
{
|
||||
powertoys_events().handle_system_menu_action(*evt);
|
||||
}
|
||||
}
|
||||
|
@ -4,4 +4,3 @@
|
||||
|
||||
void start_win_hook_event();
|
||||
void stop_win_hook_event();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user