Run programs as a service under WINNT

How can I run a VB5.0 program under NT as a service?? Or is it possible to start a program in the autoexec so it is executed even when your are not logged in???
Who is Participating?
Hope that this MSDN's articale will help you:

      How-To Run Your Application as a Service

by Chris J. Duke (July, 1997)
This article is the first in a new "How-To" series exclusively by Advanced Visual Basic. Please let me know what you would like to see in future articles.
So you've mastered Visual Basic, and now you want to release your latest masterpiece as a commercial application. At the last minute your boss tells you that your app has to run as a service under Windows NT. You tell him, "No problem!", and then you return to you workstation only to find out that your Visual Basic application can't do it. You search the Microsoft Knowledge Base for an answer, you search the net, the newsgroups, and then you wind up here... to the AVB How-To Series on Services. What you thought was impossible is now easier than you ever thought.
This article will focus on the little known secret of how to run a Visual Basic 4.0 or 5.0 application as a service using Windows 95 and Windows NT 3.51 or higher. When your application is run as a service, your application can start prior to login so that your application is running at boot time. This is useful for network applications such as web servers, mail servers or any application that must be running all the time.
Look for a follow-up article coming soon on how to instantly turn your Visual Basic application into a true NT service application simply by dropping a free ActiveX control onto a form.
Windows 95
You may ask yourself, why would you need to run your VB application as a service using Windows 95. You may also ask if Windows 95, like Windows NT, can run applications as services.
Yes, Windows 95 can run an application as a service. The reason why you would do this is to start your application prior to login. This is commonly done for servers such as web or mail servers. Due to Windows 95's lack of security, it is extremely easy to run your application as a service. This is not the case for Windows NT (see below).
Running a VB application using Windows 95 is quite simple, all it really takes is a registry entry. While this is a hell of a lot easier to do in Win95 vs. WinNT, don't think that it's trouble-free. This section will also provide you with an important work-around for a Windows 95 quirk.
To run your application as a service using Windows 95, add a new string value to this registry key:
For example, if your application was called "MicroSpud", just create a string value called "MicroSpud" and enter the full path to your executable into the data field. Follow these steps:

Step 1: Add a new string value

Step 2: Enter the path to your executable

Step 3: All done! Re boot and MicroSpud will start before login!
Windows 95 Tray Icon Quirk
Now remember that "quirk" I mentioned earlier? Well here it is. If your application loads with an icon in the tray, you will want to read this work-around. When your application first loads, you most likely add your icon to the tray as one of your Form_Load or Main() events. If Windows 95 is set to auto logon upon re boot, this problem doesn't appear to exist. However, if a user is forced to login, what will happen is this:
  Windows 95 starts
  Your application starts as a service
  Your application attempts to add its icon to the tray, but fails because there is no tray yet to add it to
  The login dialog appears
  The user enters in their username and password
  The desktop loads
  Your icon does not appear in the tray!
The solution is to repeatedly call the Shell_NotifyIcon() API function until it returns a boolean true value. One way I've found to do this is to add a timer control, which is initially enabled, set at a 5 second interval. Put the Shell_NotifyIcon() call in its event (Timer1_Timer). Once the API call returns True, disable the timer. Voila!

Windows NT
As you just found out, configuring your application run as a service under Windows 95 is a one-step process. NT, however, is a different animal entirely. Some might call it a beast. This method works well on Windows NT 3.51 or higher.
One of the reasons why Windows NT is so much more difficult is because all services under NT are controlled by the Service Control Manager (SCM). While your application's whereabouts still resides in the registry, there's a few more keys which allow it run as a NT service.
In order for any application to be able to be run as a NT service, it must first support multi-threading. This is because the application itself must create a few threads in order to communicate with the SCM. Since Visual Basic does not support threading, VB applications can not run as native NT service applications (VB5 supports "apartment model" threading, which is threading to a certain extent, but it is not real multi-threading.)
Microsoft, realizing that this is a problem not only for Visual Basic applications, but most applications, developed a solution which works extremely well. They developed a small utility which is distributed with the Windows NT Resource Kit, and also available at the bottom of this page. The utility is called SRVANY.EXE and acts as a host (or service "wrapper") for your application. In other words, it becomes the service application which does all of the dirty work and communicates with the SCM for you. When it starts, it looks to the registry to find out where your application is located. If it finds it, it starts your application. The only drawback to using this utility is that when the SCM informs SRVANY to shut down, it uses the TerminateProcess() API to kill your VB application. This is a very crude way of shutting down an application.
The configuration of your application as a NT service is straight forward, but not nearly as easy as Windows 95. Before I show you the step-by-step instructions, it is important to understand what is going on.
There are two programs Microsoft developed which pulls this trick off. The first, as I just mentioned, is SRVANY.EXE. SRVANY is a program which resides in your WINNT\SYSTEM32 folder, or the folder where your application resides. While in the end you may want to think of your application as a NT service, it's really this little gem that is the true service. When your service starts, it actually starts SRVANY, which starts your application.
The second program Microsoft developed is called INSTSRV.EXE. This program installs SRVANY as a NT service. It is a command-line driven program, and once used is no longer needed unless you wish to add another service which uses SRVANY, or you wish to un-install a previously added service. Keep it handy!
While the documentation provided with SRVANY does a good job detailing the steps involved in its use, here's a visual step-by-step guide to installing SRVANY based on a fictional application called "MicroSpud":
  You are logged in as an Administrator. You cannot install a service otherwise
  Your VB application, MicroSpud, resides here: c:\Program Files\MicroSpud\mspud.exe
  You've installed both SRVANY.EXE and INSTSRV.EXE into MicroSpud's folder

Step 1: Install SRVANY.EXE as a service using INSTSRV.EXE

Step 2: Verify that your service got added by bringing up your services applet (control panel)

Step 3: Configure your service's settings (defaults shown)
At this point, your service has been created. However, if you were to try to start it now, it would fail. You next have to tell SRVANY where your VB application resides, so it can start it when the SCM starts SRVANY. Follow these steps to complete the installation of your new service:

Step 1: Add a "Parameters" key:
1. Start your registry editor
2. Find your service under
3. Create a "Parameters" key

Step 2: Add an "Application" value:
1. Open the "Parameters" key
2. Create an "Application" string value (REG_SZ) within the "Parameters" key
3. Edit "Application" and specify the full path of your app executable (including the extension)

Step 3: Add optional values?
While not necessary, you may wish to add optional values to the "Parameters" key
1. AppParameters (String) - Specify any parameters for your application
2. AppDirectory (String) - Specify the current directory to use for your application
Congratulations! You have just successfully turned your Visual Basic application into a Windows NT service. At this point, you are probably anxious to test it out. So, shutdown NT and restart it! Your application will start prior to login and be already running long before your desktop appears.
There are some other bits of information you should know about SRVANY and your new NT service application: (Note that MicroSpud is used throughout the examples for consistency)
  The registry values are not case sensitive
  To un-install your service, use INSTSRV.EXE, eg: INSTSRV MicroSpud REMOVE
  There are three ways to start your service:
1. From the Services applet of the Control Panel
2. Using the SC.EXE utility, eg: SC start MicroSpud
3. Using the NET.EXE utility, eg: NET START MicroSpud
  There are three ways to stop your service:
1. From the Services applet of the Control Panel
2. Using the SC.EXE utility, eg: SC stop MicroSpud
3. Using the NET.EXE utility, eg: NET STOP MicroSpud
  When the service is stopped, it terminates the application via the WIN32 TerminateProcess() API. This is a drastic way to end an application. For example, it would not allow the application to prompt the user to save changes. Therefore, it is recommended to close the application before stopping the service.
  You may install SRVANY.EXE several times with different registry parameters (i.e. running different target applications) - just use a distinct service name for each instance (e.g. MicroSpud1, MicroSpud2 etc.)
