Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Windows Services in Action II

0.00/5 (No votes)
28 Dec 2007 1  
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here