Popular Posts
javax.net.ssl.SSLHandshakeException: Connection closed by peer in Android 5.0 Lollipop Recently, there is a error occurs when access website via ssl connection like below although it worked fine several days ago. // Enable SSL... Enable SSL connection for Jsoup import org.jsoup.Connection; import org.jsoup.Jsoup; import javax.net.ssl.*; import java.io.IOException; import java.security.KeyManagement... Build an OpenVPN server on android device Preparation An android device, in this case, Sony xperia Z is used Root permission required Linux Deploy for deploy i...
Blog Archive
Stats
Memory usage
If you want to know how much the GC uses try:
  1. GC.GetTotalMemory(true)
If you want to know what your process uses from Windows (VM Size column in TaskManager) try:
  1. Process.GetCurrentProcess().PrivateMemorySize64
If you want to know what your process has in RAM (as opposed to in the pagefile) (Mem Usage column in TaskManager) try:
  1. Process.GetCurrentProcess().WorkingSet64
See here for more explanation on the different sorts of memory.
DNS SERVER LIST

Google
8.8.8.8
8.8.4.4
TWNIC
192.83.166.11
211.72.210.250
HiNet
168.95.1.1
168.95.192.1
Seednet
北區 DNS (台北, 桃園, 新竹, 宜蘭, 花蓮, 苗栗)
139.175.55.244
139.175.252.16
中區 DNS (台中, 彰化, 南投, 雲林)
139.175.150.20
139.175.55.244
南區 DNS (高雄, 台南, 嘉義, 屏東, 台東)
139.175.10.20
139.175.55.244
So-net
61.64.127.1
61.64.127.2
61.64.127.5
61.64.127.6
台灣大寬頻
211.78.215.200
211.78.215.137
速博
211.78.130.10
211.78.130.11
yam天空
210.66.81.232
60.199.244.5
60.199.244.4
億聯科技
61.57.159.130
61.57.159.135
亞太寬頻
210.200.64.100
210.200.64.101
亞太東森寬頻ADSL
203.79.224.10
203.79.224.30
GIGA 和信超媒體
203.133.1.8
203.133.1.6
中央研究院
140.109.1.5
140.109.13.5
台灣大學
140.112.254.4
211.79.61.47
教育部
163.28.6.21
192.83.166.9
192.72.81.200
168.95.192.10
210.17.9.229
140.111.1.2
192.83.166.17
網路中文
202.153.205.76
220.130.187.243
PChome Online 網路家庭
210.244.29.182
210.244.29.185
58.86.34.1
克拉國際
203.67.177.2
203.67.177.3
WIS 匯智
211.76.136.72
211.76.136.77
Yahoo
68.180.131.16
68.142.255.16
121.101.152.99
68.142.196.63
119.160.247.124
202.43.223.170
202.165.104.22

Auto mount disk on startup (Ubuntu)
sudo fdisk -l (check disk)
Disk /dev/sda: 160.0 GB, 160041885696 bytes
255 heads, 63 sectors/track, 19457 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x9c29f0a3

所用裝置 Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1        3552    28531408+  83  Linux
/dev/sda2            3553        3946     3164805   82  Linux swap / Solaris
/dev/sda3            3947       19457   124582912    7  HPFS/NTFS

Disk /dev/sdb: 160.0 GB, 160041885696 bytes
255 heads, 63 sectors/track, 19457 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0xd788d788

所用裝置 Boot      Start         End      Blocks   Id  System
/dev/sdb1               1       19458   156288000    7  HPFS/NTFS
edit /etc/fstab , append red part
# /etc/fstab: static file system information.
#
# Use 'blkid -o value -s UUID' to print the universally unique identifier
# for a device; this may be used with UUID= as a more robust way to name
# devices that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults        0       0
# / was on /dev/sda1 during installation
UUID=88f5915d-7654-402e-b7de-9dbb75b27b53 /               ext4    errors=remount-ro 0       1
# swap was on /dev/sda2 during installation
UUID=6e67ce31-882f-4a5f-aec9-8d6f23fad16f none            swap    sw              0       0
/dev/scd0       /media/cdrom0   udf,iso9660 user,noauto,exec,utf8 0       0
/dev/fd0        /media/floppy0  auto    rw,user,noauto,exec,utf8 0       0
/dev/sda3    /media/DataSource    ntfs    defaults    0    0
/dev/sdb1    /media/Images    ntfs    defaults    0    0
FCKeditor button settings
打開 fckconfig.js 後,找 FCKConfig.ToolbarSets,底下就會有功能按鍵的設定,依照我們的需求增加或刪除。
參數
說明
參數
說明
Source
原始碼
DocProps
文件屬性
Save
儲存
NewPage
開新檔案
Preview
預覽
Templates
樣板
Cut
剪下
Copy
拷貝
Paste
貼上
PasteText
貼為純文字
PasteWord
Word 貼上
Print
列印
SpellCheck
拼字檢查
Undo
復原
Redo
復原
Find
尋找
Replace
取代
SelectAll
全選
RemoveFormat
清除格式
Form
表單
Checkbox
核取方塊
Radio
選項按鈕
TextField
文字區域
Select
下拉選單
Button
按鈕
ImageButton
影像按鈕
HiddenField
隱藏欄位
Bold
粗體
Italic
斜體
Underline
底線
StrikeThrough
刪除線
Subscript
下標字
Superscript
上標字
OrderedList
數字項目符號
UnorderedList
項目符號
Outdent
減少縮排
Indent
增加縮排
Blockquote
區塊引用
JustifyLeft
靠左
JustifyCenter
置中
JustifyRight
靠右
JustifyFull
左右對齊
Link
建立連結
Unlink
移除連結
Anchor
錨點
Image
插入圖片
Flash
插入Flash
Table
插入表格
Rule
插入水平線
Smiley
表情符號
SpecialChar
特殊符號
PageBreak
分頁符號
Style
樣式
FontFormat
字體格式
FontName
字型選擇
FontSize
字型大小
TextColor
文字顏色
BGColor
背景顏色
FitWindow
編輯器最大化
ShowBlocks
顯示HTML標籤區塊
About
關於FCKeditor



Application、Page、Control Life cycle (Event trigger)
  1. Application: BeginRequest
  2. Application: PreAuthenticateRequest
  3. Application: AuthenticateRequest
  4. Application: PostAuthenticateRequest
  5. Application: PreAuthorizeRequest
  6. Application: AuthorizeRequest
  7. Application: PostAuthorizeRequest
  8. Application: PreResolveRequestCache
  9. Application: ResolveRequestCache
  10. Application: PostResolveRequestCache
  11. Application: PreMapRequestHandler
  12.     Page: Construct
  13. Application: PostMapRequestHandler
  14. Application: PreAcquireRequestState
  15. Application: AcquireRequestState
  16. Application: PostAcquireRequestState
  17. Application: PreRequestHandlerExecute
  18.     Page: AddParsedSubObject
  19.     Page: CreateControlCollection
  20.     Page: AddedControl
  21.     Page: AddParsedSubObject
  22.     Page: AddedControl
  23.     Page: ResolveAdapter
  24.     Page: DeterminePostBackMode
  25.     Page: PreInit
  26.         Control: ResolveAdapter
  27.         Control: Init
  28.         Control: TrackViewState
  29.     Page: Init
  30.     Page: TrackViewState
  31.     Page: InitComplete
  32.     Page: LoadPageStateFromPersistenceMedium
  33.         Control: LoadViewState
  34.     Page: EnsureChildControls
  35.     Page: CreateChildControls
  36.     Page: PreLoad
  37.     Page: Load
  38.         Control: DataBind
  39.         Control: Load
  40.     Page: EnsureChildControls
  41.     Page: LoadComplete
  42.     Page: EnsureChildControls
  43.     Page: PreRender
  44.         Control: EnsureChildControls
  45.         Control: PreRender
  46.     Page: PreRenderComplete
  47.     Page: SaveViewState
  48.         Control: SaveViewState
  49.     Page: SaveViewState
  50.         Control: SaveViewState
  51.     Page: SavePageStateToPersistenceMedium
  52.     Page: SaveStateComplete
  53.     Page: CreateHtmlTextWriter
  54.     Page: RenderControl
  55.     Page: Render
  56.     Page: RenderChildren
  57.         Control: RenderControl
  58.     Page: VerifyRenderingInServerForm
  59.     Page: CreateHtmlTextWriter
  60.         Control: Unload
  61.         Control: Dispose
  62.     Page: Unload
  63.     Page: Dispose
  64. Application: PostRequestHandlerExecute
  65. Application: PreReleaseRequestState
  66. Application: ReleaseRequestState
  67. Application: PostReleaseRequestState
  68. Application: PreUpdateRequestCache
  69. Application: UpdateRequestCache
  70. Application: PostUpdateRequestCache
  71. Application: EndRequest
  72. Application: PreSendRequestHeaders
  73. Application: PreSendRequestContent