If your VB application interacts with the desktop, be sure to configure it to do so by clicking the Settings button for your service. You should also be aware of these programming considerations. Note that in order to trap the messages mentioned, you will need to use a utility such as SpyWorks from Desaware.
  For WIN32 graphical applications: when the currently logged-in user is logging-off, all WIN32 top-level windows receive WM_QUERYENDSESSION and WM_ENDSESSION messages. Some WIN32 applications choose to terminate upon receipt of such messages. In order for your WIN32 application to survive logoff, it must not do that: instead, your windows procedure should call the default windows procedure on these messages.
  For WIN32 Console (i.e. character-mode) applications: when the currently logged-in user is logging-off, all Console applications receive a CTRL_LOGOFF_EVENT event from the Console.
  If your Console application has registered a Console event handler (via SetConsoleCtrlHandler), it must ignore CTRL_LOGOFF_EVENT in order to survive the logoff.
Microsoft also advises you of the following:
  If SRVANY fails to start your application, try specifying as current directory the directory where your application is installed (see "AppDirectory" registry key above). SRVANY may be running under an account different than the currently logged-on user therefore environment variables may be set differently: as a result, for example, the system might have been unable to find a DLL required for your application and running it from the application's directory might help.
  Due to a restriction enforced by Windows NT on services, the application can either be interactive (have a Console, read keyboard input etc.) or have network access (not both at the same time).
Service Dependencies
An undocumented feature for SRVANY (which applies to any service, actually) you should know about is the "DependOnService" value. Suppose your service depends on other services. How does your service know that other services are available when your service starts? For example, let's say your service is a web server. You will want to make sure that TCP/IP is available before your service starts.
This binary value can be added to your service under the service key, along with the other values such as "DisplayName", "ObjectName", etc. Take a look at the following screen shot. It shows how I added a dependency for TCP/IP and the Event Log to my MicroSpud service. To find the name of the service to enter into the dialog box, use the key name from the registry for the service. For example, the key name under HKLM\SYSTEM\CurrentControlSet\Services for TCP/IP is "Tcpip", or for the event log you would enter "EventLog". For specifying more than one dependency, use 00 as a delimiter. This will show up as a period (.) in the ASCII display.

