Introduction
This is a reference on running an executable as a Windows Service. In the code, the following ideas are provided along with a reference on running an exe from a service:
- Writing a Windows Service
- Installing a Windows Service
- Uninstalling a Windows Service
- Debugging a Windows Service
Using the code
The API call used in the service code is very well documented in MSDN. We need to create a project with the code below to start using it.
#include "windows.h"
#include "tchar.h"
#define _MAX_PATH 260
TCHAR* serviceName = "";
TCHAR* exePath = "";
STARTUPINFO si;
PROCESS_INFORMATION pi;
SERVICE_STATUS serviceStatus;
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
HANDLE stopServiceEvent = 0;
DWORD exitCode = -1;
void StartExe()
{
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
CreateProcess( NULL, exePath, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi ); }
void WINAPI ServiceControlHandler( DWORD controlCode )
{
switch ( controlCode )
{
case SERVICE_CONTROL_INTERROGATE:
break;
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
TerminateProcess(pi.hProcess,
GetExitCodeProcess(pi.hProcess,&exitCode));
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
SetEvent( stopServiceEvent );
return;
case SERVICE_CONTROL_PAUSE:
break;
case SERVICE_CONTROL_CONTINUE:
break;
default:
if ( controlCode >= 128 && controlCode <= 255 )
break;
else
break;
}
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
void WINAPI ServiceMain( DWORD , TCHAR* [] )
{
serviceStatus.dwServiceType = SERVICE_WIN32;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwControlsAccepted = 0;
serviceStatus.dwWin32ExitCode = NO_ERROR;
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler( serviceName,
ServiceControlHandler );
if ( serviceStatusHandle )
{
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );
serviceStatus.dwControlsAccepted |=
(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
StartExe();
WaitForSingleObject( stopServiceEvent, INFINITE );
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
CloseHandle( stopServiceEvent );
stopServiceEvent = 0;
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
}
}
void RunService()
{
SERVICE_TABLE_ENTRY serviceTable[] =
{
{ serviceName, ServiceMain },
{ 0, 0 }
};
StartServiceCtrlDispatcher( serviceTable );
}
void InstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CREATE_SERVICE );
if ( serviceControlManager )
{
TCHAR path[ _MAX_PATH + 1 ];
if ( GetModuleFileName( 0, path, sizeof(path)/sizeof(path[0]) ) > 0 )
{
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 );
}
}
void UninstallService()
{
SC_HANDLE serviceControlManager = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
if ( serviceControlManager )
{
SC_HANDLE service = OpenService( serviceControlManager,
serviceName, SERVICE_QUERY_STATUS | DELETE );
if ( service )
{
SERVICE_STATUS serviceStatus;
if ( QueryServiceStatus( service, &serviceStatus ) )
{
if ( serviceStatus.dwCurrentState == SERVICE_STOPPED )
DeleteService( service );
}
CloseServiceHandle( service );
}
CloseServiceHandle( serviceControlManager );
}
}
int _tmain( int argc, TCHAR* argv[] )
{
if ( argc > 2 && lstrcmpi( argv[1], TEXT("install") ) == 0 )
{
serviceName = argv[2];
InstallService();
}
else if ( argc > 1 && lstrcmpi( argv[1], TEXT("uninstall") ) == 0 )
{
UninstallService();
}
else if( argc > 2 && lstrcmpi( argv[1], TEXT("start") ) == 0 )
{
exePath = argv[2];
RunService();
}
else
{
}
return 0;
}
For debugging the code, write the below code at the beginning of the _tmain
function:
__asm
{
int 3
}
This will create a break while running the service.
History
Do contact me for any updates or queries at somenshaw at gmail.com.