Showing posts with label Windows Service. Show all posts
Showing posts with label Windows Service. Show all posts

Monday, June 9, 2014

How To Convert A Console Application To A Windows Service?

Problem

Today I want to share how to easily convert an existing console application to a windows service. There are a few reasons why we would want to do that:
  1. Console application must be running all time with an active user session in a server. A long running service application should be run by a non interactive (user logon) service account.
  2. When the console application is crashed due to unhandled exception, stack overflow or whatever reason, it will not be able to start back automatically unless someone manually go and restart the application.
  3. It is possible that someone login to the server with the same current active user session and accidentally close the console application.
  4. When the server is rebooted unintentionally such as power trip, the console application cannot start automatically. (Unless configured using startup link).

Solution

I came across a MSDN magazine, and found out that people are talking about Topshelf. It is quite an interesting small framework that allow us writing a console application and then easily convert it into a windows service. This framework is independent and not relying on any other framework or component. Fore more info, visit HERE for their official site. This framework is obtainable from NuGet.

Well, let's start coding. First, create a new console application project in your Visual Studio, or if you intend to convert an existing console application into a windows service, then open existing project.

At your console application project, download the Topshelf component from NuGet.


Then, create a new class that implement ServiceControl interface from Topshelf like below. For existing console application, move all your code from the Program class to this new class. The ServiceControl interface only has 2 methods that need to be implemented. One is Start() is for code execution when the windows service is started, then Stop() for the stop.

public class FileProcessorService : ServiceControl
{
    private static FileProcessorComponent _component;

    public bool Start(HostControl hostControl)
    {
        try
        {
            //Execute my existing console application code
            _component = new FileProcessorComponent();
            _component.Start();

            return true;
        }
        catch
        {
            return false;
        }
    }

    public bool Stop(HostControl hostControl)
    {
        try
        {
            _component.Stop();
            return true;
        }
        catch
        {
            return false;
        }
    }

}

Then, at your console Program.cs Main method, create the Topshelf host that run the above service which implement the ServiceHost class like the following:

class Program
{
    static void Main(string[] args)
    {
        HostFactory.Run(host =>
        {
            host.SetServiceName("PuttyCatFileProcessor"); //cannot contain spaces or / or \
            host.SetDisplayName("Putty Cat File Processor");
            host.SetDescription("A file processor service that uses Topshelf.");
            host.StartAutomatically();

            //host.RunAs("service account name", "the password");
            //Don't think you like to expose your password in the code. =P
            //We can set it manually for one time after installing the windows service in the services.msc
               
            host.Service<FileProcessorService>();
        });
    }
}


In my host configuration, I used 4 methods that I find commonly we would use.

SetServiceName() - To set the service name, not the one display in the services.msc
SetDisplayName() - The display name that will appear in the services.msc list.
SetDescription() - The service description that will appear in services.msc and Task Manager.
StartAutomatically() - To indicate the service shall start automatically after reboot.
RunAs() - We can set the username and password which to be used to run this windows service. Though, I think no one would want to do that to expose your credential information in the code.

Now, to test your application, you can simply press F5 to proceed for debug. Even with the above code, it is NOT automatically install and run as windows service yet. It is still a console application.

This is how it look like after pressing F5:


Pros

1. Debug

So, here is one advantage that I quickly can identify is, Topshelf creates the convenience in debugging a windows service. In conventional way, we have to install windows service first with installutil.exe and then start it then from Visual Studio attach process to the windows service to debug. Sometimes, we think of workaround such as writing code to change the windows service's ServiceBase behavior to temporary run the code in console mode, so that we can simply debug windows service by just pressing F5.

2. Deployment

Another advantage is Topshelf internally already support accepting pre-defined parameters that automate the windows service install or uninstall. For example, to install your console application as a windows service, just run your console application with install parameter in a command prompt.

<Console App Name>.exe install


Note that the above verbose log during the windows service installation. It also cater for the event log registration which I suppose any error occur in the event log will be automatically logged in the application event log.

Then, you can verify your windows service installation. The display name, description, account, start mode were as defined in the code.


Here is where you set the service account information instead of entering credential information in the code which I mentioned just now.


And, here is the auto recovery that I mentioned just now that a console application cannot support.



Alright now, in order to uninstall your windows service, just run the same console application with uninstall parameter.

