Search code examples
c++cwindowsservice

Create a windows service from a compiled app


I want to create a service from my compiled C/C++ code with Microsoft Visual Studio 2015. I compiled my code and create a service with sc.exe from app executable. My code consists of an endless loop which writes a simple string to a static file within time intervals. After creating a service and reboot, it seems that service is stopped automatically. The executable works fine when directly run.

Here is the code:

#include "stdafx.h"
#include <string>
#include <cstdio>
#include <windows.h>


using namespace std;

void write_txt_file(string file_name, string input) {
    /*
    write a string to a specific txt file
    */
    FILE *f = fopen(file_name.c_str(), "a+");
    fprintf(f, "%s\n", input.c_str());
    fclose(f);
}

int main(int argc, char** argv)
{
    int i = 0;
    while (true) {
        write_txt_file("C:\\...\\Desktop\\out.txt", "Writing...#" + to_string(i));
        Sleep(5000);
        i++;
    }
}

Here is the command I use to create a service:

sc.exe create My_service binPath= "<path to executable>" start= auto

and the output of out.txt file is:

Writing...#0
Writing...#1
Writing...#2
Writing...#3
Writing...#4
Writing...#5
Writing...#6
Writing...#7
Writing...#8

Solution

  • With the help of @Aconcagua and this reference I found a simple code to just make my code compatible with windows service. In the function ServiceWorkerThread you need to just put your loop inside the while block. The service can be stopped safely.

    #include <Windows.h>
    #include <tchar.h>
    #include <string>
    #include <cstdio>
    
    
    SERVICE_STATUS        g_ServiceStatus = {0};
    SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
    HANDLE                g_ServiceStopEvent = INVALID_HANDLE_VALUE;
    
    VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv);
    VOID WINAPI ServiceCtrlHandler (DWORD);
    DWORD WINAPI ServiceWorkerThread (LPVOID lpParam);
    
    #define SERVICE_NAME  _T("My Sample Service")
    
    
    using namespace std;
    
    void write_txt_file(string file_name, string input) {
        /*
        write a string to a specific txt file
        */
        FILE *f = fopen(file_name.c_str(), "a+");
        fprintf(f, "%s\n", input.c_str());
        fclose(f);
    }
    
    
    int _tmain (int argc, TCHAR *argv[])
    {
        OutputDebugString(_T("My Sample Service: Main: Entry"));
    
        SERVICE_TABLE_ENTRY ServiceTable[] = 
        {
            {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain},
            {NULL, NULL}
        };
    
        if (StartServiceCtrlDispatcher (ServiceTable) == FALSE)
        {
           OutputDebugString(_T("My Sample Service: Main: StartServiceCtrlDispatcher returned error"));
           return GetLastError ();
        }
    
        OutputDebugString(_T("My Sample Service: Main: Exit"));
        return 0;
    }
    
    
    VOID WINAPI ServiceMain (DWORD argc, LPTSTR *argv)
    {
        DWORD Status = E_FAIL;
    
        OutputDebugString(_T("My Sample Service: ServiceMain: Entry"));
    
        g_StatusHandle = RegisterServiceCtrlHandler (SERVICE_NAME, ServiceCtrlHandler);
    
        if (g_StatusHandle == NULL) 
        {
            OutputDebugString(_T("My Sample Service: ServiceMain: RegisterServiceCtrlHandler returned error"));
            goto EXIT;
        }
    
        // Tell the service controller we are starting
        ZeroMemory (&g_ServiceStatus, sizeof (g_ServiceStatus));
        g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwServiceSpecificExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 0;
    
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE) 
        {
            OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
        }
    
        /* 
         * Perform tasks neccesary to start the service here
         */
        OutputDebugString(_T("My Sample Service: ServiceMain: Performing Service Start Operations"));
    
        // Create stop event to wait on later.
        g_ServiceStopEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
        if (g_ServiceStopEvent == NULL) 
        {
            OutputDebugString(_T("My Sample Service: ServiceMain: CreateEvent(g_ServiceStopEvent) returned error"));
    
            g_ServiceStatus.dwControlsAccepted = 0;
            g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            g_ServiceStatus.dwWin32ExitCode = GetLastError();
            g_ServiceStatus.dwCheckPoint = 1;
    
            if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
            {
                OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
            }
            goto EXIT; 
        }    
    
        // Tell the service controller we are started
        g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
        g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 0;
    
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
        }
    
        // Start the thread that will perform the main task of the service
        HANDLE hThread = CreateThread (NULL, 0, ServiceWorkerThread, NULL, 0, NULL);
    
        OutputDebugString(_T("My Sample Service: ServiceMain: Waiting for Worker Thread to complete"));
    
        // Wait until our worker thread exits effectively signaling that the service needs to stop
        WaitForSingleObject (hThread, INFINITE);
        
        OutputDebugString(_T("My Sample Service: ServiceMain: Worker Thread Stop Event signaled"));
        
        
        /* 
         * Perform any cleanup tasks
         */
        OutputDebugString(_T("My Sample Service: ServiceMain: Performing Cleanup Operations"));
    
        CloseHandle (g_ServiceStopEvent);
    
        g_ServiceStatus.dwControlsAccepted = 0;
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        g_ServiceStatus.dwWin32ExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 3;
    
        if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
        {
            OutputDebugString(_T("My Sample Service: ServiceMain: SetServiceStatus returned error"));
        }
        
        EXIT:
        OutputDebugString(_T("My Sample Service: ServiceMain: Exit"));
    
        return;
    }
    
    
    VOID WINAPI ServiceCtrlHandler (DWORD CtrlCode)
    {
        OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Entry"));
    
        switch (CtrlCode) 
        {
         case SERVICE_CONTROL_STOP :
    
            OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SERVICE_CONTROL_STOP Request"));
    
            if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
               break;
    
            /* 
             * Perform tasks neccesary to stop the service here 
             */
            
            g_ServiceStatus.dwControlsAccepted = 0;
            g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
            g_ServiceStatus.dwWin32ExitCode = 0;
            g_ServiceStatus.dwCheckPoint = 4;
    
            if (SetServiceStatus (g_StatusHandle, &g_ServiceStatus) == FALSE)
            {
                OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: SetServiceStatus returned error"));
            }
    
            // This will signal the worker thread to start shutting down
            SetEvent (g_ServiceStopEvent);
    
            break;
    
         default:
             break;
        }
    
        OutputDebugString(_T("My Sample Service: ServiceCtrlHandler: Exit"));
    }
    
    
    DWORD WINAPI ServiceWorkerThread (LPVOID lpParam)
    {
        OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Entry"));
        int i = 0;
        //  Periodically check if the service has been requested to stop
        while (WaitForSingleObject(g_ServiceStopEvent, 0) != WAIT_OBJECT_0)
        {        
            /* 
             * Perform main service function here
             */
            write_txt_file("C:\\...\\out.txt", "Writing...#" + to_string(i));
            //  Simulate some work by sleeping
            Sleep(3000);
            i++;
        }
    
        OutputDebugString(_T("My Sample Service: ServiceWorkerThread: Exit"));
    
        return ERROR_SUCCESS;
    }