InputBox in C#
  1. /// <summary>
  2. /// Summary description for InputBox.
  3. ///
  4. public class InputBoxDialog : System.Windows.Forms.Form
  5. {
  6.  
  7.     #region Windows Contols and Constructor
  8.  
  9.     private System.Windows.Forms.Label lblPrompt;
  10.     private System.Windows.Forms.Button button1;
  11.     private System.Windows.Forms.TextBox txtInput;
  12.     /// <summary>
  13.     /// Required designer variable.
  14.     ///
  15.     private System.ComponentModel.Container components = null;
  16.  
  17.     public InputBoxDialog()
  18.     {
  19.         //
  20.         // Required for Windows Form Designer support
  21.         //
  22.         InitializeComponent();
  23.  
  24.         //
  25.         // TODO: Add any constructor code after InitializeComponent call
  26.         //
  27.     }
  28.  
  29.     #endregion
  30.  
  31.     #region Dispose
  32.  
  33.     /// <summary>
  34.     /// Clean up any resources being used.
  35.     ///
  36.     protected override void Dispose(bool disposing)
  37.     {
  38.         if (disposing)
  39.         {
  40.             if (components != null)
  41.             {
  42.                 components.Dispose();
  43.             }
  44.         }
  45.         base.Dispose(disposing);
  46.     }
  47.  
  48.     #endregion
  49.  
  50.     #region Windows Form Designer generated code
  51.     /// <summary>
  52.     /// Required method for Designer support - do not modify
  53.     /// the contents of this method with the code editor.
  54.     ///
  55.     private void InitializeComponent()
  56.     {
  57.         this.lblPrompt = new System.Windows.Forms.Label();
  58.         this.button1 = new System.Windows.Forms.Button();
  59.         this.txtInput = new System.Windows.Forms.TextBox();
  60.         this.btnOK = new System.Windows.Forms.Button();
  61.         this.SuspendLayout();
  62.         //
  63.         // lblPrompt
  64.         //
  65.         this.lblPrompt.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
  66.                     | System.Windows.Forms.AnchorStyles.Left)
  67.                     | System.Windows.Forms.AnchorStyles.Right)));
  68.         this.lblPrompt.BackColor = System.Drawing.SystemColors.Control;
  69.         this.lblPrompt.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
  70.         this.lblPrompt.Location = new System.Drawing.Point(12, 17);
  71.         this.lblPrompt.Name = "lblPrompt";
  72.         this.lblPrompt.Size = new System.Drawing.Size(303, 22);
  73.         this.lblPrompt.TabIndex = 3;
  74.         //
  75.         // button1
  76.         //
  77.         this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
  78.         this.button1.Location = new System.Drawing.Point(323, 45);
  79.         this.button1.Name = "button1";
  80.         this.button1.Size = new System.Drawing.Size(64, 27);
  81.         this.button1.TabIndex = 2;
  82.         this.button1.Text = "&Cancel";
  83.         this.button1.Click += new System.EventHandler(this.button1_Click);
  84.         //
  85.         // txtInput
  86.         //
  87.         this.txtInput.Location = new System.Drawing.Point(8, 78);
  88.         this.txtInput.Name = "txtInput";
  89.         this.txtInput.Size = new System.Drawing.Size(379, 22);
  90.         this.txtInput.TabIndex = 0;
  91.         this.txtInput.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInput_KeyPress);
  92.         //
  93.         // btnOK
  94.         //
  95.         this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
  96.         this.btnOK.Location = new System.Drawing.Point(323, 12);
  97.         this.btnOK.Name = "btnOK";
  98.         this.btnOK.Size = new System.Drawing.Size(64, 27);
  99.         this.btnOK.TabIndex = 1;
  100.         this.btnOK.Text = "&OK";
  101.         this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
  102.         //
  103.         // InputBoxDialog
  104.         //
  105.         this.AutoScaleBaseSize = new System.Drawing.Size(5, 15);
  106.         this.ClientSize = new System.Drawing.Size(399, 111);
  107.         this.Controls.Add(this.txtInput);
  108.         this.Controls.Add(this.button1);
  109.         this.Controls.Add(this.btnOK);
  110.         this.Controls.Add(this.lblPrompt);
  111.         this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
  112.         this.MaximizeBox = false;
  113.         this.MinimizeBox = false;
  114.         this.Name = "InputBoxDialog";
  115.         this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
  116.         this.Text = "InputBox";
  117.         this.Load += new System.EventHandler(this.InputBox_Load);
  118.         this.ResumeLayout(false);
  119.         this.PerformLayout();
  120.  
  121.     }
  122.     #endregion
  123.  
  124.     #region Private Variables
  125.     string formCaption = string.Empty;
  126.     string formPrompt = string.Empty;
  127.     string inputResponse = string.Empty;
  128.     string defaultValue = string.Empty;
  129.     DialogResult clickedButton;
  130.     #endregion
  131.  
  132.     #region Public Properties
  133.     public string FormCaption
  134.     {
  135.         get { return formCaption; }
  136.         set { formCaption = value; }
  137.     } // property FormCaption
  138.     public string FormPrompt
  139.     {
  140.         get { return formPrompt; }
  141.         set { formPrompt = value; }
  142.     } // property FormPrompt
  143.     public string InputResponse
  144.     {
  145.         get { return inputResponse; }
  146.         set { inputResponse = value; }
  147.     } // property InputResponse
  148.     public string DefaultValue
  149.     {
  150.         get { return defaultValue; }
  151.         set { defaultValue = value; }
  152.     } // property DefaultValue
  153.     public DialogResult ClickedButton
  154.     {
  155.         get { return clickedButton; }
  156.     }
  157.     #endregion
  158.  
  159.     #region Form and Control Events
  160.     private void InputBox_Load(object sender, System.EventArgs e)
  161.     {
  162.         this.txtInput.Text = defaultValue;
  163.         this.lblPrompt.Text = formPrompt;
  164.         this.Text = formCaption;
  165.         this.txtInput.SelectionStart = 0;
  166.         this.txtInput.SelectionLength = this.txtInput.Text.Length;
  167.         this.txtInput.Focus();
  168.     }
  169.  
  170.  
  171.     private void btnOK_Click(object sender, System.EventArgs e)
  172.     {
  173.         this.clickedButton = DialogResult.OK;
  174.         InputResponse = this.txtInput.Text;
  175.         this.Close();
  176.     }
  177.  
  178.     private void button1_Click(object sender, System.EventArgs e)
  179.     {
  180.         this.clickedButton = DialogResult.Cancel;
  181.         this.Close();
  182.     }
  183.  
  184.     public static string InputBox(string prompt, string title, string defaultValue)
  185.     {
  186.         InputBoxDialog ib = new InputBoxDialog();
  187.         ib.FormPrompt = prompt;
  188.         ib.FormCaption = title;
  189.         ib.DefaultValue = defaultValue;
  190.         ib.ShowDialog();
  191.         string s = ib.InputResponse;
  192.         ib.Close();
  193.         return s;
  194.     }// method: InputBox
  195.  
  196.     private Button btnOK;
  197.  
  198.     private void txtInput_KeyPress(object sender, KeyPressEventArgs e)
  199.     {
  200.         if (e.KeyChar == 13)
  201.         {
  202.             btnOK_Click(sender, e);
  203.             e.Handled = true;
  204.         }
  205.     }
  206.     #endregion
  207. }
Thread sample
  1. class MyThread
  2. {
  3.     private int executeInterval = 15;
  4.     public int ExecuteInterval
  5.     {
  6.         get { return this.executeInterval; }
  7.         set { this.executeInterval = value; }
  8.     }
  9.     private int checkFlagInterval = 1;
  10.     public int CheckFlagInterval
  11.     {
  12.         get { return this.checkFlagInterval; }
  13.         set
  14.         {
  15.             this.checkFlagInterval = value < 1 ? 1 : value;
  16.             this.checkFlagInterval = value > this.executeInterval ? this.executeInterval : value;
  17.         }
  18.     }
  19.     private bool flag = true;
  20.     public bool Flag
  21.     {
  22.         get { return this.flag; }
  23.         set { this.flag = value; }
  24.     }
  25.     public void Run()
  26.     {
  27.         int timeout = 0;
  28.         while (this.flag)
  29.         {
  30.             if ((timeout -= checkFlagInterval) < 1)
  31.             {
  32.                 // do something
  33.                 timeout = this.executeInterval;
  34.             }
  35.             Thread.Sleep(this.checkFlagInterval * 1000);
  36.         }
  37.     }
  38. }
  39. class Program
  40. {
  41.     static void Main(string[] args)
  42.     {
  43.         MyThread myThread = new MyThread();
  44.         Thread thread = new Thread(new ThreadStart(myThread.Run));
  45.         thread.Start();
  46.  
  47.         while (myThread.Flag)
  48.         {
  49.             myThread.Flag = Console.Read() != 'q';
  50.         }
  51.     }
  52. }
Window service : dynamic load dll module and execute

Project(DLL) : ServicePlugin

Configuration

