Project(DLL) : ServicePlugin
Configuration
Duration.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Configuration { public enum Duration { Day , Week , Month } }
Logger.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; namespace ScheduleService . Configuration { public class Logger : ConfigurationElement { /// <summary> /// DLL檔案位置 /// </summary> [ ConfigurationProperty ( "binary" )] public string Binary { get { return ( string ) this [ "binary" ]; } set { this [ "binary" ] = value ; } } /// <summary> /// 實體名稱 /// </summary> [ ConfigurationProperty ( "assembly" , IsRequired = true )] public string Assembly { get { return ( string ) this [ "assembly" ]; } set { this [ "assembly" ] = value ; } } } }
MemoryUsage.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; namespace ScheduleService . Configuration { /// <summary> /// 已不使用 /// </summary> public sealed class MemoryUsage : ConfigurationElement { [ ConfigurationProperty ( "interval" , IsRequired = false , DefaultValue = 60 )] public int Interval { get { return ( int ) this [ "interval" ]; } set { this [ "interval" ] = value ; } } [ ConfigurationProperty ( "monitor" , IsRequired = false , DefaultValue = false )] public bool Monitor { get { return ( bool ) this [ "monitor" ]; } set { this [ "monitor" ] = value ; } } } }
Module.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; using System . Runtime . Serialization ; using ScheduleService . Remoting ; namespace ScheduleService . Configuration { public sealed class Module : ConfigurationElement { /// <summary> /// 模組名稱 /// </summary> [ ConfigurationProperty ( "name" , IsKey = true )] public string Name { get { return ( string ) this [ "name" ]; } set { this [ "name" ] = value ; } } /// <summary> /// 介面名稱 /// </summary> [ ConfigurationProperty ( "type" , IsRequired = true )] public string Type { get { return ( string ) this [ "type" ]; } set { this [ "type" ] = value ; } } /// <summary> /// DLL檔案位置 /// </summary> [ ConfigurationProperty ( "binary" )] public string Binary { get { return ( string ) this [ "binary" ]; } set { this [ "binary" ] = value ; } } /// <summary> /// 實體名稱 /// </summary> [ ConfigurationProperty ( "assembly" , IsRequired = true )] public string Assembly { get { return ( string ) this [ "assembly" ]; } set { this [ "assembly" ] = value ; } } /// <summary> /// 執行間隔(秒) /// </summary> [ ConfigurationProperty ( "interval" , IsRequired = false )] public int Interval { get { return ( int ) this [ "interval" ]; } set { this [ "interval" ] = value ; } } /// <summary> /// 執行週期 /// </summary> [ ConfigurationProperty ( "duration" , IsRequired = false , DefaultValue = Duration . Day )] public Duration Duration { get { return ( Duration ) this [ "duration" ]; } set { this [ "duration" ] = value ; } } /// <summary> /// 執行時間點(格式tt:MM:dd) /// </summary> [ ConfigurationProperty ( "executePoint" , IsRequired = false )] public string ExecutePoint { get { return ( string ) this [ "executePoint" ]; } set { this [ "executePoint" ] = value ; } } /// <summary> /// 執行失敗時, 嘗試重新執行一次 /// </summary> [ ConfigurationProperty ( "reTryOnFailed" , IsRequired = false , DefaultValue = false )] public bool ReTryOnFailed { get { return ( bool ) this [ "reTryOnFailed" ]; } set { this [ "reTryOnFailed" ] = value ; } } public DateTime StartTime { get ; set ; } public DateTime LastExecuted { get ; set ; } public DateTime NextExecute { get ; set ; } public Exception Exception { get ; set ; } public override string ToString () { return string . Format ( "<module name=\"{0}\" type=\"{1}\" binary=\"{2}\" assembly=\"{3}\" interval=\"{4}\" />" , this . Name , this . Type , this . Binary , this . Assembly , this . Interval ); } public override int GetHashCode () { return string . Format ( "{0}{1}{2}{3}" , this . Name , this . Type , this . Binary , this . Assembly ). GetHashCode (); } public override bool Equals ( object compareTo ) { if ( compareTo == null || !( compareTo is Module )) return false ; Module obj = compareTo as Module ; return string . Format ( "{0}{1}{2}{3}" , this . Name , this . Type , this . Binary , this . Assembly ) == string . Format ( "{0}{1}{2}{3}" , obj . Name , obj . Type , obj . Binary , obj . Assembly ); } public ModuleData ToModuleData () { ModuleData data = new ModuleData (); data . Assembly = this . Assembly ; data . Binary = this . Binary ; data . Exception = this . Exception ; data . Interval = this . Interval ; data . LastExecuted = this . LastExecuted ; data . Name = this . Name ; data . NextExecute = this . NextExecute ; data . StartTime = this . StartTime ; data . Type = this . Type ; data . Duration = this . Duration ; data . ExecutePoint = this . ExecutePoint ; data . ReTryOnFailed = this . ReTryOnFailed ; return data ; } } }
ModuleCollection.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using ScheduleService . Configuration ; using System . Configuration ; namespace ScheduleService . Configuration { public sealed class ModuleCollection : ConfigurationElementCollection { public override ConfigurationElementCollectionType CollectionType { get { return ConfigurationElementCollectionType . AddRemoveClearMap ; } } public Module this [ int index ] { get { return ( Module ) BaseGet ( index ); } set { if ( BaseGet ( index ) != null ) BaseRemove ( index ); BaseAdd ( index , value ); } } protected override ConfigurationElement CreateNewElement () { return new Module (); } protected override object GetElementKey ( ConfigurationElement element ) { return ( element as Module ). Name ; } #region Operation public void Add ( Module module ) { BaseAdd ( module ); } public void Clear () { BaseClear (); } public void Rmove ( Module module ) { BaseRemove ( module . Name ); } public void Remove ( string name ) { BaseRemove ( name ); } public void RemoveAt ( int index ) { BaseRemoveAt ( index ); } #endregion } }
MonitorPort.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; namespace ScheduleService . Configuration { public sealed class MonitorPort : ConfigurationElement { [ ConfigurationProperty ( "value" , IsRequired = true )] public int Value { get { return ( int ) this [ "value" ]; } set { this [ "value" ] = value ; } } } }
Plugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; namespace ScheduleService . Configuration { public sealed class Plugin : ConfigurationSection { ///// <summary> ///// 重載間隔 ///// </summary> //[ConfigurationProperty("reloadInterval")] //public ReloadInterval ReloadConfigInterval //{ // get { return (ReloadInterval)this["reloadInterval"]; } // set { this["reloadInterval"] = value; } //} ///// <summary> ///// 監控記憶體使用情況 ///// </summary> //[ConfigurationProperty("memoryUsage")] //public MemoryUsage MemoryUsage //{ // get { return (MemoryUsage)this["memoryUsage"]; } // set { this["memoryUsage"] = value; } //} /// <summary> /// 監控通訊埠 /// </summary> [ ConfigurationProperty ( "monitorPort" )] public MonitorPort MonitorPort { get { return ( MonitorPort ) this [ "monitorPort" ]; } set { this [ "monitorPort" ] = value ; } } /// <summary> /// 外掛log /// </summary> [ ConfigurationProperty ( "exceptionHandler" )] public Logger ExceptionHandler { get { return ( Logger ) this [ "exceptionHandler" ]; } set { this [ "exceptionHandler" ] = value ; } } /// <summary> /// 模組 /// </summary> [ ConfigurationProperty ( "plugins" )] [ ConfigurationCollection ( typeof ( ModuleCollection ), AddItemName = "module" , ClearItemsName = "clearModules" , RemoveItemName = "removeModules" )] public ModuleCollection Plugins { get { return ( ModuleCollection ) this [ "plugins" ]; } set { this [ "plugins" ] = value ; } } } }
ReloadInterval.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Configuration ; namespace ScheduleService . Configuration { /// <summary> /// 已不使用 /// </summary> public sealed class ReloadInterval : ConfigurationElement { [ ConfigurationProperty ( "value" , IsRequired = true )] public int Value { get { return ( int ) this [ "value" ]; } set { this [ "value" ] = value ; } } } }
Plugin
IAlarmPlugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Plugin { public interface IAlarmPlugin { void ExecuteAt ( object sender , System . Timers . ElapsedEventArgs e ); } }
IComboPlugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Plugin { public interface IComboPlugin { bool OnExecuting (); void OnExecuted (); } }
IComplexPlugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Plugin { public interface IComplexPlugin : ISimplePlugin , IComboPlugin , IGeneralPlugin { } }
IGeneralPlugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Plugin { public interface IGeneralPlugin { bool OnStart (); void OnStop (); } }
ISimplePlugin.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Plugin { public interface ISimplePlugin { void Execute ( object sender , System . Timers . ElapsedEventArgs e ); } }
PluginLoader.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Xml . XPath ; using System . Xml . Linq ; using System . Configuration ; using ScheduleService . Configuration ; using ScheduleService . Utilily ; namespace ScheduleService . Plugin { public class PluginLoader { private List < Module > LoadedPlugins ; /// <summary> /// 載入目前執行個體的設定檔 /// </summary> public void LoadConfig () { LoadConfig ( ConfigurationManager . OpenExeConfiguration ( ConfigurationUserLevel . None ). FilePath ); } /// <summary> /// 載入指定的設定檔 /// </summary> /// <param name="configPath"></param> public void LoadConfig ( string configPath ) { try { LoadedPlugins = new List < Module >(); #region 重新載入設定檔 ConfigurationManager . RefreshSection ( "scheduleService.plugin" ); ModuleCollection modules = (( Configuration . Plugin ) ConfigurationManager . GetSection ( "scheduleService.plugin" )). Plugins ; foreach ( Module m in modules ) { LoadedPlugins . Add ( m ); } #endregion #region 以讀檔方式重新載入設定檔 //XElement root = XElement.Load(configPath); //IEnumerator<XElement> itor = root.XPathSelectElements("//scheduleService.plugin/plugins/module").GetEnumerator(); //while (itor.MoveNext()) //{ // try // { // Module m = new Module(); // m.Assembly = itor.Current.Attribute("assembly").Value; // m.Binary = itor.Current.Attribute("binary").Value; // m.Name = itor.Current.Attribute("name").Value; // m.Type = itor.Current.Attribute("type").Value; // m.Interval = int.Parse(itor.Current.Attribute("interval").Value); // LoadedPlugins.Add(m); // } // catch (Exception ex) // { // ServiceLogger.Log(string.Format("載入模組{0}時發生錯誤", itor.Current.ToString()), ex); // } //} #endregion } catch ( Exception ex ) { ServiceLogger . Log ( string . Format ( "載入設定檔{0}時發生錯誤" , configPath ), ex ); } } /// <summary> /// 取得指定模組資料 /// </summary> /// <param name="name"></param> /// <returns></returns> public Module GetModule ( string name ) { if ( LoadedPlugins == null ) { LoadConfig (); } foreach ( Module m in LoadedPlugins ) { if ( m . Name == name ) return m ; } return null ; } /// <summary> /// 取得所有模組 /// </summary> /// <returns></returns> public Module [] GetModules () { if ( LoadedPlugins == null ) { LoadConfig (); } return LoadedPlugins . ToArray (); } /// <summary> /// 取得所有模組設定字串 /// </summary> /// <returns></returns> public string [] GetModuleStrings () { if ( LoadedPlugins == null ) { LoadConfig (); } List <string> list = new List <string> (); foreach ( Module m in LoadedPlugins ) { list . Add ( m . ToString ()); } return list . ToArray (); } } }
Timer.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Reflection ; using ScheduleService . Configuration ; using ScheduleService . Utilily ; using System . Text . RegularExpressions ; namespace ScheduleService . Plugin { public class Timer : System . Timers . Timer { private bool flag ; /// <summary> /// 模組的實體物件 /// </summary> public object Instance { get ; private set ; } /// <summary> /// 執行模組的資料 /// </summary> public ScheduleService . Configuration . Module Source { get ; private set ; } public Timer ( ScheduleService . Configuration . Module module ) : base () { try { Source = module ; base . Enabled = true ; Assembly assembly = Assembly . LoadFrom ( PathHelper . PluginPath + Source . Binary ); switch ( Source . Type ) { case "ScheduleService.Plugin.ISimplePlugin" : case "ScheduleService.Plugin.IComplexPlugin" : ISimplePlugin simplePlugin = ( ISimplePlugin ) assembly . CreateInstance ( Source . Assembly ); base . Interval = module . Interval * 1000 ; base . Elapsed += new System . Timers . ElapsedEventHandler ( Simple_Elapsed ); this . Instance = simplePlugin ; this . Source . NextExecute = DateTime . Now . AddSeconds ( this . Source . Interval ); break ; case "ScheduleService.Plugin.IAlarmPlugin" : // 定時類型 IAlarmPlugin alarmPlugin = ( IAlarmPlugin ) assembly . CreateInstance ( Source . Assembly ); base . Interval = 30000 ; // 每30秒執行檢查 base . Elapsed += new System . Timers . ElapsedEventHandler ( Alarm_Elapsed ); this . Instance = alarmPlugin ; this . Source . NextExecute = this . NextExecute ( DateTime . Now ); break ; case "ScheduleService.Plugin.IGeneralPlugin" : // 未測試 IGeneralPlugin generalPlugin = ( IGeneralPlugin ) assembly . CreateInstance ( Source . Assembly ); base . Enabled = false ; this . Instance = generalPlugin ; break ; } } catch ( Exception ex ) { ServiceLogger . Log ( string . Format ( "建立'{0}'實體時發生錯誤" , Source . Assembly ), ex ); throw ex ; } } void Alarm_Elapsed ( object sender , System . Timers . ElapsedEventArgs e ) { if ( this . flag ) { ServiceLogger . Log ( "前一個處理排程正在進行中,略過本次排程" ); return ; } if ( string . Format ( "{0:yyyyMMddHHmm}" , this . NextExecute ( DateTime . Now . AddMinutes (- 1 ))) == string . Format ( "{0:yyyyMMddHHmm}" , DateTime . Now )) { this . flag = true ; this . Enabled = false ; this . Source . LastExecuted = e . SignalTime ; // 執行時間 this . Source . NextExecute = this . NextExecute ( DateTime . Now . AddMinutes ( 5 )); // 預計下次執行時間 try { this . ExecuteAlarm ( sender , e ); this . Source . Exception = null ; } catch ( Exception ex ) { this . Source . Exception = ex ; ServiceLogger . Log ( string . Format ( "排程工作[{0}]執行時發生錯誤" , Source . Name ), ex ); if ( this . Source . ReTryOnFailed ) { // 延遲30秒後重試 System . Threading . Thread . Sleep ( 30000 ); ServiceLogger . Log ( string . Format ( "重新嘗試執行排程工作[{0}]" , Source . Name ), ex ); try { this . ExecuteAlarm ( sender , e ); } catch ( Exception exx ) { ServiceLogger . Log ( string . Format ( "重新嘗試執行排程工作[{0}]執行時發生錯誤" , Source . Name ), exx ); } } } // 強迫執行時間大於60秒, 避免重覆執行 System . Threading . Thread . Sleep ( 60000 ); this . Enabled = true ; this . flag = false ; // 回收記憶體 GC . Collect (); } } private void ExecuteAlarm ( object sender , System . Timers . ElapsedEventArgs e ) { IComboPlugin comboPlugin = this . Instance as IComboPlugin ; IAlarmPlugin alarmPlugin = this . Instance as IAlarmPlugin ; // OnExecuting bool isCanceled = false ; if ( this . Instance is IComboPlugin && comboPlugin != null ) { isCanceled = ! comboPlugin . OnExecuting (); } // Execute if ( isCanceled ) { ServiceLogger . Log ( string . Format ( "執行模組[{0}]時被前置作業取消" , Source . Name )); } else { ServiceLogger . Log ( string . Format ( "開始執行模組[{0}]" , Source . Name )); alarmPlugin . ExecuteAt ( sender , e ); ServiceLogger . Log ( string . Format ( "排程工作[{0}]執行完成" , Source . Name )); } // OnExecuted if ( this . Instance is IComboPlugin && comboPlugin != null ) { comboPlugin . OnExecuted (); } } void Simple_Elapsed ( object sender , System . Timers . ElapsedEventArgs e ) { if ( this . flag ) { ServiceLogger . Log ( "前一個處理排程正在進行中,略過本次排程" ); return ; } this . flag = true ; this . Enabled = false ; this . Source . LastExecuted = e . SignalTime ; // 執行時間 this . Source . NextExecute = e . SignalTime . AddSeconds ( this . Source . Interval ); // 預計下次執行時間 try { this . ExecuteSimple ( sender , e ); this . Source . Exception = null ; } catch ( Exception ex ) { this . Source . Exception = ex ; ServiceLogger . Log ( string . Format ( "排程工作[{0}]執行時發生錯誤" , Source . Name ), ex ); if ( this . Source . ReTryOnFailed ) { // 延遲30秒後重試 System . Threading . Thread . Sleep ( 30000 ); ServiceLogger . Log ( string . Format ( "重新嘗試執行排程工作[{0}]" , Source . Name ), ex ); try { this . ExecuteSimple ( sender , e ); } catch ( Exception exx ) { ServiceLogger . Log ( string . Format ( "重新嘗試執行排程工作[{0}]執行時發生錯誤" , Source . Name ), exx ); } } } this . Enabled = true ; this . flag = false ; // 回收記憶體 GC . Collect (); } private void ExecuteSimple ( object sender , System . Timers . ElapsedEventArgs e ) { IComboPlugin comboPlugin = this . Instance as IComboPlugin ; ISimplePlugin simplePlugin = this . Instance as ISimplePlugin ; // OnExecuting bool isCanceled = false ; if ( this . Instance is IComboPlugin && comboPlugin != null ) { isCanceled = ! comboPlugin . OnExecuting (); } // Execute if ( isCanceled ) { ServiceLogger . Log ( string . Format ( "執行模組[{0}]時被前置作業取消" , Source . Name )); } else { ServiceLogger . Log ( string . Format ( "開始執行模組[{0}]" , Source . Name )); simplePlugin . Execute ( sender , e ); ServiceLogger . Log ( string . Format ( "排程工作[{0}]執行完成" , Source . Name )); } // OnExecuted if ( this . Instance is IComboPlugin && comboPlugin != null ) { comboPlugin . OnExecuted (); } } /// <summary> /// 計算下次執行時間 /// </summary> /// <param name="baseTime"></param> /// <returns></returns> private DateTime NextExecute ( DateTime baseTime ) { int duration = - 1 ; int hour = - 1 ; int minute = - 1 ; string [] time = this . Source . ExecutePoint . Split ( new char [] { ':' }); if ( Regex . IsMatch ( this . Source . ExecutePoint , "^(\\d+:)?\\d+:\\d+$" )) { switch ( time . Length ) { case 3 : // 計算週期的值 duration = int . Parse ( time [ 0 ]); switch ( this . Source . Duration ) { case Duration . Week : if ( duration < 0 || duration > 6 ) throw new FormatException ( "執行時間點:星期數值錯誤" ); break ; case Duration . Month : if ( duration < 0 || duration > 31 ) throw new FormatException ( "執行時間點:日期數值錯誤" ); break ; } // 小時值 hour = int . Parse ( time [ 1 ]); if ( hour < 0 || hour > 23 ) throw new FormatException ( "執行時間點:小時數值錯誤" ); // 分鐘值 minute = int . Parse ( time [ 2 ]); if ( hour < 0 || hour > 59 ) throw new FormatException ( "執行時間點:分鐘數值錯誤" ); break ; case 2 : // 小時值 hour = int . Parse ( time [ 0 ]); if ( hour < 0 || hour > 23 ) throw new FormatException ( "執行時間點:小時數值錯誤" ); // 分鐘值 minute = int . Parse ( time [ 1 ]); if ( hour < 0 || hour > 59 ) throw new FormatException ( "執行時間點:分鐘數值錯誤" ); break ; default : throw new FormatException ( "執行時間點:時間格式錯誤" ); } DateTime nextExecute = baseTime ; switch ( this . Source . Duration ) { case Duration . Day : nextExecute = new DateTime ( baseTime . Year , baseTime . Month , baseTime . Day , hour , minute , 0 ); if ( nextExecute < baseTime ) nextExecute = nextExecute . AddDays ( 1 ); break ; case Duration . Week : int todayOfWeek = ( int ) DateTime . Now . DayOfWeek ; // 今天的星期 int leftDays = duration - todayOfWeek < 0 ? duration - todayOfWeek + 7 : duration - todayOfWeek ; // 距下次執行日期的天數 nextExecute = new DateTime ( baseTime . Year , baseTime . Month , baseTime . Day , hour , minute , 0 ). AddDays ( leftDays ); if ( nextExecute < baseTime ) nextExecute = nextExecute . AddDays ( 7 ); break ; case Duration . Month : duration = Math . Min ( DateTime . DaysInMonth ( baseTime . Year , baseTime . Month ), duration ); // 數值大於當月天數時, 在最後一天執行 nextExecute = new DateTime ( baseTime . Year , baseTime . Month , duration , hour , minute , 0 ); if ( nextExecute < baseTime ) nextExecute = nextExecute . AddMonths ( 1 ); break ; } return nextExecute ; } else { throw new FormatException ( "執行時間點:時間格式錯誤" ); } } } }
Remoting
MemoryUsage.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Diagnostics ; namespace ScheduleService . Remoting { [ Serializable ] public class MemoryUsage { public int NonpagedSystemMemorySize { get ; set ; } public long NonpagedSystemMemorySize64 { get ; set ; } public int PagedMemorySize { get ; set ; } public long PagedMemorySize64 { get ; set ; } public int PagedSystemMemorySize { get ; set ; } public long PagedSystemMemorySize64 { get ; set ; } public int PeakPagedMemorySize { get ; set ; } public long PeakPagedMemorySize64 { get ; set ; } public int PeakVirtualMemorySize { get ; set ; } public long PeakVirtualMemorySize64 { get ; set ; } public int PeakWorkingSet { get ; set ; } public long PeakWorkingSet64 { get ; set ; } public int PrivateMemorySize { get ; set ; } public long PrivateMemorySize64 { get ; set ; } public int VirtualMemorySize { get ; set ; } public long VirtualMemorySize64 { get ; set ; } public int WorkingSet { get ; set ; } public long WorkingSet64 { get ; set ; } protected internal void Refresh () { Process p = Process . GetCurrentProcess (); this . NonpagedSystemMemorySize = p . NonpagedSystemMemorySize ; this . NonpagedSystemMemorySize64 = p . NonpagedSystemMemorySize64 ; this . PagedMemorySize = p . PagedMemorySize ; this . PagedMemorySize64 = p . PagedMemorySize64 ; this . PagedSystemMemorySize = p . PagedSystemMemorySize ; this . PagedSystemMemorySize64 = p . PagedSystemMemorySize64 ; this . PeakPagedMemorySize = p . PeakPagedMemorySize ; this . PeakPagedMemorySize64 = p . PeakPagedMemorySize64 ; this . PeakVirtualMemorySize = p . PeakVirtualMemorySize ; this . PeakVirtualMemorySize64 = p . PeakVirtualMemorySize64 ; this . PeakWorkingSet = p . PeakWorkingSet ; this . PeakWorkingSet64 = p . PeakWorkingSet64 ; this . PrivateMemorySize = p . PrivateMemorySize ; this . PrivateMemorySize64 = p . PrivateMemorySize64 ; this . VirtualMemorySize = p . VirtualMemorySize ; this . VirtualMemorySize64 = p . VirtualMemorySize64 ; this . WorkingSet = p . WorkingSet ; this . WorkingSet64 = p . WorkingSet64 ; } } }
ModuleData.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using ScheduleService . Configuration ; namespace ScheduleService . Remoting { [ Serializable ] public sealed class ModuleData { public string Name { get ; set ; } public string Type { get ; set ; } public string Binary { get ; set ; } public string Assembly { get ; set ; } public int Interval { get ; set ; } public Duration Duration { get ; set ; } public string ExecutePoint { get ; set ; } public bool ReTryOnFailed { get ; set ; } public DateTime StartTime { get ; set ; } public DateTime LastExecuted { get ; set ; } public DateTime NextExecute { get ; set ; } public Exception Exception { get ; set ; } public override int GetHashCode () { return string . Format ( "{0}{1}{2}{3}" , this . Name , this . Type , this . Binary , this . Assembly ). GetHashCode (); } public override bool Equals ( object compareTo ) { if ( compareTo == null || !( compareTo is ModuleData )) return false ; ModuleData obj = compareTo as ModuleData ; return string . Format ( "{0}{1}{2}{3}" , this . Name , this . Type , this . Binary , this . Assembly ) == string . Format ( "{0}{1}{2}{3}" , obj . Name , obj . Type , obj . Binary , obj . Assembly ); } } }
Monitor.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using ScheduleService . Configuration ; namespace ScheduleService . Remoting { public interface Monitor { MemoryUsage GetUsage (); ModuleData [] GetModules (); } }
ServiceClient.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Runtime . Remoting . Channels . Http ; using System . Runtime . Remoting . Channels ; using System . Runtime . Remoting . Channels . Tcp ; using System . Net ; namespace ScheduleService . Remoting { public class ServiceClient : Monitor { private TcpChannel channel ; private Monitor remotingObj ; public void Connect ( string host , int port ) { if ( channel != null ) { this . Close (); } IPAddress [] ip = Dns . GetHostAddresses ( host ); if ( ip . Count () == 0 ) throw new Exception ( "無法解析主機名稱或ip" ); this . channel = new TcpChannel (); ChannelServices . RegisterChannel ( this . channel , false ); remotingObj = Activator . GetObject ( typeof ( Monitor ), string . Format ( "tcp://{0}:{1}/ScheduleService.Remoting" , ip [ 0 ], port ) ) as Monitor ; } public void Close () { if ( this . channel != null ) { ChannelServices . UnregisterChannel ( this . channel ); this . channel . StopListening ( null ); this . channel = null ; } GC . Collect (); } #region Monitor 成員 public MemoryUsage GetUsage () { try { if ( remotingObj == null ) return null ; else return remotingObj . GetUsage (); } catch //(Exception ex) { return null ; } } public ModuleData [] GetModules () { try { if ( remotingObj == null ) return null ; else return remotingObj . GetModules (); } catch //(Exception ex) { return null ; } } #endregion } }
ServiceMonitor.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . Runtime . Remoting . Channels . Tcp ; using System . Runtime . Remoting . Channels ; using System . Runtime . Remoting ; namespace ScheduleService . Remoting { public class ServiceMonitor : MarshalByRefObject , Monitor { private TcpChannel channel ; public void StartListen ( int port ) { // 結束既有的監聽 if ( this . channel != null ) { this . StopListen (); } this . channel = new TcpChannel ( port ); ChannelServices . RegisterChannel ( this . channel , false ); RemotingConfiguration . RegisterWellKnownServiceType ( typeof ( ServiceMonitor ), "ScheduleService.Remoting" , WellKnownObjectMode . SingleCall ); } public void StopListen () { if ( this . channel != null ) { ChannelServices . UnregisterChannel ( this . channel ); this . channel . StopListening ( null ); this . channel = null ; } GC . Collect (); } #region Monitor 成員 public MemoryUsage GetUsage () { MemoryUsage usage = new MemoryUsage (); usage . Refresh (); return usage ; } public ModuleData [] GetModules () { if ( ScheduleServiceBase . ModuleList == null ) { return new ModuleData [ 0 ]; } else { List < ModuleData > list = new List < ModuleData >(); foreach ( ScheduleService . Configuration . Module m in ScheduleServiceBase . ModuleList ) { list . Add ( m . ToModuleData ()); } return list . ToArray (); } } #endregion } }
Utilily
Convertor.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Utilily { public class Convertor { public static string MatchSize ( double size , SizeName sizeName ) { if ( size < 1024 || sizeName == SizeName . TB ) { return string . Format ( "{0:####.##}{1}" , size , sizeName ); } else { return Convertor . MatchSize ( size / 1024 , ( SizeName )((( int ) sizeName ) + 1 )); } } } }
ExceptionHandler.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Utilily { public interface ExceptionHandler { void Log ( string description ); void Log ( string description , Exception ex ); void Log ( string description , Exception ex , object data ); } }
FileoutputHandler.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . IO ; using ScheduleService . Configuration ; namespace ScheduleService . Utilily { public class FileoutputHandler : ScheduleService . Utilily . ExceptionHandler { #region ExceptionHandler 成員 public void Log ( string description ) { File . AppendAllText ( string . Format ( "{0}ScheduleService{1:yyyyMMdd}.log" , PathHelper . LogPath , DateTime . Now ), string . Format ( @"Time : {0} Description : {1} " , DateTime . Now , description )); } public void Log ( string description , Exception ex ) { File . AppendAllText ( string . Format ( "{0}ScheduleService{1:yyyyMMdd}.log" , PathHelper . LogPath , DateTime . Now ), string . Format ( @"Time : {0} Description : {1} Message : {2} Source : {3} Exception type : {4} Stack : {5} " , DateTime . Now , description , ex . Message , ex . Source , ex . GetType (). Name , ex . StackTrace )); } public void Log ( string description , Exception ex , object data ) { this . Log ( description , ex ); } #endregion } }
PathHelper.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using System . IO ; namespace ScheduleService . Utilily { public class PathHelper { /// <summary> /// 程式目錄 /// </summary> public static string ApplicationPath { get { return AppDomain . CurrentDomain . BaseDirectory ; } } /// <summary> /// 模組目錄 /// </summary> public static string PluginPath { get { return string . Format ( "{0}Plugin\\" , PathHelper . ApplicationPath ); } } /// <summary> /// Log目錄 /// </summary> public static string LogPath { get { string logPath = string . Format ( "{0}Log\\" , PathHelper . ApplicationPath ); if (! Directory . Exists ( logPath )) Directory . CreateDirectory ( logPath ); return logPath ; } } public static string TempPath { get { string tempPath = string . Format ( "{0}Temp\\" , PathHelper . ApplicationPath ); if (! Directory . Exists ( tempPath )) Directory . CreateDirectory ( tempPath ); return tempPath ; } } } }
ServiceLogger.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using ScheduleService . Utilily ; namespace ScheduleService . Utilily { public class ServiceLogger { private static ExceptionHandler exHandler ; public static void SetHandler ( ExceptionHandler handler ) { exHandler = handler ; } public static void Log ( string description ) { if ( exHandler != null ) exHandler . Log ( description ); } public static void Log ( string description , Exception ex ) { if ( exHandler != null ) exHandler . Log ( description , ex ); } public static void Log ( string description , Exception ex , object data ) { if ( exHandler != null ) exHandler . Log ( description , ex , data ); } } }
SizeName.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; namespace ScheduleService . Utilily { public enum SizeName : int { Bytes = 1 , KB = 2 , MB = 3 , GB = 4 , TB = 5 } }
ScheduleServiceBase.cs
using System ; using System . Collections . Generic ; using System . Linq ; using System . Text ; using ScheduleService . Plugin ; using System . Reflection ; using ScheduleService . Configuration ; using System . Diagnostics ; using System . Configuration ; using ScheduleService . Utilily ; using System . IO ; using ScheduleService . Remoting ; namespace ScheduleService { public class ScheduleServiceBase : System . ServiceProcess . ServiceBase { /// <summary> /// 排程的timer /// </summary> private List < Timer > tasks ; /// <summary> /// 目前載入的模組 /// </summary> private static List < ScheduleService . Configuration . Module > moduleList ; protected internal static List < ScheduleService . Configuration . Module > ModuleList { get { return ScheduleServiceBase . moduleList ; } } /// <summary> /// 設定檔監控 /// </summary> private FileSystemWatcher configWatcher ; /// <summary> /// 遠端監控 /// </summary> private ServiceMonitor monitor ; public ScheduleServiceBase () { this . tasks = new List < Timer >(); ScheduleServiceBase . moduleList = new List < ScheduleService . Configuration . Module >(); this . configWatcher = new FileSystemWatcher (); this . monitor = new ServiceMonitor (); // 指定Log記錄方式 ServiceLogger . SetHandler ( new FileoutputHandler ()); ServiceLogger . Log ( "狀態: 啟動服務" ); ScheduleService . Configuration . Plugin plugin = null ; try { plugin = ( ScheduleService . Configuration . Plugin ) ConfigurationManager . GetSection ( "scheduleService.plugin" ); } catch ( Exception ex ) { ServiceLogger . Log ( "初始設定檔時發生錯誤" , ex ); } try { if ( plugin != null && plugin . ExceptionHandler != null && ! string . IsNullOrEmpty ( plugin . ExceptionHandler . Binary )) { Assembly assembly = Assembly . LoadFrom ( PathHelper . PluginPath + plugin . ExceptionHandler . Binary ); ExceptionHandler handler = ( ExceptionHandler ) assembly . CreateInstance ( plugin . ExceptionHandler . Assembly ); if ( handler == null ) throw new Exception ( "建立例外處理器實體失敗" ); else ServiceLogger . SetHandler ( handler ); } } catch ( Exception ex ) { ServiceLogger . Log ( "設定例外處理器時發生錯誤" , ex ); } try { // 起始remoting this . monitor . StartListen ( plugin . MonitorPort . Value ); } catch ( Exception ex ) { ServiceLogger . Log ( "設定遠端監控時發生錯誤" , ex ); } } /// <summary> /// 測試用 /// </summary> /// <param name="args"></param> public void DoStart ( string [] args ) { this . OnStart ( args ); } /// <summary> /// 啟始設定檔載入程式 /// </summary> /// <param name="args"></param> protected override void OnStart ( string [] args ) { try { #region 監控設定是否改變 this . configWatcher . Path = PathHelper . ApplicationPath ; this . configWatcher . Filter = "*.config" ; this . configWatcher . IncludeSubdirectories = false ; this . configWatcher . EnableRaisingEvents = true ; this . configWatcher . Changed += new FileSystemEventHandler ( configWatcher_Changed ); #endregion // 載入模組 this . ChangeModule (); } catch ( Exception ex ) { ServiceLogger . Log ( "起動程式時發生錯誤" , ex ); } } /// <summary> /// 異動時新增/移除模組 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void configWatcher_Changed ( object sender , FileSystemEventArgs e ) { // 暫止監控 this . configWatcher . EnableRaisingEvents = false ; // 設定檔變更時 if ( e . Name == string . Format ( "{0}.config" , AppDomain . CurrentDomain . FriendlyName )) { this . ChangeModule (); } // 恢復監控 this . configWatcher . EnableRaisingEvents = true ; } /// <summary> /// 新增/移除模組 /// </summary> private void ChangeModule () { try { PluginLoader loader = new PluginLoader (); ScheduleService . Configuration . Module [] plugins = loader . GetModules (); #region 加入新增的模組 foreach ( ScheduleService . Configuration . Module m in plugins ) { if (! ScheduleServiceBase . moduleList . Contains ( m )) { try { Timer timer = new Timer ( m ); // 初始化(OnInit) if ( timer . Instance is IGeneralPlugin ) { IGeneralPlugin complexPlugin = ( IGeneralPlugin ) timer . Instance ; if (! complexPlugin . OnStart ()) { ServiceLogger . Log ( string . Format ( "模組[{0}]初始化時由前置作業終止,未載入模組" , m . Name )); continue ; } } timer . Start (); this . tasks . Add ( timer ); ScheduleServiceBase . moduleList . Add ( m ); // 記錄時間 m . StartTime = DateTime . Now ; ServiceLogger . Log ( string . Format ( "加入新模組[{0}]" , m . Name )); } catch ( Exception ex ) { ServiceLogger . Log ( string . Format ( "加入模組[{0}]時發生錯誤" , m . Name ), ex ); } } } #endregion #region 移除模組 ScheduleService . Configuration . Module [] currentModule = loader . GetModules (); for ( int i = this . tasks . Count - 1 ; i >= 0 ; i --) { Timer timer = this . tasks [ i ]; try { if (! currentModule . Contains ( timer . Source )) { timer . Enabled = false ; timer . Stop (); this . tasks . Remove ( timer ); ScheduleServiceBase . ModuleList . Remove ( timer . Source ); // 回收(Dispose) if ( timer . Instance is IGeneralPlugin ) { IGeneralPlugin generalPlugin = ( IGeneralPlugin ) timer . Instance ; generalPlugin . OnStop (); } ServiceLogger . Log ( string . Format ( "已移除模組[{0}]" , timer . Source . Name )); } } catch ( Exception ex ) { ServiceLogger . Log ( string . Format ( "移除模組[{0}]時發生錯誤" , timer . Source . Name ), ex ); } } #endregion // 回收記憶體 GC . Collect (); } catch ( Exception ex ) { ServiceLogger . Log ( "模組異動時發生錯誤" , ex ); } } /// <summary> /// 測試用 /// </summary> public void DoStop () { this . OnStop (); } protected override void OnStop () { // 停止監控設定檔 this . configWatcher . EnableRaisingEvents = false ; // 停止remoting channel this . monitor . StopListen (); foreach ( ScheduleService . Plugin . Timer timer in this . tasks ) { try { timer . Enabled = false ; timer . Stop (); if ( timer . Instance is IGeneralPlugin ) { IGeneralPlugin generalPlugin = ( IGeneralPlugin ) timer . Instance ; generalPlugin . OnStop (); } } catch ( Exception ex ) { ServiceLogger . Log ( "停止服務時發生錯誤" , ex ); } } ServiceLogger . Log ( "狀態: 停止服務" ); } } }
App.config
<? xml version = "1.0" encoding = "utf-8" ?> <configuration> <configSections> <section name = "scheduleService.plugin" type = "ScheduleService.Configuration.Plugin, ServicePlugin" /> </configSections> <scheduleService.plugin> <monitorPort value = "9876" /> <!--<exceptionHandler binary="MyLib.dll" assembly="MyLib.MyHandler" />--> <plugins> <!--<module name="PluginName" type="ScheduleService.Plugin.IGeneralPlugin" binary="MyLib.dll" assembly="MyLib.ClassName" />--> <!--<module name="PluginName" type="ScheduleService.Plugin.ISimplePlugin" binary="MyLib.dll" assembly="MyLib.ClassName" interval="10" reTryOnFailed="false" />--> <!-- druation = Day | Week | Month executePoint = (tt:)HH:mm --> <!--<module name="PluginName" type="ScheduleService.Plugin.IAlarmPlugin" binary="MyLib.dll" assembly="MyLib.ClassName" duration="Month" executePoint="3:15:32" reTryOnFailed="false" />--> </plugins> </scheduleService.plugin> </configuration>
Review of installation/uninstallation
% windir % \Microsoft . NET\Framework\v 2.0 . 50727 \installutil ServiceController . exe % windir % \Microsoft . NET\Framework\v 2.0 . 50727 \installutil / u ServiceController . exe