#include "core/core.hpp" #include "shared/ctx.hpp" #include "shared/names.hpp" #include #include #include #include #include #include #pragma comment(lib, "taskschd.lib") #pragma comment(lib, "comsupp.lib") #pragma comment(lib, "ole32.lib") namespace loader { namespace { constexpr std::string_view kTaskName = names::kProjectName; /// A very basic implementation, a lot of stuff is missing template class ComPtr { public: ComPtr() = default; explicit ComPtr(Ty* ptr): ptr_(ptr) { } ~ComPtr() { if (ptr_ != nullptr) { ptr_->Release(); } } ComPtr(const ComPtr&) = delete; ComPtr& operator=(const ComPtr&) = delete; [[nodiscard]] Ty* get() const { return ptr_; } [[nodiscard]] Ty* operator->() const { return ptr_; } [[nodiscard]] Ty** ref_to_ptr() { return &ptr_; } private: Ty* ptr_ = nullptr; }; void co_initialize() { static std::once_flag fl; std::call_once(fl, []() -> void { const auto result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); if (FAILED(result)) { throw std::runtime_error("failed to CoInitializeEx"); } }); } template [[nodiscard]] bool with_service(Callable&& callback) { co_initialize(); ComPtr service; auto hr = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, reinterpret_cast(service.ref_to_ptr())); if (FAILED(hr)) { return false; } hr = service->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{}); if (FAILED(hr)) { return false; } ComPtr root_folder; hr = service->GetFolder(BSTR(L"\\"), root_folder.ref_to_ptr()); if (FAILED(hr)) { return false; } /// Cleanup our task, we will recreate it in the callback if needed root_folder->DeleteTask(BSTR(kTaskName.data()), 0); return callback(service.get(), root_folder.get()); } } // namespace [[nodiscard]] bool add_to_autorun(AutorunType type) { const auto bin_path = shared::get_this_module_path(); return with_service([bin_path, type](ITaskService* service, ITaskFolder* folder) -> bool { /// Deduce the autorun config based on type const auto task_trigger = type == AutorunType::AS_SYSTEM_ON_BOOT ? TASK_TRIGGER_BOOT : TASK_TRIGGER_LOGON; const auto logon_type = type == AutorunType::AS_SYSTEM_ON_BOOT ? TASK_LOGON_SERVICE_ACCOUNT : TASK_LOGON_INTERACTIVE_TOKEN; BSTR user_id = nullptr; auto bstr_sys = bstr_t(L"SYSTEM"); if (type == AutorunType::AS_SYSTEM_ON_BOOT) { user_id = bstr_sys; } ComPtr task; auto hr = service->NewTask(0, task.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr reg_info; hr = task->get_RegistrationInfo(reg_info.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr principal; hr = task->get_Principal(principal.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr trigger_collection; hr = task->get_Triggers(trigger_collection.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr trigger; hr = trigger_collection->Create(task_trigger, trigger.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr action_collection; hr = task->get_Actions(action_collection.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr action; hr = action_collection->Create(TASK_ACTION_EXEC, action.ref_to_ptr()); if (FAILED(hr)) { return false; } ComPtr exec_action; hr = action->QueryInterface(IID_IExecAction, reinterpret_cast(exec_action.ref_to_ptr())); if (FAILED(hr)) { return false; } ComPtr settings; hr = task->get_Settings(settings.ref_to_ptr()); if (FAILED(hr)) { return false; } /// Elevated, when system boots principal->put_UserId(user_id); principal->put_LogonType(logon_type); principal->put_RunLevel(TASK_RUNLEVEL_HIGHEST); /// Info reg_info->put_Author(bstr_t(names::kRepoUrl.data())); /// Start even if we're on batteries settings->put_DisallowStartIfOnBatteries(VARIANT_FALSE); settings->put_StopIfGoingOnBatteries(VARIANT_FALSE); /// Binary exec_action->put_Path(bstr_t(bin_path.string().c_str())); exec_action->put_Arguments(bstr_t("--from-autorun")); /// Register the task and we are done ComPtr registered_task; hr = folder->RegisterTaskDefinition(bstr_t(kTaskName.data()), task.get(), TASK_CREATE_OR_UPDATE, VARIANT{}, VARIANT{}, TASK_LOGON_NONE, variant_t(L""), registered_task.ref_to_ptr()); return SUCCEEDED(hr); }); } [[nodiscard]] bool remove_from_autorun() { return with_service([](TArgs...) -> bool { return true; }); } } // namespace loader