Duration.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Configuration
  7. {
  8.     public enum Duration
  9.     {
  10.         Day,
  11.         Week,
  12.         Month
  13.     }
  14. }
Logger.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6.  
  7. namespace ScheduleService.Configuration
  8. {
  9.     public class Logger : ConfigurationElement
  10.     {
  11.         /// <summary>
  12.         /// DLL檔案位置
  13.         /// </summary>
  14.         [ConfigurationProperty("binary")]
  15.         public string Binary
  16.         {
  17.             get { return (string)this["binary"]; }
  18.             set { this["binary"] = value; }
  19.         }
  20.         /// <summary>
  21.         /// 實體名稱
  22.         /// </summary>
  23.         [ConfigurationProperty("assembly", IsRequired = true)]
  24.         public string Assembly
  25.         {
  26.             get { return (string)this["assembly"]; }
  27.             set { this["assembly"] = value; }
  28.         }
  29.     }
  30. }
MemoryUsage.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6.  
  7. namespace ScheduleService.Configuration
  8. {
  9.     /// <summary>
  10.     /// 已不使用
  11.     /// </summary>
  12.     public sealed class MemoryUsage : ConfigurationElement
  13.     {
  14.         [ConfigurationProperty("interval", IsRequired = false, DefaultValue = 60)]
  15.         public int Interval
  16.         {
  17.             get { return (int)this["interval"]; }
  18.             set { this["interval"] = value; }
  19.         }
  20.  
  21.         [ConfigurationProperty("monitor", IsRequired = false, DefaultValue = false)]
  22.         public bool Monitor
  23.         {
  24.             get { return (bool)this["monitor"]; }
  25.             set { this["monitor"] = value; }
  26.         }
  27.     }
  28. }
Module.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6. using System.Runtime.Serialization;
  7. using ScheduleService.Remoting;
  8.  
  9. namespace ScheduleService.Configuration
  10. {
  11.     public sealed class Module : ConfigurationElement
  12.     {
  13.         /// <summary>
  14.         /// 模組名稱
  15.         /// </summary>
  16.         [ConfigurationProperty("name", IsKey = true)]
  17.         public string Name
  18.         {
  19.             get { return (string)this["name"]; }
  20.             set { this["name"] = value; }
  21.         }
  22.         /// <summary>
  23.         /// 介面名稱
  24.         /// </summary>
  25.         [ConfigurationProperty("type", IsRequired = true)]
  26.         public string Type
  27.         {
  28.             get { return (string)this["type"]; }
  29.             set { this["type"] = value; }
  30.         }
  31.         /// <summary>
  32.         /// DLL檔案位置
  33.         /// </summary>
  34.         [ConfigurationProperty("binary")]
  35.         public string Binary
  36.         {
  37.             get { return (string)this["binary"]; }
  38.             set { this["binary"] = value; }
  39.         }
  40.         /// <summary>
  41.         /// 實體名稱
  42.         /// </summary>
  43.         [ConfigurationProperty("assembly", IsRequired = true)]
  44.         public string Assembly
  45.         {
  46.             get { return (string)this["assembly"]; }
  47.             set { this["assembly"] = value; }
  48.         }
  49.         /// <summary>
  50.         /// 執行間隔(秒)
  51.         /// </summary>
  52.         [ConfigurationProperty("interval", IsRequired = false)]
  53.         public int Interval
  54.         {
  55.             get { return (int)this["interval"]; }
  56.             set { this["interval"] = value; }
  57.         }
  58.  
  59.         /// <summary>
  60.         /// 執行週期
  61.         /// </summary>
  62.         [ConfigurationProperty("duration", IsRequired = false, DefaultValue = Duration.Day)]
  63.         public Duration Duration
  64.         {
  65.             get { return (Duration)this["duration"]; }
  66.             set { this["duration"] = value; }
  67.         }
  68.  
  69.         /// <summary>
  70.         /// 執行時間點(格式tt:MM:dd)
  71.         /// </summary>
  72.         [ConfigurationProperty("executePoint", IsRequired = false)]
  73.         public string ExecutePoint
  74.         {
  75.             get { return (string)this["executePoint"]; }
  76.             set { this["executePoint"] = value; }
  77.         }
  78.  
  79.         /// <summary>
  80.         /// 執行失敗時, 嘗試重新執行一次
  81.         /// </summary>
  82.         [ConfigurationProperty("reTryOnFailed", IsRequired = false, DefaultValue = false)]
  83.         public bool ReTryOnFailed
  84.         {
  85.             get { return (bool)this["reTryOnFailed"]; }
  86.             set { this["reTryOnFailed"] = value; }
  87.         }
  88.  
  89.         public DateTime StartTime { get; set; }
  90.         public DateTime LastExecuted { get; set; }
  91.         public DateTime NextExecute { get; set; }
  92.         public Exception Exception { get; set; }
  93.  
  94.         public override string ToString()
  95.         {
  96.             return string.Format("<module name=\"{0}\" type=\"{1}\" binary=\"{2}\" assembly=\"{3}\" interval=\"{4}\" />", this.Name, this.Type, this.Binary, this.Assembly, this.Interval);
  97.         }
  98.  
  99.         public override int GetHashCode()
  100.         {
  101.             return string.Format("{0}{1}{2}{3}", this.Name, this.Type, this.Binary, this.Assembly).GetHashCode();
  102.         }
  103.  
  104.         public override bool Equals(object compareTo)
  105.         {
  106.             if (compareTo == null || !(compareTo is Module)) return false;
  107.             Module obj = compareTo as Module;
  108.             return string.Format("{0}{1}{2}{3}", this.Name, this.Type, this.Binary, this.Assembly)
  109.                 == string.Format("{0}{1}{2}{3}", obj.Name, obj.Type, obj.Binary, obj.Assembly);
  110.         }
  111.  
  112.         public ModuleData ToModuleData()
  113.         {
  114.             ModuleData data = new ModuleData();
  115.             data.Assembly = this.Assembly;
  116.             data.Binary = this.Binary;
  117.             data.Exception = this.Exception;
  118.             data.Interval = this.Interval;
  119.             data.LastExecuted = this.LastExecuted;
  120.             data.Name = this.Name;
  121.             data.NextExecute = this.NextExecute;
  122.             data.StartTime = this.StartTime;
  123.             data.Type = this.Type;
  124.             data.Duration = this.Duration;
  125.             data.ExecutePoint = this.ExecutePoint;
  126.             data.ReTryOnFailed = this.ReTryOnFailed;
  127.             return data;
  128.         }
  129.     }
  130. }
ModuleCollection.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ScheduleService.Configuration;
  6. using System.Configuration;
  7.  
  8. namespace ScheduleService.Configuration
  9. {
  10.     public sealed class ModuleCollection : ConfigurationElementCollection
  11.     {
  12.         public override ConfigurationElementCollectionType CollectionType
  13.         {
  14.             get
  15.             {
  16.                 return ConfigurationElementCollectionType.AddRemoveClearMap;
  17.             }
  18.         }
  19.  
  20.         public Module this[int index]
  21.         {
  22.             get { return (Module)BaseGet(index); }
  23.             set
  24.             {
  25.                 if (BaseGet(index) != null)
  26.                     BaseRemove(index);
  27.                 BaseAdd(index, value);
  28.             }
  29.         }
  30.  
  31.         protected override ConfigurationElement CreateNewElement()
  32.         {
  33.             return new Module();
  34.         }
  35.  
  36.         protected override object GetElementKey(ConfigurationElement element)
  37.         {
  38.             return (element as Module).Name;
  39.         }
  40.  
  41.         #region Operation
  42.         public void Add(Module module)
  43.         {
  44.             BaseAdd(module);
  45.         }
  46.  
  47.         public void Clear()
  48.         {
  49.             BaseClear();
  50.         }
  51.  
  52.         public void Rmove(Module module)
  53.         {
  54.             BaseRemove(module.Name);
  55.         }
  56.  
  57.         public void Remove(string name)
  58.         {
  59.             BaseRemove(name);
  60.         }
  61.  
  62.         public void RemoveAt(int index)
  63.         {
  64.             BaseRemoveAt(index);
  65.         }
  66.         #endregion
  67.     }
  68. }
MonitorPort.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6.  
  7.  
  8. namespace ScheduleService.Configuration
  9. {
  10.     public sealed class MonitorPort : ConfigurationElement
  11.     {
  12.         [ConfigurationProperty("value", IsRequired = true)]
  13.         public int Value
  14.         {
  15.             get { return (int)this["value"]; }
  16.             set { this["value"] = value; }
  17.         }
  18.     }
  19. }
Plugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6.  
  7. namespace ScheduleService.Configuration
  8. {
  9.     public sealed class Plugin : ConfigurationSection
  10.     {
  11.         ///// <summary>
  12.         ///// 重載間隔
  13.         ///// </summary>
  14.         //[ConfigurationProperty("reloadInterval")]
  15.         //public ReloadInterval ReloadConfigInterval
  16.         //{
  17.         //    get { return (ReloadInterval)this["reloadInterval"]; }
  18.         //    set { this["reloadInterval"] = value; }
  19.         //}
  20.         ///// <summary>
  21.         ///// 監控記憶體使用情況
  22.         ///// </summary>
  23.         //[ConfigurationProperty("memoryUsage")]
  24.         //public MemoryUsage MemoryUsage
  25.         //{
  26.         //    get { return (MemoryUsage)this["memoryUsage"]; }
  27.         //    set { this["memoryUsage"] = value; }
  28.         //}
  29.         /// <summary>
  30.         /// 監控通訊埠
  31.         /// </summary>
  32.         [ConfigurationProperty("monitorPort")]
  33.         public MonitorPort MonitorPort
  34.         {
  35.             get { return (MonitorPort)this["monitorPort"]; }
  36.             set { this["monitorPort"] = value; }
  37.         }
  38.         /// <summary>
  39.         /// 外掛log
  40.         /// </summary>
  41.         [ConfigurationProperty("exceptionHandler")]
  42.         public Logger ExceptionHandler
  43.         {
  44.             get { return (Logger)this["exceptionHandler"]; }
  45.             set { this["exceptionHandler"] = value; }
  46.         }
  47.         /// <summary>
  48.         /// 模組
  49.         /// </summary>
  50.         [ConfigurationProperty("plugins")]
  51.         [ConfigurationCollection(typeof(ModuleCollection), AddItemName = "module", ClearItemsName = "clearModules", RemoveItemName = "removeModules")]
  52.         public ModuleCollection Plugins
  53.         {
  54.             get { return (ModuleCollection)this["plugins"]; }
  55.             set { this["plugins"] = value; }
  56.         }
  57.     }
  58. }
ReloadInterval.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Configuration;
  6.  
  7. namespace ScheduleService.Configuration
  8. {
  9.     /// <summary>
  10.     /// 已不使用
  11.     /// </summary>
  12.     public sealed class ReloadInterval : ConfigurationElement
  13.     {
  14.         [ConfigurationProperty("value", IsRequired = true)]
  15.         public int Value
  16.         {
  17.             get { return (int)this["value"]; }
  18.             set { this["value"] = value; }
  19.         }
  20.     }
  21. }

Plugin

IAlarmPlugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Plugin
  7. {
  8.     public interface IAlarmPlugin
  9.     {
  10.         void ExecuteAt(object sender, System.Timers.ElapsedEventArgs e);
  11.     }
  12. }
IComboPlugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Plugin
  7. {
  8.     public interface IComboPlugin
  9.     {
  10.         bool OnExecuting();
  11.         void OnExecuted();
  12.     }
  13. }
IComplexPlugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Plugin
  7. {
  8.     public interface IComplexPlugin : ISimplePlugin, IComboPlugin, IGeneralPlugin
  9.     {
  10.  
  11.     }
  12. }
IGeneralPlugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Plugin
  7. {
  8.     public interface IGeneralPlugin
  9.     {
  10.         bool OnStart();
  11.         void OnStop();
  12.     }
  13. }
ISimplePlugin.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Plugin
  7. {
  8.     public interface ISimplePlugin
  9.     {
  10.         void Execute(object sender, System.Timers.ElapsedEventArgs e);
  11.     }
  12. }
PluginLoader.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Xml.XPath;
  6. using System.Xml.Linq;
  7. using System.Configuration;
  8. using ScheduleService.Configuration;
  9. using ScheduleService.Utilily;
  10.  
  11. namespace ScheduleService.Plugin
  12. {
  13.     public class PluginLoader
  14.     {
  15.         private List<Module> LoadedPlugins;
  16.         /// <summary>
  17.         /// 載入目前執行個體的設定檔
  18.         /// </summary>
  19.         public void LoadConfig()
  20.         {
  21.             LoadConfig(ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).FilePath);
  22.         }
  23.         /// <summary>
  24.         /// 載入指定的設定檔
  25.         /// </summary>
  26.         /// <param name="configPath"></param>
  27.         public void LoadConfig(string configPath)
  28.         {
  29.             try
  30.             {
  31.                 LoadedPlugins = new List<Module>();
  32.                 #region 重新載入設定檔
  33.                 ConfigurationManager.RefreshSection("scheduleService.plugin");
  34.                 ModuleCollection modules = ((Configuration.Plugin)ConfigurationManager.GetSection("scheduleService.plugin")).Plugins;
  35.                 foreach (Module m in modules)
  36.                 {
  37.                     LoadedPlugins.Add(m);
  38.                 }
  39.                 #endregion
  40.                 #region 以讀檔方式重新載入設定檔
  41.                 //XElement root = XElement.Load(configPath);
  42.                 //IEnumerator<XElement> itor = root.XPathSelectElements("//scheduleService.plugin/plugins/module").GetEnumerator();
  43.                 //while (itor.MoveNext())
  44.                 //{
  45.                 //    try
  46.                 //    {
  47.                 //        Module m = new Module();
  48.                 //        m.Assembly = itor.Current.Attribute("assembly").Value;
  49.                 //        m.Binary = itor.Current.Attribute("binary").Value;
  50.                 //        m.Name = itor.Current.Attribute("name").Value;
  51.                 //        m.Type = itor.Current.Attribute("type").Value;
  52.                 //        m.Interval = int.Parse(itor.Current.Attribute("interval").Value);
  53.                 //        LoadedPlugins.Add(m);
  54.                 //    }
  55.                 //    catch (Exception ex)
  56.                 //    {
  57.                 //        ServiceLogger.Log(string.Format("載入模組{0}時發生錯誤", itor.Current.ToString()), ex);
  58.                 //    }
  59.                 //}
  60.                 #endregion
  61.             }
  62.             catch (Exception ex)
  63.             {
  64.                 ServiceLogger.Log(string.Format("載入設定檔{0}時發生錯誤", configPath), ex);
  65.             }
  66.         }
  67.         /// <summary>
  68.         /// 取得指定模組資料
  69.         /// </summary>
  70.         /// <param name="name"></param>
  71.         /// <returns></returns>
  72.         public Module GetModule(string name)
  73.         {
  74.             if (LoadedPlugins == null)
  75.             {
  76.                 LoadConfig();
  77.             }
  78.  
  79.             foreach (Module m in LoadedPlugins)
  80.             {
  81.                 if (m.Name == name)
  82.                     return m;
  83.             }
  84.             return null;
  85.         }
  86.         /// <summary>
  87.         /// 取得所有模組
  88.         /// </summary>
  89.         /// <returns></returns>
  90.         public Module[] GetModules()
  91.         {
  92.             if (LoadedPlugins == null)
  93.             {
  94.                 LoadConfig();
  95.             }
  96.  
  97.             return LoadedPlugins.ToArray();
  98.         }
  99.         /// <summary>
  100.         /// 取得所有模組設定字串
  101.         /// </summary>
  102.         /// <returns></returns>
  103.         public string[] GetModuleStrings()
  104.         {
  105.             if (LoadedPlugins == null)
  106.             {
  107.                 LoadConfig();
  108.             }
  109.  
  110.             List<string> list = new List<string>();
  111.             foreach (Module m in LoadedPlugins)
  112.             {
  113.                 list.Add(m.ToString());
  114.             }
  115.             return list.ToArray();
  116.         }
  117.     }
  118. }
