A WPF project running from the CMD Prompt or as a Service and has a GUI

Very often it’s necessary to run scheduled jobs or maintenance tasks once a day, once a month or even every few minutes. This can be easily achieved by creating your own Windows Service. For example if you want to send reminder e-mails to your customers. But wouldn’t it be nice to be able to also call the application from the command line so that it can do all the work and shut down afterwards? There is even a possibility of making a small script that calls all your similar applications, checks if any work has to be done, does the work and then shuts down. Also, you would like to make it possible to check for any work that needs to be done just by clicking on one button? Well, here you can find a very simple example on how all of this can be done.

First we need a class that is derived from ServiceBase. In that class need a public constructor, an OnStart and an OnStop method. For example, we want to make a service that does some usefull work every 4 minutes. An example of the code for the ServiceBase derived class is given next.

 
public class ServiceClass : ServiceBase
{
    protected System.Timers.Timer timer = new System.Timers.Timer(300000));
 
    /// <summary>
    /// Public constructor that initializes the service and
    /// sets its parameters like ServiceName.
    /// </summary>
    public ServiceClass()
    {
        CanPauseAndContinue = true;
        CanShutdown = true;
        ServiceName = "My Service Name";
    }
 
    /// <summary>
    /// Method specifies what code to execute each time a service
    /// is started. Here it configures parameters for the timer
    /// </summary>
    /// <param name="args"></param>
    protected override void OnStart(string[] args)
    {
        timer.Enabled = true;
        timer.AutoReset = true;
        timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    }
 
    /// <summary>
    /// Method specifies what code to execute each time a service
    /// is stopped. Here it configures parameters for the timer. 
    /// </summary>
    protected override void OnStop()
    {
        timer.Enabled = false;
    }
 
    /// <summary>
    /// Method specifies what code to execute each time a timer's
    /// interval is up. Here will be all the code that has to be done
    /// continuously. 
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        //do some work
    }
}

We also need a class that extends the class Installer. In that class we need to define at least the class constructor method. There we create all the installer objects necessary to install the application as a service.

 
[RunInstaller(true)]
public class MyProjectInstaller : Installer
{
    private ServiceInstaller serviceInstaller;
    private ServiceProcessInstaller processInstaller;
 
    /// <summary>
    /// The public constructor that is executed when the
    /// installation is started.
    /// </summary>
    public MyProjectInstaller()
    {
        processInstaller = new ServiceProcessInstaller();
        serviceInstaller = new ServiceInstaller();
 
        processInstaller.Account = ServiceAccount.LocalSystem;
 
        serviceInstaller.StartType = ServiceStartMode.Automatic;
        serviceInstaller.ServiceName = "My Service Name";
 
        Installers.Add(serviceInstaller);
        Installers.Add(processInstaller);
    }   
}

The code given here is the minimal code needed for the service to be installed. In the class constructor we first need to instantiate the installer objects needed for the installation. One installer is an object from the class ServiceProcessInstaller and the other one is an object of the class ServiceInstaller. Then we set up all the information about the service – how will the service start (automatically), on what account will the service run(local system) and the name of the service (My Service Name). The name has to be the same as the name defined in the ServiceBase derived class. In the end we add the two created installers to the collection of installers.
If we want some additional work done on each install or uninstall, it is possible to override the methods OnBeforeInstall and OnBeforeUninstall. For example, if we want the service to be started only if there is a certain parameter, it is possible to add this code :

 
protected override void OnBeforeInstall(System.Collections.IDictionary savedState)
{
    Context.Parameters["assemblypath"] = "\"" + 
        Context.Parameters["assemblypath"] + "\" -service";
        base.OnBeforeInstall(savedState);
}
 
protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
{
    Context.Parameters["assemblypath"] = "\"" + 
        Context.Parameters["assemblypath"]   + "\" -service";
        base.OnBeforeUninstall(savedState);
}

After doing the work required for changing the call to the service, we call the same method from the base class in order to install the service correctly.
After creating the two classes necessary for defining a windows service, we have to define the main application activity. So, what we have to do is add code that will define when the application will be executed as a windows service and when it’ll just do the work once and shutdown.

public partial class App : Application
{
   protected override void OnStartup(StartupEventArgs e)
   {
       base.OnStartup(e);
       string[] commandLineArgs = System.Environment.GetCommandLineArgs();
 
       if(commandLineArgs.Length>1 && commandLineArgs[1].Equals("-someWork"))
       {
         //do some work
       }
 
       else if(commandLineArgs.Length>1 && commandLineArgs[1].Equals("-service")) 
       {
          ServiceBase.Run(new ServiceClass());
       }
       else
       {
          // do some work
       }  
    }
}

This way it is possible to manage for the application to do different work when it’s called in different ways. All the work that the application does (as the service or called from the command line) should be defined in a separate class.
In order to install the new service, open command prompt and type:

installutil ProjectName.exe

where project name is the name of the project with the windows service and it’s installer. In order to uninstall the service type :

installutil /u ProjectName.exe

And that’s it! Your service is ready to be used! Now if we want to be able to start the application with a user interface, it’s necessary to make some changes to the class App in the method OnStartup and we also have to define the class MainWindow which will contain the entire code that needs to be done through the user interface.
In the case described above, the OnStartup class will look similar to this :