<Console App Name>.exe uninstall


3. Migration

Last advantage that I discover is I can easily migrate my existing console application into a windows service with very minimal code change. I can simply move the code from the Program class to another new class, then add a windows service host class on top of the new class to convert it into a windows services.

Cons

1. Redundant

Well, if you have experience with windows service development, actually you may find that windows service project template from Visual Studio is quite complete and generated all the basic necessary code already. It can achieve the same result as this little framework do. Somehow, I feel it is redundant.

2. Maintenance

As per this document, Topshelf prerequisites .net framework is version 4.0. Well, console project that uses later .net framework library may not be supported and used with Topshelf. Also, the project .net framework upgrade become dependent on Topshelf .net framework usage.

Summary

A long running console application should be running as a windows service for those stated reasons at the problem section above. Topshelf makes developer life easier in developing windows service by simplifying and automating the debug and deployment work. If you are interested with my complete source code, feel free to download it from HERE.





Sunday, April 20, 2014

Windows Service Monitoring Console

Problem

Today topic is covering how to make windows service interact with any desktop application. I am sure every windows service developer would face a challenge with the "no user interface" windows service nature behavior whereby you cannot know whether your service is actually doing its job or actually hanging.

The windows services are running in session 0, an environment where the application is running with no user context, no user interface, they are separated from desktop applications group. Last time in Windows XP/2003 or earlier OS, windows services allow to interact with any desktop application, and because of having this flexibility, windows service possess security threat and always become a common attack target. Therefore, in Windows Vista/2008 or newer OS, the windows services are protected and isolated from the desktop applications. From then onwards, windows service cannot direct interact with any desktop application. More info about session 0 isolation.

Solution

Even with session 0 isolation, the windows service is not actually entirely locked down, we can still make it to interact it with other application by using RPC (Remote Procedure Call) or .NET Remoting, but they are old, today I am sharing how to use WCF instead.

I have created a WPF application which is used to monitor the windows service activity. You will see all the logs written by the windows service appearing in the monitor console in real time.

Concept

The WPF application is hosting a WCF host with named pipe communication channel since the monitoring console and the windows service are sitting in the same machine. Whenever the windows service is writing log, it will call the WCF service. The WCF service receive the log and write it to a text box in WPF application.

Challenge

The windows service is running all the time even without user logon, but the monitoring console can only be run with a logon user. Therefore, the windows service cannot always call the WCF service, it would encounter error when there is no active user session and monitoring console is not running. Therefore, it should call only when it detect the monitor console is running.

Implementation

Monitoring Console

WCF Service

Create a service that receive log from windows service.

The service contract definition:

[ServiceContract]
public interface ILoggingService
{
    [OperationContract]
    void WriteLog(string value);
}

The service implementation:

public class LoggingService : ILoggingService
{
    public void WriteLog(string value)
    {
        //Locate the text box and write the receive value into it
        MainWindow main = (MainWindow)Application.Current.MainWindow;
        main.LogText.Text += value + Environment.NewLine;
    }
}

WPF Application

Create a simple interface that contain a text box. This text box will display all the log sent by the windows service.


<Window x:Class="WinSvcMonitoringTool.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TextBox x:Name="LogText" Margin="10" TextWrapping="Wrap"/>
    </Grid>
</Window>

Create WCF host when the application is opened, activated and all controls are rendered.

public partial class MainWindow : Window
{
    private ServiceHost _host = null;

    public MainWindow()
    {
        InitializeComponent();
    }
       
    protected override void OnContentRendered(EventArgs e)
    {
        //Start WCF service automatically when the window is activated
        try
        {
            _host = new ServiceHost(typeof(LoggingService));

            //Create Metadata exchange for the service
            ServiceMetadataBehavior mexBehavior = new ServiceMetadataBehavior();
            _host.Description.Behaviors.Add(mexBehavior);

            //Add service endpoints for the service and mex
            _host.AddServiceEndpoint(typeof(ILoggingService), new NetNamedPipeBinding(), "net.pipe://localhost/WinSvcMonitoringService/LoggingService.svc");
            _host.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexNamedPipeBinding(), "net.pipe://localhost/WinSvcMonitoringService/LoggingService.svc/mex");
            _host.Open();

            LogText.Text += "WCF Host started succeessfully." + Environment.NewLine;
        }
        catch (Exception ex)
        {
            if (_host != null)
                _host.Abort();

            MessageBox.Show("Service Host Error : " + ex.Message);
        }

