Windows Service to run constantly
Asked Answered
M

6

60

I've created a Windows Service called ProxyMonitor and I'm currently at the stage where the service is installs and uninstall's the way I want it.

So I execute the application like so:

C:\\Windows\\Vendor\\ProxyMonitor.exe /install

Pretty self explanatory, and then I got to services.msc and and start the service, but when I do this I get the following message:

The Proxy Monitor Service on Local Computer started and then stopped. Some services stop automatically if there is no work to do, For example, The performance Logs and Alerts Services

My code looks like so:

public static Main(string[] Args)
{
    if (System.Environment.UserInteractive)
    {
        /*
            * Here I have my install logic
        */
    }
    else
    {
        ServiceBase.Run(new ProxyMonitor());
    }
}

And then within ProxyMonitor class I have:

public ProxyMonitor()
{
}

protected override void OnStart(string[] args)
{
    base.OnStart(args);
    ProxyEventLog.WriteEntry("ProxyMonitor Started");

    running = true;
    while (running)
    {
        //Execution Loop
    }
}

and onStop() I just change the running variable to false;

What would I need to do to make the Service constantly active, as I would need to be monitoring the network I need to trace changes etc.


Update: 1

protected override void OnStart(string[] args)
{
     base.OnStart(args);
     ProxyEventLog.WriteEntry("ProxyMonitor Started");

     Thread = new Thread(ThreadWorker);
     Thread.Start();
 }

Within the ThreadWorker I have ProxyEventLogger.WriteEntry("Main thread entered") which does not get fired.

Mcclary answered 1/2, 2011 at 15:47 Comment(0)
M
150

The OnStart() callback needs to return in a timely fashion, so you'll want to kick off a thread where all your work will be performed. I would recommend adding the following fields to your class:

using System.Threading;
private ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private Thread _thread;

The _thread field will hold a reference to the System.Threading.Thread object you create in the OnStart() callback. The _shutdownEvent field holds a system-level event construct that will be used to signal the thread to stop running on service shutdown.

In the OnStart() callback, create and start your thread.

protected override void OnStart(string[] args)
{
     _thread = new Thread(WorkerThreadFunc);
     _thread.Name = "My Worker Thread";
     _thread.IsBackground = true;
     _thread.Start();
}

You need a function named WorkerThreadFunc in order for this to work. It has to match the System.Threading.ThreadStart delegate signature.

private void WorkerThreadFunc()
{
}

If you don't put anything in this function, the thread will start up and then immediately shutdown, so you have to put some logic in there that basically keeps the thread alive while you do your work. This is where the _shutdownEvent comes in handy.

private void WorkerThreadFunc()
{
    while (!_shutdownEvent.WaitOne(0)) {
        // Replace the Sleep() call with the work you need to do
        Thread.Sleep(1000);
    }
}

The while loop checks the ManualResetEvent to see if it is "set" or not. Since we initialized the object with false above, this check returns false. Inside the loop, we sleep for 1 second. You'll want to replace this with the work you need to do - monitor proxy settings, etc.

Finally, in the OnStop() callback of your Windows Service, you want to signal the thread to stop running. This is easy using the _shutdownEvent.

