How to add a timer to C# Service

%3CLINGO-SUB%20id%3D%22lingo-sub-2942185%22%20slang%3D%22en-US%22%3EHow%20to%20add%20a%20timer%20to%20C%23%20Service%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2942185%22%20slang%3D%22en-US%22%3E%3CP%3EHi%20everyone%3CBR%20%2F%3EI%20am%20new%20to%20coding%20in%20c%20%23%20and%20trying%20to%20write%20a%20windows%20service%20in%20c%20%23%2C%20my%20service%20seems%20to%20be%20working%20fine%20except%20for%20one%20thing%3A%3CBR%20%2F%3EI%20try%20to%20make%20it%20run%20in%20a%20loop%2C%20say%20every%2030%20seconds%2C%20another%20program.%3CBR%20%2F%3EBut%20for%20the%20moment%20when%20I%20launch%20my%20service%2C%20it%20executes%20the%20program%20only%20once.%3C%2FP%3E%3CP%3EMy%20code%3A%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Eusing%20System%3B%0Ausing%20System.Diagnostics%3B%0Ausing%20System.ServiceProcess%3B%0Ausing%20System.Threading%3B%0Ausing%20System.Timers%3B%0Ausing%20Timer%20%3D%20System.Timers.Timer%3B%0A%0Anamespace%20ScheduleService%0A%7B%0A%20%20%20%20public%20partial%20class%20ScheduleService%20%3A%20ServiceBase%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20private%20static%20readonly%20object%20lockobject%20%3D%20new%20object()%3B%0A%20%20%20%20%20%20%20%20private%20Timer%20_timer%3B%0A%0A%20%20%20%20%20%20%20%20private%20static%20void%20Main(string%5B%5D%20args)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20var%20service%20%3D%20new%20ScheduleService()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(Environment.UserInteractive)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20service.OnStart(args)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.WriteLine(%22Press%20any%20key%20to%20stop%20the%20program%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Console.Read()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20service.OnStop()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20else%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Run(service)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20protected%20override%20void%20OnStart(string%5B%5D%20args)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer%20%3D%20new%20Timer()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer.Enabled%20%3D%20true%3B%20%20%2F%2F%20call%20the%20Elapsed%20event%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer.Interval%20%3D%205000%3B%20%20%2F%2F%20in%20milliseconds%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer.AutoReset%20%3D%20true%3B%20%20%2F%2F%20keep%20calling%20elapsed%20method.%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer.Elapsed%20%2B%3D%20_timer_Elapsed%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20_timer.Start()%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20private%20void%20_timer_Elapsed(object%20sender%2C%20ElapsedEventArgs%20e)%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20lock%20(lockobject)%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_timer.Stop()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ExecuteStep()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_timer.Start()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20private%20void%20ExecuteStep()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Debug.Print(%22Executing...%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20Thread.Sleep(1000)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20Debug.Print(%22Done.%22)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20protected%20override%20void%20OnStop()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%7D%0A%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EHelp%20will%20be%20welcome%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-LABS%20id%3D%22lingo-labs-2942185%22%20slang%3D%22en-US%22%3E%3CLINGO-LABEL%3E.NET%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3E.Net%20Core%3C%2FLINGO-LABEL%3E%3CLINGO-LABEL%3E.NET%20Framework%3C%2FLINGO-LABEL%3E%3C%2FLINGO-LABS%3E%3CLINGO-SUB%20id%3D%22lingo-sub-2942220%22%20slang%3D%22en-US%22%3ERe%3A%20How%20to%20add%20a%20timer%20to%20C%23%20Service%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2942220%22%20slang%3D%22en-US%22%3E%3CP%3EPlease%20post%20only%20%3CSTRONG%3Erelevant%3C%2FSTRONG%3E%20code%20next%20time.%20The%20only%20relevant%20part%20is%20lines%20177-188%2C%20the%20rest%20is%20irrelevant%20to%20your%20question.%3CBR%20%2F%3E%3CBR%20%2F%3EYour%20timer%20gets%20disposed%20when%20main%20is%20exited%3B%20you%20need%20to%20pull%20it%20(it%20being%20the%20timer%20variable)%20out%20of%20main%20and%20make%20it%20a%20class%20member%20(so%20make%20it%20a%20private%20field%20on%20Service_PSTest%20for%20example)%20or%20find%20another%20way%20to%20keep%20the%20instance%20'alive'.%3C%2FP%3E%3C%2FLINGO-BODY%3E%3CLINGO-SUB%20id%3D%22lingo-sub-2942238%22%20slang%3D%22en-US%22%3ERe%3A%20How%20to%20add%20a%20timer%20to%20C%23%20Service%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2942238%22%20slang%3D%22en-US%22%3E%3CP%3EI%20can't%20build%20a%20test%20right%20now%2C%20but%20my%20first%20thought%20is%20that%20the%20issue%20is%20that%20your%20Main%20is%20completing%20(there's%20nothing%20holding%20the%20main%20thread).%20*%3C%2FP%3E%3CP%3EInstead%20of%20an%20event-based%20timer%2C%20why%20not%20use%20Task-based%20asynchronous%20code.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3ENot%20tested%2C%20but%20something%20like%3A%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-csharp%22%3E%3CCODE%3Easync%20static%20Task%20Main()%0A%7B%0A%20%20%20%20while%20(isRunning)%20%2F%2F%20A%20token%20or%20flag%20to%20hold%20the%20application%20running%20until%20the%20service%20is%20stopped%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%2F%2F%20do%20work%0A%0A%20%20%20%20%20%20%20%20await%20Task.Delay(sleepTimeInMs)%3B%0A%20%20%20%20%7D%0A%7D%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAlso%2C%20your%20OnStart%20and%20OnStop%20methods%20are%20extremely%20similar%3B%20I%20would%20look%20at%20taking%20the%20shared%20code%20to%20a%20function%20(e.g.%2C%20runPowershellScript).%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E*%20RobIII%20is%20definitely%20more%20correct%20on%20this%20point.%20The%20issue%20is%20that%20your%20Timer%20is%20no%20longer%20referenced%20at%20the%20end%20of%20Main%2C%20so%20is%20being%20collected.%3C%2FP%3E%3CP%3EI%20assume%20the%20service%20continues%20to%20say%20it's%20running%2C%20as%20it's%20life-cycle%20managed%20by%20SCM.%3C%2FP%3E%3CP%3ERegardless%2C%20my%20suggestion%20is%20one%20way%20to%20solve%20it.%3C%2FP%3E%3C%2FLINGO-BODY%3E
Occasional Contributor

Hi everyone
I am new to coding in c # and trying to write a windows service in c #, my service seems to be working fine except for one thing:
I try to make it run in a loop, say every 30 seconds, another program.
But for the moment when I launch my service, it executes the program only once.

My code: 

 

 

 

 

using System;
using System.Runtime.InteropServices;
using System.Diagnostics; // SET STATUS
using System.ComponentModel;// SET STATUS
using System.ServiceProcess;
using System.Timers;
//using System.ServiceProccess;// SET STATUS

public enum ServiceType : int
{                                       // SET STATUS [
    SERVICE_WIN32_OWN_PROCESS = 0x00000010,
    SERVICE_WIN32_SHARE_PROCESS = 0x00000020,
};                                                                    // SET STATUS ]

public enum ServiceState : int
{                                      // SET STATUS [
    SERVICE_STOPPED = 0x00000001,
    SERVICE_START_PENDING = 0x00000002,
    SERVICE_STOP_PENDING = 0x00000003,
    SERVICE_RUNNING = 0x00000004,
    SERVICE_CONTINUE_PENDING = 0x00000005,
    SERVICE_PAUSE_PENDING = 0x00000006,
    SERVICE_PAUSED = 0x00000007,
};                                                                    // SET STATUS ]

[StructLayout(LayoutKind.Sequential)]                                 // SET STATUS [
public struct ServiceStatus
{
    public ServiceType dwServiceType;
    public ServiceState dwCurrentState;
    public int dwControlsAccepted;
    public int dwWin32ExitCode;
    public int dwServiceSpecificExitCode;
    public int dwCheckPoint;
    public int dwWaitHint;
};                                                                    // SET STATUS ]

public enum Win32Error : int
{ // WIN32 errors that we may need to use
    NO_ERROR = 0,
    ERROR_APP_INIT_FAILURE = 575,
    ERROR_FATAL_APP_EXIT = 713,
    ERROR_SERVICE_NOT_ACTIVE = 1062,
    ERROR_EXCEPTION_IN_SERVICE = 1064,
    ERROR_SERVICE_SPECIFIC_ERROR = 1066,
    ERROR_PROCESS_ABORTED = 1067,
};

public class Service_PSTest : ServiceBase
{ // PSTest may begin with a digit; The class name must begin with a letter
    private System.Diagnostics.EventLog eventLog;                       // EVENT LOG
    private ServiceStatus serviceStatus;                                // SET STATUS

    public Service_PSTest()
    {
        ServiceName = "Test2";
        CanStop = true;
        CanPauseAndContinue = false;
        AutoLog = true;

        eventLog = new System.Diagnostics.EventLog();                     // EVENT LOG [
        if (!System.Diagnostics.EventLog.SourceExists(ServiceName))
        {
            System.Diagnostics.EventLog.CreateEventSource(ServiceName, "Application");
        }
        eventLog.Source = ServiceName;
        eventLog.Log = "Application";                                        // EVENT LOG ]
        EventLog.WriteEntry(ServiceName, "Test2.exe Test2()");      // EVENT LOG
    }

    [DllImport("advapi32.dll", SetLastError = true)]                      // SET STATUS
    private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);

    protected override void OnStart(string[] args)
    {
        EventLog.WriteEntry(ServiceName, "Test2.exe OnStart() // Entry. Starting script 'c:\\temp\\Test.exe\\Test1.ps1' -SCMStart"); // EVENT LOG
                                                                                                                                     // Set the service state to Start Pending.                        // SET STATUS [
                                                                                                                                     // Only useful if the startup time is long. Not really necessary here for a 2s startup time.
        serviceStatus.dwServiceType = ServiceType.SERVICE_WIN32_OWN_PROCESS;
        serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
        serviceStatus.dwWin32ExitCode = 0;
        serviceStatus.dwWaitHint = 2000; // It takes about 2 seconds to start PowerShell
        SetServiceStatus(ServiceHandle, ref serviceStatus);               // SET STATUS ]
                                                                          // Start a child process with another copy of this script
        try
        {
            Process p = new Process();
            // Redirect the output stream of the child process.
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "PowerShell.exe";
            p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & 'c:\\temp\\Test.exe\\Test1.ps1' -SCMStart"; // Works if path has spaces, but not if it contains ' quotes.
            p.Start();
            // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
            string output = p.StandardOutput.ReadToEnd();
            // Wait for the completion of the script startup code, that launches the -Service instance
            p.WaitForExit();
            if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
            // Success. Set the service state to Running.                   // SET STATUS
            serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;    // SET STATUS
        }
        catch (Exception e)
        {
            EventLog.WriteEntry(ServiceName, "Test2.exe OnStart() // Failed to start 'c:\\temp\\Test.exe\\Test1.ps1' " + e.Message, EventLogEntryType.Error); // EVENT LOG
                                                                                                                                                              // Change the service state back to Stopped.                    // SET STATUS [
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
            Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
            if (w32ex == null)
            { // Not a Win32 exception, but maybe the inner one is...
                w32ex = e.InnerException as Win32Exception;
            }
            if (w32ex != null)
            {    // Report the actual WIN32 error
                serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
            }
            else
            {                // Make up a reasonable reason
                serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
            }                                                               // SET STATUS ]
        }
        finally
        {
            serviceStatus.dwWaitHint = 0;                                   // SET STATUS
            SetServiceStatus(ServiceHandle, ref serviceStatus);             // SET STATUS
            EventLog.WriteEntry(ServiceName, "Test2.exe OnStart() // Exit"); // EVENT LOG
        }
    }

    protected override void OnStop()
    {
        EventLog.WriteEntry(ServiceName, "Test2.exe OnStop() // Entry");   // EVENT LOG
                                                                           // Start a child process with another copy of ourselves
        try
        {
            Process p = new Process();
            // Redirect the output stream of the child process.
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.RedirectStandardOutput = true;
            p.StartInfo.FileName = "PowerShell.exe";
            p.StartInfo.Arguments = "-ExecutionPolicy Bypass -c & 'c:\\temp\\Test.exe\\Test1.ps1' -SCMStop"; // Works if path has spaces, but not if it contains ' quotes.
            p.Start();
            // Read the output stream first and then wait. (To avoid deadlocks says Microsoft!)
            string output = p.StandardOutput.ReadToEnd();
            // Wait for the PowerShell script to be fully stopped.
            p.WaitForExit();
            if (p.ExitCode != 0) throw new Win32Exception((int)(Win32Error.ERROR_APP_INIT_FAILURE));
            // Success. Set the service state to Stopped.                   // SET STATUS
            serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;      // SET STATUS
        }
        catch (Exception e)
        {
            EventLog.WriteEntry(ServiceName, "Test2.exe OnStop() // Failed to stop 'c:\\temp\\Test.exe\\Test1.ps1'. " + e.Message, EventLogEntryType.Error); // EVENT LOG
                                                                                                                                                             // Change the service state back to Started.                    // SET STATUS [
            serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
            Win32Exception w32ex = e as Win32Exception; // Try getting the WIN32 error code
            if (w32ex == null)
            { // Not a Win32 exception, but maybe the inner one is...
                w32ex = e.InnerException as Win32Exception;
            }
            if (w32ex != null)
            {    // Report the actual WIN32 error
                serviceStatus.dwWin32ExitCode = w32ex.NativeErrorCode;
            }
            else
            {                // Make up a reasonable reason
                serviceStatus.dwWin32ExitCode = (int)(Win32Error.ERROR_APP_INIT_FAILURE);
            }                                                               // SET STATUS ]
        }
        finally
        {
            serviceStatus.dwWaitHint = 0;                                   // SET STATUS
            SetServiceStatus(ServiceHandle, ref serviceStatus);             // SET STATUS
            EventLog.WriteEntry(ServiceName, "Test2.exe OnStop() // Exit"); // EVENT LOG
        }
    }

    public static void Main()
    {
        Timer timer = new Timer(); // name space(using System.Timers;) 
        void OnElapsedTime(object source, ElapsedEventArgs e)
        {
            System.ServiceProcess.ServiceBase.Run(new Service_PSTest());
        }

        timer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
        timer.Interval = 5000; //number in milisecinds  
        timer.Enabled = true;
    }
}

 

Help will be welcome

 

2 Replies

Please post only relevant code next time. The only relevant part is lines 177-188, the rest is irrelevant to your question.

Your timer gets disposed when main is exited; you need to pull it (it being the timer variable) out of main and make it a class member (so make it a private field on Service_PSTest for example) or find another way to keep the instance 'alive'.

I can't build a test right now, but my first thought is that the issue is that your Main is completing (there's nothing holding the main thread). *

Instead of an event-based timer, why not use Task-based asynchronous code.

 

Not tested, but something like:

 

async static Task Main()
{
    while (isRunning) // A token or flag to hold the application running until the service is stopped
    {
        // do work

        await Task.Delay(sleepTimeInMs);
    }
}

 

 

Also, your OnStart and OnStop methods are extremely similar; I would look at taking the shared code to a function (e.g., runPowershellScript).

 

* RobIII is definitely more correct on this point. The issue is that your Timer is no longer referenced at the end of Main, so is being collected.

I assume the service continues to say it's running, as it's life-cycle managed by SCM.

Regardless, my suggestion is one way to solve it.