using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using TaskSchedulerInterop;
namespace TaskScheduler {
#region Enums
/// <summary>
/// Options for a task, used for the Flags property of a Task. Uses the
/// "Flags" attribute, so these values are combined with |.
/// Some flags are documented as Windows 95 only, but they have a
/// user interface in Windows XP so that may not be true.
/// </summary>
[Flags]
public enum TaskFlags {
/// <summary>
/// The precise meaning of this flag is elusive. The MSDN documentation describes it
/// only for use in converting jobs from the Windows NT "AT" service to the newer
/// Task Scheduler. No other use for the flag is documented.
/// </summary>
Interactive = 0x1,
/// <summary>
/// The task will be deleted when there are no more scheduled run times.
/// </summary>
DeleteWhenDone = 0x2,
/// <summary>
/// The task is disabled. Used to temporarily prevent a task from being triggered normally.
/// </summary>
Disabled = 0x4,
/// <summary>
/// The task begins only if the computer is idle at the scheduled start time.
/// The computer is not considered idle until the task's <see cref="Task.IdleWaitMinutes"/> time
/// elapses with no user input.
/// </summary>
StartOnlyIfIdle = 0x10,
/// <summary>
/// The task terminates if the computer makes an idle to non-idle transition while the task is running.
/// For information regarding idle triggers, see <see cref="OnIdleTrigger"/>.
/// </summary>
KillOnIdleEnd = 0x20,
/// <summary>
/// The task does not start if the computer is running on battery power.
/// </summary>
DontStartIfOnBatteries = 0x40,
/// <summary>
/// The task ends, and the associated application quits if the computer switches
/// to battery power.
/// </summary>
KillIfGoingOnBatteries = 0x80,
/// <summary>
/// The task runs only if the system is docked.
/// (Not mentioned in current MSDN documentation; probably obsolete.)
/// </summary>
RunOnlyIfDocked = 0x100,
/// <summary>
/// The task item is hidden.
///
/// This is implemented by setting the job file's hidden attribute. Testing revealed that clearing
/// this flag doesn't clear the file attribute, so the library sets the file attribute directly. This
/// flag is kept in sync with the task's Hidden property, so they function equivalently.
/// </summary>
Hidden = 0x200,
/// <summary>
/// The task runs only if there is currently a valid Internet connection.
/// Not currently implemented. (Check current MSDN documentation for updates.)
/// </summary>
RunIfConnectedToInternet = 0x400,
/// <summary>
/// The task starts again if the computer makes a non-idle to idle transition before all the
/// task's task_triggers elapse. (Use this flag in conjunction with KillOnIdleEnd.)
/// </summary>
RestartOnIdleResume = 0x800,
/// <summary>
/// Wake the computer to run this task. Seems to be misnamed, but the name is taken from
/// the low-level interface.
///
/// </summary>
SystemRequired = 0x1000,
/// <summary>
/// The task runs only if the user specified in SetAccountInformation() is
/// logged on interactively. This flag has no effect on tasks set to run in
/// the local SYSTEM account.
/// </summary>
RunOnlyIfLoggedOn = 0x2000
}
/// <summary>
/// Status values returned for a task. Some values have been determined to occur although
/// they do no appear in the Task Scheduler system documentation.
/// </summary>
public enum TaskStatus {
/// <summary>
/// The task is ready to run at its next scheduled time.
/// </summary>
Ready = HResult.SCHED_S_TASK_READY,
/// <summary>
/// The task is currently running.
/// </summary>
Running = HResult.SCHED_S_TASK_RUNNING,
/// <summary>
/// One or more of the properties that are needed to run this task on a schedule have not been set.
/// </summary>
NotScheduled = HResult.SCHED_S_TASK_NOT_SCHEDULED,
/// <summary>
/// The task has not yet run.
/// </summary>
NeverRun = HResult.SCHED_S_TASK_HAS_NOT_RUN,
/// <summary>
/// The task will not run at the scheduled times because it has been disabled.
/// </summary>
Disabled = HResult.SCHED_S_TASK_DISABLED,
/// <summary>
/// There are no more runs scheduled for this task.
/// </summary>
NoMoreRuns = HResult.SCHED_S_TASK_NO_MORE_RUNS,
/// <summary>
/// The last run of the task was terminated by the user.
/// </summary>
Terminated = HResult.SCHED_S_TASK_TERMINATED,
/// <summary>
/// Either the task has no triggers or the existing triggers are disabled or not set.
/// </summary>
NoTriggers = HResult.SCHED_S_TASK_NO_VALID_TRIGGERS,
/// <summary>
/// Event triggers don't have set run times.
/// </summary>
NoTriggerTime = HResult.SCHED_S_EVENT_TRIGGER
}
#endregion
/// <summary>
/// Represents an item in the Scheduled Tasks folder. There are no public constructors for Task.
/// New instances are generated by a <see cref="ScheduledTasks"/> object using Open or Create methods.
/// A task object holds COM interfaces; call its <see cref="Close"/> method to release them.
/// </summary>
public class Task : IDisposable {
#region Fields
/// <summary>
/// Internal COM interface
/// </summary>
private ITask iTask;
/// <summary>
/// Name of this task (with no .job extension)
/// </summary>
private string name;
/// <summary>
/// List of triggers for this task
/// </summary>
private TriggerList triggers;
#endregion
#region Constructors
/// <summary>
/// Internal constructor for a task, used by <see cref="ScheduledTasks"/>.
/// </summary>
/// <param name="iTask">Instance of an ITask.</param>
/// <param name="taskName">Name of the task.</param>
internal Task(ITask iTask, string taskName) {
this.iTask = iTask;
if (taskName.EndsWith(".job"))
name = taskName.Substring(0, taskName.Length-4);
else
name = taskName;
triggers = null;
this.Hidden = GetHiddenFileAttr();
}
#endregion
#region Properties
/// <summary>
/// Gets the name of the task. The name is also the filename (plus a .job extension)
/// the Task Scheduler uses to store the task information. To change the name of a
/// task, use <see cref="Save()"/> to save it as a new name and then delete
/// the old task.
/// </summary>
public string Name {
get {
return name;
}
}
/// <summary>
/// Gets the list of triggers associated with the task.
/// </summary>
public TriggerList Triggers {
get {
if (triggers == null) {
// Trigger list has not been requested before; create it
triggers = new TriggerList(iTask);
}
return triggers;
}
}
/// <summary>
/// Gets/sets the application filename that task is to run. Get returns
/// an absolute pathname. A name searched with the PATH environment variable can
/// be assigned, and the path search is done when the task is saved.
/// </summary>
public string ApplicationName {
get {
IntPtr lpwstr;
iTask.GetApplicationName(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
set {
iTask.SetApplicationName(value);
}
}
/// <summary>
/// Gets the name of the account under which the task process will run.
/// </summary>
public string AccountName {
get {
IntPtr lpwstr = IntPtr.Zero;
iTask.GetAccountInformation(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
}
/// <summary>
/// Gets/sets the comment associated with the task. The comment appears in the
/// Scheduled Tasks user interface.
/// </summary>
public string Comment {
get {
IntPtr lpwstr;
iTask.GetComment(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
set {
iTask.SetComment(value);
}
}
/// <summary>
/// Gets/sets the creator of the task. If no value is supplied, the system
/// fills in the account name of the caller when the task is saved.
/// </summary>
public string Creator {
get {
IntPtr lpwstr;
iTask.GetCreator(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
set {
iTask.SetCreator(value);
}
}
/// <summary>
/// Gets/sets the number of times to retry task execution after failure. (Not implemented.)
/// </summary>
private short ErrorRetryCount {
get {
ushort ret;
iTask.GetErrorRetryCount(out ret);
return (short)ret;
}
set {
iTask.SetErrorRetryCount((ushort)value);
}
}
/// <summary>
/// Gets/sets the time interval, in minutes, to delay between error retries. (Not implemented.)
/// </summary>
private short ErrorRetryInterval {
get {
ushort ret;
iTask.GetErrorRetryInterval(out ret);
return (short)ret;
}
set {
iTask.SetErrorRetryInterval((ushort)value);
}
}
/// <summary>
/// Gets the Win32 exit code from the last execution of the task. If the task failed
/// to start on its last run, the reason is returned as an exception. Not updated while
/// in an open task; the property does not change unless the task is closed and re-opened.
/// <exception>Various exceptions for a task that couldn't be run.</exception>
/// </summary>
public int ExitCode {
get {
uint ret = 0;
iTask.GetExitCode(out ret);
return (int)ret;
}
}
/// <summary>
/// Gets/sets the <see cref="TaskFlags"/> associated with the current task.
/// </summary>
public TaskFlags Flags {
get {
uint ret;
iTask.GetFlags(out ret);
return (TaskFlags)ret;
}
set {
iTask.SetFlags((uint)value);
}
}
/// <summary>
/// Gets/sets how long the system must remain idle, even after the trigger
/// would normally fire, before the task will run.
/// </summary>
public short IdleWaitMinutes {
get {
ushort ret, nothing;
iTask.GetIdleWait(out ret, out nothing);
return (short)ret;
}
set {
ushort m = (ushort)IdleWaitDeadlineMinutes;
iTask.SetIdleWait((ushort)value, m);
}
}
/// <summary>
/// Gets/sets the maximum number of minutes that Task Scheduler will wait for a
/// required idle period to occur.
/// </summary>
public short IdleWaitDeadlineMinutes {
get {
ushort ret, nothing;
iTask.GetIdleWait(out nothing, out ret);
return (short)ret;
}
set {
ushort m = (ushort)IdleWaitMinutes;
iTask.SetIdleWait(m, (ushort)value);
}
}
/// <summary>
/// <p>Gets/sets the maximum length of time the task is permitted to run.
/// Setting MaxRunTime also affects the value of <see cref="Task.MaxRunTimeLimited"/>.
/// </p>
/// <p>The longest MaxRunTime implemented is 0xFFFFFFFE milliseconds, or
/// about 50 days. If you set a TimeSpan longer than that, the
/// MaxRunTime will be unlimited.</p>
/// </summary>
/// <Remarks>
/// </Remarks>
public TimeSpan MaxRunTime {
get {
uint ret;
iTask.GetMaxRunTime(out ret);
return new TimeSpan((long)ret * TimeSpan.TicksPerMillisecond);
}
set {
double proposed = ((TimeSpan)value).TotalMilliseconds;
if (proposed >= uint.MaxValue) {
iTask.SetMaxRunTime(uint.MaxValue);
} else {
iTask.SetMaxRunTime((uint)proposed);
}
//iTask.SetMaxRunTime((uint)((TimeSpan)value).TotalMilliseconds);
}
}
/// <summary>
/// <p>If the maximum run time is limited, the task will be terminated after
/// <see cref="Task.MaxRunTime"/> expires. Setting the value to FALSE, i.e. unlimited,
/// invalidates MaxRunTime.</p>
/// <p>The Task Scheduler service will try to send a WM_CLOSE message when it needs to terminate
/// a task. If the message can't be sent, or the task does not respond with three minutes,
/// the task will be terminated using TerminateProcess.</p>
/// </summary>
public bool MaxRunTimeLimited {
get {
uint ret;
iTask.GetMaxRunTime(out ret);
return (ret == uint.MaxValue);
}
set {
if (value) {
uint ret;
iTask.GetMaxRunTime(out ret);
if (ret == uint.MaxValue) {
iTask.SetMaxRunTime(72*360*1000); //72 hours. Thats what Explorer sets.
}
} else {
iTask.SetMaxRunTime(uint.MaxValue);
}
}
}
/// <summary>
/// Gets the most recent time the task began running. <see cref="DateTime.MinValue"/>
/// returned if the task has not run.
/// </summary>
public DateTime MostRecentRunTime {
get {
SystemTime st = new SystemTime();
iTask.GetMostRecentRunTime(ref st);
if (st.Year == 0)
return DateTime.MinValue;
return new DateTime((int)st.Year, (int)st.Month, (int)st.Day, (int)st.Hour, (int)st.Minute, (int)st.Second, (int)st.Milliseconds);
}
}
/// <summary>
/// Gets the next time the task will run. Returns <see cref="DateTime.MinValue"/>
/// if the task is not scheduled to run.
/// </summary>
public DateTime NextRunTime {
get {
SystemTime st = new SystemTime();
iTask.GetNextRunTime(ref st);
if (st.Year == 0)
return DateTime.MinValue;
return new DateTime((int)st.Year, (int)st.Month, (int)st.Day, (int)st.Hour, (int)st.Minute, (int)st.Second, (int)st.Milliseconds);
}
}
/// <summary>
/// Gets/sets the command-line parameters for the task.
/// </summary>
public string Parameters {
get {
IntPtr lpwstr;
iTask.GetParameters(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
set {
iTask.SetParameters(value);
}
}
/// <summary>
/// Gets/sets the priority for the task process.
/// Note: ProcessPriorityClass defines two levels (AboveNormal and BelowNormal) that are
/// not documented in the task scheduler interface and can't be use on Win 98 platforms.
/// </summary>
public System.Diagnostics.ProcessPriorityClass Priority {
get {
uint ret;
iTask.GetPriority(out ret);
return (System.Diagnostics.ProcessPriorityClass)ret;
}
set {
if (value==System.Diagnostics.ProcessPriorityClass.AboveNormal ||
value==System.Diagnostics.ProcessPriorityClass.BelowNormal ) {
throw new ArgumentException("Unsupported Priority Level");
}
iTask.SetPriority((uint)value);
}
}
/// <summary>
/// Gets the status of the task. Returns <see cref="TaskStatus"/>.
/// Not updated while a task is open.
/// </summary>
public TaskStatus Status {
get {
int ret;
iTask.GetStatus(out ret);
return (TaskStatus)ret;
}
}
/// <summary>
/// Extended Flags associated with a task. These are associated with the ITask com interface
/// and none are currently defined.
/// </summary>
private int FlagsEx {
get {
uint ret;
iTask.GetTaskFlags(out ret);
return (int)ret;
}
set {
iTask.SetTaskFlags((uint)value);
}
}
/// <summary>
/// Gets/sets the initial working directory for the task.
/// </summary>
public string WorkingDirectory {
get {
IntPtr lpwstr;
iTask.GetWorkingDirectory(out lpwstr);
return CoTaskMem.LPWStrToString(lpwstr);
}
set {
iTask.SetWorkingDirectory(value);
}
}
/// <summary>
/// Hidden tasks are stored in files with
/// the hidden file attribute so they don't appear in the Explorer user interface.
/// Because there is a special interface for Scheduled Tasks, they don't appear
/// even if Explorer is set to show hidden files.
/// Functionally equivalent to TaskFlags.Hidden.
/// </summary>
public bool Hidden {
get {
return (this.Flags & TaskFlags.Hidden) != 0;
}
set {
if (value) {
this.Flags |= TaskFlags.Hidden;
} else {
this.Flags &= ~TaskFlags.Hidden;
}
}
}
/// <summary>
/// Gets/sets arbitrary data associated with the task. The tag can be used for any purpose
/// by the client, and is not used by the Task Scheduler. Known as WorkItemData in the
/// IWorkItem com interface.
/// </summary>
public object Tag {
get {
ushort DataLen;
IntPtr Data;
iTask.GetWorkItemData(out DataLen, out Data);
byte[] bytes = new byte[DataLen];
Marshal.Copy(Data, bytes, 0, DataLen);
MemoryStream stream = new MemoryStream(bytes, false);
BinaryFormatter b = new BinaryFormatter();
return b.Deserialize(stream);
}
set {
if (!value.GetType().IsSerializable)
throw new ArgumentException("Objects set as Data for Tasks must be serializable", "value");
BinaryFormatter b = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
b.Serialize(stream, value);
iTask.SetWorkItemData((ushort)stream.Length, stream.GetBuffer());
}
}
#endregion
#region Methods
/// <summary>
/// Set the hidden attribute on the file corresponding to this task.
/// </summary>
/// <param name="set">Set the attribute accordingly.</param>
private void SetHiddenFileAttr(bool set) {
IPersistFile iFile = (IPersistFile)iTask;
string fileName;
iFile.GetCurFile(out fileName);
System.IO.FileAttributes attr;
attr = System.IO.File.GetAttributes(fileName);
if (set)
attr |= System.IO.FileAttributes.Hidden;
else
attr &= ~System.IO.FileAttributes.Hidden;
System.IO.File.SetAttributes(fileName, attr);
}
/// <summary>
/// Get the hidden attribute from the file corresponding to this task.
/// </summary>
/// <returns>The value of the attribute.</returns>
private bool GetHiddenFileAttr() {
IPersistFile iFile = (IPersistFile)iTask;
string fileName;
iFile.GetCurFile(out fileName);
System.IO.FileAttributes attr;
try {
attr = System.IO.File.GetAttributes(fileName);
return (attr & System.IO.FileAttributes.Hidden) != 0;
} catch {
return false;
}
}
/// <summary>
/// Calculate the next time the task would be scheduled
/// to run after a given arbitrary time. If the task will not run
/// (perhaps disabled) then returns <see cref="DateTime.MinValue"/>.
/// </summary>
/// <param name="after">The time to calculate from.</param>
/// <returns>The next time the task would run.</returns>
public DateTime NextRunTimeAfter(DateTime after) {
//Add one second to get a run time strictly greater than the specified time.
after = after.AddSeconds(1);
//Convert to a valid SystemTime
SystemTime stAfter = new SystemTime();
stAfter.Year = (ushort)after.Year;
stAfter.Month = (ushort)after.Month;
stAfter.Day = (ushort)after.Day;
stAfter.DayOfWeek = (ushort)after.DayOfWeek;
stAfter.Hour = (ushort)after.Hour;
stAfter.Minute = (ushort)after.Minute;
stAfter.Second = (ushort)after.Second;
SystemTime stLimit = new SystemTime();
// Would like to pass null as the second parameter to GetRunTimes, indicating that
// the interval is unlimited. Can't figure out how to do that, so use a big time value.
stLimit = stAfter;
stLimit.Year = (ushort)DateTime.MaxValue.Year;
stLimit.Month = 1; //Just in case stAfter date was Feb 29, but MaxValue.Year is not a leap year!
IntPtr pTimes;
ushort nFetch = 1;
iTask.GetRunTimes(ref stAfter, ref stLimit, ref nFetch, out pTimes);
if (nFetch == 1) {
SystemTime stNext = new SystemTime();
stNext = (SystemTime)Marshal.PtrToStructure(pTimes, typeof(SystemTime));
Marshal.FreeCoTaskMem(pTimes);
return new DateTime(stNext.Year, stNext.Month, stNext.Day, stNext.Hour, stNext.Minute, stNext.Second);
} else {
return DateTime.MinValue;
}
}
/// <summary>
/// Schedules the task for immediate execution.
/// The system works from the saved version of the task, so call <see cref="Save()"/> before running.
/// If the task has never been saved, it throws an argument exception. Problems starting
/// the task are reported by the <see cref="ExitCode"/> property, not by exceptions on Run.
/// </summary>
/// <remarks>The system never updates an open task, so you don't get current results for
/// the <see cref="Status"/> or the <see cref="ExitCode"/> properties until you close
/// and reopen the task.
/// </remarks>
/// <exception cref="ArgumentException"></exception>
public void Run() {
iTask.Run();
}
/// <summary>
/// Saves changes to the established task name.
/// </summary>
/// <overloads>Saves changes that have been made to this Task.</overloads>
/// <remarks>The account name is checked for validity
/// when a Task is saved. The password is not checked, but the account name
/// must be valid (or empty).
/// </remarks>
/// <exception cref="COMException">Unable to establish existence of the account specified.</exception>
public void Save() {
IPersistFile iFile = (IPersistFile)iTask;
iFile.Save(null, false);
SetHiddenFileAttr(Hidden); //Do the Task Scheduler's work for it because it doesn't reset properly
}
/// <summary>
/// Saves the Task with a new name. The task with the old name continues to
/// exist in whatever state it was last saved. It is no longer open, because.
/// the Task object is associated with the new name from now on.
/// If there is already a task using the new name, it is overwritten.
/// </summary>
/// <remarks>See the <see cref="Save()"/>() overload.</remarks>
/// <param name="name">The new name to be used for this task.</param>
/// <exception cref="COMException">Unable to establish existence of the account specified.</exception>
public void Save(string name) {
IPersistFile iFile = (IPersistFile)iTask;
string path;
iFile.GetCurFile(out path);
string newPath;
newPath = Path.GetDirectoryName(path) + Path.DirectorySeparatorChar + name + Path.GetExtension(path);
iFile.Save(newPath, true);
iFile.SaveCompleted(newPath); /* probably unnecessary */
this.name = name;
SetHiddenFileAttr(Hidden); //Do the Task Scheduler's work for it because it doesn't reset properly
}
/// <summary>
/// Release COM interfaces for this Task. After a Task is closed, accessing its
/// members throws a null reference exception.
/// </summary>
public void Close() {
if (triggers != null) {
triggers.Dispose();
}
Marshal.ReleaseComObject(iTask);
iTask = null;
}
/// <summary>
/// For compatibility with earlier versions. New clients should use <see cref="DisplayPropertySheet()"/>.
/// </summary>
/// <remarks>
/// Display the property pages of this task for user editing. If the user clicks OK, the
/// task's properties are updated and the task is also automatically saved.
/// </remarks>
public void DisplayForEdit() {
iTask.EditWorkItem(0, 0);
}
/// <summary>
/// Argument for DisplayForEdit to determine which property pages to display.
/// </summary>
[Flags]
public enum PropPages {
/// <summary>
/// The task property page
/// </summary>
Task = 0x01,
/// <summary>
/// The schedule property page
/// </summary>
Schedule = 0x02,
/// <summary>
/// The setting property page
/// </summary>
Settings = 0x04
}
///
/// <summary>
/// Display all property pages.
/// </summary>
/// <remarks>
/// The method does not return until the user has dismissed the dialog box.
/// If the dialog box is dismissed with the OK button, returns true and
/// updates properties in the task.
/// The changes are not made permanent, however, until the task is saved. (Save() method.)
/// </remarks>
/// <returns><c>true</c> if dialog box was dismissed with OK, otherwise <c>false</c>.</returns>
/// <overloads>Display the property pages of this task for user editing.</overloads>
public bool DisplayPropertySheet() {
//iTask.EditWorkItem(0, 0); //This implementation saves automatically, so we don't use it.
return DisplayPropertySheet(PropPages.Task | PropPages.Schedule | PropPages.Settings);
}
/// <summary>
/// Display only the specified property pages.
/// </summary>
/// <remarks>
/// See the <see cref="DisplayPropertySheet()"/>() overload.
/// </remarks>
/// <param name="pages">Controls which pages are presented</param>
/// <returns><c>true</c> if dialog box was dismissed with OK, otherwise <c>false</c>.</returns>
public bool DisplayPropertySheet(PropPages pages) {
PropSheetHeader hdr = new PropSheetHeader();
IProvideTaskPage iProvideTaskPage = (IProvideTaskPage)iTask;
IntPtr[] hPages = new IntPtr[3];
IntPtr hPage;
int nPages = 0;
if ((pages & PropPages.Task) != 0) {
//get task page
iProvideTaskPage.GetPage(0, false, out hPage);
hPages[nPages++] = hPage;
}
if ((pages & PropPages.Schedule) != 0) {
//get task page
iProvideTaskPage.GetPage(1, false, out hPage);
hPages[nPages++] = hPage;
}
if ((pages & PropPages.Settings) != 0) {
//get task page
iProvideTaskPage.GetPage(2, false, out hPage);
hPages[nPages++] = hPage;
}
if (nPages == 0) throw (new ArgumentException("No Property Pages to display"));
hdr.dwSize = (uint)Marshal.SizeOf(hdr);
hdr.dwFlags = (uint) (PropSheetFlags.PSH_DEFAULT | PropSheetFlags.PSH_NOAPPLYNOW);
hdr.pszCaption = this.Name;
hdr.nPages = (uint)nPages;
GCHandle gch = GCHandle.Alloc(hPages, GCHandleType.Pinned);
hdr.phpage = gch.AddrOfPinnedObject();
int res = PropertySheetDisplay.PropertySheet(ref hdr);
gch.Free();
if (res < 0) throw (new Exception("Property Sheet failed to display"));
return res>0;
}
/// <summary>
/// Sets the account under which the task will run. Supply the account name and
/// password as parameters. For the localsystem account, pass an empty string for
/// the account name and null for the password. See Remarks.
/// </summary>
/// <param name="accountName">Full account name.</param>
/// <param name="password">Password for the account.</param>
/// <remarks>
/// <p>To have the task to run under the local system account, pass the empty string ("")
/// as accountName and null as the password. The caller must be running in
/// an administrator account or in the local system account.
/// </p>
/// <p>
/// You can also specify a null password if the task has the flag RunOnlyIfLoggedOn set.
/// This allows you to schedule a task for an account for which you don't know the password,
/// but the account must be logged on interactively at the time the task runs.</p>
/// </remarks>
public void SetAccountInformation(string accountName, string password) {
IntPtr pwd = Marshal.StringToCoTaskMemUni(password);
iTask.SetAccountInformation(accountName, pwd);
Marshal.FreeCoTaskMem(pwd);
}
/// <summary>
/// Overload for SetAccountInformation which permits use of a SecureString for the
/// password parameter. The decoded password will remain in memory only as long as
/// needed to be passed to the TaskScheduler service.
/// </summary>
/// <param name="accountName">Full account name.</param>
/// <param name="password">Password for the account.</param>
public void SetAccountInformation(string accountName, SecureString password) {
IntPtr pwd = Marshal.SecureStringToCoTaskMemUnicode(password);
iTask.SetAccountInformation(accountName, pwd);
Marshal.ZeroFreeCoTaskMemUnicode(pwd);
}
/// <summary>
/// Request that the task be terminated if it is currently running. The call returns
/// immediately, although the task may continue briefly. For Windows programs, a WM_CLOSE
/// message is sent first and the task is given three minutes to shut down voluntarily.
/// Should it not, or if the task is not a Windows program, TerminateProcess is used.
/// </summary>
/// <exception cref="COMException">The task is not running.</exception>
public void Terminate() {
iTask.Terminate();
}
/// <summary>
/// Overridden. Outputs the name of the task, the application and parameters.
/// </summary>
/// <returns>String representing task.</returns>
public override string ToString() {
return string.Format("{0} (\"{1}\" {2})", name, ApplicationName, Parameters);
}
#endregion
#region Implementation of IDisposable
/// <summary>
/// A synonym for Close.
/// </summary>
public void Dispose() {
this.Close();
}
#endregion
}
}