protected override void OnStop()
{
     _shutdownEvent.Set();
     if (!_thread.Join(3000)) { // give the thread 3 seconds to stop
         _thread.Abort();
     }
}
Moffit answered 1/2, 2011 at 17:31 Comment(12)
Thanks for that, I Understood it all and now its running as expected :)Mcclary
Glad to help. For what it's worth, I've got a couple of detailed SO tutorials that show (1) how to have your own log in the Event Viewer (#593954) and (2) how to install/uninstall your service without requiring InstallUtil.exe (#1195978).Moffit
Yea thanks so much for your help, I had previously read your first article in my search to victory, if you wish to see my code i posted it here and left a comment of what stage im at. pastebin.com/t8QQzXC9Mcclary
@MattDavis How can I pause and continue the execution of this thread? Calling _shutdownEvent.Set() and _shutdownEvent.Reset() doesn't work.Sycophant
@My-Name-Is, the way the example is organized, the thread will cease execution once _shutdownEvent.Set() is called. That's because the while loop in WorkerThreadFunc() will exit. If you want to pause and later resume that same thread, create another ManualResetEvent object to control that aspect of the thread.Moffit
You may want to call _thread.Interrup() in OnStop() as well in case the thread is sleeping or waiting (see https://mcmap.net/q/242712/-killing-a-net-thread)Carpometacarpus
Thanks for the detailed and easy to understand anwser! Helped me figure out the issue that was hauting my service haha! :)Eoin
@MattDavis Can you have a look at this question? Thanks . #33706209Stith
This is a very good answer. @MattDavis can you just edit Sleep(1000) to Thread.Sleep(1000) in the code.Borden
@Borden Yes, that's what it should have been anyway. I corrected it.Moffit
@MattDavis Thanks for detailed answer. Where I should register for SerialPort.DataReceived event? In WorkerThreadFunction? As SeiralPort listener must run continuously.Karlie
@FaizanMubasher, that is certainly an option. Hard to say for sure without seeing the code.Moffit
D
6

You need to exit your OnStart handler in order for the service controller to realize that your service has actually started. To make it work like you want, you could start a timer that ticks at an interval and processes when it ticks.

Edit:

Try putting a System.Diagnostics.Debugger.Launch() in your OnStart to see what is happening (and put a breakpoint in ThreadWorker). I would recommend wrapping this in #if DEBUG to be sure it doesn't get deployed.

I just also realized that you do not give your Thread a name:

 Thread myThread = new Thread(ThreadWorker);
 myThread.Start();
Dexterdexterity answered 1/2, 2011 at 15:50 Comment(2)
Should there be any return codes, like return 0 or return false, And a thread would be ok as well right ?Mcclary
No; the OnStart event handler returns void. As long as it exits within a reasonable timeframe (about a minute, I think), the service controller will be happy.Dexterdexterity
E
3

Sample code demonstrated using a console app. hope this will help..

 class Program
{
    private static CancellationTokenSource _cancellationTokenSource;
    private static ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
    private static Thread _serviceStartThread;
    private static Thread _serviceStopThread;

    private static int workcounter = 0;
    static void Main(string[] args)
    {

        _cancellationTokenSource = new CancellationTokenSource();
        _serviceStartThread = new Thread(DoWork);
        _serviceStopThread = new Thread(ScheduledStop);
        StartService();
        StopService();
    }

    private static void StartService()
    {
        _serviceStartThread.Start();

    }

    private static void StopService()
    {
        _serviceStopThread.Start();
    }


    /// <summary>
    /// Triggers a cancellation event for stopping the service in a timely fashion.
    /// </summary>
    private static void ScheduledStop()
    {
        while (!_shutdownEvent.WaitOne(0))
        {
            if (workcounter == 10)
            {
                _cancellationTokenSource.Cancel();
            }
        }
    }

    /// <summary>
    /// Represents a long running Task with cancellation option
    /// </summary>
    private static void DoWork()
    {

        while (!_shutdownEvent.WaitOne(0))
        {
            if(!_cancellationTokenSource.Token.IsCancellationRequested)
            {
                workcounter += 1;
                Console.Write(Environment.NewLine);
                Console.Write("Running...counter: " + workcounter.ToString());
                Thread.Sleep(1000);//Not needed, just for demo..
            }
            else
            {
                Console.Write(Environment.NewLine);
                Console.Write("Recieved cancellation token,shutting down in 5 seconds.. counter: " + workcounter.ToString());
                _shutdownEvent.Set();
                Thread.Sleep(5000);//Not needed, just for demo..
            }

        }
    }
}
Exegetics answered 7/4, 2017 at 9:5 Comment(0)
S
2

Certainly not adding a while loop in the OnStart method. This will say to the OS that the service has not started because it wasn't able to exit safely from the OnStart method. I usually create a Timer that is enabled in the OnStart method. Then in the Ticks method, I do call the necessary method in order to get the applicatin to run.

Alternatively, you can do the following:

// The main entry point for the process 
static void Main() 
{ 
    System.ServiceProcess.ServiceBase[] ServicesToRun; 
    ServicesToRun = new System.ServiceProcess.ServiceBase[] { new WinService1() }; 
    System.ServiceProcess.ServiceBase.Run(ServicesToRun); 
} 

For more information about Windows Services, you can get a skeleton example here.

Siouxie answered 1/2, 2011 at 15:56 Comment(0)
S
0

Why don't you create a new project in your solution of the type Windows Service? This sets up all the structures you need to implement, including even handlers for service start/stop events.

Stephens answered 1/2, 2011 at 15:49 Comment(3)
This is partially a training experience and I would prefer to learn the way its done from the bottom.Mcclary
I would argue that even for training (especially for training?) you would do best to learn how VS creates the scaffolding for a Windows Service and then you can learn why it works and how it works. Rather than bottom up, which as you are finding out, is much more frustrating.Stephens
This is true, the fact that point is arguable, but that's not the case here. This application is 60% achieved and maybe on my next application I will start at the Windows Service template.Mcclary
P
0

In my opinion, the most simplest way to address this issue is:

protected override void OnStart(string[] args)
{            
    new Task(() =>
    {
            new ProxyMonitor();                    
    }).Start();    
}
Prut answered 23/1, 2020 at 10:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.