Popular Posts
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... Word break tag : <wbr/> (HTML5) The  HTML  <wbr>  tag  is  used  defines  a  potential  line  break  point  if  needed.  This  stands  for  Word  BReak. This  is  u... 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:
GC.GetTotalMemory(true)
If you want to know what your process uses from Windows (VM Size column in TaskManager) try:
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:
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)
Application: BeginRequest
Application: PreAuthenticateRequest
Application: AuthenticateRequest
Application: PostAuthenticateRequest
Application: PreAuthorizeRequest
Application: AuthorizeRequest
Application: PostAuthorizeRequest
Application: PreResolveRequestCache
Application: ResolveRequestCache
Application: PostResolveRequestCache
Application: PreMapRequestHandler
    Page: Construct
Application: PostMapRequestHandler
Application: PreAcquireRequestState
Application: AcquireRequestState
Application: PostAcquireRequestState
Application: PreRequestHandlerExecute
    Page: AddParsedSubObject
    Page: CreateControlCollection
    Page: AddedControl
    Page: AddParsedSubObject
    Page: AddedControl
    Page: ResolveAdapter
    Page: DeterminePostBackMode
    Page: PreInit
        Control: ResolveAdapter
        Control: Init
        Control: TrackViewState
    Page: Init
    Page: TrackViewState
    Page: InitComplete
    Page: LoadPageStateFromPersistenceMedium
        Control: LoadViewState
    Page: EnsureChildControls
    Page: CreateChildControls
    Page: PreLoad
    Page: Load
        Control: DataBind
        Control: Load
    Page: EnsureChildControls
    Page: LoadComplete
    Page: EnsureChildControls
    Page: PreRender
        Control: EnsureChildControls
        Control: PreRender
    Page: PreRenderComplete
    Page: SaveViewState
        Control: SaveViewState
    Page: SaveViewState
        Control: SaveViewState
    Page: SavePageStateToPersistenceMedium
    Page: SaveStateComplete
    Page: CreateHtmlTextWriter
    Page: RenderControl
    Page: Render
    Page: RenderChildren
        Control: RenderControl
    Page: VerifyRenderingInServerForm
    Page: CreateHtmlTextWriter
        Control: Unload
        Control: Dispose
    Page: Unload
    Page: Dispose
Application: PostRequestHandlerExecute
Application: PreReleaseRequestState
Application: ReleaseRequestState
Application: PostReleaseRequestState
Application: PreUpdateRequestCache
Application: UpdateRequestCache
Application: PostUpdateRequestCache
Application: EndRequest
Application: PreSendRequestHeaders
Application: PreSendRequestContent
InputBox in C#
/// <summary>
/// Summary description for InputBox.
///
public class InputBoxDialog : System.Windows.Forms.Form
{

    #region Windows Contols and Constructor

    private System.Windows.Forms.Label lblPrompt;
    private System.Windows.Forms.Button button1;
    private System.Windows.Forms.TextBox txtInput;
    /// <summary>
    /// Required designer variable.
    ///
    private System.ComponentModel.Container components = null;

