Microsoft & .NETVisual BasicWriting a Windows Service in Managed C++

Writing a Windows Service in Managed C++

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.

Uninstalling the Service

If you installed your service right from the Debug directory, you don’t need to uninstall it while you’re making changes to it. Just stop it, rebuild, and start it again. But, if you want to uninstall it, just go back to the Visual Studio Command prompt, navigate to the Debug directory, and use this command:

CGNotifier.exe -Install /u

The service will disappear from the lists of services in both Server Explorer and Computer Management. However, you may have to refresh the lists to see that it’s happened.

Waking to Do Some Work

Of course, the service I’ve shown you so far doesn’t do anything. To turn it into one of those “wake up at a set time” services, a good first step is to add a configuration file to the project. Mine looks like this:

<configuration>
  <appSettings>
    <add key="runhour" value="22" />
  </appSettings>
</configuration>

As I showed in my previous column, you need to add a post-build step to the project that copies the app.config file into the target directory (Debug or Release) using the application name. The command line to enter for the post-build step is:

copy app.config $(ConfigurationName)$(TargetFileName).config

To read the setting, you can add code to OnStart or to mainLoop(), just before the loop. I prefer to keep OnStart() as empty as possible, so I added these lines to mainLoop():

String* sHour = 
  Configuration::ConfigurationSettings::
                 AppSettings->get_Item("runhour"); 
int runHour = System::Int32::Parse(sHour);
bool rantoday = false;

The loop then looks like this:

stopping = false;
while (!stopping)
{
   if (DateTime::Now.Hour == runHour && !rantoday)
   {
      // do the task
      rantoday = true;
   }
   else
      rantoday = false;
   Threading::Thread::Sleep(loopsleep);
}

You want this code to run only once each day, after we’ve ticked over to the appointed hour, so the rantoday flag flips to true after the service does whatever it’s supposed to do. It flips back to false once we’re in a different hour.

That just leaves the little matter of doing something at the appointed hour. You might root through a database for new records and send subscribers notification that something (a product? a real estate listing? a job posting?) has been added to your system. You might root through a database for old records, or the file system for old files, and delete them. There are more things a service might do than I can list. Whatever the task, you need to tell someone you did it. Because a service doesn’t have a user interface, you can’t just pop up a message box. One elegant way to leave a little trail of breadcrumbs is to use the event log.

These lines before the loop in mainLoop() set up the log:

Diagnostics::EventLog* log ;
if (! Diagnostics::EventLog::SourceExists("CGNotifierService") )
    Diagnostics::EventLog::CreateEventSource("CGNotifierService",
                                              CGNotifierLog");
log = new Diagnostics::EventLog("CGNotifierLog");
log->Source = "CGNotifierService";

While you don’t have to set both the log and the source (and most examples don’t), I like to because then my messages get their own node under Event Logs in Solution Explorer.

To write to the log, you need only one line of code—I put this in the place of the “do the task” comment:

log->WriteEntry("It's run time for the service. Insert work here.",
                Diagnostics::EventLogEntryType::Information);

There you have it: a service that can be installed and uninstalled, stopped and started, and once a day writes a message into the event log. From here, there’s no stopping you. Windows services in C++ can do anything that other .NET applications can do, and as long as you stay away from the VB and C# examples, you won’t get confused by the slightly different spin we C++ types get on service project creation.

About the Author

Kate Gregory is a founding partner of Gregory Consulting Limited (www.gregcons.com). In January 2002, she was appointed MSDN Regional Director for Toronto, Canada. Her experience with C++ stretches back to before Visual C++ existed. She is a well-known speaker and lecturer at colleges and Microsoft events on subjects such as .NET, Visual Studio, XML, UML, C++, Java, and the Internet. Kate and her colleagues at Gregory Consulting specialize in combining software develoment with Web site development to create active sites. They build quality custom and off-the-shelf software components for Web pages and other applications. Kate is the author of numerous books for Que and Sams, including Microsoft Visual C++ .NET 2003 Kick Start.

# # #

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories