October 24, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Writing a Windows Service in Managed C++

  • December 29, 2003
  • By Kate Gregory
  • Send Email »
  • More Articles »

For years, writing a service meant writing in Visual C++. It was one of those things that C++ programmers could do that VB programmers couldn't. In those days, we just said "service" or maybe "NT service," although that was a little old-fashioned. Well, now they've been christened "Windows service" and are super easy to make in VB .NET or C#. You can find examples anywhere.

But, what if you want to make one in Managed C++? After all, most experienced Visual C++ programmers have written a service or two already, and know how nicely they round out a project. If you have a server that must always be running, over on some remote machine, you don't want to write an instruction manual that tells the client to remember to start the server after every reboot; you make it a service. If you have a handy maintenance component that deletes old records, you don't want the administrator to have to run it once a week; you make it a service. Chances are, you've created a service on a previous project, and you want to create one on some current or near-future project as well.

Creating the Service Project

Everything starts out simply enough: Bring up Visual Studio, create a new project, and under Visual C++ projects, choose Windows Service (.NET). Give the service a sensible name that you'll be able to spot in the list of services on your machine; I called mine CGNotifier. The wizard creates a class that inherits from System::ServiceProcess::ServiceBase and a design view that you could use to drag on a timer, database connection, or other non-visible component. Switch to code view to take a look at the code that was generated.

You get a constructor and a Dispose method, both of which you can safely ignore, and a pair of overrides: OnStart() and OnStop(). In OnStart(), you get things going for your service. One important category of services uses event-raising objects (an instance of System::IO::FileSystemWatcher, for example), in which case you will add event handlers to the class to deal with the events raised by these objects over the life of the service. For these services, you create the objects in OnStart(). Another group of services doesn't react to things that happen, but performs a particular task at a certain time each day or week. These services can sleep all the rest of the day, but if that sleep is continual you won't be able to stop the service. It's better to put them into a tight loop that checks throughout the day to see whether the service has been stopped.

The OnStart() method is supposed to kick off the service and then return. The service won't show as started until the method is complete. This means that your tight loop can't be in OnStart() or in any method called from it directly. The most straightforward approach is to set up a separate method and call it on a new thread, like this:

private:
  bool stopping;
  int loopsleep;              //milliseconds
  Threading::Thread* servicethread;
protected:
  /// <summary>
  /// Set things in motion so your service can do its work.
  /// </summary>
    void OnStart(String* args[])
    {
      Threading::ThreadStart* threadStart = 
               new Threading::ThreadStart(this,mainLoop);
      servicethread = new Threading::Thread(threadStart);
      servicethread->Start();
    }
    void mainLoop()
    {
      loopsleep = 1000;       //milliseconds
      stopping = false;
      while (!stopping)
      {
        Threading::Thread::Sleep(loopsleep);
      }
    }

This loop will run until the service is stopped, because OnStop() sets the stopping flag:

void OnStop()
{
  stopping = true;
}

If you increase loopsleep, the service will be less responsive when you try to stop it.

Installing the Service

Although this service doesn't do anything, you can install it, start it, and stop it. To simplify the installation process, add an installer to your project. Switch back to the design view to do so. (If you like, bring up the Properties Window while you're in design view and change the ServiceName property; the wizard tacks WinService onto the end of your project name. It's best to do this before you add the installer, or else you'll need to change the service name in multiple places.) Right-click the design surface and choose Add Installer. This will create a service installer and a service process installer and show them to you in design view, ready for you to set their properties.

If you've been reading about Windows Services in .NET documentation, you may wonder why you have to add an installer. Don't you get one automatically? You do if you're working in VB or C#; you don't when you're working in C++.

The service process installer has only one property of interest: the account under which the service will run. Click serviceProcessInstaller1 to select it, and then open the Properties window. By default, the Account property is User, which means that the installer will prompt for an ID and password when you install the service, and the service will run with that user's privileges. It's more useful to run the service under a system account. There are three: LocalSystem is the only choice if your service is to be installed on a machine that does not run Windows 2003. If you are headed for 2003, LocalService is less privileged, and therefore a better choice. NetworkService allows the service to authenticate to another machine; use it only if you have to. (For example, a service that just loads a Web page or uses a public Web service doesn't need to run as NetworkService because it doesn't need to authenticate to the remote machine.)

The property you care about on the service installer is StartType: manual, automatic, or disabled. Leave it at manual for this example.

At this point, you can build the service and you're ready to install it. Open a Visual Studio command prompt and navigate to the Debug folder of the project. Now, pretend for a moment that you've been reading some more of that VB/C# how-to-write-a-service material and issue this command:

InstallUtil CGNotifier.exe

Here's what will happen:


Microsoft (R) .NET Framework Installation utility
              Version 1.1.4322.573
Copyright (C) Microsoft Corporation 1998-2002.
              All rights reserved.

Exception occurred while initializing the installation:
System.IO.FileLoadException: Unverifiable image 'CGNotifier.exe'
cannot be run.

That's reasonably terrifying, isn't it? Writing verifiable code in C++ used to be completely impossible, and it's still quite difficult. Why would there be a project wizard to create a service and no warning that your code has to be verifiable? At this point you could start to scream and run around the room in panic, but skip that—you don't need to make your service application produce verifiable code.

The code for your service that you've been looking at is in a .h file. Using Solution Explorer, find and open the corresponding .cpp file. You'll discover a main() function hidden in there—a main function that calls InstallUtil for you in a clever way that gets around the whole "verifiable code" problem. Back in the command prompt, install your service like this:

CGNotifier.exe -Install

You'll see the service install, nice and easy.

To test it, bring up Computer Management, expand Services and Applications, and select Services. You should see the new service: Right-click it and choose Start. Once it's started, switch over to Visual Studio and use Server Explorer to look at services: Choose View, Server Explorer, expand your machine name, and expand services. You should see the new service with a small green triangle to indicate it is running.

Right-click the service in Server Explorer and choose Stop. Now, take a look at the Event log (use either Computer Management or Server Explorer; I prefer Server Explorer because the events in the Application log are sorted by source) and you'll see two log entries—one telling you the service was started, and another telling you it was stopped. If you don't want these event log entries generated, use the design view of your service (not the installers) to change the AutoLog property to False.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel