Create your Windows Service
I decided to conduct one experiment, I can’t disclose the essence of it yet, but I’ll definitely describe it according to the results))) For this experiment, I need to write an application that works as a service on Windows.
I’m thinking of describing how to create a normal Win32 Console Application project in Visual Studio there is no need)))
Of course, with the _tmain function :
SERVICE_TABLE_ENTRY is a structure that describes the entry point for the service manager, in this case, the entry will occur through the ServiceMain function . The StartServiceCtrlDispatcher function actually connects our service with SCM (Service Control Manager)
Before describing the function, we need two global variables:
The SERVICE_STATUS structure is used to notify SCM of the current status of the service. Read more about the fields and their values on MSDN.
Below is the full text of the ServiceMain function:
The logic of this function is simple. First, register a function that will process control requests from SCM, for example, a stop request. Registration is performed using the RegisterServiceCtrlHandler function . And when the service starts correctly, we write values to the file with the i variable .
To change the status of the service, the SetServiceStatus function is used .
Now we describe the query processing function:
The ControlHandler is called every time SCM sends requests for changes to the state of the service. It is mainly used to describe the correct completion of a service.
There are several options, one of them using the sc utility . Installation is done with the following command:
Removing a service:
This method, as for me, is not very programmer because we will describe the installation of the service in code. Let's change a little logic of function _tmain :
We now have three more functions:
Now we can install, remove and start the service without resorting to various utilities:
Sources
To be continued if everyone does not reject :)
I’m thinking of describing how to create a normal Win32 Console Application project in Visual Studio there is no need)))
Where does the service begin?
Of course, with the _tmain function :
int _tmain(int argc, _TCHAR* argv[]) {
SERVICE_TABLE_ENTRY ServiceTable[1];
ServiceTable[0].lpServiceName = serviceName;
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
StartServiceCtrlDispatcher(ServiceTable);
}
SERVICE_TABLE_ENTRY is a structure that describes the entry point for the service manager, in this case, the entry will occur through the ServiceMain function . The StartServiceCtrlDispatcher function actually connects our service with SCM (Service Control Manager)
Service Entry Point
Before describing the function, we need two global variables:
SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;
The SERVICE_STATUS structure is used to notify SCM of the current status of the service. Read more about the fields and their values on MSDN.
Below is the full text of the ServiceMain function:
void ServiceMain(int argc, char** argv) {
int error;
int i = 0;
serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwServiceSpecificExitCode = 0;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, (LPHANDLER_FUNCTION)ControlHandler);
if (serviceStatusHandle == (SERVICE_STATUS_HANDLE)0) {
return;
}
error = InitService();
if (error) {
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwWin32ExitCode = -1;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
return;
}
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus (serviceStatusHandle, &serviceStatus);
while (serviceStatus.dwCurrentState == SERVICE_RUNNING)
{
char buffer[255];
sprintf_s(buffer, "%u", i);
int result = addLogMessage(buffer);
if (result) {
serviceStatus.dwCurrentState = SERVICE_STOPPED;
serviceStatus.dwWin32ExitCode = -1;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
return;
}
i++;
}
return;
}
The logic of this function is simple. First, register a function that will process control requests from SCM, for example, a stop request. Registration is performed using the RegisterServiceCtrlHandler function . And when the service starts correctly, we write values to the file with the i variable .
To change the status of the service, the SetServiceStatus function is used .
Now we describe the query processing function:
void ControlHandler(DWORD request) {
switch(request)
{
case SERVICE_CONTROL_STOP:
addLogMessage("Stopped.");
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (serviceStatusHandle, &serviceStatus);
return;
case SERVICE_CONTROL_SHUTDOWN:
addLogMessage("Shutdown.");
serviceStatus.dwWin32ExitCode = 0;
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus (serviceStatusHandle, &serviceStatus);
return;
default:
break;
}
SetServiceStatus (serviceStatusHandle, &serviceStatus);
return;
}
The ControlHandler is called every time SCM sends requests for changes to the state of the service. It is mainly used to describe the correct completion of a service.
Service Installation
There are several options, one of them using the sc utility . Installation is done with the following command:
sc create SampleService binpath= c:\SampleService.exe
Removing a service:
sc delete SampleService
This method, as for me, is not very programmer because we will describe the installation of the service in code. Let's change a little logic of function _tmain :
int _tmain(int argc, _TCHAR* argv[]) {
servicePath = LPTSTR(argv[0]);
if(argc - 1 == 0) {
SERVICE_TABLE_ENTRY ServiceTable[1];
ServiceTable[0].lpServiceName = serviceName;
ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
if(!StartServiceCtrlDispatcher(ServiceTable)) {
addLogMessage("Error: StartServiceCtrlDispatcher");
}
} else if( wcscmp(argv[argc-1], _T("install")) == 0) {
InstallService();
} else if( wcscmp(argv[argc-1], _T("remove")) == 0) {
RemoveService();
} else if( wcscmp(argv[argc-1], _T("start")) == 0 ){
StartService();
}
}
We now have three more functions:
int InstallService() {
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if(!hSCManager) {
addLogMessage("Error: Can't open Service Control Manager");
return -1;
}
SC_HANDLE hService = CreateService(
hSCManager,
serviceName,
serviceName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
servicePath,
NULL, NULL, NULL, NULL, NULL
);
if(!hService) {
int err = GetLastError();
switch(err) {
case ERROR_ACCESS_DENIED:
addLogMessage("Error: ERROR_ACCESS_DENIED");
break;
case ERROR_CIRCULAR_DEPENDENCY:
addLogMessage("Error: ERROR_CIRCULAR_DEPENDENCY");
break;
case ERROR_DUPLICATE_SERVICE_NAME:
addLogMessage("Error: ERROR_DUPLICATE_SERVICE_NAME");
break;
case ERROR_INVALID_HANDLE:
addLogMessage("Error: ERROR_INVALID_HANDLE");
break;
case ERROR_INVALID_NAME:
addLogMessage("Error: ERROR_INVALID_NAME");
break;
case ERROR_INVALID_PARAMETER:
addLogMessage("Error: ERROR_INVALID_PARAMETER");
break;
case ERROR_INVALID_SERVICE_ACCOUNT:
addLogMessage("Error: ERROR_INVALID_SERVICE_ACCOUNT");
break;
case ERROR_SERVICE_EXISTS:
addLogMessage("Error: ERROR_SERVICE_EXISTS");
break;
default:
addLogMessage("Error: Undefined");
}
CloseServiceHandle(hSCManager);
return -1;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
addLogMessage("Success install service!");
return 0;
}
int RemoveService() {
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(!hSCManager) {
addLogMessage("Error: Can't open Service Control Manager");
return -1;
}
SC_HANDLE hService = OpenService(hSCManager, serviceName, SERVICE_STOP | DELETE);
if(!hService) {
addLogMessage("Error: Can't remove service");
CloseServiceHandle(hSCManager);
return -1;
}
DeleteService(hService);
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
addLogMessage("Success remove service!");
return 0;
}
int StartService() {
SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
SC_HANDLE hService = OpenService(hSCManager, serviceName, SERVICE_START);
if(!StartService(hService, 0, NULL)) {
CloseServiceHandle(hSCManager);
addLogMessage("Error: Can't start service");
return -1;
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCManager);
return 0;
}
Now we can install, remove and start the service without resorting to various utilities:
SampleService.exe install
SampleService.exe remove
SampleService.exe start
Sources
To be continued if everyone does not reject :)