Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια

Creating a simple, reusable Windows Service (template code) - Part I

In this article, we'll see a way to create a simple Windows Service that can be reusable in that it can be used as the basis for any repeated operation that we would like to transform to a Windows Service.In other words, we can create our own assembly and then have the Windows Service call its entry point repeatedly over a period of time. By extending the code provided, you can even have multiple threads running at the same time.

Creating our service

First, go to Visual Studio -> New Project -> Windows Service

Visual Studio creates a very simple template for our Windows Service, that's far from working. Let's see what we can do:

First, we need a service name. That is the name by which our service will be known.

Let's go to the Region named "Component Desired Generated Code" and find the following function:

     ' NOTE: The following procedure is required by the Component Designer
    ' It can be modified using the Component Designer. 
    ' Do not modify it using the code editor.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        components = New System.ComponentModel.Container()
        Me.ServiceName = "Service1"
    End Sub

Okay, so  you've got to change the string assigned to Me.ServiceName to something of your own, let's say "MyService". The name you will put here is the name you will be using with the NET START, NET STOP etc. commands from the command line.

I won't go into any further details about the generated code. I will only say that  you can alter some other things as well like whether the service will have the right to pause or continue, and whether it can be shut down. To do this, add code to the InitializeComponent() function like this:

Me.CanPauseAndContinue = True
Me.CanShutdown = True

Defining basic members and properties

Okay. Before we continue, let's create a module with a global enumeration. We will use this as flags to keep track of our service's state later on. Don't hit me hard on the module stuff, of course you can incorporate this into your own code, no need to follow my approach here.

Here's the code:

Option Explicit On
Option Strict On

'/// <summary>
'/// Global definitions and settings.
'/// </summary>
Module mdlGlobals

    '/// <summary>
    '/// Service state enumeration
    '/// </summary>
    Public Enum ServiceModeEnum
        MODE_STOP = 0
        MODE_RUN = 1
        MODE_PAUSE = 2
        MODE_SHUTDOWN = 3
    End Enum

End Module

Now we need to define a section where we will keep our status and other information. Lets's create a region in our original service code, like the following:.

#Region " Shared Attributes "
    '/// <summary>
    '/// Current service status.
    '/// </summary>
    Private Shared s_enumMode As mdlGlobals.ServiceModeEnum
    '/// <summary>
    '/// Master service thread.
    '/// </summary>
    Private Shared s_thrdMaster As Thread = New Thread(AddressOf ServiceMasterThread)
    '/// <summary>
    '/// Start-up arguments.
    '/// </summary>
    Private Shared s_astrArgs() As String
    '/// <summary>
    '/// Event log.
    '/// </summary>
    Private Shared s_logMain As EventLog
#End Region
Okay, let's see who is who here: 

s_enumMode is used to keep track of our service status, using the ServiceModeEnum enumeration described above.
s_thrdMaster is essentially the functional part of our service. It is what will be executed when the service starts. As you see, this is the address of a thread, which means that we can make a delegate to a specific method and launch anything within that method.
s_astrArgs will be holding any start-up command-line arguments for our service. In this example, we won't have any arguments, but when you develop your own solution you probably will.
Finally, s_logMain is an EventLog object that will be used only for demonstration purposes in this example.

Extending Service Control methods

Now, as you can see, when you created the service Visual Studio created two methods in the service's code for you to implement. Here's the ORIGINAL code:

 Protected Overrides Sub OnStart(ByVal args() As String)        
        ' Add code here to start your service. This method should set things
        ' in motion so your service can do its work.
    End Sub

    Protected Overrides Sub OnStop()
        ' Add code here to perform any tear-down necessary to stop your service.
    End Sub

What it says is "add code here" we will do that but not only that! Here we have only the two most basic overrides for our service behaviour. We need to have all other overrides like OnPause(), OnContinue(), OnShutDown(). Here is the changed code for that region:

