Click here to Skip to main content
15,886,199 members
Articles / Programming Languages / C#

A New Task Scheduler Class Library for .NET

Rate me:
Please Sign up or sign in to vote.
4.93/5 (233 votes)
17 Dec 2007CPOL15 min read 3.7M   63.3K   634  
A revision of a Task Scheduler class library by David Hall
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
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I'm a long-time programmer in compilers, operating systems, microprogramming, applications, and web programming. I've got way too many years of experience piled up--old Burroughs mainframes, Unix, Macintosh, and Windows.

Software was finally beginning to get boring, but then along came .Net and brought the fun back!

Comments and Discussions