public partial class App : Application
{
   protected override void OnStartup(StartupEventArgs e)
   {
       base.OnStartup(e);
       string[] commandLineArgs = System.Environment.GetCommandLineArgs();
 
       if(commandLineArgs.Length>1 && commandLineArgs[1].Equals("-someWork"))
       {
	   //starting the application with the appropriate command arguments will start 
	   //it in console mode
           MainWindow f = new MainWindow();
           f.ConsoleMode = true;
           //do some work
        }
        else if(commandLineArgs.Length>1 && commandLineArgs[1].Equals("-service")) 
        {
	    //start application as a service
            ServiceBase.Run(new ServiceClass());
        }
        else
        {
            //in case there were no command arguments, start the user interface and do    //some work
            MainWindow f = new MainWindow();
            f.ShowDialog();
            this.Shutdown();
        }            
    }
}

In the class Application there is a property ConsoleMode which is used to indicate whether or not the application mode of running is console mode. Property ConsoleMode is defined in the class that defines the user interface, MainWindow. If we are also using some of the methods defined in the class MainWindow even when we start the application from the command line, we have to find a way to indicate that the application should shut down after the work is done. That’s why in the class that describes the user interface we have to add this wherever we want to check if the application needs to be shut down:

if (ConsoleMode)
{
    Dispatcher.Invoke(new Action(() => { Application.Current.Shutdown(); }));
}

And that’s it! Now you have built your first application that can be used as a windows service, with a user interface or called from the command line.




Ähnliche Beiträge


12 Kommentare zu “A WPF project running from the CMD Prompt or as a Service and has a GUI”



  • Mike am 3. April 2012 1:43 pm Uhr

    I’m am trying to get my WPF app which has a MainWindow user interface to run as a windows service using this code. I’ve installed my exe successfully as a service but when I start it – no UI. I’ve tried adding in the ServiceClass:

    protected override void OnStart(string[] args)
    {

    MainWindow f = new MainWindow();
    f.ShowDialog();

    //timer.Enabled = true;
    //timer.AutoReset = true;
    //timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
    }
    But it causes the service to fail on startup. What am I missing?

    Please advise – thanks.


  • Rémy Blättler am 4. April 2012 3:29 am Uhr

    The idea is not that the service has a gui, but that the same app can be started as a service OR with a gui.
    A windows service can never have a gui. In fact, it can’t even interact with a desktop application.

    Hope that clarifies things.


  • gautam am 13. June 2014 2:33 pm Uhr

    Hi Remy,
    Great article and very useful !! A quick question, if I want an app which has a ui but during install it also installs a service which will keep running without interacting with the ui window application (background data upload from folders) , do I need to follow your example only till the installer step ? So to repeat myself, the service and ui don’t communicate but are part of the same application code base, and both the window app and service gets installed in a single installation . Thanks in advance !


  • Rémy Blättler am 16. June 2014 4:03 am Uhr

    Kinda, yes. The idea of this app was to have ONE app that can either be run as a Service or normal with a GUI. If you run it in Service mode, there is no communication between the service and the GUI.


  • Gautam am 17. June 2014 12:41 am Uhr

    Thanks much Remy, will explore and try it out !


  • Alexey am 16. September 2014 7:20 pm Uhr

    Hi Remy,
    I know very little about service, trying your code – it compiles ans installutil adds it to the services list. But it won’t start in AdministrativeTools/Services, gives 2 event log messages:

    1) A timeout was reached (30000 milliseconds) while waiting for the MyWPFService service to connect.

    2) The MyWPFService service failed to start due to the following error:
    The service did not respond to the start or control request in a timely fashion.

    Can you advice what might be the cause?

    Thanks


  • Rémy Blättler am 17. September 2014 4:18 am Uhr

    Difficult to say without anymore info. Does it start otherwise?


  • Alexey am 17. September 2014 4:58 pm Uhr

    Doh, my fault – I was trying to start my service in a wrong manner, like
    sc start MyWPFService -service


  • David Adaskin am 21. May 2015 12:24 pm Uhr

    Hi Remy —
    I’ve implemented the suggestions that you make in this article and it seems to work, mostly.

    The problem I’m having is that from the time I shutdown my service (TaskManager -> Services tab) until the process goes away (as seen in TaskManager -> Processes tab) is 30 seconds.

    Other services shutdown immediately.

    How can I make my service go away more quickly?


  • Rémy Blättler am 22. May 2015 3:09 am Uhr

    Maybe your service is doing something? But maybe that is a better question for Stack Overflow.
    http://stackoverflow.com/questions/16317378/how-to-stop-windows-service-programmaticaly
    Seeing your code would help too.


  • Shay am 5. April 2017 12:03 pm Uhr

    Hi Remy,

    I have followed your guide, executed the ‘installutil’ command successfully and managed to create the service, but i am not able to start it.

    When i try to start it from CMD i get the following error:
    “Cannot start service from the command line or a debugger. A Windows Service must first be installed (using installutil.exe) and then started wuth the ServerExplorer, Windows Services Administrative tool or the NET START command”.

    When i try to start it from the ‘Services’ tool, i get the following error:
    “Windows could not start the — service on Local Computer. Error 1053: The service did not respond to the start or control request in a timely fashion.”

    Do you have any idea what am i doing wrong? I did everything exactly according to your guide.

    Thanks!


  • Rémy Blättler am 9. August 2017 10:39 am Uhr

    Difficult to say…
    Could it be that your app is actually just not starting because something is not right? Are you doing any logging? I would try to do a global catch for all Exceptions and then log those.


Leave a Reply

Your email address will not be published. Required fields are marked *



*