Nine MVP's Blog

04/03/2007

Article: มาสร้าง Windows Service ใช้กันดีกว่า (ตอนที่ 1)

Filed under: Uncategorized — Nine MVP @ 11:29 am

Article: มาสร้าง Windows Service ใช้กันดีกว่า (ตอนที่ 1)

ผมเชื่อว่ามีเพื่อน ๆ หลายคนที่ยังไม่เคยสร้างหรือใช้งานโปรเจ็คที่ต้องเขียนให้ การทำงานของโปรแกรมนั้นเป็น Service ตัวนึงโดยที่จะทำงานด้วยตัวเองไม่ต้องคอยคำสั่งจาก user ลองมาดูกันครับ

 

What article covering?

  • แนะนำความรู้พื้นฐานเพื่อทำความเข้าใจเกี่ยวกับ Windows Service
  • สร้าง Windows Service Project โดยใช้ตัวอย่างงานจริง
  • Deployment (MSI Package, MSBuild ตอนที่2)
  • Debug Windows Service (ตอนที่2)

 

What’s a Windows Service?

คือ Program ที่สร้างและ deploy ไปแล้ว จะทำงานเป็น Service รันอยู่บน Windows โดยสามารถที่จะตั้งค่าให้ run program ไปพร้อม ๆ กับ Windows start up ได้ โดยทำงานเป็น Background Service โดยที่ user ไม่ต้องสั่งงานใด ๆ

โดยการทำงานนั้นอาจจะเป็นในลักษณะของการทำงานตามช่วงเวลา, การทำงานตาม event ที่ต้องการ เช่น การทำงานร่วมกับFileSystemWatcher หรือ System.Timers.Timerเพื่อใช้ Event ที่เกิดขึ้นเป็น Trigger ในการทำงานบางอย่างตามเงื่อนไขที่ต้องการ

Mention the Windows service.

            เนื่องจากประโยชน์ของ Widows Service หลัก ๆ ก็คือ Program ที่เราสร้างนั้นสามารถที่จะทำงานได้โดย automatic ทันทีที่ Windows ได้ Start up และการทำงานของโปรแกรมนั้นจะเป็นในลักษณะของ Background Service คือจะทำงานตามที่เราได้ตั้งเงื่อนไขการทำงานไว้ หลังจาก Service Start แล้ว และ Windows Service นั้นจะสามารถเลือก User เพื่อที่จะให้สิทธิ์สั่งการทำงานของ service นั้นได้ (ค่า default ส่วนใหญ่จะใช้ LocalService, NetworkService User)  หากเพื่อน ๆ เคยเปิดดูใน Administrative Tools -> Services จะเห็น Windows Service จำนวนมากดังรูปข้างล่างนี้

ซึ่งโปรแกรมส่วนใหญ่ทั้ง MSSQL, ASP.NET, IIS, etc. ต่างก็มีการใช้ Windows Service เป็นตัวคอยควบคุมการทำงานของระบบตามที่ได้วางเงื่อนไขการทำงานต่าง ๆ ของระบบไว้ในตัว service ทำให้เสมือนโปรแกรมนั้นมีชีวิต สามารถทำงานได้ด้วยตัวเองอยู่บน Windows แต่เป็นไปตามเงื่อนไขที่เราได้กำหนดการทำงานไว้

 

Create my first Windows Service.

Create Solution and Windows Service Project

เริ่มแรกเลือกสร้างโปรเจ็คแบบ Windows Service ตามรูปข้างล่าง

จากนั้นก็จะได้โครงสร้างโปรเจ็คในหน้าต่าง Solution Explorer ตามรูปข้างล่างนี้

 

ซึ่ง Service1.cs จะเป็น file ที่เราจะทำการ coding ลงไปเพื่อควบคุมการทำงานตามที่เราต้องการ

เมื่อเราเปิดกดเลือกดู   View code icon เราจะเห็น code ตาม list ข้างล่างนี้

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

 

namespace myService

{

public partial class Service1 : ServiceBase

{

public Service1()

{

InitializeComponent();

}

 

protected override void OnStart(string[] args)

{

// TODO: Add code here to start your service.

}

 

protected override void OnStop()

{

// TODO: Add code here to perform any tear-down necessary to stop your service.

}

}

}

 

 

 

จะพบได้ว่ามี 2 methods ที่ได้ override จาก base class คือ OnStart() และ OnStop() ซึ่งจะเป็น Event default ที่ถูกสร้างมาให้ และจะเกิด Event เหล่านี้ขึ้นตามสถานะการทำงานของ service นั้น ๆ ซึ่งผมได้สรุปมาเท่าที่เราจะใช้กันบ่อย ๆ ได้ดังนี้

Event Name  Description OnStart() จะเกิด event นี้เมื่อ service ถูกสั่งให้ start ซึ่งเรานิยมใช้ในการสั่งการให้ตัวTrigger (Timer, FileSystemWatcher, Thread) ที่เราใช้ใน Service นั้นทำงาน OnStop() จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ (stop) นิยมใช้ในการ save states ต่าง ๆ ของ Service ที่จำเป็นไว้ OnPause() จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ค้างการทำงาน (pause) OnContinue() จะเกิด event นี้เมื่อ service ถูกสั่งให้สั่งให้ทำงานต่อ (resume) OnShutdown() จะเกิด event นี้เมื่อ windows ถูกสั่งให้ restart / shutdown


Implement Functionality

ต่อไปจะทำการใช้ Windows Service ในการทำ Backup file จาก Drop File Folder ย้ายไปเก็บยัง Backup File Folder ซึ่งจะมีการเพิ่มการทำงานดังนี้

 

  1. คอยตรวจสอบหากมีการ create/ paste file ลงใน drop file folder ให้ raise event
  2. ให้เก็บ full path ของ file ที่ notify เก็บไว้ใน collection ก่อนนำไป process ต่อไป
  3. ใช้ Timer ในการ process file list ใน collection ในแต่ละรอบเวลา โดยกำหนดจำนวน file ในแต่ละรอบได้ เพื่อเพิ่มประสิทธิภาพในการทำงานให้เสถียรขึ้น
  4. ให้ทำการ move file โดยอ่าน path จาก collection ไปยัง Backup Folder
  5. หากทำการ move file สำเร็จให้ลบ file path นั้นใน collection ทิ้งไป
  6. เก็บ service status(start, stop) , Application Error ไว้ใน Event Log

 

เราก็จะสร้าง file ขึ้นมา 3 files ดังนี้

  1. เพิ่ม App.config เข้ามาเพื่อใช้ในการตั้งค่าการทำงานต่าง ๆ ของ windows service โดยทำการคลิ๊กขวาที่ project -> Add -> New Item… -> Application Configuration File จากนั้น เพิ่มค่า config เข้าไปตามตารางข้างล่างนี้
<?xml version=”1.0″ encoding=”utf-8″ ?>

<configuration>

<appSettings>

<!– ServiceMode you can config for Copy=0, Remove=1, Delete=2 –>

<add key=”ServiceMode” value=”0″/>

<!– FileFilter = file mask such as *.txt, *.jpg, *.* –>

<add key=”FileFilter” value=”*.*”/>

<!– FileMonitoringPath = –>

<add key=”FileMonitoringPath” value=”c:\test\master”/>

<!– ServiceTimer = Time for service process job in Second –>

<add key=”ServiceTimer” value=”3″/>

<!– TargetFolder use to copy or move file–>

<add key=”TargetFolder” value=”c:\test\backup”/>

<!– FileNoProcess the number of file to process each timer round –>

<add key=”FileNoProcess” value=”3″/>

</appSettings>

</configuration>

 

 

 

 

 

 

 

 

 

  1. จากนั้นเพิ่ม class เข้ามาใน project โดยตั้งชื่อว่า FileBackupService.cs และเพิ่ม code ใน table ข้างล่างนี้ ซึ่งจะเป็น Class ส่วนที่เกี่ยวข้องกับการ process file ทั้ง copy, move, delete และจะมีการบันทึกค่าลงใน EventLog ซึ่งClassนี้จะถูกเรียกใช้ใน windows service

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Configuration;

using System.Diagnostics;

using System.Collections;

 

using System.IO;

using System.Timers;

 

namespace myService

{

internal class FileBackupService

{

 

internal EventLog LogService = new EventLog();

internal Timer ServiceTimer = new Timer();

internal ArrayList queueFile = new ArrayList();

internal FileSystemWatcher fsw = new FileSystemWatcher();

 

string serviceMode = “”;

string targetFolder = “”;

 

int fileNoProcess = 0;

 

internal void InitConfig()

{

 

serviceMode = ConfigurationManager.AppSettings[“ServiceMode”];

targetFolder = ConfigurationManager.AppSettings[“TargetFolder”];

fileNoProcess = int.Parse(ConfigurationManager.AppSettings[“FileNoProcess”]);

 

// file monitor config

fsw.Filter = ConfigurationManager.AppSettings[“FileFilter”];

fsw.Path = ConfigurationManager.AppSettings[“FileMonitoringPath”];

 

fsw.EnableRaisingEvents = true;

fsw.NotifyFilter = ((System.IO.NotifyFilters)

((((System.IO.NotifyFilters.FileName

| System.IO.NotifyFilters.DirectoryName)

| System.IO.NotifyFilters.Size)

| System.IO.NotifyFilters.LastWrite)));

 

fsw.Created += new FileSystemEventHandler(fsw_Created);

 

 

// timer config

ServiceTimer.Interval = double.Parse(ConfigurationManager.AppSettings[“ServiceTimer”]) * 1000;

ServiceTimer.Enabled = true;

ServiceTimer.Elapsed += new ElapsedEventHandler(ServiceTimer_Elapsed);

 

// event log config

LogService.Log = “Application”;

LogService.Source = “FileBackupService”;

 

}

 

private void ServiceTimer_Elapsed(object sender, ElapsedEventArgs e)

{

ServiceTimer.Stop();

if (queueFile.Count > 0)

for (int i = 0; i < fileNoProcess; i++)

{

if (queueFile.Count > 0)

{

ProcessFileList(queueFile[0].ToString());

queueFile.RemoveAt(0);

}

else

{

break;

}

}

ServiceTimer.Start();

}

 

private void ProcessFileList(string filepath)

{

switch (serviceMode)

{

case “0”:

FileProcess(filepath, targetFolder, FileServiceAction.COPY);

break;

case “1”:

FileProcess(filepath, targetFolder, FileServiceAction.MOVE);

break;

case “2”:

FileProcess(filepath, targetFolder, FileServiceAction.DELETE);

break;

}

}

 

private bool FileProcess(string pathFileTarget, string pathFileCopy, FileServiceAction action)

{

 

string filename = Path.GetFileName(pathFileTarget);

string taskMessage = “”;

 

try

{

switch (action)

{

case FileServiceAction.COPY:

taskMessage = “Copy “;

File.Copy(pathFileTarget, pathFileCopy + filename);

break;

case FileServiceAction.MOVE:

taskMessage = “Move “;

File.Move(pathFileTarget, pathFileCopy + filename);

break;

case FileServiceAction.DELETE:

taskMessage = “Delete “;

File.Delete(pathFileTarget);

break;

}

WriteEventLog(taskMessage + pathFileTarget + ” : Successful. “, EventLogEntryType.Information);

return true;

}

catch (Exception ex)

{

WriteEventLog(taskMessage + pathFileTarget + ” : Error Message = ” + ex.Message, EventLogEntryType.Error);

return false;

}

}

 

private void fsw_Created(object sender, FileSystemEventArgs e)

{

queueFile.Add(e.FullPath);

}

 

internal void WriteEventLog(string message, EventLogEntryType elet)

{

if (LogService != null)

{

LogService.WriteEntry(message, elet);

}

}

}

 

internal enum FileServiceAction

{

COPY,MOVE,DELETE

}

}

 

 

 

 

 

 

 

 

  1. กลับมาที่ Service1.cs file เมื่อเปิดขึ้นมาแล้วใส่ code ลงไปตาม table ข้างล่างนี้ เพื่อทำการเรียกใช้ FileBackupService Class ที่ได้สร้างไว้ในขั้นตอนที่แล้ว

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Diagnostics;

using System.ServiceProcess;

using System.Text;

 

using System.Timers;

using System.Configuration;

using System.IO;

using System.Collections;

 

namespace myService