        base.OnContentRendered(e);
    }

    protected override void OnClosed(EventArgs e)
    {
        //Close the WCF service host when the form is closed
        _host.Close();
        base.OnClosed(e);
    }

}

That's all for the monitoring console. It is ready for the windows service to call and consume. Now start coding the windows service.

Windows Service

The windows service is doing a simple write log activity for every second. Therefore, during the service OnStart event:

Task task = new Task(() =>
{
    while (true)
    {
        WriteLog("Hello World!");

        Thread.Sleep(1000);
    }
}, _source.Token);


task.Start();

Now, use "Add Service Reference" to generate the WCF client proxy class. Due to the service is created with NetNamedPipeBinding, enter the service address start with net.pipe.


Then, use the proxy class as follow:

try
{
    using (LoggingServiceClient proxy = new LoggingServiceClient())
    {
        proxy.WriteLog(log);
        proxy.Close();
    }
}
catch(Exception ex)
{
    //Error occur, write the detail to the event log
    this.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);

}

At this point of time, the windows service should be able to call the WCF service hosted in the WPF monitoring console. You will notice Hello World! is written to the text box every second.


Detect Monitoring Console

However, whenever the monitoring console is closed, the WCF host is closed as well, and the windows service will encounter error because the WCF service no longer exists. In order to solve this challenge, I have to implement something that the windows service know if the monitoring console is running or not. If it is running, it will call the WCF service, otherwise, do nothing or write the log to text file.

Windows Management Instrumentation (WMI)

WMI can be used to manage almost anything related to OS. For this case, I am going to use the ManagementEventWatcher class library to watch out for my monitoring console is opened or closed. In order to do that, I need to write a WMI query to monitor the process name that I want:

//The query to monitor WinSvcMonitoringTool.exe process is started or stopped
private const string _pStartQuery = "SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName = 'WinSvcMonitoringTool.exe'";
private const string _pStopQuery = "SELECT * FROM Win32_ProcessStopTrace WHERE ProcessName = 'WinSvcMonitoringTool.exe'";

Note: WinSvcMonitoringTool.exe is my WPF application name.

Pass in the query to the ManagementEventWatcher constructor.

_pStartWatcher = new ManagementEventWatcher(new WqlEventQuery(_pStartQuery));
_pStartWatcher.EventArrived += new EventArrivedEventHandler(ProcessStartEvent);
_pStartWatcher.Start();

_pStopWatcher = new ManagementEventWatcher(new WqlEventQuery(_pStopQuery));
_pStopWatcher.EventArrived += new EventArrivedEventHandler(ProcessStopEvent);
_pStopWatcher.Start();

Then, subscribe EventArrived event handler. So that, when it detected the monitoring console process is started, it will flag the window service to start call WCF. And also if the process is closed, the window service will stop the WCF call.

private void ProcessStartEvent(object sender, EventArrivedEventArgs e)
{
    //flag to call WCF service
    _monitorConsoleIsDetected = true;
}

private void ProcessStopEvent(object sender, EventArrivedEventArgs e)
{
    //flag to stop calling WCF service
    _monitorConsoleIsDetected = false;

}

So, my write log method has become like this after making use of the flag.

private void WriteLog(string log)
{
    //Only write log when there is any user session is active and the monitoring tool is opening
    if (_monitorConsoleIsDetected)
    {
        try
        {
            using (LoggingServiceClient proxy = new LoggingServiceClient())
            {
                proxy.WriteLog(log);
                proxy.Close();
            }
        }
        catch(Exception ex)
        {
            //Error occur, write the detail to the event log
            this.EventLog.WriteEntry(ex.Message, EventLogEntryType.Error);
        }
    }

}

Summary

After done with the WMI event watcher. Start the windows service first, it will do nothing. When you run the monitoring console exe, the windows service will automatically detect its existence, then start calling the WCF service hosted in the monitoring console. Then, the log will start appearing in the monitoring console. If you shutdown the application or logoff from the OS, the windows service will know it and then stop calling the WCF service. The only drawback about this console monitoring is you have to run it with Administrator mode, otherwise the WCF service host will not run properly.

If you are interested with my source code, feel free to download it from HERE.







Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...