    public InputBoxDialog()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        //
        // TODO: Add any constructor code after InitializeComponent call
        //
    }

    #endregion

    #region Dispose

    /// <summary>
    /// Clean up any resources being used.
    ///
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (components != null)
            {
                components.Dispose();
            }
        }
        base.Dispose(disposing);
    }

    #endregion

    #region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    ///
    private void InitializeComponent()
    {
        this.lblPrompt = new System.Windows.Forms.Label();
        this.button1 = new System.Windows.Forms.Button();
        this.txtInput = new System.Windows.Forms.TextBox();
        this.btnOK = new System.Windows.Forms.Button();
        this.SuspendLayout();
        //
        // lblPrompt
        //
        this.lblPrompt.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
                    | System.Windows.Forms.AnchorStyles.Left)
                    | System.Windows.Forms.AnchorStyles.Right)));
        this.lblPrompt.BackColor = System.Drawing.SystemColors.Control;
        this.lblPrompt.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
        this.lblPrompt.Location = new System.Drawing.Point(12, 17);
        this.lblPrompt.Name = "lblPrompt";
        this.lblPrompt.Size = new System.Drawing.Size(303, 22);
        this.lblPrompt.TabIndex = 3;
        //
        // button1
        //
        this.button1.DialogResult = System.Windows.Forms.DialogResult.Cancel;
        this.button1.Location = new System.Drawing.Point(323, 45);
        this.button1.Name = "button1";
        this.button1.Size = new System.Drawing.Size(64, 27);
        this.button1.TabIndex = 2;
        this.button1.Text = "&Cancel";
        this.button1.Click += new System.EventHandler(this.button1_Click);
        //
        // txtInput
        //
        this.txtInput.Location = new System.Drawing.Point(8, 78);
        this.txtInput.Name = "txtInput";
        this.txtInput.Size = new System.Drawing.Size(379, 22);
        this.txtInput.TabIndex = 0;
        this.txtInput.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtInput_KeyPress);
        //
        // btnOK
        //
        this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
        this.btnOK.Location = new System.Drawing.Point(323, 12);
        this.btnOK.Name = "btnOK";
        this.btnOK.Size = new System.Drawing.Size(64, 27);
        this.btnOK.TabIndex = 1;
        this.btnOK.Text = "&OK";
        this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
        //
        // InputBoxDialog
        //
        this.AutoScaleBaseSize = new System.Drawing.Size(5, 15);
        this.ClientSize = new System.Drawing.Size(399, 111);
        this.Controls.Add(this.txtInput);
        this.Controls.Add(this.button1);
        this.Controls.Add(this.btnOK);
        this.Controls.Add(this.lblPrompt);
        this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
        this.MaximizeBox = false;
        this.MinimizeBox = false;
        this.Name = "InputBoxDialog";
        this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
        this.Text = "InputBox";
        this.Load += new System.EventHandler(this.InputBox_Load);
        this.ResumeLayout(false);
        this.PerformLayout();

    }
    #endregion

    #region Private Variables
    string formCaption = string.Empty;
    string formPrompt = string.Empty;
    string inputResponse = string.Empty;
    string defaultValue = string.Empty;
    DialogResult clickedButton;
    #endregion

    #region Public Properties
    public string FormCaption
    {
        get { return formCaption; }
        set { formCaption = value; }
    } // property FormCaption
    public string FormPrompt
    {
        get { return formPrompt; }
        set { formPrompt = value; }
    } // property FormPrompt
    public string InputResponse
    {
        get { return inputResponse; }
        set { inputResponse = value; }
    } // property InputResponse
    public string DefaultValue
    {
        get { return defaultValue; }
        set { defaultValue = value; }
    } // property DefaultValue
    public DialogResult ClickedButton
    {
        get { return clickedButton; }
    }
    #endregion

    #region Form and Control Events
    private void InputBox_Load(object sender, System.EventArgs e)
    {
        this.txtInput.Text = defaultValue;
        this.lblPrompt.Text = formPrompt;
        this.Text = formCaption;
        this.txtInput.SelectionStart = 0;
        this.txtInput.SelectionLength = this.txtInput.Text.Length;
        this.txtInput.Focus();
    }


    private void btnOK_Click(object sender, System.EventArgs e)
    {
        this.clickedButton = DialogResult.OK;
        InputResponse = this.txtInput.Text;
        this.Close();
    }

    private void button1_Click(object sender, System.EventArgs e)
    {
        this.clickedButton = DialogResult.Cancel;
        this.Close();
    }

    public static string InputBox(string prompt, string title, string defaultValue)
    {
        InputBoxDialog ib = new InputBoxDialog();
        ib.FormPrompt = prompt;
        ib.FormCaption = title;
        ib.DefaultValue = defaultValue;
        ib.ShowDialog();
        string s = ib.InputResponse;
        ib.Close();
        return s;
    }// method: InputBox

    private Button btnOK;

    private void txtInput_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == 13)
        {
            btnOK_Click(sender, e);
            e.Handled = true;
        }
    }
    #endregion
}
Thread sample
class MyThread
{
    private int executeInterval = 15;
    public int ExecuteInterval
    {
        get { return this.executeInterval; }
        set { this.executeInterval = value; }
    }
    private int checkFlagInterval = 1;
    public int CheckFlagInterval
    {
        get { return this.checkFlagInterval; }
        set
        {
            this.checkFlagInterval = value < 1 ? 1 : value;
            this.checkFlagInterval = value > this.executeInterval ? this.executeInterval : value;
        }
    }
    private bool flag = true;
    public bool Flag
    {
        get { return this.flag; }
        set { this.flag = value; }
    }
    public void Run()
    {
        int timeout = 0;
        while (this.flag)
        {
            if ((timeout -= checkFlagInterval) < 1)
            {
                // do something
                timeout = this.executeInterval;
            }
            Thread.Sleep(this.checkFlagInterval * 1000);
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(new ThreadStart(myThread.Run));
        thread.Start();

        while (myThread.Flag)
        {
            myThread.Flag = Console.Read() != 'q';
        }
    }
}
Window service : dynamic load dll module and execute

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\v2.0.50727\installutil ServiceController.exe
%windir%\Microsoft.NET\Framework\v2.0.50727\installutil /u ServiceController.exe