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>