Day 12 : 19 June 2022 : Adding Windows Service Logic

My 100 Daze of Code

https://github.com/davidjwalling/100-days-of-code

#12 : Adding Windows Service Logic

Today, we'll add some Win32 API logic in Ava to install, uninstall and run as a service on Windows. We'll add logic in Driver::Service that check if the program was started with the "service" argument as the first program argument. If so, the Service Control Manager will be invoked to start the program as a service. We'll also check for "install" and "uninstall" program arguments and handle these with logic that installs and uninstalls the program, respectively. In a later post, we'll do the equivalent on Linux and macOS and show how to enable Ava to run as a daemon configured in sysctl.

Nowadays there are shortcut methods to install and run an executable as a service on Windows. But I prefer to illustrate the APIs that make this work.

Some Code Refactoring

Up to now we've been using a pointer-to-implementation (pimpl) method for exposing a public interface and keeping the implementation private. Here, we have modified the class structure to illustrate a different approach. The IDriver class has its own header, "idriver.h" that exposes a struct with pure virtual methods. The Driver class derives from IDriver and has its own header, "driver.h", that is not public. While the organization is simpler, this approach does reveal the internals of Driver when run in a debugger.

Installing the Service

To install Ava as a Windows service, simply run with "install" as the first program argument. A registry entry for the service is created and the service is added to the Service Control Manager. Here we'll run in the Visual Studio debugger first.


Instead of declaring an instance if IDriver, we now call a public function, CreateDriver, that will return the address of a singleton instance of IDriver already constructed when the library loads. Note also that Start will now return a boolean true if the program initialization is complete and the program is to be run. However, it will return false if either a startup function failed or if the program is going to be run as a service, in which case we do not need to wait on a "getline" for console input (there will be none). And, the program will continue under control of the Service Control Manager.


The Start method invokes Service, as before. But now, Service checks if the first program argument is "install" or "uninstall" and calls Manage if either specified. Manage will call Install or Uninstall as well as handle opening and closing the service control database.

Note that to install or uninstall a service, Administrative privilege must be held by the current user. Here, we simply ran as a standard user, so the service manager could not be opened. Our error-handling and logging then produces this message and the program terminates.


Now, restarting Visual Studio as Administrator and repeating the installation attempt resolves this error. The output log error reports the service is installed.

We can see the service definition in the Services tool within the Control Panel.

At a command prompt as Administrator, we can use the "sc query" and "net" commands to check our service status and start or stop the service. We can also use "netstat" to confirm that our TCP listener is started when our service is running.


If we run "regedit" we can see the Ava service and description.


Now to uninstall, we change the first program argument to "uninstall". Run as Administrator. Observe that the service is removed from the Services list. For now, though, we will leave the Registry entries intact upon uninstall.






Comments

Popular posts from this blog

Day 5 : 12 June 2022 : CMake on Windows

Day 11: 18 June 2022 : Handling Program Arguments