Timer.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Reflection;
  6. using ScheduleService.Configuration;
  7. using ScheduleService.Utilily;
  8. using System.Text.RegularExpressions;
  9.  
  10. namespace ScheduleService.Plugin
  11. {
  12.     public class Timer : System.Timers.Timer
  13.     {
  14.         private bool flag;
  15.         /// <summary>
  16.         /// 模組的實體物件
  17.         /// </summary>
  18.         public object Instance { get; private set; }
  19.         /// <summary>
  20.         /// 執行模組的資料
  21.         /// </summary>
  22.         public ScheduleService.Configuration.Module Source { get; private set; }
  23.  
  24.         public Timer(ScheduleService.Configuration.Module module)
  25.             : base()
  26.         {
  27.             try
  28.             {
  29.                 Source = module;
  30.                 base.Enabled = true;
  31.  
  32.  
  33.                 Assembly assembly = Assembly.LoadFrom(PathHelper.PluginPath + Source.Binary);
  34.                 switch (Source.Type)
  35.                 {
  36.                     case "ScheduleService.Plugin.ISimplePlugin":
  37.                     case "ScheduleService.Plugin.IComplexPlugin":
  38.                         ISimplePlugin simplePlugin = (ISimplePlugin)assembly.CreateInstance(Source.Assembly);
  39.                         base.Interval = module.Interval * 1000;
  40.                         base.Elapsed += new System.Timers.ElapsedEventHandler(Simple_Elapsed);
  41.                         this.Instance = simplePlugin;
  42.                         this.Source.NextExecute = DateTime.Now.AddSeconds(this.Source.Interval);
  43.                         break;
  44.                     case "ScheduleService.Plugin.IAlarmPlugin":  // 定時類型
  45.                         IAlarmPlugin alarmPlugin = (IAlarmPlugin)assembly.CreateInstance(Source.Assembly);
  46.                         base.Interval = 30000;  // 每30秒執行檢查
  47.                         base.Elapsed += new System.Timers.ElapsedEventHandler(Alarm_Elapsed);
  48.                         this.Instance = alarmPlugin;
  49.                         this.Source.NextExecute = this.NextExecute(DateTime.Now);
  50.                         break;
  51.                     case "ScheduleService.Plugin.IGeneralPlugin":  // 未測試
  52.                         IGeneralPlugin generalPlugin = (IGeneralPlugin)assembly.CreateInstance(Source.Assembly);
  53.                         base.Enabled = false;
  54.                         this.Instance = generalPlugin;
  55.                         break;
  56.                 }
  57.             }
  58.             catch (Exception ex)
  59.             {
  60.                 ServiceLogger.Log(string.Format("建立'{0}'實體時發生錯誤", Source.Assembly), ex);
  61.                 throw ex;
  62.             }
  63.         }
  64.  
  65.         void Alarm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
  66.         {
  67.             if (this.flag)
  68.             {
  69.                 ServiceLogger.Log("前一個處理排程正在進行中,略過本次排程");
  70.                 return;
  71.             }
  72.  
  73.             if (string.Format("{0:yyyyMMddHHmm}", this.NextExecute(DateTime.Now.AddMinutes(-1))) == string.Format("{0:yyyyMMddHHmm}", DateTime.Now))
  74.             {
  75.                 this.flag = true;
  76.                 this.Enabled = false;
  77.                 this.Source.LastExecuted = e.SignalTime; // 執行時間
  78.                 this.Source.NextExecute = this.NextExecute(DateTime.Now.AddMinutes(5));  // 預計下次執行時間
  79.                 try
  80.                 {
  81.                     this.ExecuteAlarm(sender, e);
  82.  
  83.                     this.Source.Exception = null;
  84.                 }
  85.                 catch (Exception ex)
  86.                 {
  87.                     this.Source.Exception = ex;
  88.                     ServiceLogger.Log(string.Format("排程工作[{0}]執行時發生錯誤", Source.Name), ex);
  89.                     if (this.Source.ReTryOnFailed)
  90.                     {
  91.                         // 延遲30秒後重試
  92.                         System.Threading.Thread.Sleep(30000);
  93.                         ServiceLogger.Log(string.Format("重新嘗試執行排程工作[{0}]", Source.Name), ex);
  94.                         try
  95.                         {
  96.                             this.ExecuteAlarm(sender, e);
  97.                         }
  98.                         catch (Exception exx)
  99.                         {
  100.                             ServiceLogger.Log(string.Format("重新嘗試執行排程工作[{0}]執行時發生錯誤", Source.Name), exx);
  101.                         }
  102.                     }
  103.                 }
  104.  
  105.                 // 強迫執行時間大於60秒, 避免重覆執行
  106.                 System.Threading.Thread.Sleep(60000);
  107.  
  108.                 this.Enabled = true;
  109.                 this.flag = false;
  110.  
  111.                 // 回收記憶體
  112.                 GC.Collect();
  113.             }
  114.         }
  115.  
  116.         private void ExecuteAlarm(object sender, System.Timers.ElapsedEventArgs e)
  117.         {
  118.             IComboPlugin comboPlugin = this.Instance as IComboPlugin;
  119.             IAlarmPlugin alarmPlugin = this.Instance as IAlarmPlugin;
  120.  
  121.             // OnExecuting
  122.             bool isCanceled = false;
  123.             if (this.Instance is IComboPlugin && comboPlugin != null)
  124.             {
  125.                 isCanceled = !comboPlugin.OnExecuting();
  126.             }
  127.  
  128.             // Execute
  129.             if (isCanceled)
  130.             {
  131.                 ServiceLogger.Log(string.Format("執行模組[{0}]時被前置作業取消", Source.Name));
  132.             }
  133.             else
  134.             {
  135.                 ServiceLogger.Log(string.Format("開始執行模組[{0}]", Source.Name));
  136.                 alarmPlugin.ExecuteAt(sender, e);
  137.                 ServiceLogger.Log(string.Format("排程工作[{0}]執行完成", Source.Name));
  138.             }
  139.  
  140.             // OnExecuted
  141.             if (this.Instance is IComboPlugin && comboPlugin != null)
  142.             {
  143.                 comboPlugin.OnExecuted();
  144.             }
  145.         }
  146.  
  147.  
  148.         void Simple_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
  149.         {
  150.             if (this.flag)
  151.             {
  152.                 ServiceLogger.Log("前一個處理排程正在進行中,略過本次排程");
  153.                 return;
  154.             }
  155.  
  156.             this.flag = true;
  157.             this.Enabled = false;
  158.             this.Source.LastExecuted = e.SignalTime; // 執行時間
  159.             this.Source.NextExecute = e.SignalTime.AddSeconds(this.Source.Interval);  // 預計下次執行時間
  160.             try
  161.             {
  162.                 this.ExecuteSimple(sender, e);
  163.  
  164.                 this.Source.Exception = null;
  165.             }
  166.             catch (Exception ex)
  167.             {
  168.                 this.Source.Exception = ex;
  169.                 ServiceLogger.Log(string.Format("排程工作[{0}]執行時發生錯誤", Source.Name), ex);
  170.  
  171.                 if (this.Source.ReTryOnFailed)
  172.                 {
  173.                     // 延遲30秒後重試
  174.                     System.Threading.Thread.Sleep(30000);
  175.                     ServiceLogger.Log(string.Format("重新嘗試執行排程工作[{0}]", Source.Name), ex);
  176.                     try
  177.                     {
  178.                         this.ExecuteSimple(sender, e);
  179.                     }
  180.                     catch (Exception exx)
  181.                     {
  182.                         ServiceLogger.Log(string.Format("重新嘗試執行排程工作[{0}]執行時發生錯誤", Source.Name), exx);
  183.                     }
  184.                 }
  185.             }
  186.  
  187.             this.Enabled = true;
  188.             this.flag = false;
  189.  
  190.             // 回收記憶體
  191.             GC.Collect();
  192.         }
  193.  
  194.         private void ExecuteSimple(object sender, System.Timers.ElapsedEventArgs e)
  195.         {
  196.             IComboPlugin comboPlugin = this.Instance as IComboPlugin;
  197.             ISimplePlugin simplePlugin = this.Instance as ISimplePlugin;
  198.  
  199.             // OnExecuting
  200.             bool isCanceled = false;
  201.             if (this.Instance is IComboPlugin && comboPlugin != null)
  202.             {
  203.                 isCanceled = !comboPlugin.OnExecuting();
  204.             }
  205.  
  206.             // Execute
  207.             if (isCanceled)
  208.             {
  209.                 ServiceLogger.Log(string.Format("執行模組[{0}]時被前置作業取消", Source.Name));
  210.             }
  211.             else
  212.             {
  213.                 ServiceLogger.Log(string.Format("開始執行模組[{0}]", Source.Name));
  214.                 simplePlugin.Execute(sender, e);
  215.                 ServiceLogger.Log(string.Format("排程工作[{0}]執行完成", Source.Name));
  216.             }
  217.  
  218.             // OnExecuted
  219.             if (this.Instance is IComboPlugin && comboPlugin != null)
  220.             {
  221.                 comboPlugin.OnExecuted();
  222.             }
  223.         }
  224.  
  225.         /// <summary>
  226.         /// 計算下次執行時間
  227.         /// </summary>
  228.         /// <param name="baseTime"></param>
  229.         /// <returns></returns>
  230.         private DateTime NextExecute(DateTime baseTime)
  231.         {
  232.             int duration = -1;
  233.             int hour = -1;
  234.             int minute = -1;
  235.             string[] time = this.Source.ExecutePoint.Split(new char[] { ':' });
  236.             if (Regex.IsMatch(this.Source.ExecutePoint, "^(\\d+:)?\\d+:\\d+$"))
  237.             {
  238.                 switch (time.Length)
  239.                 {
  240.                     case 3:
  241.                         // 計算週期的值
  242.                         duration = int.Parse(time[0]);
  243.                         switch (this.Source.Duration)
  244.                         {
  245.                             case Duration.Week:
  246.                                 if (duration < 0 || duration > 6) throw new FormatException("執行時間點:星期數值錯誤");
  247.                                 break;
  248.                             case Duration.Month:
  249.                                 if (duration < 0 || duration > 31) throw new FormatException("執行時間點:日期數值錯誤");
  250.                                 break;
  251.                         }
  252.                         // 小時值
  253.                         hour = int.Parse(time[1]);
  254.                         if (hour < 0 || hour > 23) throw new FormatException("執行時間點:小時數值錯誤");
  255.                         // 分鐘值
  256.                         minute = int.Parse(time[2]);
  257.                         if (hour < 0 || hour > 59) throw new FormatException("執行時間點:分鐘數值錯誤");
  258.                         break;
  259.                     case 2:
  260.                         // 小時值
  261.                         hour = int.Parse(time[0]);
  262.                         if (hour < 0 || hour > 23) throw new FormatException("執行時間點:小時數值錯誤");
  263.                         // 分鐘值
  264.                         minute = int.Parse(time[1]);
  265.                         if (hour < 0 || hour > 59) throw new FormatException("執行時間點:分鐘數值錯誤");
  266.                         break;
  267.                     default:
  268.                         throw new FormatException("執行時間點:時間格式錯誤");
  269.                 }
  270.  
  271.  
  272.                 DateTime nextExecute = baseTime;
  273.                 switch (this.Source.Duration)
  274.                 {
  275.                     case Duration.Day:
  276.                         nextExecute = new DateTime(baseTime.Year, baseTime.Month, baseTime.Day, hour, minute, 0);
  277.                         if (nextExecute < baseTime) nextExecute = nextExecute.AddDays(1);
  278.                         break;
  279.                     case Duration.Week:
  280.                         int todayOfWeek = (int)DateTime.Now.DayOfWeek; // 今天的星期
  281.                         int leftDays = duration - todayOfWeek < 0 ? duration - todayOfWeek + 7 : duration - todayOfWeek;  // 距下次執行日期的天數
  282.                         nextExecute = new DateTime(baseTime.Year, baseTime.Month, baseTime.Day, hour, minute, 0).AddDays(leftDays);
  283.                         if (nextExecute < baseTime) nextExecute = nextExecute.AddDays(7);
  284.                         break;
  285.                     case Duration.Month:
  286.                         duration = Math.Min(DateTime.DaysInMonth(baseTime.Year, baseTime.Month), duration);  // 數值大於當月天數時, 在最後一天執行
  287.                         nextExecute = new DateTime(baseTime.Year, baseTime.Month, duration, hour, minute, 0);
  288.                         if (nextExecute < baseTime) nextExecute = nextExecute.AddMonths(1);
  289.                         break;
  290.                 }
  291.                 return nextExecute;
  292.             }
  293.             else
  294.             {
  295.                 throw new FormatException("執行時間點:時間格式錯誤");
  296.             }
  297.         }
  298.     }
  299. }

