There are a lot of samples on the net that explain how to write a basic Windows NT Service. This is just another one of them. However, there are some differences:
- This one is written by me (duh... :).
- It has a lot of comments; which obviate the need of a saparate document explaining how a typical WinNT Service is supposed to work. However, you need to know the architecture of Windows NT Service to understand it. (I'll try to put together a document that explains it.)
I know I still need to explain a lot of things, but, for now, this much should suffice...
So, here it goes...
<code>
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
static TCHAR* ServiceName = TEXT("Beeper");
static SERVICE_STATUS ServiceStatus;
static SERVICE_STATUS_HANDLE hServiceStatus = 0;
static HANDLE evStopService = 0;
/******************************************************************************
* This functioon is called by the OS to notify a running service of
* it's intentions (request to start/stop/etc. ) or to query it's status.
*****************************************************************************/
void WINAPI ServiceControlHandler( DWORD ControlCode )
{
/* What does the OS want us to do? */
switch( ControlCode )
{
case SERVICE_CONTROL_INTERROGATE:
/* someone wants to know the controls/requests that we accept; the
* last statement in this function will do this for us
*/
break;
case SERVICE_CONTROL_SHUTDOWN:
/* machine is shutting down */
/* fall through to STOP the service */
case SERVICE_CONTROL_STOP:
/* we are requested to stop the service */
/* tell the OS that I am going down */
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( hServiceStatus, &ServiceStatus );
/* tell our service (the other thread) that we should stop now */
SetEvent( evStopService );
return;
case SERVICE_CONTROL_PAUSE:
/* we are requested to pause the service. That is, stop doing whatever we are
* supposed to do, but DO NOT exit from the process
*/
ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
/* we are in paused state; we should start doing whatever
* we are supposed to do
*/
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
default:
if( ControlCode >= 128 && ControlCode <= 255 )
{
/* we can use one of these control codes to handle
* requirements specific to our service
*/
break;
}
else
{ /* we are not supposed to interpret any other request; these
* codes are reserved by the OS
*/
break;
}
break;
}
/* update our status with the OS */
SetServiceStatus( hServiceStatus, &ServiceStatus );
}
/* the main() function for our service */
void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
/* populate our current state in the status object */
ServiceStatus.dwServiceType = SERVICE_WIN32;
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
ServiceStatus.dwWin32ExitCode = NO_ERROR;
ServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
ServiceStatus.dwCheckPoint = 0;
ServiceStatus.dwWaitHint = 0;
/* tell the OS that we accept PAUSE/CONTINUE, STOP and SHUTDOWN requests */
ServiceStatus.dwControlsAccepted |=
( SERVICE_ACCEPT_PAUSE_CONTINUE
| SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_SHUTDOWN);
/* register the control handler for the service */
hServiceStatus = RegisterServiceCtrlHandler( ServiceName, ServiceControlHandler );
/* registration went fine... go ahead... start the service */
if( hServiceStatus )
{
/* tell the OS that we are starting */
ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( hServiceStatus, &ServiceStatus );
/* let's pull-up our socks... do any initialisation here */
evStopService = CreateEvent( 0, FALSE, FALSE, 0 );
/* tell the OS we are in business... our initialisation went fine... */
ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( hServiceStatus, &ServiceStatus );
/* now run... */
do
{
/* beep every 5 seconds */
/* only if we are running, i.e., not paused */
if( ServiceStatus.dwCurrentState == SERVICE_RUNNING )
Beep( 1000, 100 );
/* until we are asked to stop (by our control-handler) */
}while( WaitForSingleObject( evStopService, 5000 ) == WAIT_TIMEOUT );
/* we have been asked to stop; tell the OS that we are ready to do so. */
ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( hServiceStatus, &ServiceStatus );
/* do any cleanups here... */
CloseHandle( evStopService );
evStopService = 0;
/* tell the OS that we have stopped */
ServiceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( hServiceStatus, &ServiceStatus );
}
}
/* we have just been brought up... let's run the service. */
void RunService()
{
SERVICE_TABLE_ENTRY ServiceTable[] =
{
{ ServiceName, ServiceMain },
{ 0, 0 }
};
/* this function returns only after all the services in this process
* have stopped. SCM uses this (main) thread as a dispatcher for
* threads that are used to run the services
*/
StartServiceCtrlDispatcher( ServiceTable );
}
/* we use this function to install the service */
void InstallService()
{
/* connect to SCM and tell him that we intend to create a service */
SC_HANDLE ServiceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
if( ServiceControlManager )
{
TCHAR Path[ _MAX_PATH + 1 ];
/* get the name of the .exe that we are running as */
if( GetModuleFileName( 0, Path, sizeof(Path)/sizeof(Path[0]) ) > 0 )
{
/* register this executable as a service */
SC_HANDLE Service = CreateService(
ServiceControlManager,
ServiceName, ServiceName,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, Path,
0, 0, 0, 0, 0
);
if( Service )
CloseServiceHandle( Service );
}
CloseServiceHandle( ServiceControlManager );
}
}
/* we use this function to uninstall the service we created earlier */
void UninstallService()
{
/* connect to SCM in plain CONNECT mode */
SC_HANDLE ServiceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
if( ServiceControlManager )
{
/* open the service and tell SCM that we wish to QUERY and
* DELETE this service
*/
SC_HANDLE Service = OpenService(
ServiceControlManager,
ServiceName,
SERVICE_QUERY_STATUS | DELETE
);
if( Service )
{
SERVICE_STATUS ServiceStatus;/* note that there is another global one */
/* check service's current status */
if( QueryServiceStatus( Service, &ServiceStatus ) )
{
if( ServiceStatus.dwCurrentState == SERVICE_STOPPED )
{
/* delete the service only if it not running
* (this is not a pre-requisite to delete a service )
*/
DeleteService( Service );
}
}
CloseServiceHandle( Service );
}
CloseServiceHandle( ServiceControlManager );
}
}
/* the 'normal' main() function of the process */
int main( int argc, TCHAR* argv[] )
{
/* do we have any command line parameters? */
if( argc > 1 )
{
/* are we being asked to install the service */
if( lstrcmpi( argv[1], TEXT("install") ) == 0 )
{
InstallService();
}
else
/* or are we being asked to uninstall it */
if( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 )
{
UninstallService();
}
else
/* command line parameters are not what we understand; 'stupid dog' */
{
printf( "\nUSAGE: <command> [INSTALL|UNINSTALL]\n" );
}
}
else
/* if not, then just run the service */
{
RunService();
}
return 0;
}
</code>