Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
/target
taskschd.zip
/.cargo
/.idea
10 changes: 10 additions & 0 deletions src/action_collection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use comedy::com::ComRef;
use winapi::um::taskschd::{IActionCollection};
use crate::long_getter;

pub struct ActionCollection(pub ComRef<IActionCollection>);

impl ActionCollection {
long_getter!(IActionCollection::get_Count);

}
163 changes: 163 additions & 0 deletions src/com_macro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@

/// put a bool, converting to `VARIANT_BOOL`
#[macro_export]
macro_rules! bool_putter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self, v: bool) -> Result<(), comedy::HResult> {
use crate::ole_utils::IntoVariantBool;
let v = v.into_variant_bool();
unsafe {
comedy::com_call!(self.0, $interface::$method(v))?;
}
Ok(())
}
};
}

#[macro_export]
macro_rules! bool_getter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self) -> Result<bool, comedy::HResult> {
let mut v = 0;
unsafe {
comedy::com_call!(self.0, $interface::$method(&mut v))?;
}
if v == 0 {
Ok(false)
} else {
Ok(true)
}
}
};
}


/// put a value that is already available as a `BString`
#[macro_export]
macro_rules! bstring_putter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self, v: &crate::ole_utils::BString) -> Result<(), comedy::HResult> {
unsafe {
comedy::com_call!(self.0, $interface::$method(v.as_raw_ptr()))?;
}
Ok(())
}
};
}

#[macro_export]
macro_rules! string_getter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self) -> Result<String, comedy::HResult> {
use std::os::windows::ffi::OsStringExt;
unsafe {
let mut b = std::ptr::null_mut();
let r = comedy::com_call!(self.0, $interface::$method(&mut b))?;
let s= std::ffi::OsString::from_wide(crate::ole_utils::BString::from_raw(b).ok_or_else(|| comedy::HResult::new(r))?.as_ref());
Ok(s.to_string_lossy().to_string())
}
}
};
}

#[macro_export]
macro_rules! short_getter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self) -> Result<winapi::shared::ntdef::SHORT, comedy::HResult> {
unsafe {
let mut i = 0;
comedy::com_call!(self.0, $interface::$method(&mut i))?;
Ok(i)
}
}
};
}

#[macro_export]
macro_rules! long_getter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self) -> Result<winapi::shared::ntdef::LONG, comedy::HResult> {
unsafe {
let mut i = 0;
comedy::com_call!(self.0, $interface::$method(&mut i))?;
Ok(i)
}
}
};
}
#[macro_export]
macro_rules! get_repetition {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self) -> Result<String, comedy::HResult> {
use winapi::um::taskschd::{ITrigger};

unsafe {
let mut repeat = std::ptr::null_mut();
let r = comedy::com_call!(self.0, ITrigger::get_Repetition(&mut repeat))?;
// 将 *mut IRepetitionPattern 转换为 NonNull<IRepetitionPattern>
if let Some(nonnull) = std::ptr::NonNull::new(repeat) {
// 使用 NonNull<IRepetitionPattern> 构造 ComRef<IRepetitionPattern>
let com_ref = comedy::com::ComRef::from_raw(nonnull);
// 使用 ComRef<IRepetitionPattern> 构造 RepetitionPattern
let mut repetition_pattern = crate::repetition_pattern::RepetitionPattern(com_ref);
Ok(repetition_pattern.to_string())
// 现在你可以使用 repetition_pattern 了
} else {
Err(comedy::HResult::new(r))
}
}
}
};
}
/// put a `chrono::DateTime` value
#[macro_export]
macro_rules! datetime_putter {
($interface:ident :: $method:ident) => {
#[allow(non_snake_case)]
pub fn $method(&mut self, v: chrono::DateTime<chrono::Utc>) -> Result<(), comedy::HResult> {
let v = crate::try_to_bstring!(v.to_rfc3339_opts(chrono::SecondsFormat::Secs, true))?;
unsafe {
comedy::com_call!(self.0, $interface::$method(v.as_raw_ptr()))?;
}
Ok(())
}
};
}


/// put a value of type `$ty`, which implements `AsRef<OsStr>`
#[macro_export]
macro_rules! to_os_str_putter {
($interface:ident :: $method:ident, $ty:ty) => {
#[allow(non_snake_case)]
pub fn $method(&mut self, v: $ty) -> Result<(), comedy::HResult> {
let v = crate::try_to_bstring!(v)?;
unsafe {
comedy::com_call!(self.0, $interface::$method(v.as_raw_ptr()))?;
}
Ok(())
}
};
}

