Click here to Skip to main content
Click here to Skip to main content

Creating a C# Service Step-by-Step Lesson II

By , 9 Apr 2003
 

Introduction

Recently I was trying to write a C# service using the .NET framework and ran into a lot of issues that were not covered in the documentation or if they were, the solution wasn't easily inferred. So I decided to write a series of articles describing in step-by-step detail, how I went about solving the problems I encountered.

This is a multi-article contribution which it broken down as follows:

  1. Creating the project, initial service and installation
  2. Adding additional services to the application
  3. Adding support for custom event logging

Using the code

Starting with the code we developed during the last lesson, we are going to create some child services that are responsible for different aspects of the Spades game server. During this lesson, we are going to create a Login service, Lobby service and Game service. All these services will derive from the SpadesChildServiceBase class that we developed in the last lesson. We will also need to create an instance of these classes in the Application object, as well as create different ServiceInstaller classes to install the 3 different child services.

So without further delay, lets get started creating our first child process. All the child processes will be similar in structure, so I won't list the source code for each one, but the basic difference is just the class name. So let's go ahead and create a new class and call it SpadesLoginService and derive it from SpadesChildServiceBase. Your code should look like this when you are done.

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;

namespace CodeBlooded.Spades.Services
{
    public class SpadesLoginService : SpadesChildServiceBase
    {
        public SpadesLoginService()
        {
            this.ServiceName = "SpadesLoginSvc";
        }

        /// <SUMMARY>
        /// Set things in motion so your service can do its work.
        /// </SUMMARY>
        protected override void OnStart(string[] args)
        {
            base.OnStart( args );
        }
 
        /// <SUMMARY>
        /// Stop this service.
        /// </SUMMARY>
        protected override void OnStop()
        {
            base.OnStop();
        }

        /// <SUMMARY>
        /// 
        /// </SUMMARY>
        /// <RETURNS></RETURNS>
        protected override int Execute() {
            
            // for right now we'll just log a message in the
            // Application message log to let us know that
            // our service is working
            System.Diagnostics.EventLog.WriteEntry(ServiceName, 
                                        ServiceName + "::Execute()");

            return 0;
        }
    }
}

Since we want our child service to start the admin service if it's not already started, we need to add this code to the SpadesChildServiceBase class. We will also need to add some shutdown code to the SpadesAdminService class to shutdown all the child services when we stop the admin service.

Open up the SpadesChildServiceBase.cs file and in the OnStart method, we will use a ServiceController to gain access to a specific service on a specific computer and control the service via the Start or Stop methods. Just add the following code to your OnStart method of the SpadesChildServiceBase class.

protected override void OnStart(string[] args)
{
    // gain access to the SpadesAdminSvc on the local computer.
    ServiceController controlAdmin = 
       new ServiceController("SpadesAdminSvc", ".");

    if( controlAdmin.Status != 
        System.ServiceProcess.ServiceControllerStatus.Running &&
        controlAdmin.Status != ServiceControllerStatus.StartPending ) {
        
        // start the admin service
        controlAdmin.Start();
    }

    base.OnStart( args );
}

We need to create an instance of the SpadesLoginService class in the Application class, in the Main method. The code looks like:

servicesToRun = new ServiceBase[]{    new SpadesAdminService(),
                new SpadesLoginService() };

All we have left to do at this point is, create another ServiceInstaller class in our SpadesInstaller class. The code looks like the following and is just like the code we wrote in the previous article. The only thing different in the child services is that, we configure the installer to add the ServiceDependOn keys to the registry. Since this key is a MULTI_SZ type in the registry, it can hold multiple strings, so the way we do it in .NET is to create a string array and add the different strings to the array. Since we really only have one service that the child services depend on, we only need the one entry of SpadesAdminSvc.

        public SpadesInstaller()
        {

            // ...
            
            ServiceInstaller serviceLogin = new ServiceInstaller();

            serviceLogin.StartType    = ServiceStartMode.Manual;
            serviceLogin.ServiceName    = "SpadesLoginSvc";
            serviceLogin.DisplayName    = "Spades Login Service";
            serviceLogin.ServicesDependedOn  
                  = new string[] { "SpadesAdminSvc" };
            
            // ...
            Installers.Add( serviceLogin );
        }

Go ahead and create 2 more classes, one SpadesGameService and SpadesLobbyService both of which derive from SpadesChildServiceBase. Be sure to change the ServiceName property in your constructor. In our case, they should be SpadesGameSvc and SpadesLobbySvc respectively.

You will also need to create an instance of each in the Application::Main method and add them to the servicesToRun variable.

You will also need to create two ServiceInstaller class instances in the SpadesInstaller class just like we did above. When you are done, the Main method and the SpadesInstaller files should look like this:

        static void Main(string[] args)
        {
            // we'll go ahead and create an array so that
            // we can add the different services that
            // we'll create over time.
            ServiceBase[]    servicesToRun;

            // to create a new instance of a new service,
            // just add it to the list of services 
            // specified in the ServiceBase array constructor.
            servicesToRun = new ServiceBase[]{    
                            new SpadesAdminService(), 
                            new SpadesLoginService(),
                            new SpadesGameService(),
                            new SpadesLobbyService() };

            // now run all the service that we have created.
            // This doesn't actually cause the services
            // to run but it registers the services with the
            // Service Control Manager so that it can
            // when you start the service the SCM will
            // call the OnStart method of the service.
            ServiceBase.Run( servicesToRun );
        }

