Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C#
Article

C# Generic Dynamic Windows Service using .NET Reflection

Rate me:
Please Sign up or sign in to vote.
4.05/5 (9 votes)
30 Jan 2008CPOL3 min read 80.4K   704   54   19
A technique to create Windows Service apps that's configurable and dynamic by using the .NET Reflection.

Introduction

This article shows a technique to design Windows Service apps with the following goals:

  1. Can be debugged like a "Console" application without the need to invoke the "Installer" program.
  2. The main "Windows Service" code should be reusable and can be rapidly applied to any new "Windows Service" application development.
  3. Separates the "Server Logic" and allows dynamic switching if desired.
  4. The details of the "Windows Service" during installation are configurable via an XML file.
  5. The loading of the server logic is configurable via an XML file.

The sample code I presented here is the basic framework and has been tested with Visual Studio 2008 and .NET 3.5 Framework. But the concept should be applicable to other versions as well.

Background

If you have never created a Windows Service application, there is a "walkthrough" article on the MSDN library site. Obviously, there're many useful articles on The Code Project as well.

Using the Code

By using the presented technique, to create a Windows Service app will just require you to create a new "Console" project using the Visual Studio.

The sample Visual Studio solution consists of 4 projects. They are:

  1. GDWS.Common - That's where the generic reusable code for the Windows Service sits.
  2. GDWS.ExampleService - This is an example "server" logic that's aimed to be built as a .NET assembly.
  3. GDWS.ExampleServiceProgram - This is an example "Console" app that actually invokes the server logic.
  4. SetupExampleService - This is a typical "Setup & Deployment" project to demonstrate that this technique also works seamlessly with the standard setup mechanism.

In a nutshell, the Main() program is as simple as this:

C#
namespace GDWS.ExampleServiceProgram
{
  class Program
  {
    static void Main(string[] args)

    {
      ServiceMainProgram.ServiceMain(args);
    }
  }

  [RunInstaller(true)]
  public class ExampleServiceInstaller : CustomServiceInstaller
  {
  }
}

Class - CustomServiceInstaller

This class mainly reads the ServiceInstall.xml file in order to tell Windows Installer the "Service Name", the "Service Display Name", and the "Service Descriptions".

Class - ServiceMainProgram

In order to load the "server logic" dynamically, .NET Reflection is used to load the DLL that encapsulates the server logic. The two constructors are private, and you use the two static methods to invoke the process.

C#
public class ServiceMainProgram
{
  ...
  ...
  public static void Service(string[] args, Type type)...

  public static void Service(string[] args)...
  ...
} 

The static method that takes two arguments requires you to submit a Type, so that during creation of an object internally it can use Reflection to find out that the two required static methods StartThreadProc and StopThreadProc are present.

The second static method that takes only one argument reads the name of the Type from the ServiceConfig.xml file, which also tells it where to find the server logic assembly DLL. Once the path of the DLL is located, the assembly is loaded by using Reflection.

C#
private Type LoadAssemby(string configFileName)
{
  ...
  ...
  Assembly asm = Assembly.LoadFile(Path.GetFullPath(assemblyFullPath));
  ...
  Type type = asm.GetType(typeName);
  ...
}

Once the Type is found and checked, the following code can utilize the reflected methods:

C#
private void Run()
{
  if (debugMode) RunDebug();
  else
  {
    ServiceBase[] servicesToRun = new ServiceBase[]
        { new GenericService(threadProcType) };
    ServiceBase.Run(servicesToRun);
  }
}

private void RunDebug()
{
  ...
  ...
  MethodInfo mStart = threadProcType.GetMethod(START_THREAD_PROC);
  // assuming static method, hence no need to pass any instantiated object
  mStart.Invoke(null, null);
  bool stop = false;
  while (!stop)
  {
    ...
    if (k.KeyChar == 'q' || k.KeyChar == 'Q')
    {
      ...
      MethodInfo mStop = threadProcType.GetMethod(STOP_THREAD_PROC);
      // assuming static method, hence no need to pass any instantiated object
      mStop.Invoke(null, null);
      stop = true;
    }
    ...
  }
}

Debugging

For debugging purposes, define a command line argument debug or under the Command Prompt, just type yourservice.exe debug to invoke the program into debug mode.

Installing as a Proper Windows Service

You can install the sample service app using the bundled Setup project.

To quickly test the app as a Windows Service, you can use the installutil.exe and uninstall it by calling installutil.exe /u.

ServiceConfig.xml

XML
<?xml version="1.0" encoding="utf-8" ?>
<ConfigService>
<ServiceName value="ExampleService"/>
<ServiceDisplayName value="An Example Service"/>
<ServiceDescription value="An example service
    that demonstrates a generic and dynamic technique."/>
</ConfigService>

ServiceInstall.xml

XML
<?xml version="1.0" encoding="utf-8" ?>
<InstallService>
<ServiceAssemblyFullPath value=
    "..\..\..\GDWS.ExampleService\bin\Debug\GDWS.ExampleService.dll"/>
<TypeName value="GDWS.ExampleService.ThreadProcExample"/>
</InstallService>

Please note that, although the ServiceAssemblyFullPath is specified as a relative path, the code will try to locate it locally if it cannot find it at the first instance.

History

  • 2008-01-22: Article created

License

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


Written By
AH
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionService work in debug mode but does not in release Pin
romanlopg7-Dec-09 8:09
romanlopg7-Dec-09 8:09 
AnswerRe: Service work in debug mode but does not in release Pin
romanlopg7-Dec-09 10:31
romanlopg7-Dec-09 10:31 
GeneralRe: Service work in debug mode but does not in release Pin
AH9-Dec-09 5:14
AH9-Dec-09 5:14 
I'm glad it's working for you now.
GeneralUnable to run the console program in Release Mode Pin
noms18-Oct-09 19:28
noms18-Oct-09 19:28 
GeneralRe: Unable to run the console program in Release Mode Pin
AH18-Oct-09 21:27
AH18-Oct-09 21:27 
QuestionCan't able to Run the sample Code Pin
noms4-Aug-09 18:48
noms4-Aug-09 18:48 
AnswerRe: Can't able to Run the sample Code Pin
AH4-Aug-09 21:05
AH4-Aug-09 21:05 
QuestionWhy call this method ServiceMain? Pin
Tawani Anyangwe21-Apr-09 5:32
Tawani Anyangwe21-Apr-09 5:32 
AnswerRe: Why call this method ServiceMain? Pin
AH21-Apr-09 6:19
AH21-Apr-09 6:19 
GeneralConflict between TWO 'Generic Dynamic Windows Services' Pin
KeesDekker21-Apr-09 1:03
KeesDekker21-Apr-09 1:03 
GeneralRe: Conflict between TWO 'Generic Dynamic Windows Services' Pin
AH21-Apr-09 1:58
AH21-Apr-09 1:58 
GeneralError 1053 .... Pin
Kris-I26-Mar-09 8:37
Kris-I26-Mar-09 8:37 
GeneralRe: Error 1053 .... Pin
AH26-Mar-09 11:22
AH26-Mar-09 11:22 
GeneralRe: Error 1053 .... Pin
Kris-I26-Mar-09 23:41
Kris-I26-Mar-09 23:41 
GeneralRe: Error 1053 .... Pin
AH27-Mar-09 1:36
AH27-Mar-09 1:36 
GeneralA Question.. Pin
zeroresult22-Jan-08 15:58
zeroresult22-Jan-08 15:58 
GeneralRe: A Question.. Pin
Anand Morbia22-Jan-08 23:57
Anand Morbia22-Jan-08 23:57 
GeneralRe: A Question.. Pin
AH23-Jan-08 2:17
AH23-Jan-08 2:17 
Generalreally useful~ Pin
MDH Trade22-Jan-08 15:31
MDH Trade22-Jan-08 15:31 

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.