#Region " Definition of Service Related Methods "

    '/// <summary>
    '/// Start the Service.
    '/// </summary>
    '/// <param name="args">Start-up arguments</param>
    Protected Overrides Sub OnStart(ByVal args() As String)
        Dim intI As Int32

        'Store command args
        If Not args Is Nothing Then
            ReDim Me.s_astrArgs(args.Length - 1)
            For intI = 0 To args.Length - 1
                Me.s_astrArgs(intI) = args(intI)
            Next
        End If

        'Start the Service
        Me.ServiceStart()

    End Sub

    '/// <summary>
    '/// Stop the Service.
    '/// </summary>
    Protected Overrides Sub OnStop()

        'Stop the Service
        Me.ServiceStop()

    End Sub

    '/// <summary>
    '/// Pause the Service.
    '/// </summary>
    Protected Overrides Sub OnPause()

        'Pause the Service
        Me.ServicePause()

    End Sub

    '/// <summary>
    '/// Restore the Service.
    '/// </summary>
    Protected Overrides Sub OnContinue()

        'Resume the Service
        Me.ServiceContinue()

    End Sub

    '/// <summary>
    '/// Shutdown the Service.
    '/// </summary>
    Protected Overrides Sub OnShutdown()

        'Shutdown the Service
        Me.ServiceShutdown()

    End Sub

#End Region

No need to go into much detail here. To keep things clear, the overrides call our own private methods (which you'll see below), one for each override. The only thing that we've added is the handling of the command-line arguments in the OnStart() sub which are put into the string array s_astrArgs for further use.

Do we need to have the overrides call our own private methods? The answer is, no. We could do all the work inside the overrides. But I decided to showcase it this way to make things a bit more clear. Where you will implement the actual functionality is really up to you.

 

Creating a Master Thread for our Service

Before we examine what our private methods for the service functionality do, let's see how we have defined the thread (remember s_thrdMaster?)

#Region " Threads "

    '/// <summary>
    '/// Master thread
    '/// </summary>
    Private Shared Sub ServiceMasterThread()

        'Catch thread exceptions
        Try

            'Repeat while in RUN/PAUSE Mode
            While s_enumMode = mdlGlobals.ServiceModeEnum.MODE_RUN Or _
                s_enumMode = mdlGlobals.ServiceModeEnum.MODE_PAUSE

                'If this is not a Pause mode
                If s_enumMode <> mdlGlobals.ServiceModeEnum.MODE_PAUSE Then

                    'TODO: Do the job!
                    Thread.CurrentThread.Sleep(5000)
                    s_logMain.WriteEntry(String.Format("{0} Working... ", Now))

                End If

                'Sleep a bit
                Thread.CurrentThread.Sleep(1000)

            End While

        Catch ex As ThreadAbortException
            'An Abort request was received

            'TODO: Handle error

        Catch ex As Exception
            'General error

            'TODO: Handle error

        End Try

    End Sub

#End Region

This sub represents our main service thread. This thread will be running whenever the service starts and it will be stopping whenever the service stops.

Why a thread? Because, it's a service! Usually, a service does repeated tasks (well, ok, not all the time, but we won't attempt to cover all possible cases here). The point here is that you could possibly have a bunch of threads, each of which perform a different job. Our main thread is enough, though, to perform simple actions. What our Sub does here is to write an entry to the Event Log every 5 seconds. At the same time, it checks the state of the service (we'll see how we set that later) and decides whether it must do the stuff it's supposed to or just ignore it because the service's state has changed.

Read Part II

 

kick it on DotNetKicks.com

 

 

 

Έχουν δημοσιευτεί Πέμπτη, 15 Σεπτεμβρίου 2005 12:43 μμ από το μέλος cap
Δημοσίευση στην κατηγορία:

Ενημέρωση για Σχόλια

Αν θα θέλατε να λαμβάνετε ένα e-mail όταν γίνονται ανανεώσεις στο περιεχόμενο αυτής της δημοσίευσης, παρακαλούμε γίνετε συνδρομητής εδώ

Παραμείνετε ενήμεροι στα τελευταία σχόλια με την χρήση του αγαπημένου σας RSS Aggregator και συνδρομή στη Τροφοδοσία RSS με σχόλια

Σχόλια:

# Creating a simple, reusable Windows Service (template code) - Part I

Τρίτη, 2 Ιανουαρίου 2007 6:34 μμ by DotNetKicks.com
You've been kicked (a good thing) - Trackback from DotNetKicks.com

Ποιά είναι η άποψή σας για την παραπάνω δημοσίευση;

(απαιτούμενο) 
απαιτούμενο 
(απαιτούμενο) 
Εισάγετε τον κωδικό:
CAPTCHA Image