📜 ⬆️ ⬇️

Creating your own Windows Service

I decided to conduct one experiment, I can’t disclose the essence of it yet, but by the results I will definitely describe it))) For this experiment, I need to write an application that works as a service in Windows.

I think there is no need to describe how to create a normal Win32 Console Application project in Visual Studio)))

How to start the service?


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 input will be through the ServiceMain function . The StartServiceCtrlDispatcher function actually links our service with the 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 the SCM of the current service status. You can read more about the fields and their meanings 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, we register a function that will process control requests from SCM, for example, a request to stop. Registration is done using the function RegisterServiceCtrlHandler . And when the service starts correctly, we write the variable i to the file of value.
To change the status of the service, the function SetServiceStatus is used .
Now we will 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 invoked every time the SCM sends requests for service status changes. Basically it is used to describe the correct completion of the service.
')

Service installation


There are several options, one of them using the sc utility. Installation is performed by the following command:
sc create SampleService binpath= c:\SampleService.exe

Deleting a service:
sc delete SampleService

This method, as for me, is not very programmatic, therefore we will describe the installation of the service in the code. Let's change a bit the logic of the _tmain function :
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, delete 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 banish :)

Source: https://habr.com/ru/post/71533/


All Articles