/// put a value of type `$ty`, which implements `ToString`
#[macro_export]
macro_rules! to_string_putter {
($interface:ident :: $method:ident, $ty:ty) => {
#[allow(non_snake_case)]
pub fn $method(&mut self, v: $ty) -> Result<(), comedy::HResult> {
let v = crate::try_to_bstring!(v.to_string())?;
unsafe {
comedy::com_call!(self.0, $interface::$method(v.as_raw_ptr()))?;
}
Ok(())
}
};
}
79 changes: 79 additions & 0 deletions src/exec_action.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::OsStrExt;
use std::path::Path;
use comedy::com::ComRef;
use comedy::{com_call, HResult, Win32Error};
use winapi::shared::winerror::ERROR_BAD_ARGUMENTS;
use winapi::um::taskschd::IExecAction;
use crate::ole_utils::BString;
use crate::{string_getter, bstring_putter, to_os_str_putter, try_to_bstring};

pub struct ExecAction(pub ComRef<IExecAction>);

impl ExecAction {
string_getter!(IExecAction::get_Path);
string_getter!(IExecAction::get_Arguments);


to_os_str_putter!(IExecAction::put_Path, &Path);
to_os_str_putter!(IExecAction::put_WorkingDirectory, &Path);

pub fn put_path(&mut self, path: &str) -> Result<(), HResult> {
unsafe {
com_call!(self.0, IExecAction::put_Path(try_to_bstring!(path)?.as_raw_ptr()))?;
}
Ok(())
}
#[allow(non_snake_case)]
pub fn put_Arguments(&mut self, args: &[OsString]) -> Result<(), HResult> {
// based on `make_command_line()` from libstd
// https://github.com/rust-lang/rust/blob/37ff5d388f8c004ca248adb635f1cc84d347eda0/src/libstd/sys/windows/process.rs#L457

let mut s = Vec::new();

fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr) -> Result<(), HResult> {
cmd.push('"' as u16);

let mut backslashes: usize = 0;
for x in arg.encode_wide() {
if x == 0 {
return Err(HResult::from(Win32Error::new(ERROR_BAD_ARGUMENTS))
.file_line(file!(), line!()));
}

if x == '\\' as u16 {
backslashes += 1;
} else {
if x == '"' as u16 {
// Add n+1 backslashes for a total of 2n+1 before internal '"'.
cmd.extend((0..=backslashes).map(|_| '\\' as u16));
}
backslashes = 0;
}
cmd.push(x);
}

// Add n backslashes for a total of 2n before ending '"'.
cmd.extend((0..backslashes).map(|_| '\\' as u16));
cmd.push('"' as u16);

Ok(())
}

for arg in args {
if !s.is_empty() {
s.push(' ' as u16);
}

// always quote args
append_arg(&mut s, arg.as_ref())?;
}

let args = BString::from_slice(s).map_err(|e| e.file_line(file!(), line!()))?;

unsafe {
com_call!(self.0, IExecAction::put_Arguments(args.as_raw_ptr()))?;
}
Ok(())
}
}
39 changes: 38 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,39 @@
pub mod taskschd;
/* 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 https://mozilla.org/MPL/2.0/. */

//! A partial type-safe interface for Windows Task Scheduler 2.0
//!
//! This provides structs thinly wrapping the taskschd interfaces, with methods implemented as
//! they've been needed for the update agent.
//!
//! If it turns out that much more flexibility is needed in task definitions, it may be worth
//! generating an XML string and using `ITaskFolder::RegisterTask` or
//! `ITaskDefinition::put_XmlText`, rather than adding more and more boilerplate here.
//!
//! See https://docs.microsoft.com/windows/win32/taskschd/task-scheduler-start-page for
//! Microsoft's documentation.



pub mod ole_utils;
pub mod com_macro;
pub mod task_settings;
pub mod task_definition;
pub mod registration_info;
pub mod trigger_daily;
pub mod exec_action;
pub mod registered_task;
pub mod task_folder;
pub mod task_service;
pub mod trigger_boot;
pub mod trigger_time;
pub mod repetition_pattern;
pub mod trigger_weekly;
pub mod trigger_monthly;
pub mod trigger_event;
pub mod trigger_logon;
mod action_collection;
mod principal;
mod task_folder_collection;
mod trigger_idle;
Loading