And the SpadesInstaller class.

        public SpadesInstaller()
        {
            ServiceProcessInstaller process = 
                     new ServiceProcessInstaller();

            process.Account = ServiceAccount.LocalSystem;

            ServiceInstaller serviceAdmin = new ServiceInstaller();

            serviceAdmin.StartType    = ServiceStartMode.Manual;
            serviceAdmin.ServiceName    = "SpadesAdminSvc";
            serviceAdmin.DisplayName    = "Spades Administration Service";
            
            ServiceInstaller serviceLogin = new ServiceInstaller();

            serviceLogin.StartType    = ServiceStartMode.Manual;
            serviceLogin.ServiceName    = "SpadesLoginSvc";
            serviceLogin.DisplayName    = "Spades Login Service";
            serviceLogin.ServicesDependedOn = 
                        new string[] { "SpadesAdminSvc" };

            ServiceInstaller serviceGame = new ServiceInstaller();

            serviceGame.StartType    = ServiceStartMode.Manual;
            serviceGame.ServiceName    = "SpadesGameSvc";
            serviceGame.DisplayName    = "Spades Game Service";
            serviceGame.ServicesDependedOn = 
                        new string[] { "SpadesAdminSvc" };

            ServiceInstaller serviceLobby = new ServiceInstaller();

            serviceLobby.StartType    = ServiceStartMode.Manual;
            serviceLobby.ServiceName    = "SpadesLobbySvc";
            serviceLobby.DisplayName    = "Spades Lobby Service";
            serviceLobby.ServicesDependedOn = 
                        new string[] { "SpadesAdminSvc" };

            // Microsoft didn't add the ability to add a
            // description for the services we are going to install
            // To work around this we'll have to add the
            // information directly to the registry but I'll leave
            // this exercise for later.


            // now just add the installers that we created to
            // our parents container, the documentation
            // states that there is not any order that you need
            // to worry about here but I'll still
            // go ahead and add them in the order that makes sense.
            Installers.Add( process );
            Installers.Add( serviceAdmin );
            Installers.Add( serviceLogin );
            Installers.Add( serviceGame );
            Installers.Add( serviceLobby );
        }

In my next article, we're going to add support for having our own custom event log so that we can log our messages to it, but to take advantage of it we will also need to make sure it's installed. So we'll have to create an installer for it. I ran into problems doing this and will go into detail on how I had to solve the problem.

History

  • 1.0 - 10 Apr 2003
    • First release version.

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

About the Author

Terry Denham
Architect Match.com
United States United States
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Questionhow to create account using c#memberMohammamd Ali5 Jul '09 - 18:12 
Generalcreating multithreads in C#memberalzrrog11 May '07 - 11:02 
how can I creat multithreads code to multiply 3x3 matrix and mean while calculate the processingtime for each thread (here i will have a 9 threads),which function in C# should i use and how, please help me !!!!Smile | :)
GeneralVery usefulmemberVicent Son22 Oct '06 - 15:23 
GeneralGreate article, easy to follow, well done!memberChristopherAtCodeProject14 Mar '06 - 15:04 
GeneralA Complete Bookmembersanalnaduvath1 Feb '06 - 19:46 
GeneralRe: A Complete Bookmemberrizwan.afsar7 Apr '06 - 8:05 
QuestionHOW TO DEAL WITH SYSTEM TRAYmemberBinoy Patel26 Oct '04 - 19:10 
AnswerRe: HOW TO DEAL WITH SYSTEM TRAYmemberMember 1841425 Nov '10 - 1:34 
QuestionReally nice article+samples/but why?membertlongman9 Oct '04 - 8:35 
AnswerRe: Really nice article+samples/but why?memberTerry Denham13 Oct '04 - 6:37 
GeneralGreat Article !!!memberMehdi Moshtaghi6 Jul '04 - 19:53 
GeneralProblem with Multiple InstallersmemberCBoland30 Jan '04 - 4:09 
GeneralExcellent articlesussPer Søderlind26 Oct '03 - 12:14 
GeneralQuestionmembernlgarvey25 Apr '03 - 10:12 
GeneralRe: QuestionmemberRay Cassick25 Apr '03 - 10:25 
GeneralRe: QuestionmemberTerry Denham25 Apr '03 - 13:19 
GeneralRe: QuestionsussAnonymous14 Oct '03 - 6:19 
GeneralRe: QuestionmemberTerry Denham13 Oct '04 - 6:40 
GeneralRe: Questionmembercasualinfoguy6 Jun '05 - 10:39 
GeneralRe: QuestionmemberMember 246301711 May '08 - 15:55 
GeneralRe: QuestionmemberCBoland30 Jan '04 - 3:55 
GeneralRe: Questionmembermykone15 Feb '06 - 11:49 
GeneralGreat !memberGriffonRL11 Apr '03 - 0:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 10 Apr 2003
Article Copyright 2003 by Terry Denham
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid