using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WatchdogLib.WPF;
using WatchdogLib.Thread;
using System.Timers;
using Watchdog.WatchdogLib.Process;
using System.Diagnostics;
using WatchdogLib.File;
using WatchdogLib.Tools;
using Watchdog.WatchdogLib.File;
using Watchdog.WatchdogLib.Registry;
using Watchdog.WatchdogLib.Assembly;
using System.Windows.Threading;
using Watchdog.WatchdogLib.Other;
using Watchdog.WPFWindows;
using Watchdog.WatchdogLib.Monitor;
using Watchdog.WatchdogLib.Net;
using Watchdog.WatchdogLib.Win32;
namespace Watchdog
{
///
/// Interaction logic for MainWindow.xaml
///
public partial class HiddenMainWindow : Window
{
#region Private Statics
// Core Timers * And HiddenWindow Var *
private static KeepHidden _HiddenWindow = null;
private static TTimer _timer = new TTimer(TTimer_ElapsedEventHandler, 1523, false, false); // Kicks in every ~1.5 Seconds
private static TTimer _timerScheduler = new TTimer(TTimer_ElapsedEventHandlerScheduler, 61491, false, false); // Kicks in every ~1 Minute
private static TTimer _timerSchedulerBackup = new TTimer(TTimer_ElapsedEventHandlerSchedulerBackup, 99986, false, false); // Kicks in every 1.665 Minutes (uneven so timers have the highest chance of not overlapping)
// In case an unexpected process shutdown occurs, let's try to do more clean-up
private static bool s_WindowCloseGotCalled = false;
// to makes sure we have a clean shut down * Prev State Tracking *
private static bool s_bIsTimerRunning = false;
private static bool s_bIsSchedulerTimerRunning = false;
private static bool s_bIsSchedulerBackupTimerRunning = false;
// Start/Stop Timers *While shuting down _timer can be null* calling These functions and ignore any errors,
// Functions return the * previous state before calling * so if the previous state was 'Off' then it returns false, previous state was 'On' it returns true;
private static bool StartTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsTimerRunning; try { _timer.Start(); s_bIsTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } }
private static bool StopTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsTimerRunning; try { _timer.Stop(); s_bIsTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } }
private static bool StartSchedulerTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerTimerRunning; try { _timerScheduler.Start(); s_bIsSchedulerTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } }
private static bool StopSchedulerTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerTimerRunning; try { _timerScheduler.Stop(); s_bIsSchedulerTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } }
//private static bool StartSchedulerBackupTimer(uint IntervalInMiliseconds = 99986) { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Start(IntervalInMiliseconds); s_bIsSchedulerBackupTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } }
private static bool StartSchedulerBackupTimer(uint IntervalInMiliseconds) { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Start(IntervalInMiliseconds); s_bIsSchedulerBackupTimerRunning = true; } catch (Exception) { /* ignore */ } return bPrevState; } }
private static bool StopSchedulerBackupTimer() { lock (_HiddenWindow) { bool bPrevState = s_bIsSchedulerBackupTimerRunning; try { _timerSchedulerBackup.Stop(); s_bIsSchedulerBackupTimerRunning = false; } catch (Exception) { /* ignore */ } return bPrevState; } }
#endregion
#region Internal Statics * (Monitor API) *
///
/// Use this to Start the Process Monitor * Also Called by Scheduler and Scheduler Backup *
///
/// true to force a one-time refresh of XML DS Data, false otherwise
/// true to interact with desktop, false otherwise
//internal static void StartMonitoring(bool bReloadConfigurationData = false, bool bShowUserPopup = true)
internal static void StartMonitoring(bool bReloadConfigurationData, bool bShowUserPopup)
{
// If the Monitor was Paused Prior? - restart the Timers
if (App.State_IsMonitorPaused())
{
lock (_HiddenWindow)
{
// Set to Running
App.State_MonitorStarted();
// Re-start Timers
StartTimer();
StartScheduler();
StartSchedulerBackup(99986);
// Log and Inform the User
App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STARTED_FROM_PAUSE", App.APPLICATION_NAME_SHORT, false));
if(bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_START_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME));
// * Set the System Tray Icon According to Running State *
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState());
return;
}
}
// Is the Monitor already running?
if (App.State_MonitorIsInRunningState())
return;
// Set to Running
App.State_MonitorStarted();
// Let's get Started
lock (_HiddenWindow)
{
// Reset the Last Scheduler Run Time
if (!App.State_IsSchedulerExecuting())
App.setting.LastSchedulerRun = DateTime.MinValue;
// Reset the Last Scheduler Backup Run Time
if (!App.State_IsSchedulerBackupExecuting())
App.setting.LastSchedulerBackupRun = DateTime.MinValue;
// Reset Error State * But Only if we are NOT being Called by the Scheduler *
if (!App.State_IsSchedulerExecuting())
App.State_Error_ResetErrorState();
// Log Start of monitoring
App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STARTED", App.APPLICATION_NAME_SHORT, bReloadConfigurationData ? "True" : "False"));
// * Set the System Tray Icon According to Running State *
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState());
// Popup Std Start Monitoring Message
if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet())
{
if (App.State_IsSchedulerExecuting())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME));
else if (App.State_IsSchedulerBackupExecuting())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY2", App.APPLICATION_NAME_SHORT));
else
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_START_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME));
}
// Reload Configuration, if neccessary
App.xmlfile.ForceRefreshOnNext_ReadData = bReloadConfigurationData;
if (!bReloadConfigurationData)
{
// make sure that at least all the process/service info is cleared
App.xmlfile.ReadData(false).ClearAllStartNFailTimes();
// also clear PID information
App.xmlfile.ReadData(false).MonitoredProcesses.ClearPidInformation();
}
// Let the Schedulers Handle the starting of the Timers,
// * don't start anything if it is already executing - concurrency *
if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting())
{
if(App.setting.SchedulerSettingsExist || App.setting.SchedulerBackupSettingsExist)
{
// When we StartMonitoring, we should delay start the Scheduler,
// to give the timer a change to start all processes. 10 Seconds should do.
ElapsedEventHandler timedStart = delegate(object sender, ElapsedEventArgs e)
{
Timer timer = (Timer)sender;
timer.Stop();
StartScheduler(); // * Start the Scheduler, if it is set *
StartSchedulerBackup(99986); // * Start the Scheduler Backup, if it is set *
timer.Dispose();
timer = null;
};
TTimer delayStartScheduler = new TTimer(timedStart, 5000, true, true);
}
}
// Allow Scheduler to manage this timer
if(!App.State_IsSchedulerExecuting())
StartTimer();
}
}
///
/// Pause the Monitor * Also Called by Scheduler Backup *
///
/// true to interact with desktop, false otherwise
//internal static void PauseMonitoring(bool bShowUserPopup = true)
internal static void PauseMonitoring(bool bShowUserPopup)
{
if (App.State_IsMonitorPaused() || !App.State_MonitorIsInRunningState())
return;
App.log.Info("PauseMonitoring Called");
lock (_HiddenWindow)
{
// Stop Both Timers, inside a Lock,
// should do the trick * Neither should execute *
StopTimer();
StopScheduler();
if (!App.State_IsSchedulerBackupExecuting())
StopSchedulerBackup();
// Mark the System as a Paused State
App.State_MonitorPaused();
// Log Pause of monitoring
App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_PAUSED", App.APPLICATION_NAME_SHORT));
if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_MONITOR_PAUSED", App.APPLICATION_NAME_SHORT));
// * Set the System Tray Icon According to Current State *
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState());
}
}
///
/// Restarts the Monitor * Also Called by Scheduler *
///
/// Time in seconds to wait before starting (after stopping)
/// true to interact with desktop, false otherwise
//internal static void RestartMonitoring(uint nWaitBeforeStartSeconds = 4, bool bShowUserPopup = true)
internal static void RestartMonitoring(uint nWaitBeforeStartSeconds, bool bShowUserPopup)
{
App.log.Info("RestartMonitoring Called");
// Stop the Monitor
StopMonitoring(bShowUserPopup, false);
// Sleep, for a little while,...
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(nWaitBeforeStartSeconds));
// Now Start again...
StartMonitoring(!App.State_IsSchedulerExecuting(), bShowUserPopup);
}
///
/// Restarts the Monitor, including all services
///
/// Time in seconds to wait before starting (after stopping)
/// true to interact with desktop, false otherwise
//internal static void RestartAllMonitoring(uint nWaitBeforeStartSeconds = 4, bool bShowUserPopup = true)
internal static void RestartAllMonitoring(uint nWaitBeforeStartSeconds, bool bShowUserPopup)
{
App.log.Info("RestartAllMonitoring Called");
// Stop the Monitor
StopMonitoring(bShowUserPopup, true);
// Sleep, for a little while,...
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(nWaitBeforeStartSeconds));
// Now Start again...
StartMonitoring(!App.State_IsSchedulerExecuting(), bShowUserPopup);
}
///
/// Stops Monitoring, including all Services
///
/// true to interact with desktop, false otherwise
//internal static void StopAllMonitoring(bool bShowUserPopup = true)
internal static void StopAllMonitoring(bool bShowUserPopup)
{
App.log.Info("StopAllMonitoring Called");
StopMonitoring(bShowUserPopup, true);
}
///
/// Use this to Stop the Process Monitor * Also Called by Scheduler and Scheduler Backup *
///
//internal static void StopMonitoring(bool bShowUserPopup = true, bool bForceShutdownOfServices = false)
internal static void StopMonitoring(bool bShowUserPopup, bool bForceShutdownOfServices)
{
// Is the Monitor even running?
if (!App.State_MonitorIsInRunningState())
return;
// Set to Stopped
App.State_MonitorStopped();
lock (_HiddenWindow)
{
// Reset Error State * But Only if we are NOT being Called by the Scheduler *
if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting())
App.State_Error_ResetErrorState();
// Log Stop of monitoring
App.log.Info(AppResx.GetStringFormated("MESSAGE_MONITOR_STOPPED", App.APPLICATION_NAME_SHORT));
// Stop the Internal Timers * Always do this * just in case
StopTimer();
StopSchedulerTimer();
if (!App.State_IsSchedulerBackupExecuting())
StopSchedulerBackupTimer();
// Read the config
WatchdogConfiguration config = App.xmlfile.ReadData(false);
// * Set the System Tray Icon According to Running State *
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState());
// Popup Std Stop Monitoring Message * But Only if Scheduler && Scheduler Backup is NOT running *
if (bShowUserPopup && !App.State_SpecialMode_IsRunAsServiceSet())
{
if(App.State_IsSchedulerExecuting())
{
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT));
App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT));
}
else if (App.State_IsSchedulerBackupExecuting())
{
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT));
App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP_DISPLAY1", App.APPLICATION_NAME_SHORT));
}
else
{
if (config.ConfiguredHasAny)
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_STOP_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME, bForceShutdownOfServices));
else
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME));
}
if (config.ConfiguredHasAny)
App.log.Info(AppResx.GetStringFormated("MESSAGE_STOP_SUCCESS", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME, bForceShutdownOfServices));
else
App.log.Error(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME));
}
// Shutdown any of our Running Processes
TraceM.TraceBegin();
bool bKillApplications = !(App.State_SpecialCircum_ShouldWeNotCloseApplicationsOnExit() && s_WindowCloseGotCalled);
if(bKillApplications)
ProcessKillAllMonitoredProcessesIfAny(config);
TraceM.TraceEnd("Killing Running Applications");
// Shutdown any of our Running Services
TraceM.TraceBegin();
ServicesKillAllMonitoredServicesIfAny(config, bForceShutdownOfServices);
TraceM.TraceEnd("Killing Running Services");
// Try Reclaim memory by running the Garbage Collector
// * Really appears to not be doing much *
if (!App.State_IsSchedulerExecuting() && !App.State_IsSchedulerBackupExecuting())
{
TraceM.TraceBegin();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
TraceM.TraceEnd("Garbage Collector Called");
}
}
}
///
/// Use this to Start the Scheduler
///
internal static void StartScheduler()
{
lock (_HiddenWindow)
{
// Start the Scheduler Only if there is any Scheduler Information (and the Settings Window
// isn't currently being shown - Settings window disables/enables Scheduler on it's own *
if (!App.State_IsSchedulerExecuting() && App.setting.SchedulerSettingsExist &&
!WPFWindowMaker.IsWindowOfTypeWPFWindowShowing_WithinLock(WPFWindow.Settings_Window))
StartSchedulerTimer();
}
}
///
/// Use this to Stop the Scheduler
///
internal static void StopScheduler()
{
lock (_HiddenWindow)
{
StopSchedulerTimer();
}
}
///
/// Use this to Start the Scheduler Backup
///
//internal static void StartSchedulerBackup(uint IntervalInMiliseconds = 99986)
internal static void StartSchedulerBackup(uint IntervalInMiliseconds)
{
lock (_HiddenWindow)
{
// Start the Scheduler Backup Only if there is any Scheduler Backup Information (and the Settings Window
// isn't currently being shown - Settings window disables/enables Scheduler Backup on it's own *
if (!App.State_IsSchedulerBackupExecuting() && App.setting.SchedulerBackupSettingsExist &&
!WPFWindowMaker.IsWindowOfTypeWPFWindowShowing_WithinLock(WPFWindow.Settings_Window))
StartSchedulerBackupTimer(IntervalInMiliseconds);
}
}
///
/// Use this to Stop the Scheduler Backup
///
internal static void StopSchedulerBackup()
{
lock (_HiddenWindow)
{
StopSchedulerBackupTimer();
}
}
#endregion
#region Construction
///
/// Constructor
///
public HiddenMainWindow()
{
InitializeComponent();
// Only continue, if no fatal application error occured
if (!App.s_FatalErrorOccured)
{
// * Force this window to remain hidden indefinitly*
_HiddenWindow = new KeepHidden(this);
// Assign ProcessExit Event to Handle more specialized Cleanup
AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
// We DON'T NEED TO SHOW THE HIDDEN WINDOW * Triggering OnLoad(), if we are
// being launched via the Command-Line.
if (App.State_SpecialMode_IsCommandLineSet())
{
// If we are in Command-Line Mode * Then Hidden Window should never be shown *
// in fact we'll enter into a whole new entry point * CommandLine MODE
ElapsedEventHandler EnterCommandLineMode = delegate(object sender, ElapsedEventArgs e)
{
Timer timer = (Timer)sender;
timer.Stop();
// * Enter CommandLine Mode *
CMDlineHandler.EnterCommandLineMode();
timer.Dispose();
timer = null;
// Done Here, shut down the app
App.Current.Shutdown();
};
TTimer timerEnterCommandLineMode = new TTimer(EnterCommandLineMode, 500, true, true);
}
else
{
// Calling Show() directly in the Constructor causes a .Net Run-time Crash
// upon first launch only. Calling it .5 seconds later does the trick of avoiding that error,
// because by then the object is fully constructed
ElapsedEventHandler DelayShowHiddenWindow = delegate(object sender, ElapsedEventArgs e)
{
Timer timer = (Timer)sender;
timer.Stop();
// * Show the Hidden Window *
_HiddenWindow.Show();
timer.Dispose();
timer = null;
};
TTimer timerDelayShowHiddenWindow = new TTimer(DelayShowHiddenWindow, 500, true, true);
}
}
}
#endregion
#region Process Monitor / Service Monitor * CORE LOGIC *
///
/// Called every Nth Interval, does the grunt of the work for this whole
/// Application. It checks the processes, and if they are running, etc...
/// * Core Business Logic *
///
private static void TTimer_ElapsedEventHandler(object sender, ElapsedEventArgs e)
{
lock (_HiddenWindow)
{
// Stop the timer to prevent multiple call-Ins, while work is still being done
StopTimer();
// Everything is Good, Monitor the Processes / Services Start any processes / Services that aren't available
if (App.State_MonitorIsInRunningState() && !App.State_IsMonitorPaused())
{
// Get the Configuration
WatchdogConfiguration config = App.xmlfile.ReadData(false);
// * NO CONFIGURATION * Stop! - Nothing to monitor
if (!config.ConfiguredHasAny)
{
StopMonitoring(true, false);
return;
}
// * Read the Ini Configuration Settings *
TraceM.TraceBegin();
uint nMaxFailureCount = App.setting.MaxFailureCount;
uint nMaxFailureCountHour = App.setting.MaxFailureCountHour;
TraceM.TraceEnd("Monitor - Reading in INI Configuration");
// * SERVICE MONITORING *
TraceM.TraceBegin();
bool bErrorThrownInServicesMonitor = !ServiceMonitor(config, nMaxFailureCount, nMaxFailureCountHour);
TraceM.TraceEnd("* Service Monitoring *");
// * PROCESS MONITORING *
TraceM.TraceBegin();
bool bErrorThrownInProcessMonitor = !ProcessMonitor(config, nMaxFailureCount, nMaxFailureCountHour);
TraceM.TraceEnd("* Process Monitoring *");
// If Any Unknown Errors were Thrown! Stop the Monitor
if (bErrorThrownInServicesMonitor || bErrorThrownInProcessMonitor)
{
StopMonitoring(true, false);
////
// An Error Occured with the Application * Better Notify User, if specified *
////
if (App.setting.EmailNotificationEnabled && App.setting.EmailSettingsValid)
{
bool bIsSend = Emailer.SendEmail(App.setting.EmailStmpServer, App.setting.EmailSenderEmail, App.APPLICATION_NAME_SHORT,
App.setting.EmailReceiverEmail, AppResx.GetStringFormated("ERROR_EMAIL_SUBJECT", App.APPLICATION_NAME_SHORT),
AppResx.GetStringFormated("ERROR_EMAIL_BODY", App.APPLICATION_NAME_SHORT, App.setting.EmailCustomMessageText),
App.setting.EmailSmtpPort, String.Empty, App.setting.EmailStmpServerRequiresAuth, App.setting.EmailSmtpUsername, App.setting.EmailSmtpPassword,
App.setting.EmailStmpServerRequiresSSL, 30);
if (!bIsSend)
App.log.Error("Failed to Send ERROR_EMAIL");
else
App.log.Info(String.Format("Email (ERROR_EMAIL) Notification send to {0}", App.setting.EmailReceiverEmail));
}
return;
}
if (!App.State_IsMonitorPaused()) // If the System Is Paused, no need to start the Timer again
{
// Re-Start the timer, if all went well
// * At the end of each Cycle, Set the System Tray Icon Accordingly * Keep track when we were last run
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.SetSysTrayIconAccordingToRunningState(App.State_GetSystemState());
StartTimer(); // Everything went well, continue...
}
}
}
}
///
/// Used by the Scheduler Timer to determine if a restart is to occur
///
/// now datetime stamp
/// scheduler start datetime stamp
/// datetime when last run occured
/// numeric value indicating hours or minutes
/// true if hours, false if minutes
/// true if we want the Scheduler to Restart, false otherwise
private static bool PerformRestartTimeSchedulerTriggered(DateTime now, DateTime start, DateTime LastRun, uint nHoursOrMinutes, bool bIsHours)
{
// * OLD CALCULATION * OLD SCHOOL * Make sure enough time has passed since last run, otherwise no need to continue
//TimeSpan tsLastRun = now - LastRun;
//bool bEnoughTimeSinceLastRunHasPassed = bIsHours ? (((double)(tsLastRun.TotalMinutes / 60)) >= ((double)nHoursOrMinutes)) : (tsLastRun.TotalMinutes >= ((double)nHoursOrMinutes));
//return bEnoughTimeSinceLastRunHasPassed;
// If the current time is within 2 minutes of last run, there is no need to continue
TimeSpan tsDiffBetweenNowAndLastRun = (now - LastRun);
bool bIsWithin2MinutesOfLastRun = (tsDiffBetweenNowAndLastRun.TotalMinutes >= 0) && (tsDiffBetweenNowAndLastRun.TotalMinutes <= 2.9997);
if (bIsWithin2MinutesOfLastRun)
return false;
// Check the start time * if we are near the start time and a last run hasn't occured, then yes, run this *
TimeSpan tsDiffBetweenNowAndStart = (now - start);
bool bIsWithin2MinutesOfScheduledStartTime = (tsDiffBetweenNowAndStart.TotalMinutes >= 0) && (tsDiffBetweenNowAndStart.TotalMinutes <= 2.9997);
if (bIsWithin2MinutesOfScheduledStartTime)
return true;
// Perform Cycle Calculation
double durationInMinutes = bIsHours ? nHoursOrMinutes * 60 : nHoursOrMinutes;
double remCycle = tsDiffBetweenNowAndStart.TotalMinutes % durationInMinutes;
bool bIsWithin2MinutesOfRemCycle = (remCycle >= 0) && (remCycle <= 2.9997);
return bIsWithin2MinutesOfRemCycle;
}
///
/// Called every Nth Interval, does the grunt of the work for the Scheduler
/// * Core Business Logic *
///
private static void TTimer_ElapsedEventHandlerScheduler(object sender, ElapsedEventArgs e)
{
lock (_HiddenWindow)
{
// *imp * stop the process/services monitor (so that while
// this timer occurs it won't conflict with what happens here
bool bPrevStateTimer = StopTimer();
// Stop the Scheduler timer to prevent multiple call-Ins, while work is still being done
StopSchedulerTimer();
// Everything is Good,
if (App.State_MonitorIsInRunningState() && !App.State_IsSchedulerExecuting() && !App.State_IsMonitorPaused())
{
// Set the Scheduler State
App.State_SchedulerExecution_Started();
// Fetch scheduler settings
bool bFoundSchedulerSettings = false;
DateTime dtStart = DateTime.MinValue;
DateTime dtLastRun = DateTime.MinValue;
uint nHoursOrMinutes = 0;
bool bIsHours = true;
// Only Do something if there are any settings
bFoundSchedulerSettings = App.setting.GetSchedulerSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours);
if (!bFoundSchedulerSettings)
return; // No Scheduler Settings, no need to continue
// Get the Now Time
DateTime dtNow = DateTime.Now;
// Make sure we've reached the Start Time, else there is no need to continue
if (dtNow >= dtStart && PerformRestartTimeSchedulerTriggered(dtNow, dtStart, dtLastRun, nHoursOrMinutes, bIsHours))
{
// Log the fact that Scheduler is about to stop / start
App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_STARTSTOP", App.APPLICATION_NAME_SHORT, App.APP_XML_DS_FILENAME));
// * Perform Restart *
RestartMonitoring(4, true);
// Set Last Run
App.setting.LastSchedulerRun = dtNow;
}
// Reset the Scheduler State
App.State_SchedulerExecution_Stopped();
}
if (!App.State_IsMonitorPaused()) // If the System Is Paused, no need to start the Timers again
{
// Re-Start the Scheduler timer * Everything went well s *
StartScheduler();
// Now it should be save to start the Main Monitor Timer
if (bPrevStateTimer)
StartTimer();
}
}
}
///
/// Integrity - When we fire a start Trigger with SchedulerBackupTriggerOccured(), in case another call in occurs,
/// we know to ignore it
///
private static DateTime _LastStartTriggerFired = DateTime.MinValue;
///
/// Integrity - When we fire a end Trigger with SchedulerBackupTriggerOccured(), in case another call in occurs,
/// we know to ignore it
///
private static DateTime _LastEndTriggerFired = DateTime.MinValue;
///
/// Deterimes the State of the Scheduler Backup and what action to take
///
/// The Now DateTime
/// Scheduled Start DateTime
/// Last Run, should be set when the first start occured and passed into here for checking
/// the duration from start to end
/// we want to perform the following action * Trigger Occured *, 0 = None, 1 = Start, 2 = End
private static uint SchedulerBackupTriggerOccured (DateTime dtNow, DateTime dtScheduledStartDT, DateTime dtLastRun, TimeSpan tsDuration)
{
// Time Of Day Vars
TimeSpan TimeOfDayNow = dtNow.TimeOfDay;
TimeSpan TimeOfDayScheduled = dtScheduledStartDT.TimeOfDay;
// Debug
//dtNow = DateTime.Parse("8/13/2011 01:57:29 AM");
//TimeOfDayNow = new TimeSpan(1, 57, 29);
//TimeOfDayScheduled = new TimeSpan(1, 56, 11);
//tsDuration = new TimeSpan(4, 0, 0);
//dtLastRun = DateTime.Parse("1/1/0001 12:00:00 AM");
// Time of Day Within Time Calc;
TimeSpan tsDiffBetweenNowAndScheduled = (TimeOfDayNow - TimeOfDayScheduled);
bool bIsWithin2MinutesOfScheduledTime = (tsDiffBetweenNowAndScheduled.TotalMinutes > 0) && (tsDiffBetweenNowAndScheduled.TotalMinutes <= 2.9997);
TimeSpan tsDiffBetweenNowAndScheduledDurationEnd = TimeOfDayNow - (TimeOfDayScheduled + tsDuration);
if(tsDiffBetweenNowAndScheduledDurationEnd.TotalMinutes < 0) // A Day must have passed
tsDiffBetweenNowAndScheduledDurationEnd = tsDiffBetweenNowAndScheduledDurationEnd + new TimeSpan(24, 00, 00);
bool bIsWithin2MinutesOfScheduledTimeDurationEnd = (tsDiffBetweenNowAndScheduledDurationEnd.TotalMinutes <= 2.9997);
// Last Run Calculations * make sure to see if it was started prior
// ~Last Run allows us to determine if there exists a start for an end,
// we only check the end event to see if a start occured
if (bIsWithin2MinutesOfScheduledTimeDurationEnd)
{
TimeSpan tsDiffBetweenNowAndLastRun = dtNow - dtLastRun;
TimeSpan tsDiffInDuration = tsDiffBetweenNowAndLastRun - tsDuration;
// In the event the Monitor App got started somewhere in between times,
// and we trigger the end event (because time was reached), we
// are checking last run, to see if it coincides with this end event,
// if it doesn't (as in it is way too long ago ~here anything above 10 minutes out
// of bounds from the duration), we won't trigger the end event, assuming
// that it never started in this cycle to begin with, waiting for the next cycle
bool bIsLastRunOutOfBounce = (tsDiffInDuration.TotalMinutes > 0) && (tsDiffInDuration.TotalMinutes >= 10);
if (bIsLastRunOutOfBounce)
return 0;
}
// Integrity Check * prevent multiple callins within time boundary 2.9997, * or actually
// within the same day * may as well, we only allow 1 start/end trigger per day
if (bIsWithin2MinutesOfScheduledTime)
{
TimeSpan ts = dtNow - _LastStartTriggerFired;
if ((ts.TotalMinutes > 0) && (ts.TotalMinutes > 1000)) // we should only be firing on start trigger per day aka 1440, but 1000 is a good check (even)
_LastStartTriggerFired = dtNow;
else
bIsWithin2MinutesOfScheduledTime = false;
}
else if (bIsWithin2MinutesOfScheduledTimeDurationEnd)
{
TimeSpan ts = dtNow - _LastEndTriggerFired;
if ((ts.TotalMinutes > 0) && (ts.TotalMinutes > 1000)) // we should only be firing on end trigger per day aka 1440, but 1000 is a good check (even)
_LastStartTriggerFired = dtNow;
else
bIsWithin2MinutesOfScheduledTime = false;
}
// Trigger Start / Stop / Or Nothing
if (bIsWithin2MinutesOfScheduledTime)
return 1;
else if (bIsWithin2MinutesOfScheduledTimeDurationEnd)
return 2;
else
return 0;
}
///
/// Called every Nth Interval, does the grunt of the work for the Scheduler Backup
/// * Core Business Logic *
///
private static void TTimer_ElapsedEventHandlerSchedulerBackup(object sender, ElapsedEventArgs e)
{
lock (_HiddenWindow)
{
// Stop the Scheduler Backup timer to prevent multiple call-Ins, while work is still being done
StopSchedulerBackupTimer();
// Everything is Good,
//if (App.State_MonitorIsInRunningState() && !App.State_IsSchedulerBackupExecuting()) // BUG
if (!App.State_IsSchedulerBackupExecuting())
{
// Set the Scheduler State
App.State_SchedulerBackupExecution_Started();
// Fetch scheduler Backup settings
bool bFoundSchedulerBackupSettings = false;
DateTime dtStart = DateTime.MinValue;
DateTime dtLastRun = DateTime.MinValue;
uint nHoursOrMinutes = 0;
bool bIsHours = true;
// Only Do something if there are any settings
bFoundSchedulerBackupSettings = App.setting.GetSchedulerBackupSettings(out dtStart, out dtLastRun, out nHoursOrMinutes, out bIsHours);
if (!bFoundSchedulerBackupSettings)
return; // No Scheduler Backup Settings, no need to continue
// Get the Now Time
DateTime dtNow = DateTime.Now;
// Make sure we've reached the Start Time, else there is no need to continue
if (dtNow >= dtStart)
{
// Get the Duration
TimeSpan tsDuration = bIsHours ? TimeSpan.FromHours(nHoursOrMinutes) : TimeSpan.FromMinutes(nHoursOrMinutes);
// Determine the State of the Scheduler Backup an the Action To Take
uint nTriggerResult = SchedulerBackupTriggerOccured(dtNow, dtStart, dtLastRun, tsDuration);
bool bPerformAnAction = (nTriggerResult == 1) || (nTriggerResult == 2);
if (bPerformAnAction)
{
// Get Current State
App.StateSystem state = App.State_GetSystemState();
bool bStopInsteadOfPause = App.setting.SchedulerBackupStopInsteadOfPause;
bool bStopServices = App.setting.SchedulerBackupStopServices;
// Log the fact that Scheduler Backup is about to pause / stop / start
App.log.Info(AppResx.GetStringFormated("MESSAGE_SCHEDULER_BACKUP_STARTSTOP", App.APPLICATION_NAME_SHORT, (state == App.StateSystem.Paused)));
// * Perform System Pause * or
// * Perform System Start *, or * System Stop *, depending on system state and Settings
switch (nTriggerResult)
{
case 1: // Duration Start
{
// We are in a wrong state to perform a start
if (state != App.StateSystem.Error &&
state != App.StateSystem.Running)
{
App.log.Error(String.Format("SchedulerBackup Wrong State for Action 'Duration Start' for State {0}. SchedulerBackup Trigger Ignored", Enum.GetName(typeof(App.StateSystem), state)));
}
else
{
// Perform Action
if (bStopInsteadOfPause)
StopMonitoring(true, bStopServices);
else
PauseMonitoring(true);
// Mark Scheduler Backup as Started
App.setting.LastSchedulerBackupRun = dtNow;
}
break;
}
case 2: // Duration End
{
// We are in a wrong state to perform a start
if (state != App.StateSystem.Paused &&
state != App.StateSystem.Stopped)
{
App.log.Error(String.Format("SchedulerBackup Wrong State for Action 'Duration End' for State {0}. SchedulerBackup Trigger Ignored", Enum.GetName(typeof(App.StateSystem), state)));
}
else
{
// Perform Action
StartMonitoring(false, true);
}
break;
}
}
}
}
// Reset the Scheduler Backup State
App.State_SchedulerBackupExecution_Stopped();
}
// Re-Start the Scheduler Backup timer * Waiting for the next iteration *
StartSchedulerBackup(99986);
}
}
///
/// * Core Logic * Monitor Processes from the Configuration. Start Any Processes that are Ok to
/// be started (ProcessOkToBeStarted_Check) that are currently not running
///
/// Monitor Configuration
/// Max Failure Count
/// Max Failure Count in given Hour Timespan
/// true if successfull, false if ProcessMonitor Threw an Error
private static bool ProcessMonitor(WatchdogConfiguration config, uint nMaxFailureCount, uint nMaxFailureCountHour)
{
// * PROCESS MONITORING *
if (config.ConfiguredHasAnyProcesses)
{
try
{
// Start Any Processes that are NOT Running
uint uProcessStartSuccessCount = 0;
uint uProcessStarFailCount = 0;
List nonRunningProcesses = Processes_ThatNeedToBeStarted_Check(config);
if (nonRunningProcesses.Count > 0)
{
// Iterate each non-running Process and try to Start it
foreach (WatchdogConfiguration.ProcessExe mProcess in nonRunningProcesses)
{
// Check that the Process can be started
if (!ProcessOkToBeStarted_Check(mProcess, nMaxFailureCount, nMaxFailureCountHour))
continue;
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_START_PROCESS", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1);
uint PID = PStarter.StartProcess(PStartInfo.CreateProcess(mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.WorkingDirectory, false, ProcessWindowStyle.Normal, true), false, false);
if (PID > 0)
{
// Add to Start Attempt, Set the PID, log, and incr. Counter
mProcess.AddStartTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure
mProcess.PID = PID;
App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_SUCCESS", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1);
App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_SUCCESS2", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.GetStartTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1);
uProcessStartSuccessCount++;
}
else
{
// Add to Failure, Reset the PID, log, and incr. Counter
mProcess.AddFailTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure
mProcess.PID = 0;
App.log.Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms), -1);
App.log.Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED2", mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, mProcess.GetFailTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1);
uProcessStarFailCount++;
}
}
// Imp! - Let the User know Visually that Processes were started or failed to start
if (uProcessStartSuccessCount > 0 && uProcessStarFailCount == 0)
{
App.log.Info(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_COUNT", uProcessStartSuccessCount));
if(!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_COUNT", (int)uProcessStartSuccessCount));
}
else if (uProcessStartSuccessCount == 0 && uProcessStarFailCount > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_FAIL_COUNT", uProcessStarFailCount));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_FAIL_COUNT", (int)uProcessStarFailCount));
}
else if (uProcessStartSuccessCount > 0 && uProcessStarFailCount > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_AND_N_FAIL_COUNT", uProcessStartSuccessCount, uProcessStarFailCount));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_PROCESS_N_SUCCESS_AND_N_FAIL_COUNT", (int)uProcessStartSuccessCount, (int)uProcessStarFailCount));
}
// If any Process Changes occured * Refresh the System Tray Notification Area *
if (uProcessStartSuccessCount > 0 || uProcessStarFailCount > 0)
{
if (!App.State_SpecialMode_IsRunAsServiceSet())
Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea();
}
}
else
{
// We are in a * good State * No Configuration was found that isn't running,
// use this time to make sure that our StartedPID Table is in sync * Perform general internal DS Cleanup *
PStarter.PerformGeneralCleanupOfAllStartedProcessIDs(config.MonitoredProcesses.QueryAllPidInfo(false, true));
}
}
catch (Exception ex) { App.log.Error("Process Monitor Running Error Thrown", ex); return false; }
}
return true;
}
///
/// Checks to see if the process is Ok to be Started. Checks error state as well as timeout values
///
/// a valid process exe, that is going to be started
/// Max Failure Count
/// Max Failure Count in given Hour Timespan
/// true if it is ok to start, false otherwise
private static bool ProcessOkToBeStarted_Check(WatchdogConfiguration.ProcessExe processExe, uint nMaxFailureCount, uint nMaxFailureCountHour)
{
if (processExe == null)
return false;
// * Ignore any process marked as errored *
if (processExe.InErrorState)
return false;
// If there was a failure starting the process before,... check
// the last failed time flag, we don't want to try over and over again, every second,
// but we'll give it a try every minute
if (!processExe.LastFailTimeTimeoutExceeded(TimeSpan.FromMinutes(1)))
{
return false;
}
else if (processExe.GetFailTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount))
{
// We are now officially in an error state
App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
processExe.InErrorState = true;
App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
return false; // skip Starting of this Process; Start Failure Count too High
}
// If the process started correctly, but then failed shortly thereafter,
// then we want to keep track of it and make sure that don't try to continue starting it
if (!processExe.LastStartTimeTimeoutExceeded(TimeSpan.FromMinutes(1)))
{
return false;
}
else if (processExe.GetStartTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount))
{
// We are now officially in an error state
App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
processExe.InErrorState = true;
App.log.Info(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_PROCESS_FAILED_ERROR_STATE", processExe.ProcessExeFileNameNPath, processExe.CommandLinePrms));
return false; // skip Starting of this Process; Start Count too High
}
return true;
}
///
/// Static variable * For Performance * used by Processes_ThatNeedToBeStarted_Check()
///
private static List s_nonRunningProcesses = new List();
///
/// Goes thru the configuration and returns back a list of all processes that need to be started
///
/// Monitor Configuration
/// List of all processes currently not running that require a new start, empty list if none found
private static List Processes_ThatNeedToBeStarted_Check(WatchdogConfiguration config)
{
// Clear Non-Running List
s_nonRunningProcesses.Clear();
// We don't want to run thru all command-line parameters all the time * Time-Hog *
// ~instead we want to check our run-time configuration to see if we have any start/fail information,
// if we don't, then we MUST run thru everything, because this means our PID info will be out of date
// ~If we are in an Error state, don't perform in-depth search, otherwise once the error state is reached
// we'll always fail here and always perform the in-depth search pointlessly since we'll never launch that process
bool bPerformInDepthProcessSearch = config.MonitoredProcesses.HasIncompletePidInformation() && (App.State_GetSystemState() != App.StateSystem.Error);
if (bPerformInDepthProcessSearch)
{
// Cache Process Lookups * for performance *
ProcessW.ClearProcessObjsGetterCache();
foreach (WatchdogConfiguration.ProcessExe mProcess in config.MonitoredProcesses.ProcessExes)
{
// Immediatly Cache the Process Getter Calls * Better Performance *
Process[] RunningProcessesOfExe = ProcessW.AllRunningProcessesOf(mProcess.ProcessName, true, true);
// Perform ExactProcessExeNCommandLinePrms Matching
Process rProcess = null;
int nProcessIndex = -1;
TraceM.TraceBegin();
bool bFoundMatch = ProcessW.FoundExactProcessExeNCommandLinePrmsInRunningProcesses(RunningProcessesOfExe, mProcess.ProcessExeFileNameNPath, mProcess.CommandLinePrms, (int)mProcess.PID, out rProcess, out nProcessIndex);
TraceM.TraceEnd("Calling FoundExactProcessExeNCommandLinePrmsInRunningProcessess");
// If no Match was found in the Running Processes, add to the list
if (!bFoundMatch)
{
s_nonRunningProcesses.Add(mProcess);
}
else if(rProcess != null) // we found a match * update the configuration *
{
// Update the Pid in our configuration
mProcess.PID = (uint)rProcess.Id;
rProcess = null;
}
}
}
else // Quick Search * Better Performances *
{
// Fastest way to check for PID changes is to retrieve
// all Pids on the system once and then do everything in memory
TraceM.TraceBegin();
List AllComputerPIDs = ProcessW.AllRunningPids();
foreach (uint pid in config.MonitoredProcesses.QueryAllPidInfo(false, true))
{
if (!AllComputerPIDs.Contains(pid))
{
WatchdogConfiguration.ProcessExe p = config.MonitoredProcesses.GetProcessExeForPid(pid);
if(p != null)
s_nonRunningProcesses.Add(p);
}
}
TraceM.TraceEnd("Going Thru Applications Pid-by-Pid");
}
// Return all Processes that aren't running
return s_nonRunningProcesses;
}
///
/// Iterates thru the Configured Processes and Try's to Kill Any Processes that
/// are running.
///
private static void ProcessKillAllMonitoredProcessesIfAny(WatchdogConfiguration config)
{
// Kill all Processes that exist in our configuration
uint nAssumedRunningProcesses = 0;
uint nErroredProcesses = 0;
uint nProcessesKilled = 0;
// Allow the User to not kill certain processes via the Restart Scheduler
bool bIncludeNonRestartProcesses = !App.State_IsSchedulerExecuting();
// Check Configuration
if (config.ConfiguredHasAnyProcesses)
{
nErroredProcesses = config.MonitoredProcesses.NumberOfPidsInErrorState();
if (nErroredProcesses > 0)
App.log.Info(AppResx.GetStringFormated("MESSAGE_NPROCESSES_IN_AN_ERROR_STATE", App.APPLICATION_NAME_SHORT, nErroredProcesses));
nAssumedRunningProcesses = (uint)config.MonitoredProcesses.QueryAllPidInfo(false, bIncludeNonRestartProcesses).Length;
if (nAssumedRunningProcesses > 0)
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_N_PROCESSES", App.APPLICATION_NAME_SHORT, nAssumedRunningProcesses));
if (nAssumedRunningProcesses <= 0) // nothing to do here
return;
}
else
{
return; // nothing to do here
}
// Iterate the Configuration * Use the PIDs whe have stored *
foreach (uint pid in config.MonitoredProcesses.QueryAllPidInfo(false, bIncludeNonRestartProcesses))
{
WatchdogConfiguration.ProcessExe p = config.MonitoredProcesses.GetProcessExeForPid(pid);
bool bSuccess = PStarter.KillProcess(pid, false, 1, true, 2);
if (bSuccess)
{
App.log.Info(AppResx.GetStringFormated("MESSAGE_CLOSE_PID_SUCCESS", pid, p.ProcessExeFileNameNPath, p.CommandLinePrms), -1);
nProcessesKilled++;
}
else
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_CLOSE_PID_FAILURE", pid, p.ProcessExeFileNameNPath, p.CommandLinePrms), -1);
}
}
// Refresh the taskbar, after killing all the processes
if(nProcessesKilled > 0 && !App.State_SpecialMode_IsRunAsServiceSet())
Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea();
// Let the User know that errors occured
if (App.State_GetSystemState() != App.StateSystem.Error) /* if We already are in an error state, no need to pop-up a message again */
{
if (nProcessesKilled != nAssumedRunningProcesses)
{
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_FAILURE", (int)(nAssumedRunningProcesses - nProcessesKilled)));
App.log.Error(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_FAILURE", (config.MonitoredProcesses.ProcessExes.Length - nProcessesKilled)));
}
else
{
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_SUCCESS", nProcessesKilled));
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_CLOSE_N_PROCESSES_SUCCESS", nProcessesKilled));
}
}
}
///
/// * Core Logic * Monitor Services from the Configuration. Start Any Services that are Ok to
/// be started (ServiceOkToBeStarted_Check) that are currently not running
///
/// Monitor Configuration
/// Max Failure Count
/// Max Failure Count in given Hour Timespan
/// true if successfull, false if ServiceMonitor Threw an Error
private static bool ServiceMonitor(WatchdogConfiguration config, uint nMaxFailureCount, uint nMaxFailureCountHour)
{
// * SERVICE MONITORING *
if (config.ConfiguredHasAnyServices)
{
try
{
// Check To see if the Service is up and running, if it isn't, restart it * simple stuff *
uint uServiceStartSuccessCount = 0;
uint uServiceStartFailCount = 0;
foreach (WatchdogConfiguration.ServiceExe s in config.MonitoredServices.ServiceExes)
{
// Start the Service up to Five Times * Simplified way of doing this *
TraceM.TraceBegin();
bool bIsServiceRunning = ServiceW.IsServiceRunning(s.Name);
TraceM.TraceEnd("Checking Service is Running");
if (!bIsServiceRunning)
{
// Check that the Service can be started
if (!ServiceOkToBeStarted_Check(s, nMaxFailureCount, nMaxFailureCountHour))
continue;
// * Try to Restart the Service *
TraceM.TraceBegin();
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_RESTART_SERVICE", s.Name), -1);
bool bRestartOk = ServiceW.RestartService(s.Name, true, 240 , 2);
TraceM.TraceEnd("Restarting Service");
if (bRestartOk)
{
// Add to Start Attempt, Log the Successful Launch, incr. Start Service Counter
s.AddStartTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure
App.log.Info(AppResx.GetStringFormated("MESSAGE_START_SERVICE_SUCCESS", s.Name, s.GetStartTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1);
uServiceStartSuccessCount++;
}
else
{
// Add to Failure, Log the Errored Launch, incr. Failed Service Counter
s.AddFailTime(DateTime.Now, (int)(nMaxFailureCount + 2)); // *Always* Keep the List 2 bigger than nMaxFailureCount, this way we can track up to nMaxFailure
App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED", s.Name, s.GetFailTimesInGivenTimeSpan(TimeSpan.FromHours(nMaxFailureCountHour)), nMaxFailureCountHour), -1);
uServiceStartFailCount++;
}
}
}
// Imp! - Let the User know Visually that Processes were started or failed to start
if (uServiceStartSuccessCount > 0 && uServiceStartFailCount == 0)
{
App.log.Info(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_COUNT", uServiceStartSuccessCount));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_COUNT", (int)uServiceStartSuccessCount));
}
else if (uServiceStartSuccessCount == 0 && uServiceStartFailCount > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_FAIL_COUNT", uServiceStartFailCount));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_FAIL_COUNT", (int)uServiceStartFailCount));
}
else if (uServiceStartSuccessCount > 0 && uServiceStartFailCount > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_AND_N_FAIL_COUNT", uServiceStartSuccessCount, uServiceStartFailCount));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_SERVICE_N_SUCCESS_AND_N_FAIL_COUNT", (int)uServiceStartSuccessCount, (int)uServiceStartFailCount));
}
}
catch (Exception ex) { App.log.Error("Error Thrown in Service Monitor", ex); return false; };
}
return true;
}
///
/// Checks to see if the service is Ok to be Started. Checks error state as well as timeout values
///
/// a valid service exe, that is going to be started
/// Max Failure Count
/// Max Failure Count in given Hour Timespan
/// true if it is ok to start, false otherwise
private static bool ServiceOkToBeStarted_Check(WatchdogConfiguration.ServiceExe serviceExe, uint nMaxFailureCount, uint nMaxFailureCountHour)
{
if (serviceExe == null)
return false;
// * Ignore any services marked as errored *
if (serviceExe.InErrorState)
return false;
// If there was a failure starting the Service before,... check
// the last failed time flag, we don't want to try over and over again, every second,
// but we'll give it a try every minute
if (!serviceExe.LastFailTimeTimeoutExceeded(TimeSpan.FromMinutes(1)))
{
return false;
}
else if (serviceExe.GetFailTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) // Default 1 Hour
{
// We are now officially in an error state
App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
serviceExe.InErrorState = true;
App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
return false; // skip Starting of this Service; Start Failure Count too High
}
// If the service started correctly, but then failed shortly thereafter,
// then we want to keep track of it and make sure that don't try to continue starting it
if (!serviceExe.LastStartTimeTimeoutExceeded(TimeSpan.FromMinutes(1)))
{
return false;
}
else if (serviceExe.GetStartTimesInGivenTimeSpanMaxTryExceeded(new TimeSpan((int)nMaxFailureCountHour, 0, 0), nMaxFailureCount)) // Default 1 Hour
{
// We are now officially in an error state
App.State_Error_SetErrorState(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
serviceExe.InErrorState = true;
App.log.Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_START_SERVICE_FAILED_ERROR_STATE", serviceExe.Name));
return false; // skip Starting of this Service; Start Count too High
}
return true;
}
///
/// Iterates thru the Configured Processes and Try's to Kill Any Services that
/// are running and configured to be Stopped.
///
/// Set to true to force shutting down of services, false otherwise
//private static void ServicesKillAllMonitoredServicesIfAny(WatchdogConfiguration config, bool bForceShutdownOfServices = false)
private static void ServicesKillAllMonitoredServicesIfAny(WatchdogConfiguration config, bool bForceShutdownOfServices)
{
// Should we shutdown Services???
bool bRestartServices = bForceShutdownOfServices || (App.State_IsSchedulerExecuting() && App.setting.SchedulerRestartsServices);
if (bRestartServices)
{
int nServicesClosed = 0;
int nServicesNotClosed = 0;
// Allow the User to not kill certain services via the Restart Scheduler
bool bIncludeNonRestartServices = !App.State_IsSchedulerExecuting();
if (config.ConfiguredHasAnyServices)
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_N_SERVICES", App.APPLICATION_NAME_SHORT, config.MonitoredServices.QueryAllServiceInfo(false, bIncludeNonRestartServices).Length));
foreach (string ServiceName in config.MonitoredServices.QueryAllServiceInfo(false, bIncludeNonRestartServices))
{
// This is only getting called when the scheduler is executing...
// the scheduler will be responsible for starting the monitor which in turn will start the services and keep track of all
// service starts, hence all we have to do here is shut down the services * they will automatically be restarted *
App.log.Info(AppResx.GetStringFormated("MESSAGE_ABOUT_TO_CLOSE_SERVICE", ServiceName), -1);
bool bCloseServiceSuccess = ServiceW.StopService(ServiceName, true, 240);
if (bCloseServiceSuccess)
{
App.log.Info(AppResx.GetStringFormated("MESSAGE_CLOSE_SERVICE_SUCCESS", ServiceName), -1);
nServicesClosed++;
}
else
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_CLOSE_SERVICE_FAILURE", ServiceName), -1);
nServicesNotClosed++;
}
}
// Imp! - Let the User know Visually that Services were Stopped
if (nServicesClosed > 0 && nServicesNotClosed == 0)
{
App.log.Info(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_COUNT", nServicesClosed));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_COUNT", nServicesClosed));
}
else if (nServicesClosed == 0 && nServicesNotClosed > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_FAIL_COUNT", nServicesNotClosed));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_FAIL_COUNT", nServicesNotClosed));
}
else if (nServicesClosed > 0 && nServicesNotClosed > 0)
{
App.log.Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_AND_N_FAIL_COUNT", nServicesClosed, nServicesNotClosed));
if (!App.State_SpecialMode_IsRunAsServiceSet())
WatchdogSysTray.PopUpBallon_Error(AppResx.GetStringFormated("MESSAGE_STOPSERVICE_N_SUCCESS_AND_N_FAIL_COUNT", nServicesClosed, nServicesNotClosed));
}
}
}
#endregion
#region Loaded/Unloaded Event Handlers
///
/// Handles the Main Window StartUp event.
///
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// * To make sure, we are not fatally closing *
if (App.s_FatalErrorOccured)
return;
// * To make sure, do nothing in command-line mode *
if (App.State_SpecialMode_IsCommandLineSet())
return;
if (!App.State_SpecialMode_IsRunAsServiceSet())
{
// Performance Load WPF Windows
WPFWindows.WPFWindowMaker.PerformanceCache();
// Set Registry key to Start w. Windows, if specified
bool bStartWithWindows = App.setting.StartWithWindows;
App.log.Info(String.Format("Starting with Windows is set to: '{0}'", bStartWithWindows));
}
// Ensure proper existence of HardLink, if specified
bool bCreateHardLink = App.setting.CreateExeHardLinkInRootPath;
App.log.Info(String.Format("Creating .Exe HardLink in Root Path is set to: '{0}'", bCreateHardLink));
// If we are deployed by ClickOnce, let's modify the uninstall icon (Add/Remove) Programs
// ~there appears to be no better way than modifying the registry manually * Now Add/Remove will have a nice
// little App icon instead of the default ClickOnce Icon *
string strKeyFetched = RegKey.GetKey("Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + App.APPLICATION_PRODUCT_CLICKONCE_ID, "DisplayIcon", "");
if(!String.IsNullOrEmpty(strKeyFetched))
RegKey.SetKey("Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + App.APPLICATION_PRODUCT_CLICKONCE_ID, "DisplayIcon", AssemblyW.SpecializedAssemblyInfo.GetAssemblyFileNameNPath(AssemblyW.AssemblyST.Entry));
// Sleep a little before showing the System Tray
System.Threading.Thread.Sleep(250);
if (!App.State_SpecialMode_IsRunAsServiceSet())
{
// Refresh the taskbar, when launching (maybe there are trailing instances)
Watchdog.WatchdogLib.Win32.Functions.RefreshTaskbarNotificationArea();
// Show the System Tray Icon/Menu
WatchdogSysTray.WatchdogSysTray_Show();
}
// Start Process Monitoring upon start? * Default is Yes *
if (App.setting.StartMonitorUponStart)
{
// We should only start the monitor, if there is any configuration
if (App.xmlfile.ReadData(false).ConfiguredHasAny)
{
StartMonitoring(false, true);
}
else if (!App.State_SpecialMode_IsRunAsServiceSet())
{
WatchdogSysTray.PopUpBallon_Info(AppResx.GetStringFormated("MESSAGE_NO_CONFIGURATION_FOUND", App.APP_XML_DS_FILENAME));
WPFWindows.WPFWindowMaker.CreateNShowWindow(WPFWindows.WPFWindow.Settings_Window);
}
else
{
// Nothing to do Here (we must be in Service Mode), without a configuration
App.FatalExceptionStopExecution("No Configuration Found in ServiceMode", false);
}
}
}
///
/// Handles the Main Window Exit event.
///
private void Window_Closed(object sender, EventArgs e)
{
// * To make sure, we are not fatally closing *
if (App.s_FatalErrorOccured)
return;
// * To make sure, do nothing in command-line mode *
if (App.State_SpecialMode_IsCommandLineSet())
return;
// for unexpected cleanup
s_WindowCloseGotCalled = true;
// Stop the Process Monitor
StopMonitoring(false, false);
// Make sure the latest DS is saved to file
App.xmlfile.SaveData();
if (!App.State_SpecialMode_IsRunAsServiceSet())
{
// Close the System Tray Icon/Menu
WatchdogSysTray.WatchdogSysTray_Hide();
// Unload All Windows (incl. Performance) * Close Log Tail if exists Prior *
LogViewerWindow.CloseFileTailBeforeExitIfExists();
WPFWindows.WPFWindowMaker.CloseAll();
}
}
///
/// Handle Specialized Cleanup
///
void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
try
{
// Stop Internal Timers
StopTimer();
StopSchedulerTimer();
StopSchedulerBackupTimer();
if (_timer != null)
_timer.Dispose();
if (_timerScheduler != null)
_timerScheduler.Dispose();
if (_timerSchedulerBackup != null)
_timerSchedulerBackup.Dispose();
// Make sure that the close get's called * Unexpected cleanup *
if (!s_WindowCloseGotCalled)
Window_Closed(null, null);
}
catch (Exception) { /* ignore */ }
}
#endregion
}
}