diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 34fb32bd4b..853a1ac795 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -112,7 +112,6 @@ - @@ -132,13 +131,9 @@ - - - NOT Installed and CREATESCHEDULEDTASK = 1 - NOT Installed @@ -196,10 +191,6 @@ BinaryKey="PTCustomActions" DllEntry="TerminateProcessesCA" /> - - @@ -232,15 +223,6 @@ Property="DeleteDotnetRuntimeHardlinks" Value="[INSTALLFOLDER]" /> - - - action) +{ + HRESULT hr = S_OK; + HANDLE hUserToken = NULL; + DWORD dwSessionId; + ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId); + auto rv = WTSQueryUserToken(dwSessionId, &hUserToken); + + if (rv == 0) + { + hr = E_ABORT; + ExitOnFailure(hr, "Failed to query user token"); + } + + HANDLE hUserTokenDup; + if (DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation, TOKEN_TYPE::TokenPrimary, &hUserTokenDup) == 0) + { + CloseHandle(hUserToken); + CloseHandle(hUserTokenDup); + hr = E_ABORT; + ExitOnFailure(hr, "Failed to duplicate user token"); + } + + if (ImpersonateLoggedOnUser(hUserTokenDup)) + { + if (!action(hUserTokenDup)) + { + hr = E_ABORT; + ExitOnFailure(hr, "Failed to execute action"); + } + + RevertToSelf(); + CloseHandle(hUserToken); + CloseHandle(hUserTokenDup); + } + else + { + hr = E_ABORT; + ExitOnFailure(hr, "Failed to duplicate user token"); + } + +LExit: + return SUCCEEDED(hr); +} + UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) { HRESULT hr = S_OK; @@ -117,34 +162,15 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) if (isSystemUser) { - HANDLE hUserToken = NULL; - DWORD dwSessionId; - ProcessIdToSessionId(GetCurrentProcessId(), &dwSessionId); - auto rv = WTSQueryUserToken(dwSessionId, &hUserToken); - - if (rv == 0) - { - ExitOnFailure(hr, "Failed to query user token"); - } - - HANDLE hUserTokenDup; - if (DuplicateTokenEx(hUserToken, TOKEN_ALL_ACCESS, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityImpersonation, TOKEN_TYPE::TokenPrimary, &hUserTokenDup) == 0) - { - CloseHandle(hUserToken); - CloseHandle(hUserTokenDup); - ExitOnFailure(hr, "Failed to duplicate user token"); - } - - if (ImpersonateLoggedOnUser(hUserTokenDup)) - { + auto action = [&commandLine](HANDLE userToken) { STARTUPINFO startupInfo{ .cb = sizeof(STARTUPINFO), .wShowWindow = SW_SHOWNORMAL }; PROCESS_INFORMATION processInformation; PVOID lpEnvironment = NULL; - CreateEnvironmentBlock(&lpEnvironment, hUserTokenDup, FALSE); + CreateEnvironmentBlock(&lpEnvironment, userToken, FALSE); CreateProcessAsUser( - hUserTokenDup, + userToken, NULL, commandLine.data(), NULL, @@ -158,20 +184,20 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) if (!CloseHandle(processInformation.hProcess)) { - er = ERROR_INSTALL_FAILURE; + return false; } if (!CloseHandle(processInformation.hThread)) { - er = ERROR_INSTALL_FAILURE; + return false; } - RevertToSelf(); - CloseHandle(hUserToken); - CloseHandle(hUserTokenDup); - } - else + return true; + }; + + if (!ImpersonateLoggedInUserAndDoSomething(action)) { - ExitOnFailure(hr, "Failed to duplicate user token"); + hr = E_ABORT; + ExitOnFailure(hr, "ImpersonateLoggedInUserAndDoSomething failed"); } } else @@ -195,10 +221,12 @@ UINT __stdcall LaunchPowerToysCA(MSIHANDLE hInstall) if (!CloseHandle(processInformation.hProcess)) { + hr = E_ABORT; ExitOnFailure(hr, "Failed to close process handle"); } if (!CloseHandle(processInformation.hThread)) { + hr = E_ABORT; ExitOnFailure(hr, "Failed to close thread handle"); } } @@ -411,7 +439,7 @@ UINT __stdcall UninstallServicesCA(MSIHANDLE hInstall) { HRESULT hr = S_OK; UINT er = ERROR_SUCCESS; - hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); + hr = WcaInitialize(hInstall, "UninstallServicesCA"); ExitOnFailure(hr, "Failed to initialize"); @@ -422,278 +450,6 @@ LExit: return WcaFinalize(er); } - -// Creates a Scheduled Task to run at logon for the current user. -// The path of the executable to run should be passed as the CustomActionData (Value). -// Based on the Task Scheduler Logon Trigger Example: -// https://learn.microsoft.com/windows/win32/taskschd/logon-trigger-example--c---/ -UINT __stdcall CreateScheduledTaskCA(MSIHANDLE hInstall) -{ - HRESULT hr = S_OK; - UINT er = ERROR_SUCCESS; - - TCHAR username_domain[USERNAME_DOMAIN_LEN]; - TCHAR username[USERNAME_LEN]; - - std::wstring wstrTaskName; - - ITaskService* pService = nullptr; - ITaskFolder* pTaskFolder = nullptr; - ITaskDefinition* pTask = nullptr; - IRegistrationInfo* pRegInfo = nullptr; - ITaskSettings* pSettings = nullptr; - ITriggerCollection* pTriggerCollection = nullptr; - IRegisteredTask* pRegisteredTask = nullptr; - IPrincipal* pPrincipal = nullptr; - ITrigger* pTrigger = nullptr; - ILogonTrigger* pLogonTrigger = nullptr; - IAction* pAction = nullptr; - IActionCollection* pActionCollection = nullptr; - IExecAction* pExecAction = nullptr; - - LPWSTR wszExecutablePath = nullptr; - - hr = WcaInitialize(hInstall, "CreateScheduledTaskCA"); - ExitOnFailure(hr, "Failed to initialize"); - - Logger::info(L"CreateScheduledTaskCA Initialized."); - - // ------------------------------------------------------ - // Get the Domain/Username for the trigger. - // - // This action needs to run as the system to get elevated privileges from the installation, - // so GetUserNameEx can't be used to get the current user details. - // The USERNAME and USERDOMAIN environment variables are used instead. - 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); - - Logger::info(L"Current user detected: {}", username_domain); - - // Task Name. - wstrTaskName = L"Autorun for "; - wstrTaskName += username; - - // Get the executable path passed to the custom action. - hr = WcaGetProperty(L"CustomActionData", &wszExecutablePath); - ExitOnFailure(hr, "Failed to get the executable path from CustomActionData."); - - // COM and Security Initialization is expected to have been done by the MSI. - // It couldn't be done in the DLL, anyway. - // ------------------------------------------------------ - // Create an instance of the Task Service. - hr = CoCreateInstance(CLSID_TaskScheduler, - nullptr, - CLSCTX_INPROC_SERVER, - IID_ITaskService, - reinterpret_cast(&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 = nullptr; - 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); - } - Logger::info(L"PowerToys task folder created."); - } - - // If the same task exists, remove it. - pTaskFolder->DeleteTask(_bstr_t(wstrTaskName.c_str()), 0); - - // 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); - hr = pSettings->put_Priority(4); - ExitOnFailure(hr, "Cannot put_Priority 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. - 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")); - if (FAILED(hr)) - { - Logger::error(L"Cannot put the trigger ID: {}", hr); - } - - // 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")); - if (FAILED(hr)) - { - Logger::error(L"Cannot put the trigger delay: {}", hr); - } - - // 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. - - // 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 - 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")); - if (FAILED(hr)) - { - Logger::error(L"Cannot put the principal ID: {}", hr); - } - - hr = pPrincipal->put_UserId(_bstr_t(username_domain)); - if (FAILED(hr)) - { - Logger::error(L"Cannot put principal user Id: {}", hr); - } - - hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN); - if (FAILED(hr)) - { - Logger::error(L"Cannot put principal logon type: {}", hr); - } - - // Run the task with the highest available privileges. - hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA); - pPrincipal->Release(); - ExitOnFailure(hr, "Cannot put principal run level: %x", hr); - - // ------------------------------------------------------ - // Save the task in the PowerToys folder. - { - _variant_t SDDL_FULL_ACCESS_FOR_EVERYONE = L"D:(A;;FA;;;WD)"; - hr = pTaskFolder->RegisterTaskDefinition( - _bstr_t(wstrTaskName.c_str()), - pTask, - TASK_CREATE_OR_UPDATE, - _variant_t(username_domain), - _variant_t(), - TASK_LOGON_INTERACTIVE_TOKEN, - SDDL_FULL_ACCESS_FOR_EVERYONE, - &pRegisteredTask); - ExitOnFailure(hr, "Error saving the Task : %x", hr); - } - - Logger::info(L"Scheduled task created for the current user."); - -LExit: - ReleaseStr(wszExecutablePath); - 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 (!SUCCEEDED(hr)) - { - PMSIHANDLE hRecord = MsiCreateRecord(0); - MsiRecordSetString(hRecord, 0, TEXT("Failed to create a scheduled task to start PowerToys at user login. You can re-try to create the scheduled task using the PowerToys settings.")); - MsiProcessMessage(hInstall, static_cast(INSTALLMESSAGE_WARNING + MB_OK), hRecord); - } - - er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; - return WcaFinalize(er); -} - // Removes all Scheduled Tasks in the PowerToys folder and deletes the folder afterwards. // Based on the Task Scheduler Displaying Task Names and State example: // https://learn.microsoft.com/windows/desktop/TaskSchd/displaying-task-names-and-state--c---/ diff --git a/installer/PowerToysSetupCustomActions/CustomAction.def b/installer/PowerToysSetupCustomActions/CustomAction.def index 697ceaba84..7c3c058d9c 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.def +++ b/installer/PowerToysSetupCustomActions/CustomAction.def @@ -4,7 +4,6 @@ EXPORTS LaunchPowerToysCA CheckGPOCA ApplyModulesRegistryChangeSetsCA - CreateScheduledTaskCA DetectPrevInstallPathCA RemoveScheduledTasksCA TelemetryLogInstallSuccessCA diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index de72d9aa2d..dccd19d167 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -104,7 +104,7 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) if (is_process_elevated()) { delete_auto_start_task_for_this_user(); - create_auto_start_task_for_this_user(general_configs.GetNamedBoolean(L"run_elevated", false)); + create_auto_start_task_for_this_user(run_as_elevated); } else { @@ -127,6 +127,12 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save) delete_auto_start_task_for_this_user(); } } + else + { + delete_auto_start_task_for_this_user(); + create_auto_start_task_for_this_user(run_as_elevated); + } + if (json::has(general_configs, L"enabled")) { for (const auto& enabled_element : general_configs.GetNamedObject(L"enabled"))