Click here to Skip to main content
16,020,249 members
Articles / Programming Languages / C#
Article

Windows Service to Host Multiple Processes

Rate me:
Please Sign up or sign in to vote.
3.21/5 (10 votes)
10 Oct 2006CPOL2 min read 44.3K   487   21   5
A Windows service which spawns mutiple threads

Introduction

This application framework defines an easy way to host multiple modules (DLLs) in one single Windows service with a single configuration file. This way, a group of services can be packaged as one single Windows service.

Background

Windows services are indispensable for any IT setup. These are used to perform simple to moderately complex tasks. After some time, the list of Windows services start growing and become difficult to maintain. Windows OS has several processes running in svchost.exe. Although third parties can add their processes to run under svchost.exe, I could not find any documentation explaining the steps required to add your custom modules to this process. So I developed an easy framework to run a window service which can host other assemblies (DLLs). New assemblies can be added just by changing the configuration file, no compilation is needed.

Using the Code

To use this framework, you need basic understanding of the following:

  • System.Configuration
  • System.Threading
  • System.Reflection

The app.config file is extensible. Each process assembly can define its own name/value section. First define section group as follows:

XML
//    <sectionGroup name="SvcsConfig">
//      <section name="TimedActivity" 
//          type="System.Configuration.NameValueSectionHandler" />
//      <section name="WebDownload" 
//          type="System.Configuration.NameValueSectionHandler" />
//    </sectionGroup>

And then the (configuration) section as follows:

XML
// <TimedActivity>
//  <add key="Assembly" 
//       value="c:\work\WinSvcsGroup\TimedActivity\bin\debug\TimedActivity.dll" />
//  <add key="Type" value="TimedActivity.WriteTimeToFile" />
//  <add key="FileToWrite" value="c:\Demo\TimeOut.txt" />
//</TimedActivity>

//<WebDownload>
//  <add key="Assembly" 
//       value="c:\work\WinSvcsGroup\WebDownload\bin\debug\WebDownload.dll" />
//  <add key="Type" value="WebDownload.WebDownload" />
//  <add key="Folder" value="c:\Demo\downloads" />
//</WebDownload>

The first two keys Assembly and Type are mandatory. These specify name of assembly and class within an assembly that has the entry point for processing. Other additional keys are module specific and can be added, as many as required. The service reads the above information to spawn multiple threads and host different modules.

The custom assemblies, which are built to perform work, need to be inherited from an abstract class WinSvcThread.

C#
//abstract public class WinSvcThread
//{
//        private string section;
//        public WinSvcThread(string Section)
//        {
//            section = "SvcsConfig/" + Section;
//        }
//        #endregion

//        public  virtual void  Process()
//        {
//
//        }

//        public string Section
//        {
//            get
//            {
//                return section;
//            }
//        }

//    }

The derived class needs to define a constructor and override virtual method Process, which is the entry point for the processing.

To illustrate, in the sample application, there are two assemblies hosted in Windows service WinScvcsHost. Assembly TimedActivity writes DateTime.Now to a text file, and WebDownload downloads files from a website.

The worker assembly, TimedActivity is implemented as follows:

C#
//public class WriteTimeToFile : WinSvcThread
//{
//    Timer Clock;
//    /// <summary>
//    /// Constructor. Implement as follows.
//    /// </summary>
//    /// <param name="str">
//    /// sets the configuration section related to this process
//    /// </param>
//    public WriteTimeToFile(string str)
//        : base(str)
//    {

//    }
//    /// <summary>
//    /// The entry point for the module.
//    /// The business logic for thread should go here.
//    /// </summary>
//    override public void Process()
//    {
//        try
//        {
//            Clock = new Timer();
//            // raise event after two minutes.
//            Clock.Interval = 1000 * 60 * 2;
//            Clock.Start();
//            Clock.Elapsed += new ElapsedEventHandler(Timer_Tick);
//        }
//        catch (Exception es)
//        {
//            EventLog.WriteEntry("WriteTimeToFile TimerTick ", es.Message);
//        }
//    }
//    public void Timer_Tick(object sender, EventArgs eArgs)
//    {
//        try
//        {
//            if (sender == Clock)
//            {
//                StreamWriter SW;
//                NameValueCollection nvc =
//                (NameValueCollection)ConfigurationManager.GetSection(base.Section);
//                string FileToWrite = nvc["FileToWrite"];

//                SW = File.AppendText(FileToWrite);
//                SW.WriteLine(DateTime.Now.ToString());
//                SW.Close();
//            }
//        }
//        catch (Exception es)
//        {
//            EventLog.WriteEntry("Timer_Tick ", es.Message);
//        }

//    }
//}

Advantages

  • Since each service runs as a thread, the system resources are conserved.
  • It is easy to manage the group rather than several independent services.
  • The custom assemblies can each use the App.config.

History

  • 10th October, 2006: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Nothing special about me.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Member 1075870615-May-14 7:35
Member 1075870615-May-14 7:35 
Generalreinventing the wheel Pin
ilke can23-Oct-06 23:16
ilke can23-Oct-06 23:16 
AnswerRe: reinventing the wheel Pin
Arjun Arora24-Oct-06 2:44
Arjun Arora24-Oct-06 2:44 
GeneralRe: reinventing the wheel Pin
ilke can24-Oct-06 7:48
ilke can24-Oct-06 7:48 
Still if you can write the reflection code necessary for loading the dll's you can do the same with the services you run on startup and not recompile your application if you need that so badly but i really wouldn't advise this kind of solution. Your code will become needlessly difficult.

If we're so much in need to add assemblies to our application on the fly without recompiling it so the problem at the moment is: "What will happen when you'll want to update a dll you included in your config file ?". As you're not monitoring the configuration file changes you'll have to do a "stop-start" which i find is quiet close to rebuilding, redeploying the application in the .Net world.

On the other hand if you want to monitor the config file and reload the dll's that have changed (which i don't advise too) you'll have to deal with shadow copying which won't work unless you unload all the assemblies you've loaded inside your Application Domain. This can easily become a "stop-start" too Smile | :)

What i saw from your code is that basically it just runs a new thread for a given method which is not that difficult. By the way if i would all the time need to include processes to my services i would say that these processes are part of my project so i just include them either by putting them in or referencing and if i need parallel processing i launch threads for them to run in my code. If not i would never include all of them together cause this can possibly cause management problems when i'll have to deal with a problem for one of the assemblies which can affect others behaviour and so on.

Anyway if you remove or add or update a component in your production environment this is a change in your production environment and will need attention so the compilation is not the hardest or the most important part in this process to consider.

GeneralRe: reinventing the wheel Pin
giammin22-Oct-12 1:19
giammin22-Oct-12 1:19 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.