diff options
Diffstat (limited to 'MediaBrowser.Common/ScheduledTasks')
15 files changed, 213 insertions, 96 deletions
diff --git a/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs b/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs index 395c73a84..845faf31a 100644 --- a/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/BaseScheduledTask.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// </summary> /// <typeparam name="TKernelType">The type of the T kernel type.</typeparam> public abstract class BaseScheduledTask<TKernelType> : IScheduledTask - where TKernelType : IKernel + where TKernelType : class, IKernel { /// <summary> /// Gets the kernel. @@ -26,6 +26,47 @@ namespace MediaBrowser.Common.ScheduledTasks protected TKernelType Kernel { get; private set; } /// <summary> + /// Gets the logger. + /// </summary> + /// <value>The logger.</value> + protected ILogger Logger { get; private set; } + + /// <summary> + /// Gets the task manager. + /// </summary> + /// <value>The task manager.</value> + protected ITaskManager TaskManager { get; private set; } + + /// <summary> + /// Initializes a new instance of the <see cref="BaseScheduledTask{TKernelType}" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + /// <exception cref="System.ArgumentNullException">kernel</exception> + protected BaseScheduledTask(TKernelType kernel, ITaskManager taskManager, ILogger logger) + { + if (kernel == null) + { + throw new ArgumentNullException("kernel"); + } + if (taskManager == null) + { + throw new ArgumentNullException("taskManager"); + } + if (logger == null) + { + throw new ArgumentNullException("logger"); + } + + Kernel = kernel; + TaskManager = taskManager; + Logger = logger; + + ReloadTriggerEvents(true); + } + + /// <summary> /// The _last execution result /// </summary> private TaskResult _lastExecutionResult; @@ -199,7 +240,7 @@ namespace MediaBrowser.Common.ScheduledTasks try { return JsonSerializer.DeserializeFromFile<IEnumerable<TaskTriggerInfo>>(ConfigurationFilePath) - .Select(t => ScheduledTaskHelpers.GetTrigger(t, Kernel)) + .Select(ScheduledTaskHelpers.GetTrigger) .ToList(); } catch (IOException) @@ -228,7 +269,7 @@ namespace MediaBrowser.Common.ScheduledTasks _triggersInitialized = true; - ReloadTriggerEvents(); + ReloadTriggerEvents(false); JsonSerializer.SerializeToFile(_triggers.Select(ScheduledTaskHelpers.GetTriggerInfo), ConfigurationFilePath); } @@ -291,28 +332,10 @@ namespace MediaBrowser.Common.ScheduledTasks } /// <summary> - /// Gets the logger. - /// </summary> - /// <value>The logger.</value> - protected ILogger Logger { get; private set; } - - /// <summary> - /// Initializes the specified kernel. - /// </summary> - /// <param name="kernel">The kernel.</param> - /// <param name="logger">The logger.</param> - public void Initialize(IKernel kernel, ILogger logger) - { - Logger = logger; - - Kernel = (TKernelType)kernel; - ReloadTriggerEvents(); - } - - /// <summary> /// Reloads the trigger events. /// </summary> - private void ReloadTriggerEvents() + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + private void ReloadTriggerEvents(bool isApplicationStartup) { foreach (var trigger in Triggers) { @@ -320,7 +343,7 @@ namespace MediaBrowser.Common.ScheduledTasks trigger.Triggered -= trigger_Triggered; trigger.Triggered += trigger_Triggered; - trigger.Start(); + trigger.Start(isApplicationStartup); } } @@ -335,7 +358,7 @@ namespace MediaBrowser.Common.ScheduledTasks Logger.Info("{0} fired for task: {1}", trigger.GetType().Name, Name); - Kernel.TaskManager.QueueScheduledTask(this); + TaskManager.QueueScheduledTask(this); } /// <summary> @@ -392,7 +415,7 @@ namespace MediaBrowser.Common.ScheduledTasks CurrentCancellationTokenSource = null; CurrentProgress = null; - Kernel.TaskManager.OnTaskCompleted(this); + TaskManager.OnTaskCompleted(this); } /// <summary> diff --git a/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs b/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs index 5e60bb718..ed302ed39 100644 --- a/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/BaseTaskTrigger.cs @@ -27,13 +27,13 @@ namespace MediaBrowser.Common.ScheduledTasks await Task.Delay(1000).ConfigureAwait(false); - Start(); + Start(false); } /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal abstract void Start(); + protected internal abstract void Start(bool isApplicationStartup); /// <summary> /// Stops waiting for the trigger action diff --git a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs index c1cf1a9a3..fb749f77c 100644 --- a/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/DailyTrigger.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal override void Start() + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + protected internal override void Start(bool isApplicationStartup) { DisposeTimer(); diff --git a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs index 95d1edf63..cba5fc5d0 100644 --- a/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/IScheduledTask.cs @@ -1,6 +1,4 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Tasks; using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -74,13 +72,6 @@ namespace MediaBrowser.Common.ScheduledTasks void Cancel(); /// <summary> - /// Initializes the specified kernel. - /// </summary> - /// <param name="kernel">The kernel.</param> - /// <param name="logger">The logger.</param> - void Initialize(IKernel kernel, ILogger logger); - - /// <summary> /// Cancels if running. /// </summary> void CancelIfRunning(); diff --git a/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs new file mode 100644 index 000000000..430208869 --- /dev/null +++ b/MediaBrowser.Common/ScheduledTasks/ITaskManager.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Common.ScheduledTasks +{ + public interface ITaskManager : IDisposable + { + /// <summary> + /// Gets the list of Scheduled Tasks + /// </summary> + /// <value>The scheduled tasks.</value> + IScheduledTask[] ScheduledTasks { get; } + + /// <summary> + /// Cancels if running and queue. + /// </summary> + /// <typeparam name="T"></typeparam> + void CancelIfRunningAndQueue<T>() + where T : IScheduledTask; + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <typeparam name="T"></typeparam> + void QueueScheduledTask<T>() + where T : IScheduledTask; + + /// <summary> + /// Queues the scheduled task. + /// </summary> + /// <param name="task">The task.</param> + void QueueScheduledTask(IScheduledTask task); + + /// <summary> + /// Adds the tasks. + /// </summary> + /// <param name="tasks">The tasks.</param> + void AddTasks(IEnumerable<IScheduledTask> tasks); + + /// <summary> + /// Called when [task completed]. + /// </summary> + /// <param name="task">The task.</param> + void OnTaskCompleted(IScheduledTask task); + } +}
\ No newline at end of file diff --git a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs index 1ead484c8..759447b10 100644 --- a/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/IntervalTrigger.cs @@ -23,7 +23,8 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal override void Start() + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + protected internal override void Start(bool isApplicationStartup) { DisposeTimer(); diff --git a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs index 95c4c6a66..9942da17f 100644 --- a/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs +++ b/MediaBrowser.Common/ScheduledTasks/ScheduledTaskHelpers.cs @@ -78,11 +78,10 @@ namespace MediaBrowser.Common.ScheduledTasks /// Converts a TaskTriggerInfo into a concrete BaseTaskTrigger /// </summary> /// <param name="info">The info.</param> - /// <param name="kernel">The kernel.</param> /// <returns>BaseTaskTrigger.</returns> /// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.ArgumentException">Invalid trigger type: + info.Type</exception> - public static BaseTaskTrigger GetTrigger(TaskTriggerInfo info, IKernel kernel) + public static BaseTaskTrigger GetTrigger(TaskTriggerInfo info) { if (info.Type.Equals(typeof(DailyTrigger).Name, StringComparison.OrdinalIgnoreCase)) { @@ -144,7 +143,7 @@ namespace MediaBrowser.Common.ScheduledTasks if (info.Type.Equals(typeof(StartupTrigger).Name, StringComparison.OrdinalIgnoreCase)) { - return new StartupTrigger(kernel); + return new StartupTrigger(); } throw new ArgumentException("Unrecognized trigger type: " + info.Type); diff --git a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs index 84775924f..a254d2be9 100644 --- a/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/StartupTrigger.cs @@ -1,6 +1,4 @@ -using MediaBrowser.Common.Kernel; -using System; -using System.Threading.Tasks; +using System.Threading.Tasks; namespace MediaBrowser.Common.ScheduledTasks { @@ -10,33 +8,17 @@ namespace MediaBrowser.Common.ScheduledTasks public class StartupTrigger : BaseTaskTrigger { /// <summary> - /// Gets the kernel. - /// </summary> - /// <value>The kernel.</value> - protected IKernel Kernel { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="StartupTrigger" /> class. - /// </summary> - /// <param name="kernel">The kernel.</param> - public StartupTrigger(IKernel kernel) - { - Kernel = kernel; - } - - /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal override void Start() - { - Kernel.ReloadCompleted += Kernel_ReloadCompleted; - } - - async void Kernel_ReloadCompleted(object sender, EventArgs e) + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + protected internal async override void Start(bool isApplicationStartup) { - await Task.Delay(2000).ConfigureAwait(false); + if (isApplicationStartup) + { + await Task.Delay(2000).ConfigureAwait(false); - OnTriggered(); + OnTriggered(); + } } /// <summary> @@ -44,7 +26,6 @@ namespace MediaBrowser.Common.ScheduledTasks /// </summary> protected internal override void Stop() { - Kernel.ReloadCompleted -= Kernel_ReloadCompleted; } } } diff --git a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs index 3075a8587..45d1fae8e 100644 --- a/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/SystemEventTrigger.cs @@ -18,7 +18,8 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal override void Start() + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + protected internal override void Start(bool isApplicationStartup) { switch (SystemEvent) { diff --git a/MediaBrowser.Common/ScheduledTasks/TaskManager.cs b/MediaBrowser.Common/ScheduledTasks/TaskManager.cs index b71ab8161..946c42d2e 100644 --- a/MediaBrowser.Common/ScheduledTasks/TaskManager.cs +++ b/MediaBrowser.Common/ScheduledTasks/TaskManager.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; using System; using System.Collections.Generic; @@ -10,9 +9,15 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Class TaskManager /// </summary> - public class TaskManager : BaseManager<IKernel> + internal class TaskManager : ITaskManager { /// <summary> + /// Gets the list of Scheduled Tasks + /// </summary> + /// <value>The scheduled tasks.</value> + public IScheduledTask[] ScheduledTasks { get; private set; } + + /// <summary> /// The _task queue /// </summary> private readonly List<Type> _taskQueue = new List<Type>(); @@ -25,12 +30,17 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Initializes a new instance of the <see cref="TaskManager" /> class. /// </summary> - /// <param name="kernel">The kernel.</param> /// <param name="logger">The logger.</param> - public TaskManager(IKernel kernel, ILogger logger) - : base(kernel) + public TaskManager(ILogger logger) { + if (logger == null) + { + throw new ArgumentException("logger"); + } + _logger = logger; + + ScheduledTasks = new IScheduledTask[] {}; } /// <summary> @@ -40,7 +50,7 @@ namespace MediaBrowser.Common.ScheduledTasks public void CancelIfRunningAndQueue<T>() where T : IScheduledTask { - Kernel.ScheduledTasks.OfType<T>().First().CancelIfRunning(); + ScheduledTasks.OfType<T>().First().CancelIfRunning(); QueueScheduledTask<T>(); } @@ -51,7 +61,7 @@ namespace MediaBrowser.Common.ScheduledTasks public void QueueScheduledTask<T>() where T : IScheduledTask { - var scheduledTask = Kernel.ScheduledTasks.OfType<T>().First(); + var scheduledTask = ScheduledTasks.OfType<T>().First(); QueueScheduledTask(scheduledTask); } @@ -64,7 +74,7 @@ namespace MediaBrowser.Common.ScheduledTasks { var type = task.GetType(); - var scheduledTask = Kernel.ScheduledTasks.First(t => t.GetType() == type); + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); lock (_taskQueue) { @@ -91,7 +101,7 @@ namespace MediaBrowser.Common.ScheduledTasks /// Called when [task completed]. /// </summary> /// <param name="task">The task.</param> - internal void OnTaskCompleted(IScheduledTask task) + public void OnTaskCompleted(IScheduledTask task) { // Execute queued tasks lock (_taskQueue) @@ -100,7 +110,7 @@ namespace MediaBrowser.Common.ScheduledTasks foreach (var type in copy) { - var scheduledTask = Kernel.ScheduledTasks.First(t => t.GetType() == type); + var scheduledTask = ScheduledTasks.First(t => t.GetType() == type); if (scheduledTask.State == TaskState.Idle) { @@ -111,5 +121,39 @@ namespace MediaBrowser.Common.ScheduledTasks } } } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// <summary> + /// Releases unmanaged and - optionally - managed resources. + /// </summary> + /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> + protected virtual void Dispose(bool dispose) + { + foreach (var task in ScheduledTasks) + { + task.Dispose(); + } + } + + /// <summary> + /// Adds the tasks. + /// </summary> + /// <param name="tasks">The tasks.</param> + public void AddTasks(IEnumerable<IScheduledTask> tasks) + { + var myTasks = ScheduledTasks.ToList(); + + myTasks.AddRange(tasks); + + ScheduledTasks = myTasks.ToArray(); + } } } diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs index 2a9bc4a0d..b06134ee2 100644 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteCacheFileTask.cs @@ -1,8 +1,7 @@ using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Threading; @@ -13,10 +12,20 @@ namespace MediaBrowser.Common.ScheduledTasks.Tasks /// <summary> /// Deletes old cache files /// </summary> - [Export(typeof(IScheduledTask))] public class DeleteCacheFileTask : BaseScheduledTask<IKernel> { /// <summary> + /// Initializes a new instance of the <see cref="DeleteCacheFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public DeleteCacheFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> /// Creates the triggers that define when the task will run /// </summary> /// <returns>IEnumerable{BaseTaskTrigger}.</returns> diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs index a1068a263..0b243cb10 100644 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/Tasks/DeleteLogFileTask.cs @@ -1,8 +1,7 @@ using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.IO; using System.Linq; using System.Threading; @@ -13,10 +12,20 @@ namespace MediaBrowser.Common.ScheduledTasks.Tasks /// <summary> /// Deletes old log files /// </summary> - [Export(typeof(IScheduledTask))] public class DeleteLogFileTask : BaseScheduledTask<IKernel> { /// <summary> + /// Initializes a new instance of the <see cref="DeleteLogFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public DeleteLogFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> /// Creates the triggers that define when the task will run /// </summary> /// <returns>IEnumerable{BaseTaskTrigger}.</returns> diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs index a4f06f205..35cbe98f1 100644 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/Tasks/ReloadLoggerTask.cs @@ -1,8 +1,7 @@ using MediaBrowser.Common.Kernel; -using MediaBrowser.Model.Tasks; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; @@ -11,10 +10,20 @@ namespace MediaBrowser.Common.ScheduledTasks.Tasks /// <summary> /// Class ReloadLoggerFileTask /// </summary> - [Export(typeof(IScheduledTask))] public class ReloadLoggerFileTask : BaseScheduledTask<IKernel> { /// <summary> + /// Initializes a new instance of the <see cref="ReloadLoggerFileTask" /> class. + /// </summary> + /// <param name="kernel">The kernel.</param> + /// <param name="taskManager">The task manager.</param> + /// <param name="logger">The logger.</param> + public ReloadLoggerFileTask(IKernel kernel, ITaskManager taskManager, ILogger logger) + : base(kernel, taskManager, logger) + { + } + + /// <summary> /// Gets the default triggers. /// </summary> /// <returns>IEnumerable{BaseTaskTrigger}.</returns> diff --git a/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs b/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs index f9950424f..f02293a5e 100644 --- a/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs +++ b/MediaBrowser.Common/ScheduledTasks/Tasks/SystemUpdateTask.cs @@ -1,7 +1,7 @@ using MediaBrowser.Common.Kernel; +using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.ComponentModel.Composition; using System.Threading; using System.Threading.Tasks; @@ -10,7 +10,6 @@ namespace MediaBrowser.Common.ScheduledTasks.Tasks /// <summary> /// Plugin Update Task /// </summary> - [Export(typeof(IScheduledTask))] public class SystemUpdateTask : BaseScheduledTask<IKernel> { /// <summary> @@ -22,8 +21,11 @@ namespace MediaBrowser.Common.ScheduledTasks.Tasks /// Initializes a new instance of the <see cref="SystemUpdateTask" /> class. /// </summary> /// <param name="appHost">The app host.</param> - [ImportingConstructor] - public SystemUpdateTask([Import("appHost")] IApplicationHost appHost) + /// <param name="taskManager">The task manager.</param> + /// <param name="kernel">The kernel.</param> + /// <param name="logger">The logger.</param> + public SystemUpdateTask(IApplicationHost appHost, ITaskManager taskManager, IKernel kernel, ILogger logger) + : base(kernel, taskManager, logger) { _appHost = appHost; } diff --git a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs index 136dc8b13..afeacc2b3 100644 --- a/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs +++ b/MediaBrowser.Common/ScheduledTasks/WeeklyTrigger.cs @@ -29,7 +29,8 @@ namespace MediaBrowser.Common.ScheduledTasks /// <summary> /// Stars waiting for the trigger action /// </summary> - protected internal override void Start() + /// <param name="isApplicationStartup">if set to <c>true</c> [is application startup].</param> + protected internal override void Start(bool isApplicationStartup) { DisposeTimer(); |
