diff --git a/src/runner/auto_start_helper.cpp b/src/runner/auto_start_helper.cpp index fff2af98bc..ac0d56d118 100644 --- a/src/runner/auto_start_helper.cpp +++ b/src/runner/auto_start_helper.cpp @@ -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)); } - diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 3c7a4661bf..4dc1d58dd9 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -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 powertoys_to_enable; + std::unordered_set 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(); + } } - } } diff --git a/src/runner/general_settings.h b/src/runner/general_settings.h index 1cb230d6f2..d35a1125b1 100644 --- a/src/runner/general_settings.h +++ b/src/runner/general_settings.h @@ -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(); diff --git a/src/runner/lowlevel_keyboard_event.cpp b/src/runner/lowlevel_keyboard_event.cpp index 04572bfb2c..ace61df544 100644 --- a/src/runner/lowlevel_keyboard_event.cpp +++ b/src/runner/lowlevel_keyboard_event.cpp @@ -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(lParam); - event.wParam = wParam; - if (powertoys_events().signal_event(ll_keyboard, reinterpret_cast(&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(lParam); + event.wParam = wParam; + if (powertoys_events().signal_event(ll_keyboard, reinterpret_cast(&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; + } } diff --git a/src/runner/main.cpp b/src/runner/main.cpp index 9510174cae..d39bcad128 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -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 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 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; } diff --git a/src/runner/powertoy_module.cpp b/src/runner/powertoy_module.cpp index 0459fe00e9..5ed239ebce 100644 --- a/src/runner/powertoy_module.cpp +++ b/src/runner/powertoy_module.cpp @@ -3,32 +3,37 @@ #include "lowlevel_keyboard_event.h" #include -std::unordered_map& modules() { - static std::unordered_map modules; - return modules; +std::unordered_map& modules() +{ + static std::unordered_map modules; + return modules; } -PowertoyModule load_powertoy(const std::wstring& filename) { - auto handle = winrt::check_pointer(LoadLibraryW(filename.c_str())); - auto create = reinterpret_cast(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(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); } diff --git a/src/runner/powertoy_module.h b/src/runner/powertoy_module.h index acb790f6e4..0e9a9652dc 100644 --- a/src/runner/powertoy_module.h +++ b/src/runner/powertoy_module.h @@ -11,87 +11,108 @@ class PowertoyModule; #include -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 handle; - std::unique_ptr module; - std::wstring name; + std::unique_ptr handle; + std::unique_ptr module; + std::wstring name; }; PowertoyModule load_powertoy(const std::wstring& filename); diff --git a/src/runner/powertoys_events.cpp b/src/runner/powertoys_events.cpp index 00b119bedb..f5680b6b7a 100644 --- a/src/runner/powertoys_events.cpp +++ b/src/runner/powertoys_events.cpp @@ -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; } \ No newline at end of file diff --git a/src/runner/powertoys_events.h b/src/runner/powertoys_events.h index 261b5a2a17..4135bfd477 100644 --- a/src/runner/powertoys_events.h +++ b/src/runner/powertoys_events.h @@ -4,24 +4,25 @@ #include #include -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> receivers; - std::unordered_set system_menu_receivers; + std::shared_mutex mutex; + std::unordered_map> receivers; + std::unordered_set system_menu_receivers; }; PowertoysEvents& powertoys_events(); void first_subscribed(const std::wstring& event); void last_unsubscribed(const std::wstring& event); - diff --git a/src/runner/restart_elevated.cpp b/src/runner/restart_elevated.cpp index e3e224ac8a..0f05aa86a0 100644 --- a/src/runner/restart_elevated.cpp +++ b/src/runner/restart_elevated.cpp @@ -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(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(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; + } } diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 913774fa04..a923b38511 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -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{new (std::nothrow)char[size]}; + auto pproc_buffer = std::unique_ptr{ new (std::nothrow) char[size] }; auto pptal = reinterpret_cast(pproc_buffer.get()); if (!pptal) { diff --git a/src/runner/system_menu_helper.cpp b/src/runner/system_menu_helper.cpp index a5986feb8e..9df5a37d83 100644 --- a/src/runner/system_menu_helper.cpp +++ b/src/runner/system_menu_helper.cpp @@ -3,127 +3,158 @@ #include -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& 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& 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(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(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{}; } diff --git a/src/runner/system_menu_helper.h b/src/runner/system_menu_helper.h index f40a90ae32..09e4c0b931 100644 --- a/src/runner/system_menu_helper.h +++ b/src/runner/system_menu_helper.h @@ -10,33 +10,34 @@ class PowertoyModuleIface; -class SystemMenuHelper : public PowertoySystemMenuIface { +class SystemMenuHelper : public PowertoySystemMenuIface +{ public: - // PowertoySystemMenuIface - virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector& config) override; - virtual void ProcessSelectedItem(PowertoyModuleIface* module, HWND window, const wchar_t* itemName) override; + // PowertoySystemMenuIface + virtual void SetConfiguration(PowertoyModuleIface* module, const std::vector& 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> ProcessedModules{}; + // Store processed modules per window to avoid handling it multiple times. + std::unordered_map> ProcessedModules{}; - // Keep mappings form item id to the module who created it and item name for faster processing later. - std::unordered_map> IdMappings{}; + // Keep mappings form item id to the module who created it and item name for faster processing later. + std::unordered_map> IdMappings{}; - // Store configurations provided by module. - // This will be used to create custom system menu items and to handle updates. - std::unordered_map> Configurations{}; + // Store configurations provided by module. + // This will be used to create custom system menu items and to handle updates. + std::unordered_map> Configurations{}; }; SystemMenuHelper& SystemMenuHelperInstace(); diff --git a/src/runner/trace.cpp b/src/runner/trace.cpp index 774e7e07c7..4d5f902ca0 100644 --- a/src/runner/trace.cpp +++ b/src/runner/trace.cpp @@ -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)); } diff --git a/src/runner/trace.h b/src/runner/trace.h index a253f11200..66390378bd 100644 --- a/src/runner/trace.h +++ b/src/runner/trace.h @@ -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); }; diff --git a/src/runner/tray_icon.cpp b/src/runner/tray_icon.cpp index d28ddef92e..5c31412070 100644 --- a/src/runner/tray_icon.cpp +++ b/src/runner/tray_icon.cpp @@ -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(&__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(&__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(&__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(&__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); + } } diff --git a/src/runner/tray_icon.h b/src/runner/tray_icon.h index 08bcb43aa6..1180bc5e71 100644 --- a/src/runner/tray_icon.h +++ b/src/runner/tray_icon.h @@ -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); diff --git a/src/runner/unhandled_exception_handler.cpp b/src/runner/unhandled_exception_handler.cpp index b9f43dbef9..ade3945df6 100644 --- a/src/runner/unhandled_exception_handler.cpp +++ b/src/runner/unhandled_exception_handler.cpp @@ -1,5 +1,5 @@ #include "pch.h" -#if _DEBUG && _WIN64 +#if _DEBUG && _WIN64 #include "unhandled_exception_handler.h" #include #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 diff --git a/src/runner/unhandled_exception_handler.h b/src/runner/unhandled_exception_handler.h index 71d3831f57..2aaf07b099 100644 --- a/src/runner/unhandled_exception_handler.h +++ b/src/runner/unhandled_exception_handler.h @@ -1,4 +1,4 @@ #pragma once -#if _DEBUG && _WIN64 +#if _DEBUG && _WIN64 void init_global_error_handlers(); #endif diff --git a/src/runner/win_hook_event.cpp b/src/runner/win_hook_event.cpp index 65c7e46730..7924027127 100644 --- a/src/runner/win_hook_event.cpp +++ b/src/runner/win_hook_event.cpp @@ -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(&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(&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(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(data); + if (evt->event == EVENT_SYSTEM_MENUSTART || evt->event == EVENT_OBJECT_INVOKED) + { + powertoys_events().handle_system_menu_action(*evt); + } } diff --git a/src/runner/win_hook_event.h b/src/runner/win_hook_event.h index 9d88898b6e..24bff1a0be 100644 --- a/src/runner/win_hook_event.h +++ b/src/runner/win_hook_event.h @@ -4,4 +4,3 @@ void start_win_hook_event(); void stop_win_hook_event(); -