|
To use this utility, you need to:
-
First write/test/debug your application without ntservice. Your application should be working properly as a standalone application (generated by generate-executable or generate-application) before attempting to involve ntservice.
-
Compile ntservice.cl and update your application so that it loads ntservice.fasl.
-
The main function in your application should call ntservice:start-service as soon as possible. ntservice:start-service will be responsible for executing any initialization functions which your program may need. It is also responsible for starting the main loop of your program. NOTE: ntservice:start-service executes the form (exit 0 :no-unwind t :quiet t) when the service is stopped. If you need things to happen before excl:exit is called, use the :stop keyword argument to start-serviceto pass in a function that can do cleanup. See the definition of ntservice:start-service below for more information.
-
Regenerate your application w/ the updated code.
-
Call ntservice:create-service to add your program to the list of Windows services. Usually this would be done by the program that installs your application. See testapp.cl for an example of how to add command-line switches to your program to allow a user to add/remove the service easily.
-
Try it out! Your service should now be listed in the Services control panel applet. Try starting and stopping your service. If it works as planned, you can use the Services control panel applet to make the service start automatically instead of manually.
Other notes:
-
If you want to remove your program from the list of services, load ntservice.fasl and evaluate (ntservice:delete-service name), where name is the name you gave the service in your call to ntservice:create-service. It seems to be possible to request deletion of a running service. This disables the service from further starts and marks it for deletion once it stops. delete-service turns t if the removal was successful, otherwise it returns three values: nil, the Windows error code, and a string with the name of the function that actually failed.
-
The LocalSystem account is very powerful! Be careful of what you allow your program to do. Also note that the LocalSystem account usually does not have access to network filesystems. This may lead to confusion if your service tries to access drive letters that are mapped to network drives, or if it tries to access remote filesystems via UNC names (\\host\\share\file).
-
You can use the Services control panel applet to change who the service runs as. Note that no account but LocalSystem will be able to interact w/ the desktop (i.e., your program's window will be invisible if you don't run as LocalSystem).
See testapp.cl for an example skeleton for a service application.
The definition for ntservice:start-service is as follows:
(defun start-service (main &key init stop)
)
main should be a function (or a symbol naming a function) which constitutes the main loop of your program. This function will be called when the service starts running. No arguments are passed to this function. This function should never return--if it does, Windows will complain that the service terminated prematurely].
The keyword arguments init and stop are optional.
init specifies a function (or a symbol naming a function) that should be executed before the main loop is executed. Such a function might load in configuration settings or verify the system environment. The init function should be prepared to accept a single argument. This argument is the list of "Start parameters" that have been specified for the service. This list is usually empty but can be modified using the Services control panel applet. If the init function returns nil, the service will not be started and an error will be logged and/or reported. Make sure init returns non-nil under normal circumstances.
stop specifies a function (or a symbol naming a function) that should be executed when the service is to be stopped. Such a function might do work that your application needs done before Lisp exits. This function should do its job fairly swiftly, otherwise Windows might complain that the service is not stopping properly. No arguments are passed to this function.
Remember that ntservice:start-service never returns to its caller. It executes the following form to exit your program:
(exit 0 :no-unwind t :quiet t)
The definition for ntservice:create-service is as follows:
(defun create-service (name displaystring cmdline
&key (start :manual))
)
name should be a string that identifies your service. 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.
displaystring should be a string that contains the display name 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.
cmdline should be a string that contains the command line for executing your service program. The first word in the string should be the fully-qualified pathname to the executable.
start can either be :manual or :auto. If :manual, the service must be started and stopped manually. If :auto, the service will start automatically at boot time.
Return values: if create-service is successful, it returns t. If it is not successful, it returns two values: nil and the Windows error code. You can use ntserver:winstrerror to convert the code into a string.
Your service will be created with the following properties:
-
Manual start
-
Run as LocalSystem account
-
Allow program to interact with desktop
|