Remoting

MemoryUsage.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Diagnostics;
  6.  
  7. namespace ScheduleService.Remoting
  8. {
  9.     [Serializable]
  10.     public class MemoryUsage
  11.     {
  12.         public int NonpagedSystemMemorySize { get; set; }
  13.         public long NonpagedSystemMemorySize64 { get; set; }
  14.         public int PagedMemorySize { get; set; }
  15.         public long PagedMemorySize64 { get; set; }
  16.         public int PagedSystemMemorySize { get; set; }
  17.         public long PagedSystemMemorySize64 { get; set; }
  18.         public int PeakPagedMemorySize { get; set; }
  19.         public long PeakPagedMemorySize64 { get; set; }
  20.         public int PeakVirtualMemorySize { get; set; }
  21.         public long PeakVirtualMemorySize64 { get; set; }
  22.         public int PeakWorkingSet { get; set; }
  23.         public long PeakWorkingSet64 { get; set; }
  24.         public int PrivateMemorySize { get; set; }
  25.         public long PrivateMemorySize64 { get; set; }
  26.         public int VirtualMemorySize { get; set; }
  27.         public long VirtualMemorySize64 { get; set; }
  28.         public int WorkingSet { get; set; }
  29.         public long WorkingSet64 { get; set; }
  30.  
  31.         protected internal void Refresh()
  32.         {
  33.             Process p = Process.GetCurrentProcess();
  34.             this.NonpagedSystemMemorySize = p.NonpagedSystemMemorySize;
  35.             this.NonpagedSystemMemorySize64 = p.NonpagedSystemMemorySize64;
  36.             this.PagedMemorySize = p.PagedMemorySize;
  37.             this.PagedMemorySize64 = p.PagedMemorySize64;
  38.             this.PagedSystemMemorySize = p.PagedSystemMemorySize;
  39.             this.PagedSystemMemorySize64 = p.PagedSystemMemorySize64;
  40.             this.PeakPagedMemorySize = p.PeakPagedMemorySize;
  41.             this.PeakPagedMemorySize64 = p.PeakPagedMemorySize64;
  42.             this.PeakVirtualMemorySize = p.PeakVirtualMemorySize;
  43.             this.PeakVirtualMemorySize64 = p.PeakVirtualMemorySize64;
  44.             this.PeakWorkingSet = p.PeakWorkingSet;
  45.             this.PeakWorkingSet64 = p.PeakWorkingSet64;
  46.             this.PrivateMemorySize = p.PrivateMemorySize;
  47.             this.PrivateMemorySize64 = p.PrivateMemorySize64;
  48.             this.VirtualMemorySize = p.VirtualMemorySize;
  49.             this.VirtualMemorySize64 = p.VirtualMemorySize64;
  50.             this.WorkingSet = p.WorkingSet;
  51.             this.WorkingSet64 = p.WorkingSet64;
  52.         }
  53.     }
  54. }
ModuleData.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ScheduleService.Configuration;
  6.  
  7. namespace ScheduleService.Remoting
  8. {
  9.     [Serializable]
  10.     public sealed class ModuleData
  11.     {
  12.         public string Name { get; set; }
  13.         public string Type { get; set; }
  14.         public string Binary { get; set; }
  15.         public string Assembly { get; set; }
  16.         public int Interval { get; set; }
  17.         public Duration Duration { get; set; }
  18.         public string ExecutePoint { get; set; }
  19.         public bool ReTryOnFailed { get; set; }
  20.  
  21.         public DateTime StartTime { get; set; }
  22.         public DateTime LastExecuted { get; set; }
  23.         public DateTime NextExecute { get; set; }
  24.         public Exception Exception { get; set; }
  25.  
  26.  
  27.         public override int GetHashCode()
  28.         {
  29.             return string.Format("{0}{1}{2}{3}", this.Name, this.Type, this.Binary, this.Assembly).GetHashCode();
  30.         }
  31.  
  32.         public override bool Equals(object compareTo)
  33.         {
  34.             if (compareTo == null || !(compareTo is ModuleData)) return false;
  35.             ModuleData obj = compareTo as ModuleData;
  36.             return string.Format("{0}{1}{2}{3}", this.Name, this.Type, this.Binary, this.Assembly)
  37.                 == string.Format("{0}{1}{2}{3}", obj.Name, obj.Type, obj.Binary, obj.Assembly);
  38.         }
  39.  
  40.     }
  41. }
Monitor.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ScheduleService.Configuration;
  6.  
  7. namespace ScheduleService.Remoting
  8. {
  9.     public interface Monitor
  10.     {
  11.         MemoryUsage GetUsage();
  12.  
  13.         ModuleData[] GetModules();
  14.     }
  15. }
ServiceClient.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Runtime.Remoting.Channels.Http;
  6. using System.Runtime.Remoting.Channels;
  7. using System.Runtime.Remoting.Channels.Tcp;
  8. using System.Net;
  9.  
  10. namespace ScheduleService.Remoting
  11. {
  12.     public class ServiceClient : Monitor
  13.     {
  14.         private TcpChannel channel;
  15.         private Monitor remotingObj;
  16.  
  17.         public void Connect(string host, int port)
  18.         {
  19.             if (channel != null)
  20.             {
  21.                 this.Close();
  22.             }
  23.  
  24.             IPAddress[] ip = Dns.GetHostAddresses(host);
  25.             if (ip.Count() == 0) throw new Exception("無法解析主機名稱或ip");
  26.  
  27.             this.channel = new TcpChannel();
  28.             ChannelServices.RegisterChannel(this.channel, false);
  29.  
  30.             remotingObj = Activator.GetObject(
  31.                 typeof(Monitor),
  32.                 string.Format("tcp://{0}:{1}/ScheduleService.Remoting", ip[0], port)
  33.             ) as Monitor;
  34.         }
  35.  
  36.         public void Close()
  37.         {
  38.             if (this.channel != null)
  39.             {
  40.                 ChannelServices.UnregisterChannel(this.channel);
  41.                 this.channel.StopListening(null);
  42.                 this.channel = null;
  43.             }
  44.  
  45.             GC.Collect();
  46.         }
  47.  
  48.         #region Monitor 成員
  49.  
  50.         public MemoryUsage GetUsage()
  51.         {
  52.             try
  53.             {
  54.                 if (remotingObj == null)
  55.                     return null;
  56.                 else
  57.                     return remotingObj.GetUsage();
  58.             }
  59.             catch //(Exception ex)
  60.             {
  61.                 return null;
  62.             }
  63.         }
  64.  
  65.         public ModuleData[] GetModules()
  66.         {
  67.             try
  68.             {
  69.  
  70.                 if (remotingObj == null)
  71.                     return null;
  72.                 else
  73.                     return remotingObj.GetModules();
  74.             }
  75.             catch //(Exception ex)
  76.             {
  77.                 return null;
  78.             }
  79.         }
  80.  
  81.         #endregion
  82.     }
  83. }
