#Region "Includes" Imports System.ServiceProcess Imports System.Data.SqlClient Imports System.Xml Imports Common.Common #End Region Public Class BMSService Inherits System.ServiceProcess.ServiceBase #Region "Members" Private m_TimerStarter As System.Threading.Timer Private m_TimerWatcher As System.Threading.Timer Private m_EventLog As EventLog Private m_StartJobs As DataSet Private m_WatchJobs As DataSet Private m_Common As Common.Common #End Region #Region " Component Designer generated code " Public Sub New() MyBase.New() ' This call is required by the Component Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call End Sub 'UserService overrides dispose to clean up the component list. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub ' The main entry point for the process _ Shared Sub Main() ' Dim ServicesToRun() As System.ServiceProcess.ServiceBase ' More than one NT Service may run within the same process. To add ' another service to this process, change the following line to ' create a second service object. For example, ' ' ServicesToRun = New System.ServiceProcess.ServiceBase () {New Service1, New MySecondUserService} ' ' ServicesToRun = New System.ServiceProcess.ServiceBase() {New BMSService} ' System.ServiceProcess.ServiceBase.Run(ServicesToRun) '----------------- 'DEBUG check don't work in windows service manager... #If DEBUG Then Dim service As New BMSService service.StartService() System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite) #Else Dim ServicesToRun() As System.ServiceProcess.ServiceBase ServicesToRun = New System.ServiceProcess.ServiceBase() {New BMSService} System.ServiceProcess.ServiceBase.Run(ServicesToRun) #End If End Sub 'Required by the Component Designer Private components As System.ComponentModel.IContainer ' NOTE: The following procedure is required by the Component Designer ' It can be modified using the Component Designer. ' Do not modify it using the code editor. Private Sub InitializeComponent() ' 'BMSService ' Me.ServiceName = "BMS Serivce" End Sub #End Region #Region "Service Start / Stopp" Protected Overrides Sub OnStart(ByVal args() As String) StartService() End Sub Protected Overrides Sub OnStop() End Sub #End Region #Region "Private Methods" 'Occures ever time the timer elapses Sub TimeElapsedStarter(ByVal stateInfo As Object) Try 'Load and execute starter Jobs m_StartJobs.Clear() DataAccess.Job.LoadJobs(m_Common, JobType.StartJob, m_StartJobs) ExecuteStartJobs() Catch ex As Exception WriteEventLog("Fehler TimeElapsedStarter: " & ex.Message & Environment.NewLine & Environment.NewLine & ex.StackTrace, EventLogEntryType.Error) End Try End Sub 'Occures ever time the timer elapses Sub TimeElapsedWatcher(ByVal stateInfo As Object) Try 'Load and execute starter Jobs m_WatchJobs.Clear() DataAccess.Job.LoadJobs(m_Common, JobType.WatchJob, m_WatchJobs) ExecuteWatchJobs() Catch ex As Exception WriteEventLog("Fehler TimeElapsedWatcher: " & ex.Message & Environment.NewLine & Environment.NewLine & ex.StackTrace, EventLogEntryType.Error) End Try End Sub 'Initializes all the service start stuff... Private Sub StartService() Try m_EventLog = New EventLog m_Common = New Common.Common 'Cannot use System.Timers.Timer due a Bug (http://support.microsoft.com/default.aspx?scid=kb;en-us;842793) Dim autoEvent As New System.Threading.AutoResetEvent(False) Dim timerStarterDelegate As System.Threading.TimerCallback = AddressOf TimeElapsedStarter Dim timerWatcherDelegate As System.Threading.TimerCallback = AddressOf TimeElapsedWatcher m_TimerStarter = New System.Threading.Timer(timerStarterDelegate, autoEvent, 1000, m_Common.StartJobInterval) m_TimerWatcher = New System.Threading.Timer(timerWatcherDelegate, autoEvent, m_Common.WatchJobInterval, m_Common.WatchJobInterval) m_StartJobs = New DataSet m_WatchJobs = New DataSet Catch ex As Exception WriteEventLog("Fehler: " & ex.Message & Environment.NewLine & Environment.NewLine & ex.StackTrace, EventLogEntryType.Error) End Try End Sub 'Executes all watch jobs Private Sub ExecuteWatchJobs() Try 'Check if there are any jobs If m_WatchJobs.Tables.Count > 0 And m_WatchJobs.Tables.Item(0).Rows.Count > 0 Then Dim dt As DataTable Dim dsFailedJobs As New DataSet Dim startTime As New DateTime, endTime As New DateTime Dim houres As Integer, minutes As Integer dt = m_WatchJobs.Tables.Item(0) Dim dr As DataRow For Each dr In dt.Rows 'Check if job is in valid date range If dr.Item("DatumStart") <= DateTime.Now And CType(dr.Item("DatumEnde"), Date).AddDays(1) >= DateTime.Now Then ConvertToDateTime(dr.Item("ZeitVon"), dr.Item("ZeitBis"), startTime, endTime) If startTime <= DateTime.Now And endTime >= DateTime.Now Then 'job seems to be in his valid datetime range... Dim lastRunStart As DateTime If dr("LastRunStart") Is DBNull.Value Then 'use dummy date if the job never run before lastRunStart = New DateTime(1) Else lastRunStart = CDate(dr("LastRunStart")) End If 'get all failed starter jobs dsFailedJobs.Clear() DataAccess.Job.GetFailedStartJobs(m_Common, dr("ProgrammId"), dsFailedJobs) SendNotifications(dsFailedJobs, "Das Programm ##PROG_NAME## hat nicht innerhalb der Zeitspanne von " & m_Common.MaximalStartDuration.ToString() & " Minuten die Start() Methode der BMS-Dll aufgerufen. Möglicherweise konnte das Programm nicht gestartet werden.", JobType.StartJob) 'check for failed execution jobs dsFailedJobs.Clear() DataAccess.Job.GetFailedExecJobs(m_Common, dr("ProgrammId"), dsFailedJobs) If dsFailedJobs.Tables.Count > 0 And dsFailedJobs.Tables(0).Rows.Count > 0 Then SendNotifications(dsFailedJobs, "Das Programm ##PROG_NAME## konnte nicht innerhalb der vordefinierten Laufzeit von " & dsFailedJobs.Tables(0).Rows(0)("MaxLaufzeit").ToString() & " Minuten ausgeführt werden.", JobType.WatchJob) End If End If End If Next End If Catch ex As Exception Throw ex End Try End Sub 'Executes all starter jobs Private Sub ExecuteStartJobs() Try 'Check if there are any jobs If m_StartJobs.Tables.Count > 0 And m_StartJobs.Tables.Item(0).Rows.Count > 0 Then Dim dt As DataTable Dim startTime As New DateTime, endTime As New DateTime dt = m_StartJobs.Tables.Item(0) Dim dr As DataRow For Each dr In dt.Rows If dr("RunJob") Then 'RunJob has to run as fast as possible If Not dr("IsRunning") Then 'job isn't already running Dim job As New Job(m_Common, dr("JobId"), dr("Beschreibung"), dr("ProgrammId"), CType(dr("JobTypId"), JobType), CType(dr("JobStartTypId"), JobStartType)) If Not dr("ParentProgrammId") Is DBNull.Value Then Dim parentProgrammId As Integer = dr("ParentProgrammId") 'job has a relation to another job, validate if parent job is currently executing... If Not DataAccess.Job.CheckIsRunning(m_Common.DSN, parentProgrammId) Then 'parent job is not running, -> run job 'NachLetzterAusfuerung False, cause no one cares about next start time on RunJobs job.Launch(True, False) End If Else 'run job job.Launch(True, False) End If End If Else 'Check if job is in valid date range If dr.Item("DatumStart") <= DateTime.Now And CType(dr.Item("DatumEnde"), Date).AddDays(1) >= DateTime.Now Then ConvertToDateTime(dr.Item("ZeitVon"), dr.Item("ZeitBis"), startTime, endTime) If startTime <= DateTime.Now And endTime >= DateTime.Now Then If dr("NextStartDate") < DateTime.Now Then 'interval has elapsed (should already be checked in GetJobs SP...) If Not dr("IsRunning") Then 'job isn't already running Dim job As New Job(m_Common, dr("JobId"), dr("Beschreibung"), dr("ProgrammId"), CType(dr("JobTypId"), JobType), CType(dr("JobStartTypId"), JobStartType)) If Not dr("ParentProgrammId") Is DBNull.Value Then Dim parentProgrammId As Integer = dr("ParentProgrammId") 'job has a relation to another job, validate if parent job is currently executing... If Not DataAccess.Job.CheckIsRunning(m_Common.DSN, parentProgrammId) Then 'parent job is not running, -> run job job.Launch(False, dr("NachLetzterAusfuerung")) End If Else 'run job job.Launch(False, dr("NachLetzterAusfuerung")) End If End If End If End If End If End If Next End If Catch ex As Exception Throw ex End Try End Sub 'Sends notifications to the pre-definied receivers (file or mail) Private Sub SendNotifications(ByVal ds As DataSet, ByVal message As String, ByVal jobType As JobType) Try Dim drFailed As DataRow, drNotifications As DataRow Dim dsNotifications As New DataSet Dim hasNotiLimitReached As Boolean hasNotiLimitReached = False If ds.Tables.Count > 0 Then If ds.Tables(0).Rows.Count > 0 Then For Each drFailed In ds.Tables(0).Rows 'check if we already reached the limits for sending start/satch notifications If jobType = jobType.WatchJob Then If drFailed("NotiCounter") >= m_Common.MaxWatcherNotifications Then hasNotiLimitReached = True Else hasNotiLimitReached = False End If Else If drFailed("NotiCounter") >= m_Common.MaxStarterNotifications Then hasNotiLimitReached = True Else hasNotiLimitReached = False End If End If If Not hasNotiLimitReached Then 'get all notifications to a programm dsNotifications.Clear() DataAccess.Job.GetNotifications(m_Common, CInt(drFailed("ProgrammId")), dsNotifications) If dsNotifications.Tables.Count > 0 Then 'seems to have rows in a table... DataAccess.Job.SendNotification(m_Common, CInt(drFailed("ProgrammId")), dsNotifications, message, drFailed("JobId"), jobType) End If End If If drFailed("NachLetzterAusfuerung") Then DataAccess.Job.SetJobLastRun(m_Common.DSN, drFailed("JobId"), LastRun.End) If Not drFailed("RunJob") Then 'calc next start exec time if its not a run job DataAccess.Job.SetNextExecDateTime(m_Common.DSN, drFailed("JobId")) End If End If Next End If End If Catch ex As Exception Throw ex End Try End Sub #End Region #Region "Helper Methods" 'Converts the times from database to a datetime datatyp for better handling Private Sub ConvertToDateTime(ByVal startTime As String, ByVal endTime As String, ByRef startDateTime As DateTime, ByRef endDateTime As DateTime) Try Dim houres As Integer, minutes As Integer 'let's parse some time stuff to get start and end time in a better datatype... houres = Left(startTime, 2) minutes = Right(startTime, 2) startDateTime = DateTime.Today.AddHours(houres) startDateTime = startDateTime.AddMinutes(minutes) houres = Left(endTime, 2) minutes = Right(endTime, 2) endDateTime = DateTime.Today.AddHours(houres) endDateTime = endDateTime.AddMinutes(minutes) 'if endtime is less, endtime is on next day 'Ex: Start 23:15; End 02:45 If endDateTime < startDateTime Then endTime = endDateTime.AddDays(1) End If Catch ex As Exception Throw ex End Try End Sub 'Writes an message to windows event log Private Sub WriteEventLog(ByVal errorMessage As String, ByVal eventLogType As EventLogEntryType) Try m_Common.Log(m_Common.SERVICE_DISPLAY_NAME, "Quelle: " & SERVICE_DISPLAY_NAME & Environment.NewLine & "Meldung: " & errorMessage, eventLogType) Catch ex As Exception System.Diagnostics.EventLog.WriteEntry(SERVICE_DISPLAY_NAME, errorMessage, eventLogType) System.Diagnostics.EventLog.WriteEntry(SERVICE_DISPLAY_NAME, ex.Message + Environment.NewLine + ex.StackTrace, eventLogType.Error) End Try End Sub #End Region End Class