WinBatch Tech Support Home

Database Search

If you can't find the information using the categories below, post a question over in our WinBatch Tech Support Forum.

TechHome

Tutorials
plus

Can't find the information you are looking for here? Then leave a message over on our WinBatch Tech Support Forum.

Service Scripts

 Keywords:  

WinBatch Script as a 'Native Service'

It is possible to create a compiled WinBatch program that will run as a native service on Windows NT family systems. It requires you have the WinBatch+Compiler software package, because the script *MUST* be compiled.

Why Run as a Service?

Writting the Service Code

I recommend using the following sample service script template to help write the service. This template allows the script to handle any service requests to Start, Stop or Pause a service. For example, this code allows you to handle the user manually stopping the service from the Sevice Control Manager in the Control Panel.

If you want the WinBatch service to wait for a particular time or event to occur, you can use any of the normal WIL methods (TimeDelay, TimeWait, WinWaitExist, etc.). Or use any of the following service functions: SvcSetAccept, SvcSetState, SvcWaitForCmd. You can use loops or branching (For, While, Goto, etc.) to cause the WinBatch service to continue running for an indefinite period of time.

Windows NT native service 'Sample service script template':

;Initialze variables for the SvcSetAccept function
SERVICE_ACCEPT_STOP             =   1    ;The service can be stopped
SERVICE_ACCEPT_PAUSE_CONTINUE   =   2    ;The service can be paused and continued
SERVICE_ACCEPT_SHUTDOWN         =   4    ;The service is notified when system shutdown occurs
SERVICE_ACCEPT_HARDWAREPRFCHG   =   3    ;The computer's hardware profile has changed.
SERVICE_ACCEPT_POWEREVENT       =   64   ;The computer's power status has changed.
SERVICE_ACCEPT_SESSIONCHANGE    =   128  ;The computer's session status has changed (requires XP/2003 or newer.)
SERVICE_ACCEPT_PRESHUTDOWN      =   256  ;The computer is about to shutdown (requires Vista/2008 or newer.)
SERVICE_ACCEPT_LOGOFF           =   32768;The service is notified when user logoff occurs

;Initialize variables for the SvcSetState function
SERVICE_STATE_STOPPED           =   1   ;The service is not running
SERVICE_STATE_STOP_PENDING      =   3   ;The service is stopping
SERVICE_STATE_RUNNING           =   4   ;The service is running
SERVICE_STATE_CONTIN_PENDING  =   5   ;The service continue is pending
SERVICE_STATE_PAUSE_PENDING     =   6   ;The service pause is pending
SERVICE_STATE_PAUSED            =   7   ;The service is paused

;Initialize variables for the SvcWaitForCmd function
SERVICE_CONTROL_NOT_SERVICE     =  -1   ;Script not running as a service
SERVICE_CONTROL_TIMEOUT         =   0   ;Timeout occurred or no codes to process
SERVICE_CONTROL_STOP            =   1   ;Requests the service to stop
SERVICE_CONTROL_PAUSE           =   2   ;Requests the service to pause
SERVICE_CONTROL_CONTINUE        =   3   ;Requests the paused service to resume
SERVICE_CONTROL_SHUTDOWN        =   5   ;Requests the service to perform cleanup tasks, because the system is shutting down
SERVICE_CONTROL_HARDWAREPRFCHG  =   12  ;The computer's hardware profile has changed.
SERVICE_CONTROL_POWEREVENT      =   13  ;The power status has changed.
SERVICE_CONTROL_SESSIONCHANGE   =   14  ;The session status has changed (requires XP/2003 or newer.)
SERVICE_CONTROL_PRESHUTDOWN     =   15  ;The system will be shutting down (requires Vista/2008 or newer.)
SERVICE_CONTROL_USER128         = 128   ;User command 128
SERVICE_CONTROL_USER129         = 129   ;User command 129
SERVICE_CONTROL_USER130         = 130   ;User command 130
SERVICE_CONTROL_USER131         = 131   ;User command 131
;                                       ;More user commands as needed
SERVICE_CONTROL_USER255         = 255   ;User command 255
SERVICE_CONTROL_LOGOFF          = 32768 ;logoff notification

;Setup debugging prompt strings....
debugcodes="0: Timeout|1: Stop|2: Pause|3: Continue|5: Shutdown|128: User Cmd 128|129:User Cmd 129|32768: Logoff"

;Tell system that we want specific notifications
flag=SvcSetAccept( SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_LOGOFF)
If flag== -1
   DoingDebug=@TRUE
   Pause("Debug Mode","Not currently running as a service")
Else
   DoingDebug=@FALSE
   ;Set up error handling
   IntControl(12,2+8,0,0,0)                    ;Tell WinBatch to not honor terminate and
                                               ;not complain on Windows exit
   IntControl(38,1,"errorlog.txt",0,0) ;Route fatal errors to a log file
EndIf

;Now for the main service loop
;in this service we respond to all control messages
;and check an ini file for work every 5 seconds
BoxOpen("Initializing","main service loop")
While @TRUE
   If DoingDebug==@FALSE
      code=SvcWaitForCmd(5000)      ; Timeout in 5 seconds
   Else
       ;For Debugging.  Prompt tester to see what code should be pretended here
       code=AskItemlist("Service Debug",debugcodes,"|",@UNSORTED,@SINGLE)
       If code=="" Then Continue
       code=ItemExtract(1,code,":")
   EndIf

   Switch code
          Case SERVICE_CONTROL_TIMEOUT
               ;Timeout occurred
               BoxTitle("SERVICE_CONTROL_TIMEOUT")
               ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
               GoSub DoWork
               ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
               Break
          Case SERVICE_CONTROL_STOP
               ;Stop command received
               BoxText("Stop command received")
               SvcSetState(SERVICE_STATE_STOP_PENDING)
               ;do stop cleanup work here
               TimeDelay(5)
               SvcSetState(SERVICE_STATE_STOPPED)
               Exit    ;Goodbye
               Break
          Case SERVICE_CONTROL_PAUSE
               ;Pause command received
               BoxText("Pause command received")
               SvcSetState(SERVICE_STATE_PAUSE_PENDING)
               ;do pause cleanup work here
               SvcSetState(SERVICE_STATE_PAUSED)
               Break
          Case SERVICE_CONTROL_CONTINUE
               ;Continue command received
               BoxText("Continue command received")
               SvcSetState(SERVICE_STATE_CONTIN_PENDING)
               ;do resume from pause state initilization here
               SvcSetState(SERVICE_STATE_RUNNING)
               Break
          Case SERVICE_CONTROL_SHUTDOWN
               ;Shutdown notification received
               ;Approx. 20 seconds to process
               BoxText("Shutdown notification received")
               SvcSetState(SERVICE_STATE_STOP_PENDING)
               ;do stop cleanup work here
               SvcSetState(SERVICE_STATE_STOPPED)
               Exit    ;Goodbye
               Break
          Case SERVICE_CONTROL_USER128
               ;User command 128 received
               BoxText("User command 128 received")
               Break
          Case SERVICE_CONTROL_USER129
               ;User command 129 received
               BoxText("User command 129 received")
               Break
          Case SERVICE_CONTROL_LOGOFF
               ;Logoff command received
               BoxText("Logoff command received")
               Break
         Case code
               ;Unrecognized command received
               BoxText("Unrecognized command received")
               Break
      EndSwitch

EndWhile

;Note.  The preceeding loop never exits
Exit   ; Just a formality

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;The DoWork subroutine can be used to execute the code you want to run while
;the service has not control requests
:DoWork
   BoxText("WinBatch Services test is running")
Return

Compiling a Service

Compile the WinBatch script as usual using the 'Small EXE for Networked PC's' option in the WinBatch Compiler, but make sure to specify an output file with an extension of ".EXS" instead of ".EXE". Alternatively, you can simply rename an existing WinBatch program by changing its extension from ".EXE" to ".EXS".

Since you compiled using the Small EXE option, you should copy the necessary dlls: WIL dll and any Extender dlls used by the service script, to the same directory as the service (.EXS) file.

Installing a Service

You can install a WinBatch service (or any service) using the wntSvcCreate function in the WIL Windows NT extender. See the Win32 Network Extender help file for further details.

A WinBatch service can be configured to run automatically on system startup, or manually on demand. In either case, when the WinBatch service starts up it processes the script just like a normal WinBatch program.

Windows NT native service install code (INSTALL_SVC.WBT):

#DefineFunction InstallSvc(SvcKeyName, SvcStringValues, SvcNumValues)
;Parameters:
;[SvcKeyName]
;The actual registry key name of the service to be looked for
;
;[SvcStringValues]
;Is a is a tab-delimited or vertical-bar ('|') delimited list of string properties for the service being created,
;in the following format: "ServiceName | DisplayName | BinaryPathName | LoadOrderGroup | ServiceStartName | Password"
;         ServiceName:
;         String that specifies the name of the service to install. The maximum string length is 256 characters. The service control
;        manager database preserves the case of the characters, but service name comparisons are always case insensitive. Forward-slash
;        (/) and back-slash (\) are invalid service name characters.
;
;         DisplayName:
;         String that is to be used by user interface programs to identify the service.  This string has a maximum length of 256
;        characters.  The name is case-preserved in the service control manager.  Display name comparisons are always case-insensitive.
;
;         BinaryPathName:
;         The fully qualified path to the service binary file.
;
;         LoadOrderGroup:
;         The load ordering group of which this service is a member.  If a blank string, the service does not belong to a group.
;        The registry has a list of load ordering groups located at:
;         HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\ServiceGroupOrder
;         The startup program uses this list to load groups of services in a specified order with respect to the other groups in the list.
;        You can place a service in a group so that another service can depend on the group.  The order in which a service starts is
;        determined by the following criteria:
;         1. The order of groups in the registry's load-ordering group list.  Services in groups in the load-ordering group list are
;           started first, followed by services in groups not in the load-ordering group list and then services that do not belong to a group.
;         2. The service's dependencies listed in the "Dependencies" parameter and the dependencies of other services dependent on the service.
;
;         ServiceStartName:
;         If the service type is SERVICE_WIN32_OWN_PROCESS or SERVICE_WIN32_SHARE_PROCESS, this name is the account name in the form of
;        "DomainName\Username", which the service process will be logged on as when it runs.  If the account belongs to the built-in
;        domain, ".\Username" can be specified. To log on as the LocalSystem account, specify "LocalSystem". If the service type is
;        SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER, this name is the Windows NT driver object name (that is, \FileSystem\Rdr
;        or \Driver\Xns) which the input and output (I/O) system uses to load the device driver. You can specify both a ServiceStartName
;        and a Password together, in the form "ServiceStartName|Password". If ServiceStartName does not contain a '\', then '.\' will
;        automatically be prepended, unless ServiceStartName == "LocalSystem".
;
;         Password:
;         Password to the account name specified by the "ServiceStartName" parameter if the service type is SERVICE_WIN32_OWN_PROCESS
;        or SERVICE_WIN32_SHARE_PROCESS.  If the service type is SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER, this parameter
;        is ignored.
;
;[SvcNumValues]
;A tab-delimited or vertical-bar ('|') delimited list of numeric properties for the service being created, in the
;following format:"ServiceType | StartType | ErrorControl"
;         ServiceType:
;         Service type are the flags to indicate the type of service.  In addition, for a SERVICE_WIN32 service, the SERVICE_INTERACTIVE_PROCESS
;        flag might be set, indicating that the service process can interact with the desktop:
;         Value   Name
;         1      SERVICE_KERNEL_DRIVER
;         2      SERVICE_FILE_SYSTEM_DRIVER
;         16      SERVICE_WIN32_OWN_PROCESS
;         32      SERVICE_WIN32_SHARE_PROCESS
;         In addition to the SERVICE_WIN32 service, the SERVICE_INTERACTIVE_PROCESS flag may be set, indicating that the service process can
;         interact with the desktop: Note: ServiceStartName must be LocalSystem in order to interact with the desktop.
;         256   SERVICE_INTERACTIVE_PROCESS
;
;         StartType:
;         Specifies when to start the service.  One of the following values is specified:
;         Value   Name
;         0      SERVICE_BOOT_START
;         1      SERVICE_SYSTEM_START
;         2      SERVICE_AUTO_START
;         3      SERVICE_DEMAND_START
;         4      SERVICE_DISABLED
;
;         ErrorControl:
;         Specifies the severity of the error if this service fails to start during startup, and determines the action taken by the startup
;        program if failure occurs.  One of the following values can be specified:
;         Value   Name
;         0      SERVICE_ERROR_IGNORE
;         1      SERVICE_ERROR_NORMAL
;         2      SERVICE_ERROR_SEVERE
;         3      SERVICE_ERROR_CRITICAL


IntControl(72,2,0,0,0)
bExists = wntSvcStatus('',SvcKeyName,1000,0)
If bExists
      wntSvcDelete( "", SvcKeyName, 1000)
EndIf
ErrorMode(@OFF)
wntSvcCreate('',SvcStringValues,SvcNumValues,'','')
RC = LastError()
ErrorMode(@CANCEL)
If (RC != 0)
   TempMsg = StrCat('wntSvcCreate("","',SvcStringValues,'",',SvcNumValues,',"","")  RC = ',RC)
   TempMsg = StrCat(TempMsg,@CRLF,@CRLF,'Error!  Unable to create the service.  Aborting service installation...')
   Message("ERROR",TempMsg)
   Return
EndIf
Return

:CANCEL
IntControl(72,2,0,0,0)
Return

#EndFunction

AddExtender("wwwnt34i.dll")

SvcKeyName="My Service Name"
DisplayName="My Service Display Name"
types="EXS Files|*.EXS"
BinaryPathName=AskFilename("Select Compiled Service Script", "C:\", types, "", 1)

LoadOrderGroup=""
ServiceStartName="LocalSystem"
Password=""

;CONSTANTS
SERVICE_WIN32_OWN_PROCESS = 16
SERVICE_INTERACTIVE_PROCESS = 256
SERVICE_AUTO_START  = 2
SERVICE_ERROR_NORMAL = 1

SvcStringValues = StrCat(SvcKeyName,"|",DisplayName,"|",BinaryPathName,"|",LoadOrderGroup,"|",ServiceStartName,"|",Password)
SvcNumValues = StrCat(SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS,@TAB,SERVICE_AUTO_START,@TAB,SERVICE_ERROR_NORMAL)

InstallSvc(SvcKeyName, SvcStringValues, SvcNumValues)
Message("Complete",StrCat("Installed service: ",SvcKeyName))

Uninstalling a Service

You can uninstall/remove a WinBatch service (or any service) using the wntSvcDelete function in the WIL Windows NT extender. See the Win32 Network Extender help file for further details.

Windows NT native service Uninstall code (UNINSTALL_SVC.WBT):

#DefineFunction UnInstallSvc(SvcKeyName)
;Parameters:
;[SvcKeyName]
;The actual registry key name of the service to be looked for

IntControl(72,2,0,0,0)
bExists = wntSvcStatus('',SvcKeyName,1000,0)
If bExists
   ErrorMode(@OFF)
   wntSvcDelete( "", SvcKeyName, 1000)
   RC = LastError()
   ErrorMode(@CANCEL)
   If (RC != 0)
      TempMsg = StrCat('wntSvcDelete("","',SvcKeyName,'",1000)  RC = ',RC)
      TempMsg = StrCat(TempMsg,@CRLF,@CRLF,'Error!  Unable to delete the service.  Aborting service deletion...')
      Message("Error",TempMsg)
      Return
   EndIf
EndIf
Return

:CANCEL
IntControl(72,2,0,0,0)
Return

#EndFunction

AddExtender("wwwnt34i.dll")

SvcKeyName="My Service Name"
UnInstallSvc(SvcKeyName)
Message("Service Uninstall Complete",StrCat("Service: ",SvcKeyName, " is no longer installed"))

Starting and Stopping a Service

Once the service is installed you can start the service either manually in the control panel or with a script using the Win32 Network Extender function wntSvcStart. See the Win32 Network Extender help file for further details.

When processing reaches the end of the script (or a Return or Exit command, or a Cancel event), the WinBatch service will automatically stop. You can force a WinBatch service to stop prematurely by Stopping the service manually in the Service Manager (in Control Panel), or using another service control program (such as the wntSvcControl function in the WIL Windows NT extender). A WinBatch service will exit automatically on Windows shutdown. WinBatch services do not exit when a user logs out.

Sample Code to Start a Service (START_SVC.WBT)

;Starting a service
AddExtender("wwwnt34i.dll")
SvcKeyName="My Service Name"

SERVICE_RUNNING = 4
state = wntSvcStatus('', SvcKeyName, 1000, 2) ;get service status
If state != SERVICE_RUNNING
    wntSvcStart('', SvcKeyName, 1000, "", "") ;start service
EndIf
While state != SERVICE_RUNNING ;loop until service starts again.
   TimeDelay(1)
   ErrorMode(@OFF)
   wntSvcControl('', SvcKeyName, 1000, 4) ;update status in servicemanager
   ErrorMode(@CANCEL)
   state = wntSvcStatus('', SvcKeyName, 1000, 2) ;get service status
EndWhile
Pause(SvcKeyName,"Service should be started now")
Exit

Sample Code to Stop a Service (STOP_SVC.WBT)

;Stopping a service
AddExtender("wwwnt34i.dll")
SvcKeyName="My Service Name"

SERVICE_STOPPED = 1
state = wntSvcStatus('', SvcKeyName, 1000, 2) ;get service status
If state != SERVICE_STOPPED
   wntSvcControl('', SvcKeyName, 1000, 1) ;stop service
EndIf

While state != SERVICE_STOPPED ;loop until service is reported as stopped.
   TimeDelay(1)
   ErrorMode(@OFF)
   wntSvcControl('', SvcKeyName, 1000, 4) ;update status in servicemanager
   ErrorMode(@CANCEL)
   state = wntSvcStatus('', SvcKeyName, 1000, 2) ;get service status
EndWhile
Pause(SvcKeyName, "Service should be stopped now")
Exit

Special Notes:

A native NT service has 2 possible configurations: Those are your choices and they are enforced by the design of WinNT/2K/XP/03.

If you are making a WinBatch service that will NOT be installed as an interactive service, you should use IntControl(38) to prevent WIL from displaying any unexpected error message boxes. If a non-interactive service attempts to interact with the desktop, it can cause the script to hang.

Troubleshooting Service Scripts

Please see the followinging Windows System Journal Article 'Why Do Certain Win32 Technologies Misbehave in Windows NT Services?': http://www.microsoft.com/msj/0398/service2.aspx

The DebugTrace function is very useful when debugging service scripts. See the Windows Interfance Language Help file for details.


Article ID:   W17357
File Created: 2013:04:04:10:21:46
Last Updated: 2013:04:04:10:21:46