It’s often useful for an application to be able to start automatically when the system starts or to be able to operate with a different set of permissions from the currently logged-in user. Windows services offer these features. In this article, I’ll show you how to create a Windows service with Visual Basic and a little third-party help.
Some key differences
Windows services are a bit different from a traditional Windows application, and you need to keep a few things in mind when developing them:
- Services are meant to be noninteractive and run in the background, even when no user is logged on. If your service displays a dialog or message box, there may be no one there to dismiss it, causing it to hang indefinitely. Report any errors your application generates via an error log.
- If no user account is specified for a service, it operates under the LocalSystem account, which has heavily restricted access to the system registry and network resources and can’t easily share objects with other applications.
- Services are given only a brief period of time to perform any initialization tasks—take too long, and the system will assume your service is not responding.
Trying to do the impossible
Why do we need third-party help? Because writing a service application in VB is basically impossible. The problem is twofold:
- A service always uses at least two execution threads. One belongs to the service’s executable, the other, a handler thread, is used by Windows’ service controller to communicate with the service. As everyone knows (say it with me), VB is not thread-safe.
- In order to support communication with the service controller, a service must be able to accept function callbacks from different execution threads. Although VB has technically supported callbacks since version 5 with the AddressOf keyword, marshalling a callback into VB from another thread is problematic and prone to producing erratic behavior.
As if that’s not enough, straight from Redmond we have, “Microsoft does not currently recommend, and does not support, running Visual Basic applications as Microsoft Windows NT Services because the applications may exhibit unstable behavior when installed and run as Microsoft Windows NT Services.” You can substitute any flavor of Windows you want into that statement and it’ll still be true. That’s about the most forceful “don’t do this” answer Microsoft will give.
I’m all for doing things I’ve been told not to do, so we’re not going to let that stop us. Obviously, though, we are going to need some help to be able to pull this off. There are presently three options: the SrvAny.exe utility, an unsupported but free Microsoft workaround, and Desaware’s NT Service Toolkit.
SrvAny.exe was the original solution to the VB as Windows service conundrum, provided with the original Windows NT 4 Resource Kit. It is essentially a very simple service that can be made to spawn a normal application when started and take care of shutting down that application when the service is stopped. This is a crude solution. The spawned application has no way of receiving messages from the service controller, and when the service is stopped, SrvAny.exe unceremoniously kills the spawned app’s process. That means no shutdown or cleanup code in the VB application could be reasonably expected to run. Certainly, we can find something better.
Some time ago, Microsoft published a technical article containing the C++ source code for an ActiveX control, NtSvc.ocx, which could allow a VB application to operate as a service. This control provides a much better alternative to SrvAny.exe for basic service functionality. To compile the source, you’ll need Visual C++ with the Unicode MFC libraries installed. Since it’s unsupported, documentation for the control is nonexistent, although you do get a sample VB project to get you started.
To use the control, drop it onto a VB form, write code to handle the control’s Start and Stop events, provide a facility to call the Install and Uninstall methods, and enter unique service and display names for your service. At this point, you’ll have a basic service that can initialize itself when the operating system starts, run without interruptions across logins by different users, and automatically shut down when the operating system is halted.
The control provides properties for supplying a user ID and password if you’d like your service to operate with a specific user account’s security permissions. It also provides pause and continue functionality and a Control event through which messages from the service controller can be received.
I was able to get the control compiled and build a simple ADO-based record-counting service with just a small amount of trouble. There are minor problems with the Control event, which has an illegal VB parameter name. And the Running method, which is supposed to indicate whether the service is running or has been stopped or paused, always returns false. Additionally, when a method call fails, the control provides no error information. Fortunately, the first two problems are easily corrected if you are fairly competent with C++, and error reporting can easily be added if you aren’t afraid of hacking around with AFX dispatch maps.
Regardless of the problems, with a little modification, the NtSvc.ocx is certainly adequate for a simple service solution. It is, after all, free, and it’s infinitely better than the SrvAny.exe solution. But what if you need a more robust solution with some technical support to back you up?
Desaware’s NT Service Toolkit is by far the best option I have seen for implementing a service in Visual Basic. The toolkit consists of a configurable service executable and a set of two class interfaces, one to define and configure your service and a second one you implement in an ActiveX DLL or EXE. The service executable does all the dirty work, communicating with the service controller and passing along any controller messages to your service component through those defined interfaces.
Desaware’s demo comes with six sample programs and is fully functional except that it supports only one service at a time. Using the demo, I was easily able to implement my record-counter service. Doing so took a bit more code than did the NtSvc.ocx implementation, but I wound up with a more robust service capable of responding to a wider variety of events. I was also able to implement a simple ActiveX object server using the demo.
Evaluating the lineup
Of the three solutions we looked at in this article, Desaware’s NT Service Toolkit would be my first choice. It’s the most complete solution, making it easy to build truly complex Windows services with Visual Basic. The NtSvc.ocx is a basic solution useful for experimentation or for a simple service, while SrvAny.exe should really be avoided and used only as a last result.