{

public partial class Service1 : ServiceBase

{

private FileBackupService fbs;

 

public Service1()

{

InitializeComponent();

fbs = new FileBackupService();

}

 

protected override void OnStart(string[] args)

{

            fbs.InitConfig();

            fbs.ServiceTimer.Start();

}

 

protected override void OnStop()

{

            fbs.ServiceTimer.Stop();

            fbs = null;

}

 

}

}

 

 

 

  1. ต่อมาทำการเพิ่มตัว Installer Class ให้ Service ที่เราได้สร้างขึ้นมาดังนี้
    1. ไปที่ Solution Explorer เลือกที่ Service1.cs คลิ๊กขวาเลือก Open
    2. จากนั้นเปิดมาก็จะได้จอ เทา ๆ ให้คลี๊กขวาที่พื้นที่ของจอเทา ๆ แล้วเลือก Add Installer
    3. จากนั้นจะได้หน้าจอที่มี 2 components ปรากฎอยู่ตามรูปด้านล่างนี้

 

    1. จากนั้นให้เลือกที่ serviceProcessInstaller1 แล้ว config properties ตาม รูปข้างล่างนี้

    1. และเลือก serviceInstaller1 แล้ว config properties ตาม รูปข้างล่างนี้

  1. ทำการ build project และ ให้ไปใน folder bin ของ project ที่ build ได้มา
    1. จากนั้นทำการสร้าง bat file ด้วยการ new file เป็น text file แล้วเปิดขึ้นมาด้วย notepad หรืออะไรก็แล้วแต่ copy คำสั่งในตารางข้างล่างนี้ใส่ไป และ save as เป็นชื่อ InstallService.bat
%WINDIR%\Microsoft.NET\Framework\v2.0.50727\installutil.exe myService.exe

 

NET START “FileBackupService”

 

โดยคำสั่งด้านบนนี้จะเป็นการเรียก utility ที่ชื่อ RegSvcs.exe มาทำการ install windows service ที่เราได้สร้างไว้ และเรียกคำสั่ง net start เพื่อให้ทำการ start service ของเรา

    1. ต่อมาสร้าง bat file ขึ้นมาอีกตัว สำหรับการ Uninstall Service ที่เรา install ไปโดยทำตามขั้นตอนของ a. ด้านบน แล้ว copy คำสั่งในตางรางด้านล่างนี้ไปวาง จากนั้น save as เป็น UninstallService.bat
NET STOP “FileBackupService”

 

%WINDIR%\Microsoft.NET\Framework\v2.0.50727\installutil.exe /u myService.exe

 

 

 

โดยคำสั่ง NET STOP ด้านบนเป็นการหยุด Windows Service ของที่ยัง Start อยู่ก่อน

จากนั้นก็ใช้ installutil.exe /u เพื่อทำการ uninstall

  1. ทำการทดลองใช้งาน Windows Service ที่เราสร้าง
    1. สร้าง folder ไว้ใน C:\Test\master ไว้สำหรับเป็น folder ที่จะมีมี file มาdrop เพื่อให้ตัว Service ทำ monitor file
    2. สร้าง folder ไว้ใน C:\Test\backup ไว้สำหรับให้ service ทำการ copy file จาก master folder มา drop ลงที่นี่เพื่อทำ backup
    3. จากนั้นเข้าไปใน Bin folder ของ project แล้วทำการ run InstallService.bat  file เพื่อเริ่มทำการ Install Windows Service ที่เราสร้างขึ้น
    4. จากนั้นไป หา file อะไรก็ได้ทำการ copy แล้วนำมา paste ลงใน C:\Test\master เพื่อทดสอบ
    5. หลังจาก paste เสร็จแล้วให้เข้าไปดูที่ C:\Test\backup จะเห็นว่ามี file ที่เรา paste ไว้ใน C:\Test\master เกิดขึ้นที่ C:\Test\backup นี้ด้วย
    6. หากมีอะไรผิดปกติให้เข้าไปเปิด EventLog ที่ Administrative Tools -> Event Viewers เลือกดูใน Application Group จะเห็นว่า Service ที่เราสร้างไว้มีการบันทึก Log เก็บไว้ตลอดตามรูปข้างล่างนี้

 

 

Download Source code here!

http://greatfriends.biz/files/WinService.rar

to be continued…

 

 

 

2 Comments »

  1. ได้รับความรู้เพิ่มขึ้นเยอะเลย ขอบคุณคับ

    Comment by unk — 15/01/2011 @ 1:33 am

  2. ขอบคุณมากครับ ได้ความรู้ที่อยากทำมานานมากๆ ตั้งเเต่เมื่อหลายปีก่อน

    Comment by Kathawoody Neko — 19/11/2015 @ 4:18 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: