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
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;
}