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


5 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 !


Leave a Reply

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



*