Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Run an executable as a Windows Service

1.65/5 (5 votes)
8 Jul 2008CPOL 1  
Quick way to run an executable as a Windows Service.

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:

  1. Writing a Windows Service
  2. Installing a Windows Service
  3. Uninstalling a Windows Service
  4. 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.

C++
#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) );

    // Start the child process. 
    CreateProcess( NULL, // No module name (use command line). 
        exePath, // Command line. 
        NULL,             // Process handle not inheritable. 
        NULL,             // Thread handle not inheritable. 
        FALSE,            // Set handle inheritance to FALSE. 
        0,                // No creation flags. 
        NULL,             // Use parent's environment block. 
        NULL,             // Use parent's starting directory. 
        &si,              // Pointer to STARTUPINFO structure.
        &pi );            // Pointer to PROCESS_INFORMATION structure.
}

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 )
                // user defined control code
                break;
            else
                // unrecognised control code
                break;
    }

    SetServiceStatus( serviceStatusHandle, &serviceStatus );
}

void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
    // initialise service status
    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 )
    {
        // service is starting
        serviceStatus.dwCurrentState = SERVICE_START_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        // do initialisation here
        stopServiceEvent = CreateEvent( 0, FALSE, FALSE, 0 );

        // running
        serviceStatus.dwControlsAccepted |= 
               (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
        serviceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        StartExe();
        WaitForSingleObject( stopServiceEvent, INFINITE ); 

        // service was stopped
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );

        // do cleanup here
        CloseHandle( stopServiceEvent );
        stopServiceEvent = 0;

        // service is now stopped
        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
    {
        //show error message
    }

    return 0;
}

For debugging the code, write the below code at the beginning of the _tmain function:

ASM
__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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)