Windows Services in Action II






4.96/5 (29 votes)
This article demonstrates an example of uses of Windows services
Introduction
This article demonstrates a real-world solution. It is my second article explaining Windows Services. If you are not familiar with Windows services on the .NET Framework, you can read my first article, "Windows Services in Action I".
This service will track the hours of a day. It will read the database if the time matches the hour specified in the config file. It will send an e-mail if there is(are) matching date(s).
Background
In this article, you can find many details of coding like reading from config.file, reading from a database, using a timer control, and sending an e-mail in HTML format.
Using the Code
Creating Event Logs
Having an event log in hand is very useful to track our service. So first, let’s create one.
(If you want to make life easier, you can download code from the previous article now. There is already an Eventlog
control there.)
It can be dragged-and-dropped from the toolbox into SimpleService.cs (Design mode).
Add codes to create an Eventlog
control and its detail. After that, you will have the code block below:
public SimpleService()
{
InitializeComponent();
// Initialize eventLogSimple
if (!System.Diagnostics.EventLog.SourceExists("SimpleSource"))
System.Diagnostics.EventLog.CreateEventSource("SimpleSource", "SimpleLog");
// SimpleSource will appear as a column in eventviewer.
eventLogSimple.Source = "SimpleSource";
// SimpleLog will appear as an event folder.
eventLogSimple.Log = "SimpleLog";
}
Configuration File
Because there isn't a regular UI, we can use a config file to pass any parameters via the config file. So you can simply add a config file by right-clicking the Project in the Solution Explorer Visual Studio 2005. Click Add, New Item and Application Configuration File, then click OK. After that, you will see the app.config file in your project section. Double click to open it and add the following lines in the <appSettings>
section:
<appSettings>
<!-- example value : key="Hour" value="09:00"-->
<add key="Hour" value="10:00"/>
</appSettings>
This means our service will read the data and send emails at 10:00
. (I am assuming the time settings are based on 0-24-hours.)
Reading Config File
Let's do this job in a simplistic way. At first, we must add the System.Configuration
library adding the line below:
using System.Configuration;
Then let’s create a function reading from the config file. The function looks like below:
private string ReadConfigFile()
{
// Create an AppSettingReader object.
System.Configuration.AppSettingsReader appsreader
=new AppSettingsReader();
String value = String.Empty;
// Get the value Hour from config file.
value = (String)appsreader.GetValue("Hour", value.GetType());
// Return it.
return value.ToString();
}
Reading Data
We can use an Access table named bd.mdb and use OldeDb Provider of the .NET Framework. So we must add using System.Data.OleDb;
to the top of SimpleService.cs. We must also create connection string, and query in a while
loop and send mail to the person(s) who was(were) born on the day. Sending mail is achieved by another function which is explained shortly.
Here is the code block:
private bool ReadDataFromDB()
{
string sPName = "";
string sPSurname = "";
string sPEmail = "";
// What are the day and month today?
string strMonth = DateTime.Now.Month.ToString();
string strDay = DateTime.Now.Day.ToString();
//string strAppPath = System.IO.Directory.GetCurrentDirectory();
// Create query string which selects Last names,
// Names and Emails if the day and month match.
string sQuery = "Select * from BirthDays where Days='" +
strDay + "' and Months='" + strMonth + "';";
string sConnString = @"Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=c:\bd.mdb";
// Write the stage and the brand new query string to the log.
eventLogSimple.WriteEntry("ReadDataFromDB() called...");
eventLogSimple.WriteEntry(sQuery);
// Create DB objects.
// Assuming that bd.mdb found on c: driver root : c:\bd.mdb
OleDbConnection DBConn = new OleDbConnection(sConnString);
OleDbCommand DBComm = new OleDbCommand(sQuery);
DBComm.CommandType = CommandType.Text;
DBComm.Connection = DBConn;
// let's do DB jobs in a try.
try
{
eventLogSimple.WriteEntry("In try...");
DBConn.Open();
OleDbDataReader DBReader = DBComm.ExecuteReader();
while (DBReader.Read())
{
// Write the stage to the log.
eventLogSimple.WriteEntry("In while...");
sPName = DBReader.GetValue(2).ToString();
sPSurname = DBReader.GetValue(1).ToString();
sPEmail = DBReader.GetValue(5).ToString();
SendMailToStaff(sPEmail, sPName + " " + sPSurname);
SendMailToWebAdmin("admin@company.com",
"Birthday service sent e-mail to: "+ sPName + " " + sPSurname);
// Write the job done to the log.
eventLogSimple.WriteEntry
("E-mail: " + DBReader.GetValue(5).ToString()
+ " - Staff Name: "+sPName + " " + sPSurname);
}
}
catch
{
// Write if there is an error to the log.
eventLogSimple.WriteEntry("An error has occurred!");
}
finally
{
// Close connection
DBConn.Close();
}
return true;
}
Creating a Timer
Now we need a timer which will decide when to act:
private System.Timers.Timer mytimer = null;
And then we must create a timer
object in the OnStart
. It will look like below:
protected override void OnStart(string[] args)
{
// Write "started.." to the log file.
eventLogSimple.WriteEntry("Simple service started!");
// Get time to send from config file.
sTimeToSend = ReadConfigFile();
// Write "time to send" to the log file.
eventLogSimple.WriteEntry("Time to send: " + sTimeToSend);
// Create timer object and set timer tick to an hour
MyTimer = new System.Timers.Timer(3600000);
MyTimer.Elapsed += new System.Timers.ElapsedEventHandler
(this.ServiceTimer_Tick);
MyTimer.Enabled = true;
}
Then, let's create a Tick
event:
private void ServiceTimer_Tick(object sender, System.Timers.ElapsedEventArgs e)
{
MyTimer.Enabled = false;
string sCurrentHour = DateTime.Now.Hour.ToString() + ":00";
DateTime dCurrentHour = Convert.ToDateTime(sCurrentHour);
DateTime dTimeToSend = Convert.ToDateTime(sTimeToSend);
int iComparison = DateTime.Compare(dTimeToSend, dCurrentHour);
if (iComparison == 0)
ReadDataFromDB();
MyTimer.Enabled = true;
}
Sending E-mails
To send mail, we will use the System.Net.Mail;
library so we must add to the top again:
using System.Net.Mail;
The service will send an informative email to the Web admin.
private bool SendMailToWebAdmin(string sTo, string sText)
{
SmtpClient SClient = new SmtpClient();
System.Net.Mail.MailMessage msgMail = new System.Net.Mail.MailMessage();
// Most probably the SClient.Host will vary for you.
// So you must write your own.
SClient.Host = "10.1.1.28";
SClient.Port = 25;
MailAddress strFrom = new MailAddress("xyx@xyzmail.com");
string strTo = sTo;
string strSubject = "Corporate Portal Service -
Birthday Congratulation - (Monitoring) ";
string strEmailBody = sText;
msgMail.Body = strEmailBody;
msgMail.Subject = strSubject;
msgMail.To.Add(strTo);
msgMail.From = strFrom;
msgMail.IsBodyHtml = true;
if (strTo.Trim() != "")
{
try
{
SClient.Send(msgMail);
eventLogSimple.WriteEntry("Sent");
return true;
}
catch
{
eventLogSimple.WriteEntry("Failed");
return false;
}
}
return false;
}
Mails that we are sending to people is achieved by another function which sends HTML content. Unfortunately, I could not place HTML here. I usually send a very colourful page.
private bool SendMailToStaff(string sTo, string sName)
{
SmtpClient SClient = new SmtpClient();
System.Net.Mail.MailMessage msgMail = new System.Net.Mail.MailMessage();
// Most probably the SClient.Host will vary for you.
// So you must write your own.
SClient.Host = "10.1.1.28";
SClient.Port = 25;
MailAddress strFrom = new MailAddress("xyx@xyzmail.com");
string strTo = sTo;
string strSubject = "Happy Birthday "+sName+"!";
string strEmailBody = @"HTML STRING COMES HERE";
msgMail.Body = strEmailBody;
msgMail.Subject = strSubject;
msgMail.To.Add(strTo);
msgMail.From = strFrom;
msgMail.IsBodyHtml = true;
if (strTo.Trim() != "")
{
try
{
SClient.Send(msgMail);
eventLogSimple.WriteEntry("Sent");
return true;
}
catch
{
eventLogSimple.WriteEntry("Failed");
return false;
}
}
return false;
}
Points of Interest
I think using services is the best choice in our case. If you think that I am wrong, please let me know.
History
- 2007-12-28: This is the 4th release of the service which has been running for 2 years.