ServiceMonitor.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Runtime.Remoting.Channels.Tcp;
  6. using System.Runtime.Remoting.Channels;
  7. using System.Runtime.Remoting;
  8.  
  9. namespace ScheduleService.Remoting
  10. {
  11.     public class ServiceMonitor : MarshalByRefObject, Monitor
  12.     {
  13.         private TcpChannel channel;
  14.  
  15.         public void StartListen(int port)
  16.         {
  17.             // 結束既有的監聽
  18.             if (this.channel != null)
  19.             {
  20.                 this.StopListen();
  21.             }
  22.  
  23.             this.channel = new TcpChannel(port);
  24.             ChannelServices.RegisterChannel(this.channel, false);
  25.             RemotingConfiguration.RegisterWellKnownServiceType(
  26.                 typeof(ServiceMonitor),
  27.                 "ScheduleService.Remoting",
  28.                 WellKnownObjectMode.SingleCall
  29.             );
  30.         }
  31.  
  32.         public void StopListen()
  33.         {
  34.             if (this.channel != null)
  35.             {
  36.                 ChannelServices.UnregisterChannel(this.channel);
  37.                 this.channel.StopListening(null);
  38.                 this.channel = null;
  39.             }
  40.  
  41.             GC.Collect();
  42.         }
  43.  
  44.         #region Monitor 成員
  45.  
  46.         public MemoryUsage GetUsage()
  47.         {
  48.             MemoryUsage usage = new MemoryUsage();
  49.             usage.Refresh();
  50.             return usage;
  51.         }
  52.  
  53.         public ModuleData[] GetModules()
  54.         {
  55.             if (ScheduleServiceBase.ModuleList == null)
  56.             {
  57.                 return new ModuleData[0];
  58.             }
  59.             else
  60.             {
  61.                 List<ModuleData> list = new List<ModuleData>();
  62.                 foreach (ScheduleService.Configuration.Module m in ScheduleServiceBase.ModuleList)
  63.                 {
  64.                     list.Add(m.ToModuleData());
  65.                 }
  66.                 return list.ToArray();
  67.             }
  68.         }
  69.  
  70.         #endregion
  71.     }
  72. }

Utilily

Convertor.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Utilily
  7. {
  8.     public class Convertor
  9.     {
  10.         public static string MatchSize(double size, SizeName sizeName)
  11.         {
  12.             if (size < 1024 || sizeName == SizeName.TB)
  13.             {
  14.                 return string.Format("{0:####.##}{1}", size, sizeName);
  15.             }
  16.             else
  17.             {
  18.                 return Convertor.MatchSize(size / 1024, (SizeName)(((int)sizeName) + 1));
  19.             }
  20.         }
  21.     }
  22. }
ExceptionHandler.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Utilily
  7. {
  8.     public interface ExceptionHandler
  9.     {
  10.         void Log(string description);
  11.         void Log(string description, Exception ex);
  12.         void Log(string description, Exception ex, object data);
  13.     }
  14. }
FileoutputHandler.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. using ScheduleService.Configuration;
  7.  
  8. namespace ScheduleService.Utilily
  9. {
  10.     public class FileoutputHandler : ScheduleService.Utilily.ExceptionHandler
  11.     {
  12.         #region ExceptionHandler 成員
  13.  
  14.         public void Log(string description)
  15.         {
  16.             File.AppendAllText(string.Format("{0}ScheduleService{1:yyyyMMdd}.log", PathHelper.LogPath, DateTime.Now),
  17.                 string.Format(@"Time : {0}
  18. Description : {1}
  19.  
  20. ", DateTime.Now, description));
  21.         }
  22.  
  23.         public void Log(string description, Exception ex)
  24.         {
  25.             File.AppendAllText(string.Format("{0}ScheduleService{1:yyyyMMdd}.log", PathHelper.LogPath, DateTime.Now),
  26.                 string.Format(@"Time : {0}
  27. Description : {1}
  28. Message : {2}
  29. Source : {3}
  30. Exception type : {4}
  31. Stack : {5}
  32.  
  33. ", DateTime.Now, description, ex.Message, ex.Source, ex.GetType().Name, ex.StackTrace));
  34.         }
  35.  
  36.         public void Log(string description, Exception ex, object data)
  37.         {
  38.             this.Log(description, ex);
  39.         }
  40.  
  41.         #endregion
  42.     }
  43. }
PathHelper.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6.  
  7. namespace ScheduleService.Utilily
  8. {
  9.     public class PathHelper
  10.     {
  11.         /// <summary>
  12.         /// 程式目錄
  13.         /// </summary>
  14.         public static string ApplicationPath
  15.         {
  16.             get { return AppDomain.CurrentDomain.BaseDirectory; }
  17.         }
  18.         /// <summary>
  19.         /// 模組目錄
  20.         /// </summary>
  21.         public static string PluginPath
  22.         {
  23.             get { return string.Format("{0}Plugin\\", PathHelper.ApplicationPath); }
  24.         }
  25.         /// <summary>
  26.         /// Log目錄
  27.         /// </summary>
  28.         public static string LogPath
  29.         {
  30.             get
  31.             {
  32.                 string logPath = string.Format("{0}Log\\", PathHelper.ApplicationPath);
  33.                 if (!Directory.Exists(logPath))
  34.                     Directory.CreateDirectory(logPath);
  35.                 return logPath;
  36.             }
  37.         }
  38.  
  39.         public static string TempPath
  40.         {
  41.             get
  42.             {
  43.                 string tempPath = string.Format("{0}Temp\\", PathHelper.ApplicationPath);
  44.                 if (!Directory.Exists(tempPath))
  45.                     Directory.CreateDirectory(tempPath);
  46.                 return tempPath;
  47.             }
  48.         }
  49.     }
  50. }
ServiceLogger.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ScheduleService.Utilily;
  6.  
  7. namespace ScheduleService.Utilily
  8. {
  9.     public class ServiceLogger
  10.     {
  11.         private static ExceptionHandler exHandler;
  12.  
  13.         public static void SetHandler(ExceptionHandler handler)
  14.         {
  15.             exHandler = handler;
  16.         }
  17.  
  18.         public static void Log(string description)
  19.         {
  20.             if (exHandler != null)
  21.                 exHandler.Log(description);
  22.         }
  23.         public static void Log(string description, Exception ex)
  24.         {
  25.             if (exHandler != null)
  26.                 exHandler.Log(description, ex);
  27.         }
  28.         public static void Log(string description, Exception ex, object data)
  29.         {
  30.             if (exHandler != null)
  31.                 exHandler.Log(description, ex, data);
  32.         }
  33.     }
  34. }
SizeName.cs
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. namespace ScheduleService.Utilily
  7. {
  8.     public enum SizeName : int
  9.     {
  10.         Bytes = 1,
  11.         KB = 2,
  12.         MB = 3,
  13.         GB = 4,
  14.         TB = 5
  15.     }
  16. }

ScheduleServiceBase.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using ScheduleService.Plugin;
  6. using System.Reflection;
  7. using ScheduleService.Configuration;
  8. using System.Diagnostics;
  9. using System.Configuration;
  10. using ScheduleService.Utilily;
  11. using System.IO;
  12. using ScheduleService.Remoting;
  13.  
  14. namespace ScheduleService
  15. {
  16.     public class ScheduleServiceBase : System.ServiceProcess.ServiceBase
  17.     {
  18.         /// <summary>
  19.         /// 排程的timer
  20.         /// </summary>
  21.         private List<Timer> tasks;
  22.         /// <summary>
  23.         /// 目前載入的模組
  24.         /// </summary>
  25.         private static List<ScheduleService.Configuration.Module> moduleList;
  26.         protected internal static List<ScheduleService.Configuration.Module> ModuleList { get { return ScheduleServiceBase.moduleList; } }
  27.         /// <summary>
  28.         /// 設定檔監控
  29.         /// </summary>
  30.         private FileSystemWatcher configWatcher;
  31.         /// <summary>
  32.         /// 遠端監控
  33.         /// </summary>
  34.         private ServiceMonitor monitor;
  35.  
  36.         public ScheduleServiceBase()
  37.         {
  38.             this.tasks = new List<Timer>();
  39.             ScheduleServiceBase.moduleList = new List<ScheduleService.Configuration.Module>();
  40.             this.configWatcher = new FileSystemWatcher();
  41.             this.monitor = new ServiceMonitor();
  42.  
  43.             // 指定Log記錄方式
  44.             ServiceLogger.SetHandler(new FileoutputHandler());
  45.             ServiceLogger.Log("狀態: 啟動服務");
  46.  
  47.             ScheduleService.Configuration.Plugin plugin = null;
  48.             try
  49.             {
  50.                 plugin = (ScheduleService.Configuration.Plugin)ConfigurationManager.GetSection("scheduleService.plugin");
  51.             }
  52.             catch (Exception ex)
  53.             {
  54.                 ServiceLogger.Log("初始設定檔時發生錯誤", ex);
  55.             }
  56.  
  57.             try
  58.             {
  59.                 if (plugin != null && plugin.ExceptionHandler != null && !string.IsNullOrEmpty(plugin.ExceptionHandler.Binary))
  60.                 {
  61.                     Assembly assembly = Assembly.LoadFrom(PathHelper.PluginPath + plugin.ExceptionHandler.Binary);
  62.                     ExceptionHandler handler = (ExceptionHandler)assembly.CreateInstance(plugin.ExceptionHandler.Assembly);
  63.                     if (handler == null)
  64.                         throw new Exception("建立例外處理器實體失敗");
  65.                     else
  66.                         ServiceLogger.SetHandler(handler);
  67.                 }
  68.             }
  69.             catch (Exception ex)
  70.             {
  71.                 ServiceLogger.Log("設定例外處理器時發生錯誤", ex);
  72.             }
  73.  
  74.             try
  75.             {
  76.                 // 起始remoting
  77.                 this.monitor.StartListen(plugin.MonitorPort.Value);
  78.             }
  79.             catch (Exception ex)
  80.             {
  81.                 ServiceLogger.Log("設定遠端監控時發生錯誤", ex);
  82.             }
  83.         }
  84.  
  85.  
  86.         /// <summary>
  87.         /// 測試用
  88.         /// </summary>
  89.         /// <param name="args"></param>
  90.         public void DoStart(string[] args)
  91.         {
  92.             this.OnStart(args);
  93.         }
  94.         /// <summary>
  95.         /// 啟始設定檔載入程式
  96.         /// </summary>
  97.         /// <param name="args"></param>
  98.         protected override void OnStart(string[] args)
  99.         {
  100.             try
  101.             {
  102.                 #region 監控設定是否改變
  103.                 this.configWatcher.Path = PathHelper.ApplicationPath;
  104.                 this.configWatcher.Filter = "*.config";
  105.                 this.configWatcher.IncludeSubdirectories = false;
  106.                 this.configWatcher.EnableRaisingEvents = true;
  107.                 this.configWatcher.Changed += new FileSystemEventHandler(configWatcher_Changed);
  108.                 #endregion
  109.  
  110.                 // 載入模組
  111.                 this.ChangeModule();
  112.             }
  113.             catch (Exception ex)
  114.             {
  115.                 ServiceLogger.Log("起動程式時發生錯誤", ex);
  116.             }
  117.         }
  118.  
  119.         /// <summary>
  120.         /// 異動時新增/移除模組
  121.         /// </summary>
  122.         /// <param name="sender"></param>
  123.         /// <param name="e"></param>
  124.         void configWatcher_Changed(object sender, FileSystemEventArgs e)
  125.         {
  126.             // 暫止監控
  127.             this.configWatcher.EnableRaisingEvents = false;
  128.  
  129.             // 設定檔變更時
  130.             if (e.Name == string.Format("{0}.config", AppDomain.CurrentDomain.FriendlyName))
  131.             {
  132.                 this.ChangeModule();
  133.             }
  134.  
  135.             // 恢復監控
  136.             this.configWatcher.EnableRaisingEvents = true;
  137.         }
  138.         /// <summary>
  139.         /// 新增/移除模組
  140.         /// </summary>
  141.         private void ChangeModule()
  142.         {
  143.             try
  144.             {
  145.                 PluginLoader loader = new PluginLoader();
  146.                 ScheduleService.Configuration.Module[] plugins = loader.GetModules();
  147.  
  148.                 #region 加入新增的模組
  149.                 foreach (ScheduleService.Configuration.Module m in plugins)
  150.                 {
  151.                     if (!ScheduleServiceBase.moduleList.Contains(m))
  152.                     {
  153.                         try
  154.                         {
  155.                             Timer timer = new Timer(m);
  156.                             // 初始化(OnInit)
  157.                             if (timer.Instance is IGeneralPlugin)
  158.                             {
  159.                                 IGeneralPlugin complexPlugin = (IGeneralPlugin)timer.Instance;
  160.                                 if (!complexPlugin.OnStart())
  161.                                 {
  162.                                     ServiceLogger.Log(string.Format("模組[{0}]初始化時由前置作業終止,未載入模組", m.Name));
  163.                                     continue;
  164.                                 }
  165.                             }
  166.                             timer.Start();
  167.                             this.tasks.Add(timer);
  168.                             ScheduleServiceBase.moduleList.Add(m);
  169.  
  170.                             // 記錄時間
  171.                             m.StartTime = DateTime.Now;
  172.                             ServiceLogger.Log(string.Format("加入新模組[{0}]", m.Name));
  173.                         }
  174.                         catch (Exception ex)
  175.                         {
  176.                             ServiceLogger.Log(string.Format("加入模組[{0}]時發生錯誤", m.Name), ex);
  177.                         }
  178.                     }
  179.                 }
  180.                 #endregion
  181.  
  182.                 #region 移除模組
  183.                 ScheduleService.Configuration.Module[] currentModule = loader.GetModules();
  184.                 for (int i = this.tasks.Count - 1; i >= 0; i--)
  185.                 {
  186.                     Timer timer = this.tasks[i];
  187.                     try
  188.                     {
  189.                         if (!currentModule.Contains(timer.Source))
  190.                         {
  191.                             timer.Enabled = false;
  192.                             timer.Stop();
  193.                             this.tasks.Remove(timer);
  194.                             ScheduleServiceBase.ModuleList.Remove(timer.Source);
  195.                             // 回收(Dispose)
  196.                             if (timer.Instance is IGeneralPlugin)
  197.                             {
  198.                                 IGeneralPlugin generalPlugin = (IGeneralPlugin)timer.Instance;
  199.                                 generalPlugin.OnStop();
  200.                             }
  201.                             ServiceLogger.Log(string.Format("已移除模組[{0}]", timer.Source.Name));
  202.                         }
  203.                     }
  204.                     catch (Exception ex)
  205.                     {
  206.                         ServiceLogger.Log(string.Format("移除模組[{0}]時發生錯誤", timer.Source.Name), ex);
  207.                     }
  208.                 }
  209.                 #endregion
  210.  
  211.                 // 回收記憶體
  212.                 GC.Collect();
  213.             }
  214.             catch (Exception ex)
  215.             {
  216.                 ServiceLogger.Log("模組異動時發生錯誤", ex);
  217.             }
  218.         }
  219.         /// <summary>
  220.         /// 測試用
  221.         /// </summary>
  222.         public void DoStop()
  223.         {
  224.             this.OnStop();
  225.         }
  226.         protected override void OnStop()
  227.         {
  228.             // 停止監控設定檔
  229.             this.configWatcher.EnableRaisingEvents = false;
  230.             // 停止remoting channel
  231.             this.monitor.StopListen();
  232.  
  233.             foreach (ScheduleService.Plugin.Timer timer in this.tasks)
  234.             {
  235.                 try
  236.                 {
  237.                     timer.Enabled = false;
  238.                     timer.Stop();
  239.                     if (timer.Instance is IGeneralPlugin)
  240.                     {
  241.                         IGeneralPlugin generalPlugin = (IGeneralPlugin)timer.Instance;
  242.                         generalPlugin.OnStop();
  243.                     }
  244.                 }
  245.                 catch (Exception ex)
  246.                 {
  247.                     ServiceLogger.Log("停止服務時發生錯誤", ex);
  248.                 }
  249.             }
  250.             ServiceLogger.Log("狀態: 停止服務");
  251.         }
  252.     }
  253. }
App.config
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.     <configSections>
  4.         <section name="scheduleService.plugin" type="ScheduleService.Configuration.Plugin, ServicePlugin"/>
  5.     </configSections>
  6.     <scheduleService.plugin>
  7.         <monitorPort value="9876" />
  8.         <!--<exceptionHandler binary="MyLib.dll" assembly="MyLib.MyHandler" />-->
  9.         <plugins>
  10.             <!--<module name="PluginName" type="ScheduleService.Plugin.IGeneralPlugin" binary="MyLib.dll" assembly="MyLib.ClassName" />-->
  11.             <!--<module name="PluginName" type="ScheduleService.Plugin.ISimplePlugin" binary="MyLib.dll" assembly="MyLib.ClassName" interval="10" reTryOnFailed="false" />-->
  12.             <!--
  13.                 druation = Day | Week | Month
  14.                 executePoint = (tt:)HH:mm
  15.             -->
  16.             <!--<module name="PluginName" type="ScheduleService.Plugin.IAlarmPlugin" binary="MyLib.dll" assembly="MyLib.ClassName" duration="Month" executePoint="3:15:32" reTryOnFailed="false" />-->
  17.         </plugins>
  18.     </scheduleService.plugin>
  19. </configuration>
Review of installation/uninstallation
  1. %windir%\Microsoft.NET\Framework\v2.0.50727\installutil ServiceController.exe
  2. %windir%\Microsoft.NET\Framework\v2.0.50727\installutil /u ServiceController.exe