Windows 95 and Windows NT Services
This article focused on configuring your Visual Basic 4.0 or 5.0 application to run as a service using Windows 95 and Windows NT 3.51 or higher. With the release of Visual Basic 5.0 came new capabilities never available for VB programmers, however you still cannot create native NT services. According to Desaware's advertisements, their new SpyWorks 5.0 product allows you to bypass SRVANY and create true Visual Basic NT services. I'll review this product (as well as a drop-in service OCX) and update this article in the future.
I hope you found this information useful. It is provided for free to readers of the Advanced Visual Basic web site ( Reproduction in whole or part is prohibited. Your comments and feedback are welcome, however I'm afraid that I cannot provide technical support for SRVANY and INSTSRV. Please contact Microsoft technical support or search their knowledge base for help.
Download SRVANY
To download SRVANY.EXE, INSTSRV.EXE, and supporting documentation, click the following download link. It is the latest version at the time this article was written (Q2 1997) and supports Windows NT 3.51 and Windows NT 4.0.

See Q175948 :INFO: Running Visual Basic Applications as Windows NT Services
INFO: Running Visual Basic Applications as Windows NT Services

Last reviewed: October 30, 1997
Article ID: Q175948 The information in this article applies to:

•Microsoft Visual Basic Learning, Professional, and Enterprise Editions for Windows, version 5.0 •Microsoft Visual Basic Standard, Professional, and Enterprise Editions for Windows, version 4.0


Microsoft does not currently recommend 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. Microsoft Visual Basic 4.0 does not support Callbacks nor is it thread-safe. While Visual Basic 5.0 is apartment-model thread-safe, there is no way to marshal calls back into a Visual Basic 5.0 program through the AddressOf operator. This behavior is by design. This article includes some examples of this unstable behavior, a short explanation of the possible causes of the instability, and a possible workaround.


Three Examples of Unstable Behavior

1.When a Microsoft Visual Basic application displays a dialog and then terminates, the name of the .EXE file disappears from the Services applet dialog, but the Visual Basic application dialog leaves behind a gray rectangle on the user interface.

2.When the Microsoft Visual Basic application is launched from a service, the .EXE file name briefly appears in the Services applet dialog but the Visual Basic application does not seem to launch.

3.Microsoft Visual Basic applications that are OLE Servers will return read-only properties successfully and, when the OLE Server terminates, the .EXE file name disappears from the Services applet dialog. Yet, subsequent attempts to instantiate that OLE Server fail with OLE automation errors or the Services process will hang.

Why This Unstable Behavior Occurs

A service in Microsoft Windows NT is a program that is written as a console application and runs whenever the operating system is running. Services are commonly employed to provide facilities such as directory replication, system activity auditing, process monitoring, or communications support.

Services log on to Microsoft Windows NT under the LocalSystem account, which is a predefined local account used by system processes.

A service that runs in the context of the LocalSystem account inherits these characteristics:

•The service cannot open the registry key HKEY_CURRENT_USER. •The service can open the registry key HKEY_LOCAL_MACHINE\SECURITY. •The service has limited access to network resources, such as shares and pipes, because it has no credentials and must connect using a null session. •If the service opens a command window and runs a batch file, the user could hit CTRL+C to terminate the batch file and gain access to a command window with LocalSystem permissions. •The service cannot share objects with other applications unless they are opened using a DACL, which allows a user or group of users access, or NULL DACL, which allows everyone access. Specifying a NULL DACL is not the same as specifying NULL, which means that access is only granted to applications with the same security context. •Service programs that require anything more than a brief moment to perform necessary initialization may be required to create another thread to do the common initialization while the main thread continues with processing.

By convention, Microsoft Windows NT[ASCII 153] Services are designed to be non-interactive. That is, in most cases, they do their work without any user interaction. Services are usually configured and controlled with an applet installed in the Control Panel. This requires that the programmer add code to provide for these additional requirements:

•Report warnings and errors in the system or application logs. You typically can't use output to the screen because there may be no user logged on. •Control the service through a separate application or through a control Panel applet. This involves implementing a communication mechanism for your service. •Install and remove the service from the system.

However, with Microsoft Visual Basic 4.0, the possibility remains that your application will eventually post an unhandled error dialog from the Services process which will hang the Microsoft Visual Basic application.

The Unattended EXE compile option available with Microsoft Visual Basic 5.0 does address the unexpected dialog problem, but thread safety remains an issue. Visual Basic 5.0 is apartment-model thread-safe unless or until a program uses the AddressOf operator. There is currently no way for Visual Basic to marshal multiple calls back into a Visual Basic 5.0 application through AddressOf. Therefore, it is not currently recommended that you install a Microsoft Visual Basic application as a Microsoft Windows NT Service.


The Microsoft Technical Article, "NT Service: An OLE Control for Creating Windows NT Services in Visual Basic," describes an OLE Control that enables developers to create Visual Basic applications that function as Microsoft Windows NT services. With the NTService control, you can install a service, log events, and respond to start, stop, pause, and continue events.

Other Technologies

Developers can expect difficulties with efforts to employ Microsoft technologies such as MAPI, ODBC, DCOM, OLE Automation, and DAO in a Microsoft Windows NT Service written in Microsoft Visual Basic. For instance, MAPI contains user interface elements that must be anticipated and suppressed, and may require access to parts of the registry that are either unavailable to a service or require a special security context for use.

For this reason, and those already noted, Microsoft advises developers to avoid using these technologies in a Microsoft Windows NT Service written in Microsoft Visual Basic.

Microsoft Visual Basic 5.0

While it is possible to write a Microsoft Windows NT service as an unattended .EXE in Microsoft Visual Basic 5.0 (as the following code sample demonstrates), it is still not advisable to implement services because Microsoft Visual Basic 5.0 is not thread-safe and the possibility still remains that an unexpected dialog may post from within the Visual Basic run- time environment.

This example provides for installing and uninstalling itself as a service by running the .EXE with a command line parameter as shown here:

   MyService.exe install
   MyService.exe uninstall

After installing the service, you will need to configure it using the Control Panel Services applet. From the applet, you can start, stop, and pause the service. You can also set it up to start automatically during boot-up.

Add the following code to a .BAS module:

   Option Explicit

   Private Const SERVICE_WIN32_OWN_PROCESS = &H10&
   Private Const SERVICE_WIN32_SHARE_PROCESS = &H20&

   Private Const SERVICE_ACCEPT_STOP = &H1

   Private Const SC_MANAGER_CONNECT = &H1
   Private Const SC_MANAGER_LOCK = &H8

   Private Const SERVICE_QUERY_CONFIG = &H1
   Private Const SERVICE_CHANGE_CONFIG = &H2
   Private Const SERVICE_QUERY_STATUS = &H4
   Private Const SERVICE_START = &H10
   Private Const SERVICE_STOP = &H20
   Private Const SERVICE_PAUSE_CONTINUE = &H40
   Private Const SERVICE_INTERROGATE = &H80
                                       SERVICE_QUERY_CONFIG Or _
                                       SERVICE_CHANGE_CONFIG Or _
                                       SERVICE_QUERY_STATUS Or _
                                       SERVICE_ENUMERATE_DEPENDENTS Or _
                                       SERVICE_START Or _
                                       SERVICE_STOP Or _
                                       SERVICE_PAUSE_CONTINUE Or _
                                       SERVICE_INTERROGATE Or _

   Private Const SERVICE_DEMAND_START As Long = &H3

   Private Const SERVICE_ERROR_NORMAL As Long = &H1

   End Enum

   Private Enum SERVICE_STATE
   End Enum

      lpServiceName As String
      lpServiceProc As Long
      lpServiceNameNull As Long
      lpServiceProcNull As Long
   End Type

   Private Type SERVICE_STATUS
      dwServiceType As Long
      dwCurrentState As Long
      dwControlsAccepted As Long
      dwWin32ExitCode As Long
      dwServiceSpecificExitCode As Long
      dwCheckPoint As Long
      dwWaitHint As Long
   End Type

   Private Declare Function StartServiceCtrlDispatcher _
      Lib "advapi32.dll" Alias "StartServiceCtrlDispatcherA" _
      (lpServiceStartTable As SERVICE_TABLE_ENTRY) As Long
   Private Declare Function RegisterServiceCtrlHandler _
      Lib "advapi32.dll" Alias "RegisterServiceCtrlHandlerA" _
      (ByVal lpServiceName As String, ByVal lpHandlerProc As Long) _
      As Long
   Private Declare Function SetServiceStatus _
      Lib "advapi32.dll" (ByVal hServiceStatus As Long, _
      lpServiceStatus As SERVICE_STATUS) As Long
   Private Declare Function OpenSCManager _
      Lib "advapi32.dll" Alias "OpenSCManagerA" _
      (ByVal lpMachineName As String, ByVal lpDatabaseName As String, _
      ByVal dwDesiredAccess As Long) As Long
   Private Declare Function CreateService _
      Lib "advapi32.dll" Alias "CreateServiceA" _
      (ByVal hSCManager As Long, ByVal lpServiceName As String, _
      ByVal lpDisplayName As String, ByVal dwDesiredAccess As Long, _
      ByVal dwServiceType As Long, ByVal dwStartType As Long, _
      ByVal dwErrorControl As Long, ByVal lpBinaryPathName As String, _
      ByVal lpLoadOrderGroup As String, ByVal lpdwTagId As String, _
      ByVal lpDependencies As String, ByVal lp As String, _
      ByVal lpPassword As String) As Long
   Private Declare Function DeleteService _
      Lib "advapi32.dll" (ByVal hService As Long) As Long
   Declare Function CloseServiceHandle _
      Lib "advapi32.dll" (ByVal hSCObject As Long) As Long
   Declare Function OpenService _
      Lib "advapi32.dll" Alias "OpenServiceA" _
      (ByVal hSCManager As Long, ByVal lpServiceName As String, _
      ByVal dwDesiredAccess As Long) As Long

   '** Change SERVICE_NAME as needed
   Private Const SERVICE_NAME As String = "MyService"

   Private hServiceStatus As Long
   Private ServiceStatus As SERVICE_STATUS

   Sub Main()
      Dim hSCManager As Long
      Dim hService As Long
      Dim ServiceTableEntry As SERVICE_TABLE_ENTRY
      Dim b As Boolean
      Dim cmd As String

      cmd = Trim(LCase(Command()))
      Select Case cmd
         Case "install"                      'Install service on machine
            hSCManager = OpenSCManager(vbNullString, vbNullString, _
            hService = CreateService(hSCManager, SERVICE_NAME, _
                       SERVICE_NAME, SERVICE_ALL_ACCESS, _
                       SERVICE_WIN32_OWN_PROCESS, _
                       App.Path & "\" & App.EXEName, vbNullString, _
                       vbNullString, vbNullString, vbNullString, _
            CloseServiceHandle hService
            CloseServiceHandle hSCManager
         Case "uninstall"                   'Remove service from machine
            hSCManager = OpenSCManager(vbNullString, vbNullString, _
            hService = OpenService(hSCManager, SERVICE_NAME, _
            DeleteService hService
            CloseServiceHandle hService
            CloseServiceHandle hSCManager
         Case Else                                    'Start the service
            ServiceTableEntry.lpServiceName = SERVICE_NAME
            ServiceTableEntry.lpServiceProc = _
                                           FncPtr(AddressOf ServiceMain)
            b = StartServiceCtrlDispatcher(ServiceTableEntry)
      End Select
   End Sub

   Sub ServiceMain(ByVal dwArgc As Long, ByVal lpszArgv As Long)
      Dim b As Boolean

      'Set initial state
      ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
      ServiceStatus.dwCurrentState = SERVICE_START_PENDING
      ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP _
                                      Or SERVICE_ACCEPT_PAUSE_CONTINUE _
                                      Or SERVICE_ACCEPT_SHUTDOWN
      ServiceStatus.dwWin32ExitCode = 0
      ServiceStatus.dwServiceSpecificExitCode = 0
      ServiceStatus.dwCheckPoint = 0
      ServiceStatus.dwWaitHint = 0

      hServiceStatus = RegisterServiceCtrlHandler(SERVICE_NAME, _
                       AddressOf Handler)
      ServiceStatus.dwCurrentState = SERVICE_START_PENDING
      b = SetServiceStatus(hServiceStatus, ServiceStatus)

      '** Do Initialization Here

      ServiceStatus.dwCurrentState = SERVICE_RUNNING
      b = SetServiceStatus(hServiceStatus, ServiceStatus)

      '** Perform tasks -- if none exit

      ''** If an error occurs the following should be used for shutting
      ''** down:
      ''   SetServerStatus SERVICE_STOP_PENDING
      ''   Clean up
      ''   SetServerStatus SERVICE_STOPPED
   End Sub

   Sub Handler(ByVal fdwControl As Long)
      Dim b As Boolean

      Select Case fdwControl
            '** Do whatever it takes to pause here.
            ServiceStatus.dwCurrentState = SERVICE_PAUSED
            '** Do whatever it takes to continue here.
            ServiceStatus.dwCurrentState = SERVICE_RUNNING
            ServiceStatus.dwWin32ExitCode = 0
            ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING
            ServiceStatus.dwCheckPoint = 0
            ServiceStatus.dwWaitHint = 0     'Might want a time estimate
            b = SetServiceStatus(hServiceStatus, ServiceStatus)
            '** Do whatever it takes to stop here.
            ServiceStatus.dwCurrentState = SERVICE_STOPPED
            'Fall through to send current status.
         Case Else
      End Select
      'Send current status.
      b = SetServiceStatus(hServiceStatus, ServiceStatus)
   End Sub

   Function FncPtr(ByVal fnp As Long) As Long
      FncPtr = fnp
   End Function


For more information, see the following directory on MSDN:

   \Platform SDK\Windows Base Services\Executables\Services

For more detailed information on "Using MAPI from a Windows NT Service", refer to:

For additional information, please see the following article in the Microsoft Knowledge Base:

   ARTICLE-ID: Q137890
   TITLE     : HOWTO: Create A User-Defined Service

Keywords          : VB4ALL VB4WIN vb5all VBKBEnv VBKBProgramming
Version           : WINDOWS:4.0,5.0
Platform          : WINDOWS
Issue type        : kbinfo
Take a look at for an example.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.