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)))

    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 :)

    Also popular now: