How do one write a service for Win32 ?
I have written a caller-id server wich reads a string from a modem
and posts it to a sql database.. Simple routine.. Now i am bored of
starting it manually everytime i restart my win2k server.. On my
linux box i allready run it as a deamon.. But since my friends would
like to have it in win32 env. I am wondering if someone has a
snippet how to build a win32 service and how i shall do to make it
pop up amongst the services..Thanx in advance
Carl Saxmark-- Carl Saxmark, June 26, 2004 09:58 PM (email)
Answers:
I did it. It works fine. To allow easy debuging I put whole functionality in unit with following interface:FUNCTION MyServiceInitialization ( argc : Cardinal ; argv : ppChar ; VAR specificError : Cardinal )
FUNCTION MyServiceStart ( VAR specificError : Cardinal ) : Cardinal ;
PROCEDURE MyServiceStop ;
PROCEDURE MyServicePause ;
PROCEDURE MyServiceContinue ;
Names are self explanatory (I hope). To test this unit I run it from normal application program. But to install it as a service I have prepared special main program interfacing to Windows service machine. You can find source at the bottom of message. Ignore calls to TraceInit() and Trace(), Replace oll occurances of NetPeruse to your service name and go!
This is based on information found in MSDN.Adam Kaminski
-----------------------------------------------------------------
PROGRAM NetPeruseService;
USES
Windows, NetPeruseUnit, TraceUnit;CONST
SCMDLL = 'ADVAPI32.DLL';TYPE
SERVICE_STATUS_HANDLE = Cardinal;
LPSERVICE_MAIN_FUNCTION = PROCEDURE ( argc : Cardinal ; VAR argv : ppChar );
LPHANDLER_FUNCTION = PROCEDURE ( Opcode : Cardinal );
LPSERVICE_TABLE_ENTRY = ^SERVICE_TABLE_ENTRY;
SERVICE_TABLE_ENTRY = RECORD
lpServiceName : pChar;
lpServiceProc : LPSERVICE_MAIN_FUNCTION;
END;CONST
SERVICE_WIN32_OWN_PROCESS = $00000010;
//SERVICE_WIN32_SHARE_PROCESS = $00000020;
//SERVICE_WIN32 = (SERVICE_WIN32_OWN_PROCESS OR SERVICE_WIN32_SHARE_PROCESS);
//
// Controls
//
SERVICE_CONTROL_STOP = $00000001;
SERVICE_CONTROL_PAUSE = $00000002;
SERVICE_CONTROL_CONTINUE = $00000003;
SERVICE_CONTROL_INTERROGATE = $00000004;
SERVICE_CONTROL_SHUTDOWN = $00000005;
//
// Service State -- for CurrentState
//
SERVICE_STOPPED = $00000001;
SERVICE_START_PENDING = $00000002;
SERVICE_STOP_PENDING = $00000003;
SERVICE_RUNNING = $00000004;
//SERVICE_CONTINUE_PENDING = $00000005;
//SERVICE_PAUSE_PENDING = $00000006;
SERVICE_PAUSED = $00000007;
//
// Controls Accepted (Bit Mask)
//
SERVICE_ACCEPT_STOP = $00000001;
SERVICE_ACCEPT_PAUSE_CONTINUE = $00000002;
SERVICE_ACCEPT_SHUTDOWN = $00000004;TYPE
//LPSERVICE_STATUS = ^SERVICE_STATUS;
SERVICE_STATUS = RECORD
dwServiceType : Cardinal;
dwCurrentState : Cardinal;
dwControlsAccepted : Cardinal;
dwWin32ExitCode : Cardinal;
dwServiceSpecificExitCode : Cardinal;
dwCheckPoint : Cardinal;
dwWaitHint : Cardinal;
END;FUNCTION StartServiceCtrlDispatcher ( lpServiceStartTable : LPSERVICE_TABLE_ENTRY ) : Boolean ;
stdcall ; external SCMDLL name 'StartServiceCtrlDispatcherA';FUNCTION RegisterServiceCtrlHandler ( lpServiceName : pChar ; lpHandleProc : LPHANDLER_FUNCTION ) : SERVICE_STATUS_HANDLE ;
stdcall ; external SCMDLL name 'RegisterServiceCtrlHandlerA';FUNCTION SetServiceStatus ( hServiceStatus : SERVICE_STATUS_HANDLE ; VAR ServiceStatus : SERVICE_STATUS ) : Boolean ;
stdcall ; external SCMDLL name 'SetServiceStatus';VAR
MyServiceStatus : SERVICE_STATUS;
MyServiceStatusHandle : SERVICE_STATUS_HANDLE;
PROCEDURE MyServiceCtrlHandler ( Opcode : Cardinal ) ; stdcall ;BEGIN { MyServiceCtrlHandler }
Trace(Trace1, Start, 'MyServiceCtrlHandler');
CASE ( Opcode ) OF
SERVICE_CONTROL_PAUSE:
BEGIN
// Do whatever it takes to pause here.
MyServicePause();
MyServiceStatus.dwCurrentState := SERVICE_PAUSED;
END;
SERVICE_CONTROL_CONTINUE:
BEGIN
// Do whatever it takes to continue here.
MyServiceContinue();
MyServiceStatus.dwCurrentState := SERVICE_RUNNING;
END;
SERVICE_CONTROL_SHUTDOWN, // Do whatever it takes to shutdown here.
SERVICE_CONTROL_STOP: // Do whatever it takes to stop here.
BEGIN
IF ( Opcode = SERVICE_CONTROL_SHUTDOWN )
THEN Trace(TraceLog, Cont, 'Shutdown request received')
ELSE Trace(TraceLog, Cont, 'Stop request received');
MyServiceStop;
MyServiceStatus.dwCurrentState := SERVICE_STOP_PENDING; { W przykladzie bylo SERVICE_STOPPED_PENDING }
MyServiceStatus.dwWin32ExitCode := 0;
MyServiceStatus.dwCheckPoint := 0;
MyServiceStatus.dwWaitHint := 0;
IF ( NOT SetServiceStatus( MyServiceStatusHandle, MyServiceStatus) ) THEN
BEGIN
Trace(TraceError, Cont, 'SetServiceStatus: ', GetLastError());
Exit;
END;
{ leaving NetPeruse }
Trace(Trace1, Finish, 'MyServiceCtrlHandler');
Exit;
END;
SERVICE_CONTROL_INTERROGATE:
BEGIN
// Do whatever it takes to pause here.
//MyServiceStatus.dwCurrentState := SERVICE_PAUSED;
END;
END; { CASE ( Opcode ) OF }
{ send current status }
IF ( NOT SetServiceStatus( MyServiceStatusHandle, MyServiceStatus) )
THEN Trace(TraceError, Cont, 'SetServiceStatus: ', GetLastError());
Trace(Trace1, Finish, 'MyServiceCtrlHandler');
END; { MyServiceCtrlHandler }PROCEDURE MyServiceMain ( argc : Cardinal ; VAR argv : ppChar ) ; stdcall ;
VAR
status : Cardinal;
specificError : Cardinal;BEGIN { MyServiceMain }
Trace(Trace1, Start, 'MyServiceMain');
MyServiceStatus.dwServiceType := SERVICE_WIN32_OWN_PROCESS;
MyServiceStatus.dwCurrentState := SERVICE_START_PENDING;
MyServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP OR SERVICE_ACCEPT_PAUSE_CONTINUE OR SERVICE_ACCEPT_SHUTDOWN;
MyServiceStatus.dwWin32ExitCode := 0;
MyServiceStatus.dwServiceSpecificExitCode := 0;
MyServiceStatus.dwCheckPoint := 0;
MyServiceStatus.dwWaitHint := 0;MyServiceStatusHandle := RegisterServiceCtrlHandler('NetPeruse', MyServiceCtrlHandler);
IF ( MyServiceStatusHandle = 0 ) THEN
BEGIN
Trace(TraceError, Cont, 'RegisterServiceCtrlHandler failed: ', GetLastError());
Exit;
END;{ Inicjalizacja service'u }
status := MyServiceInitialization(argc, argv, specificError);
IF ( status <> 0 ) THEN
BEGIN
MyServiceStatus.dwCurrentState := SERVICE_STOPPED;
MyServiceStatus.dwWin32ExitCode := status;
MyServiceStatus.dwServiceSpecificExitCode := specificError;
MyServiceStatus.dwCheckPoint := 0;
MyServiceStatus.dwWaitHint := 0;
SetServiceStatus( MyServiceStatusHandle, MyServiceStatus);
Trace(TraceError, Cont, 'MyServiceInitialization: ', specificError);
Exit;
END;{ Initialization complete - report running status }
MyServiceStatus.dwCurrentState := SERVICE_RUNNING;
MyServiceStatus.dwCheckPoint := 0;
MyServiceStatus.dwWaitHint := 0;
IF ( NOT SetServiceStatus( MyServiceStatusHandle, MyServiceStatus) ) THEN
BEGIN
Trace(TraceError, Cont, 'SetServiceStatus: ', GetLastError());
Exit;
END;status := MyServiceStart(specificError);
MyServiceStatus.dwCurrentState := SERVICE_STOPPED;
MyServiceStatus.dwWin32ExitCode := status;
MyServiceStatus.dwServiceSpecificExitCode := specificError;
MyServiceStatus.dwCheckPoint := 0;
MyServiceStatus.dwWaitHint := 0;
SetServiceStatus( MyServiceStatusHandle, MyServiceStatus);Trace(Trace1, Finish, 'MyServiceMain');
END; { MyServiceMain }
VAR
DispatchTable : ARRAY [ 0 .. 1 ] OF SERVICE_TABLE_ENTRY;
specificError : Cardinal;
BEGIN { NetPeruseService }
TraceInit(ParamStr(1)+'NetPeruse.log');
Trace(TraceLog, Start, 'NetPeruseService');
IF ( MyServiceInitialization ( argc, argv, specificError ) <> 0 ) THEN
BEGIN
Trace(TraceError, Cont, 'MyServiceInitialization: ', specificError);
Exit;
END;
DispatchTable[0].lpServiceName := 'NetPeruse';
DispatchTable[0].lpServiceProc := @MyServiceMain;
DispatchTable[1].lpServiceName := NIL;
DispatchTable[1].lpServiceProc := NIL;
IF ( NOT StartServiceCtrlDispatcher(DispatchTable) ) THEN
BEGIN
Trace(TraceError, Cont, 'StartServiceCtrlDispatcher: ', GetLastError());
Trace(Trace1, Finish, 'NetPeruseService');
Exit;
END;
Trace(TraceLog, Finish, 'NetPeruseService');
END. { NetPeruseService }-- Adam Kaminski, November 26, 2004 07:06 PM (email)
