Loading toolkit/components/moz.build +3 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,9 @@ DIRS += ["captivedetect"] if CONFIG["OS_TARGET"] != "Android": DIRS += ["terminator"] if CONFIG["OS_TARGET"] == "WINNT": DIRS += ["taskscheduler"] DIRS += ["build"] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": Loading toolkit/components/taskscheduler/components.conf 0 → 100644 +18 −0 Original line number Diff line number Diff line # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. Classes = [] if buildconfig.substs['OS_TARGET'] == 'WINNT': Classes += [ { 'cid': '{e2113dfc-8efe-43a1-8a20-ad720dd771d6}', 'contract_ids': ['@mozilla.org/win-task-scheduler-service;1'], 'type': 'nsWinTaskSchedulerService', 'headers': ['nsWinTaskScheduler.h'], 'processes': ProcessSelector.MAIN_PROCESS_ONLY, }, ] toolkit/components/taskscheduler/moz.build 0 → 100644 +23 −0 Original line number Diff line number Diff line # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. with Files("**"): BUG_COMPONENT = ("Toolkit", "Application Update") XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.ini"] XPIDL_MODULE = "taskscheduler" XPCOM_MANIFESTS += ["components.conf"] # This whole component is currently Windows-only, but a Mac implementation is planned. # Only the Windows C++ interface is an XPCOM component. if CONFIG["OS_TARGET"] == "WINNT": XPIDL_SOURCES += ["nsIWinTaskSchedulerService.idl"] EXPORTS += ["nsWinTaskScheduler.h"] SOURCES += ["nsWinTaskScheduler.cpp"] OS_LIBS += ["taskschd"] FINAL_LIBRARY = "xul" toolkit/components/taskscheduler/nsIWinTaskSchedulerService.idl 0 → 100644 +104 −0 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" /** * An interface for Windows Task Scheduler 2.0. * Documentation for the underlying APIs can be found at * https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page */ [scriptable, main_process_scriptable_only, uuid(a8d36901-0b6a-46c3-a214-a9e1d5d6047a)] interface nsIWinTaskSchedulerService : nsISupports { /** * Register (create) a task from an XML definition. * The task will be created so that it only runs as the current user * (TASK_LOGON_INTERACTIVE_TOKEN). * * @throws NS_ERROR_FILE_NOT_FOUND if the folder does not exist. * @throws NS_ERROR_FILE_ALREADY_EXISTS if the task already existed and aUpdateExisting is false. * * @param aFolderName Full name of the folder in which to create the task, starting with "\". * * @param aTaskName Name of the task. * * @param aDefinitionXML XML definition of the task. This is passed directly to Task Scheduler, * see the schema at * https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-schema * * @param aUpdateExisting Whether to update an existing task with the same name, default false. */ void registerTask(in wstring aFolderName, in wstring aTaskName, in wstring aDefinitionXML, [optional] in boolean aUpdateExisting); /** * Validate the XML task definition with Task Scheduler without creating a task, for testing. * Doesn't throw if only the final ITaskFolder::RegisterTask() fails. * * @param aDefinitionXML Definition to validate. * @return HRESULT from ITaskFolder::RegisterTask() * Success should be S_OK (0). XML validation failure could be one of * SCHED_E_UNEXPECTED_NODE, SCHED_E_NAMESPACE, SCHED_E_INVALIDVALUE, * SCHED_E_MISSINGNODE, SCHED_E_MALFORMEDXML, but there may be others. */ long validateTaskDefinition(in wstring aDefinitionXML); /** * Get the registration information for a task. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder or task do not exist. * * @param aFolderName Full name of the folder containing the task, starting with "\". * @param aTaskName Name of the task to read. * @return Registration information for the task, as XML text. */ AString getTaskXML(in wstring aFolderName, in wstring aTaskName); /** * Delete a task. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder or task do not exist. * * @param aFolderName Full name of the folder containing the task, starting with "\". * @param aTaskName Name of the task to delete. */ void deleteTask(in wstring aFolderName, in wstring aTaskName); /** * List the names of all tasks in a task folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder doesn't exist. * * @param aFolderName The full name of the task folder to enumerate, starting with "\". * * @return An array with the names of the tasks found. */ Array<AString> getFolderTasks(in wstring aFolderName); /** * Create a new task subfolder under a given parent folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the parent folder does not exist. * @throws NS_ERROR_FILE_ALREADY_EXISTS if the subfolder already exists. * * @param aParentFolderName Immediate parent for the new folder, starting with "\". * @param aSubFolderName Name of the new folder to create. */ void createFolder(in wstring aParentFolderName, in wstring aSubFolderName); /** * Delete a folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the parent folder does not exist. * @throws NS_ERROR_FILE_DIR_NOT_EMPTY if the folder was not empty. * * @param aParentFolderName Immediate parent of the folder to delete, starting with "\". * @param aSubFolderName Name of the folder to delete. */ void deleteFolder(in wstring aParentFolderName, in wstring aSubFolderName); }; toolkit/components/taskscheduler/nsWinTaskScheduler.cpp 0 → 100644 +308 −0 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsWinTaskScheduler.h" #include <windows.h> #include <comdef.h> #include <taskschd.h> #include "nsString.h" #include "mozilla/RefPtr.h" #include "mozilla/ResultVariant.h" using namespace mozilla; struct SysFreeStringDeleter { void operator()(BSTR aPtr) { ::SysFreeString(aPtr); } }; using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>; static nsresult ToNotFoundOrFailure(HRESULT hr) { if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { return NS_ERROR_FILE_NOT_FOUND; } else { return NS_ERROR_FAILURE; } } static nsresult ToAlreadyExistsOrFailure(HRESULT hr) { if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { return NS_ERROR_FILE_ALREADY_EXISTS; } else { return NS_ERROR_FAILURE; } } [[nodiscard]] static Result<RefPtr<ITaskFolder>, HRESULT> GetTaskFolder( const char16_t* aFolderName) { HRESULT hr; RefPtr<ITaskService> scheduler = nullptr; hr = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, getter_AddRefs(scheduler)); if (FAILED(hr)) { return Err(hr); } // Connect to the local Task Scheduler. hr = scheduler->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{}); if (FAILED(hr)) { return Err(hr); } BStrPtr bstrFolderName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aFolderName))); RefPtr<ITaskFolder> folder = nullptr; hr = scheduler->GetFolder(bstrFolderName.get(), getter_AddRefs(folder)); if (FAILED(hr)) { return Err(hr); } return folder; } [[nodiscard]] static Result<RefPtr<IRegisteredTask>, HRESULT> GetRegisteredTask( const char16_t* aFolderName, const char16_t* aTaskName) { auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return Err(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); RefPtr<IRegisteredTask> task = nullptr; HRESULT hr = folder.unwrap()->GetTask(bstrTaskName.get(), getter_AddRefs(task)); if (FAILED(hr)) { return Err(hr); } return task; } NS_IMPL_ISUPPORTS(nsWinTaskSchedulerService, nsIWinTaskSchedulerService) NS_IMETHODIMP nsWinTaskSchedulerService::GetTaskXML(const char16_t* aFolderName, const char16_t* aTaskName, nsAString& aResult) { if (!aFolderName || !aTaskName) { return NS_ERROR_NULL_POINTER; } auto task = GetRegisteredTask(aFolderName, aTaskName); if (!task.isOk()) { return ToNotFoundOrFailure(task.unwrapErr()); } { BSTR bstrXml = nullptr; if (FAILED(task.unwrap()->get_Xml(&bstrXml))) { return NS_ERROR_FAILURE; } aResult.Assign(bstrXml, ::SysStringLen(bstrXml)); ::SysFreeString(bstrXml); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::RegisterTask(const char16_t* aFolderName, const char16_t* aTaskName, const char16_t* aDefinitionXML, bool aUpdateExisting) { if (!aFolderName || !aTaskName || !aDefinitionXML) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); BStrPtr bstrXml = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML))); LONG flags = aUpdateExisting ? TASK_CREATE_OR_UPDATE : TASK_CREATE; TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN; // The outparam is not needed, but not documented as optional. RefPtr<IRegisteredTask> unusedTaskOutput = nullptr; HRESULT hr = folder.unwrap()->RegisterTask( bstrTaskName.get(), bstrXml.get(), flags, VARIANT{} /* userId */, VARIANT{} /* password */, logonType, VARIANT{} /* sddl */, getter_AddRefs(unusedTaskOutput)); if (FAILED(hr)) { return ToAlreadyExistsOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::ValidateTaskDefinition( const char16_t* aDefinitionXML, int32_t* aResult) { if (!aDefinitionXML) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(reinterpret_cast<const char16_t*>(L"\\")); if (!folder.isOk()) { return NS_ERROR_FAILURE; } BStrPtr bstrXml = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML))); LONG flags = TASK_VALIDATE_ONLY; TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN; // The outparam is not needed, but not documented as optional. RefPtr<IRegisteredTask> unusedTaskOutput = nullptr; HRESULT hr = folder.unwrap()->RegisterTask( nullptr /* path */, bstrXml.get(), flags, VARIANT{} /* userId */, VARIANT{} /* password */, logonType, VARIANT{} /* sddl */, getter_AddRefs(unusedTaskOutput)); if (aResult) { *aResult = hr; } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::DeleteTask(const char16_t* aFolderName, const char16_t* aTaskName) { if (!aFolderName || !aTaskName) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); HRESULT hr = folder.unwrap()->DeleteTask(bstrTaskName.get(), 0 /* flags */); if (FAILED(hr)) { return ToNotFoundOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::GetFolderTasks(const char16_t* aFolderName, nsTArray<nsString>& aResult) { if (!aFolderName) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } RefPtr<IRegisteredTaskCollection> taskCollection = nullptr; if (FAILED(folder.unwrap()->GetTasks(TASK_ENUM_HIDDEN, getter_AddRefs(taskCollection)))) { return NS_ERROR_FAILURE; } LONG taskCount = 0; if (FAILED(taskCollection->get_Count(&taskCount))) { return NS_ERROR_FAILURE; } aResult.Clear(); for (LONG i = 0; i < taskCount; ++i) { RefPtr<IRegisteredTask> task = nullptr; // nb: Collections are indexed from 1. if (FAILED(taskCollection->get_Item(_variant_t(i + 1), getter_AddRefs(task)))) { return NS_ERROR_FAILURE; } BStrPtr bstrTaskName; { BSTR tempTaskName = nullptr; if (FAILED(task->get_Name(&tempTaskName))) { return NS_ERROR_FAILURE; } bstrTaskName = BStrPtr(tempTaskName); } aResult.AppendElement(nsString(bstrTaskName.get())); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::CreateFolder(const char16_t* aParentFolderName, const char16_t* aSubFolderName) { if (!aParentFolderName || !aSubFolderName) { return NS_ERROR_NULL_POINTER; } auto parentFolder = GetTaskFolder(aParentFolderName); if (!parentFolder.isOk()) { return ToNotFoundOrFailure(parentFolder.unwrapErr()); } BStrPtr bstrSubFolderName = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName))); HRESULT hr = parentFolder.unwrap()->CreateFolder(bstrSubFolderName.get(), VARIANT{}, // sddl nullptr); // ppFolder if (FAILED(hr)) { return ToAlreadyExistsOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::DeleteFolder(const char16_t* aParentFolderName, const char16_t* aSubFolderName) { if (!aParentFolderName || !aSubFolderName) { return NS_ERROR_NULL_POINTER; } auto parentFolder = GetTaskFolder(aParentFolderName); if (!parentFolder.isOk()) { return ToNotFoundOrFailure(parentFolder.unwrapErr()); } BStrPtr bstrSubFolderName = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName))); HRESULT hr = parentFolder.unwrap()->DeleteFolder(bstrSubFolderName.get(), 0 /* flags */); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY)) { return NS_ERROR_FILE_DIR_NOT_EMPTY; } else { return ToNotFoundOrFailure(hr); } } return NS_OK; } Loading
toolkit/components/moz.build +3 −0 Original line number Diff line number Diff line Loading @@ -110,6 +110,9 @@ DIRS += ["captivedetect"] if CONFIG["OS_TARGET"] != "Android": DIRS += ["terminator"] if CONFIG["OS_TARGET"] == "WINNT": DIRS += ["taskscheduler"] DIRS += ["build"] if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows": Loading
toolkit/components/taskscheduler/components.conf 0 → 100644 +18 −0 Original line number Diff line number Diff line # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. Classes = [] if buildconfig.substs['OS_TARGET'] == 'WINNT': Classes += [ { 'cid': '{e2113dfc-8efe-43a1-8a20-ad720dd771d6}', 'contract_ids': ['@mozilla.org/win-task-scheduler-service;1'], 'type': 'nsWinTaskSchedulerService', 'headers': ['nsWinTaskScheduler.h'], 'processes': ProcessSelector.MAIN_PROCESS_ONLY, }, ]
toolkit/components/taskscheduler/moz.build 0 → 100644 +23 −0 Original line number Diff line number Diff line # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. with Files("**"): BUG_COMPONENT = ("Toolkit", "Application Update") XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell/xpcshell.ini"] XPIDL_MODULE = "taskscheduler" XPCOM_MANIFESTS += ["components.conf"] # This whole component is currently Windows-only, but a Mac implementation is planned. # Only the Windows C++ interface is an XPCOM component. if CONFIG["OS_TARGET"] == "WINNT": XPIDL_SOURCES += ["nsIWinTaskSchedulerService.idl"] EXPORTS += ["nsWinTaskScheduler.h"] SOURCES += ["nsWinTaskScheduler.cpp"] OS_LIBS += ["taskschd"] FINAL_LIBRARY = "xul"
toolkit/components/taskscheduler/nsIWinTaskSchedulerService.idl 0 → 100644 +104 −0 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" /** * An interface for Windows Task Scheduler 2.0. * Documentation for the underlying APIs can be found at * https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-start-page */ [scriptable, main_process_scriptable_only, uuid(a8d36901-0b6a-46c3-a214-a9e1d5d6047a)] interface nsIWinTaskSchedulerService : nsISupports { /** * Register (create) a task from an XML definition. * The task will be created so that it only runs as the current user * (TASK_LOGON_INTERACTIVE_TOKEN). * * @throws NS_ERROR_FILE_NOT_FOUND if the folder does not exist. * @throws NS_ERROR_FILE_ALREADY_EXISTS if the task already existed and aUpdateExisting is false. * * @param aFolderName Full name of the folder in which to create the task, starting with "\". * * @param aTaskName Name of the task. * * @param aDefinitionXML XML definition of the task. This is passed directly to Task Scheduler, * see the schema at * https://docs.microsoft.com/en-us/windows/win32/taskschd/task-scheduler-schema * * @param aUpdateExisting Whether to update an existing task with the same name, default false. */ void registerTask(in wstring aFolderName, in wstring aTaskName, in wstring aDefinitionXML, [optional] in boolean aUpdateExisting); /** * Validate the XML task definition with Task Scheduler without creating a task, for testing. * Doesn't throw if only the final ITaskFolder::RegisterTask() fails. * * @param aDefinitionXML Definition to validate. * @return HRESULT from ITaskFolder::RegisterTask() * Success should be S_OK (0). XML validation failure could be one of * SCHED_E_UNEXPECTED_NODE, SCHED_E_NAMESPACE, SCHED_E_INVALIDVALUE, * SCHED_E_MISSINGNODE, SCHED_E_MALFORMEDXML, but there may be others. */ long validateTaskDefinition(in wstring aDefinitionXML); /** * Get the registration information for a task. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder or task do not exist. * * @param aFolderName Full name of the folder containing the task, starting with "\". * @param aTaskName Name of the task to read. * @return Registration information for the task, as XML text. */ AString getTaskXML(in wstring aFolderName, in wstring aTaskName); /** * Delete a task. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder or task do not exist. * * @param aFolderName Full name of the folder containing the task, starting with "\". * @param aTaskName Name of the task to delete. */ void deleteTask(in wstring aFolderName, in wstring aTaskName); /** * List the names of all tasks in a task folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the folder doesn't exist. * * @param aFolderName The full name of the task folder to enumerate, starting with "\". * * @return An array with the names of the tasks found. */ Array<AString> getFolderTasks(in wstring aFolderName); /** * Create a new task subfolder under a given parent folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the parent folder does not exist. * @throws NS_ERROR_FILE_ALREADY_EXISTS if the subfolder already exists. * * @param aParentFolderName Immediate parent for the new folder, starting with "\". * @param aSubFolderName Name of the new folder to create. */ void createFolder(in wstring aParentFolderName, in wstring aSubFolderName); /** * Delete a folder. * * @throws NS_ERROR_FILE_NOT_FOUND if the parent folder does not exist. * @throws NS_ERROR_FILE_DIR_NOT_EMPTY if the folder was not empty. * * @param aParentFolderName Immediate parent of the folder to delete, starting with "\". * @param aSubFolderName Name of the folder to delete. */ void deleteFolder(in wstring aParentFolderName, in wstring aSubFolderName); };
toolkit/components/taskscheduler/nsWinTaskScheduler.cpp 0 → 100644 +308 −0 Original line number Diff line number Diff line /* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsWinTaskScheduler.h" #include <windows.h> #include <comdef.h> #include <taskschd.h> #include "nsString.h" #include "mozilla/RefPtr.h" #include "mozilla/ResultVariant.h" using namespace mozilla; struct SysFreeStringDeleter { void operator()(BSTR aPtr) { ::SysFreeString(aPtr); } }; using BStrPtr = mozilla::UniquePtr<OLECHAR, SysFreeStringDeleter>; static nsresult ToNotFoundOrFailure(HRESULT hr) { if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { return NS_ERROR_FILE_NOT_FOUND; } else { return NS_ERROR_FAILURE; } } static nsresult ToAlreadyExistsOrFailure(HRESULT hr) { if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)) { return NS_ERROR_FILE_ALREADY_EXISTS; } else { return NS_ERROR_FAILURE; } } [[nodiscard]] static Result<RefPtr<ITaskFolder>, HRESULT> GetTaskFolder( const char16_t* aFolderName) { HRESULT hr; RefPtr<ITaskService> scheduler = nullptr; hr = CoCreateInstance(CLSID_TaskScheduler, nullptr, CLSCTX_INPROC_SERVER, IID_ITaskService, getter_AddRefs(scheduler)); if (FAILED(hr)) { return Err(hr); } // Connect to the local Task Scheduler. hr = scheduler->Connect(VARIANT{}, VARIANT{}, VARIANT{}, VARIANT{}); if (FAILED(hr)) { return Err(hr); } BStrPtr bstrFolderName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aFolderName))); RefPtr<ITaskFolder> folder = nullptr; hr = scheduler->GetFolder(bstrFolderName.get(), getter_AddRefs(folder)); if (FAILED(hr)) { return Err(hr); } return folder; } [[nodiscard]] static Result<RefPtr<IRegisteredTask>, HRESULT> GetRegisteredTask( const char16_t* aFolderName, const char16_t* aTaskName) { auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return Err(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); RefPtr<IRegisteredTask> task = nullptr; HRESULT hr = folder.unwrap()->GetTask(bstrTaskName.get(), getter_AddRefs(task)); if (FAILED(hr)) { return Err(hr); } return task; } NS_IMPL_ISUPPORTS(nsWinTaskSchedulerService, nsIWinTaskSchedulerService) NS_IMETHODIMP nsWinTaskSchedulerService::GetTaskXML(const char16_t* aFolderName, const char16_t* aTaskName, nsAString& aResult) { if (!aFolderName || !aTaskName) { return NS_ERROR_NULL_POINTER; } auto task = GetRegisteredTask(aFolderName, aTaskName); if (!task.isOk()) { return ToNotFoundOrFailure(task.unwrapErr()); } { BSTR bstrXml = nullptr; if (FAILED(task.unwrap()->get_Xml(&bstrXml))) { return NS_ERROR_FAILURE; } aResult.Assign(bstrXml, ::SysStringLen(bstrXml)); ::SysFreeString(bstrXml); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::RegisterTask(const char16_t* aFolderName, const char16_t* aTaskName, const char16_t* aDefinitionXML, bool aUpdateExisting) { if (!aFolderName || !aTaskName || !aDefinitionXML) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); BStrPtr bstrXml = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML))); LONG flags = aUpdateExisting ? TASK_CREATE_OR_UPDATE : TASK_CREATE; TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN; // The outparam is not needed, but not documented as optional. RefPtr<IRegisteredTask> unusedTaskOutput = nullptr; HRESULT hr = folder.unwrap()->RegisterTask( bstrTaskName.get(), bstrXml.get(), flags, VARIANT{} /* userId */, VARIANT{} /* password */, logonType, VARIANT{} /* sddl */, getter_AddRefs(unusedTaskOutput)); if (FAILED(hr)) { return ToAlreadyExistsOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::ValidateTaskDefinition( const char16_t* aDefinitionXML, int32_t* aResult) { if (!aDefinitionXML) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(reinterpret_cast<const char16_t*>(L"\\")); if (!folder.isOk()) { return NS_ERROR_FAILURE; } BStrPtr bstrXml = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aDefinitionXML))); LONG flags = TASK_VALIDATE_ONLY; TASK_LOGON_TYPE logonType = TASK_LOGON_INTERACTIVE_TOKEN; // The outparam is not needed, but not documented as optional. RefPtr<IRegisteredTask> unusedTaskOutput = nullptr; HRESULT hr = folder.unwrap()->RegisterTask( nullptr /* path */, bstrXml.get(), flags, VARIANT{} /* userId */, VARIANT{} /* password */, logonType, VARIANT{} /* sddl */, getter_AddRefs(unusedTaskOutput)); if (aResult) { *aResult = hr; } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::DeleteTask(const char16_t* aFolderName, const char16_t* aTaskName) { if (!aFolderName || !aTaskName) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } BStrPtr bstrTaskName = BStrPtr(::SysAllocString(reinterpret_cast<const OLECHAR*>(aTaskName))); HRESULT hr = folder.unwrap()->DeleteTask(bstrTaskName.get(), 0 /* flags */); if (FAILED(hr)) { return ToNotFoundOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::GetFolderTasks(const char16_t* aFolderName, nsTArray<nsString>& aResult) { if (!aFolderName) { return NS_ERROR_NULL_POINTER; } auto folder = GetTaskFolder(aFolderName); if (!folder.isOk()) { return ToNotFoundOrFailure(folder.unwrapErr()); } RefPtr<IRegisteredTaskCollection> taskCollection = nullptr; if (FAILED(folder.unwrap()->GetTasks(TASK_ENUM_HIDDEN, getter_AddRefs(taskCollection)))) { return NS_ERROR_FAILURE; } LONG taskCount = 0; if (FAILED(taskCollection->get_Count(&taskCount))) { return NS_ERROR_FAILURE; } aResult.Clear(); for (LONG i = 0; i < taskCount; ++i) { RefPtr<IRegisteredTask> task = nullptr; // nb: Collections are indexed from 1. if (FAILED(taskCollection->get_Item(_variant_t(i + 1), getter_AddRefs(task)))) { return NS_ERROR_FAILURE; } BStrPtr bstrTaskName; { BSTR tempTaskName = nullptr; if (FAILED(task->get_Name(&tempTaskName))) { return NS_ERROR_FAILURE; } bstrTaskName = BStrPtr(tempTaskName); } aResult.AppendElement(nsString(bstrTaskName.get())); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::CreateFolder(const char16_t* aParentFolderName, const char16_t* aSubFolderName) { if (!aParentFolderName || !aSubFolderName) { return NS_ERROR_NULL_POINTER; } auto parentFolder = GetTaskFolder(aParentFolderName); if (!parentFolder.isOk()) { return ToNotFoundOrFailure(parentFolder.unwrapErr()); } BStrPtr bstrSubFolderName = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName))); HRESULT hr = parentFolder.unwrap()->CreateFolder(bstrSubFolderName.get(), VARIANT{}, // sddl nullptr); // ppFolder if (FAILED(hr)) { return ToAlreadyExistsOrFailure(hr); } return NS_OK; } NS_IMETHODIMP nsWinTaskSchedulerService::DeleteFolder(const char16_t* aParentFolderName, const char16_t* aSubFolderName) { if (!aParentFolderName || !aSubFolderName) { return NS_ERROR_NULL_POINTER; } auto parentFolder = GetTaskFolder(aParentFolderName); if (!parentFolder.isOk()) { return ToNotFoundOrFailure(parentFolder.unwrapErr()); } BStrPtr bstrSubFolderName = BStrPtr( ::SysAllocString(reinterpret_cast<const OLECHAR*>(aSubFolderName))); HRESULT hr = parentFolder.unwrap()->DeleteFolder(bstrSubFolderName.get(), 0 /* flags */); if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY)) { return NS_ERROR_FILE_DIR_NOT_EMPTY; } else { return ToNotFoundOrFailure(hr); } } return NS_OK; }