How to Write a Windows service? Refer to Chapter 13 of win sys program to add some notes missing in the book

Source: Internet
Author: User
1. Create a Windows console application. The project created by vs's built-in Windows Service template cannot be understood.

2. Write the _ tmain function as follows:

Code: select all
/*  Main routine that starts the service control dispatcher */
VOID _tmain (int argc, LPTSTR argv[])
{
   SERVICE_TABLE_ENTRY DispatchTable[] =
   {
      { ServiceName,            ServiceMain   },
      { NULL,                  NULL }
   };

   StartServiceCtrlDispatcher (DispatchTable);
   return;
}

The key is that the servicemain function is defined here.

3. Then there is the servicemain function:

Code: select all
/*   ServiceMain entry point, called when the service is created by
   the main program.  */
void WINAPI ServiceMain (DWORD argc, LPTSTR argv[])
{
   HANDLE hFile = NULL;
   DWORD Context = 1;
   size_t bytes_need_write = 0;
   DWORD bytes_written = 0;
   TCHAR write_buffer[255];
   SYSTEMTIME cur_time;
   DWORD n;
   LARGE_INTEGER file_size;

   // init logger
   logger_set_root_directory(TEXT("C:\\"));

   hServStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
   hServStatus.dwCurrentState = SERVICE_START_PENDING;
   hServStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
   hServStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
   hServStatus.dwServiceSpecificExitCode = 0;
   hServStatus.dwCheckPoint = 0;
   // in 2000ms, we should increment dwCheckPoint or set new status
   // while current status is a PENDING start/continue/stop state
   hServStatus.dwWaitHint = 5000;

   /* Warning. Older VC++ version do not have RegisterServiceCtrlHandlerEx
    * defined. You can use RegisterServiceCtrlHandler just as well */
#ifdef RegisterServiceCtrlHandlerEx
   hSStat = RegisterServiceCtrlHandlerEx(ServiceName, ServerCtrlHandlerEx, &Context);
#else
   hSStat = RegisterServiceCtrlHandler(ServiceName, ServerCtrlHandler);
#endif

   if (hSStat == NULL) {
      GET_ERROR_CODE(n);
      UTILS_RIF_WITH_LOG(FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
         TEXT("Register service ctrl handle failed. Reason: %s\n"),
         utils_format_error_string(n));
   }

   // PENDING start
   if (!SetServiceStatus (hSStat, &hServStatus)) {
      GET_ERROR_CODE(n);
      UTILS_RIF_WITH_LOG(FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
         TEXT("Set service status failed. Reason: %s\n"),
         utils_format_error_string(n));
   }

   // Open the file and log current time
   hFile = CreateFile(TEXT("C:\\testserv.txt"), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if (hFile == INVALID_HANDLE_VALUE) {
      GET_ERROR_CODE(n);
      UTILS_RIF_WITH_LOG(FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
         TEXT("Create testserv file failed. Reason: %s\n"),
         utils_format_error_string(n));
   }

   // Seek to the end of file
   UTILS_RETURN_IF_FAIL(GetFileSizeEx(hFile, &file_size));
   UTILS_RETURN_IF_FAIL(SetFilePointerEx(hFile, file_size, NULL, FILE_BEGIN));

   GetLocalTime(&cur_time);
   StringCchPrintf(write_buffer, _countof(write_buffer), TEXT("TestServ started, write time: %d-%d-%d %d:%d:%d\n"),
      cur_time.wYear, cur_time.wMonth, cur_time.wDay, cur_time.wHour, cur_time.wMinute, cur_time.wSecond);
   StringCchLength(write_buffer, _countof(write_buffer), &bytes_need_write);
   WriteFile(hFile, write_buffer, bytes_need_write * sizeof(TCHAR), &bytes_written, NULL);

   hServStatus.dwCheckPoint = 0;
   hServStatus.dwWin32ExitCode = NO_ERROR;
   hServStatus.dwServiceSpecificExitCode = 0;
   hServStatus.dwCurrentState = SERVICE_RUNNING;
   if (!SetServiceStatus(hSStat, &hServStatus)) {
      GET_ERROR_CODE(n);
      UTILS_RIF_WITH_LOG(FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
         TEXT("Set service status failed. Reason: %s\n"),
         utils_format_error_string(n));
   }

   // block here until service stop
   while (!service_terminate) {
      Sleep(1000);
   }

   // stop service
   GetLocalTime(&cur_time);
   StringCchPrintf(write_buffer, _countof(write_buffer), TEXT("TestServ stopped, write time: %d-%d-%d %d:%d:%d\n"),
      cur_time.wYear, cur_time.wMonth, cur_time.wDay, cur_time.wHour, cur_time.wMinute, cur_time.wSecond);
   StringCchLength(write_buffer, _countof(write_buffer), &bytes_need_write);
   WriteFile(hFile, write_buffer, bytes_need_write * sizeof(TCHAR), &bytes_written, NULL);
   CloseHandle(hFile);

   hServStatus.dwCheckPoint = 0;
   hServStatus.dwWin32ExitCode = NO_ERROR;
   hServStatus.dwServiceSpecificExitCode = 0;
   hServStatus.dwCurrentState = SERVICE_STOPPED;
   if (!SetServiceStatus(hSStat, &hServStatus)) {
      GET_ERROR_CODE(n);
      UTILS_RIF_WITH_LOG(FALSE, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
         TEXT("Set service status failed. Reason: %s\n"),
         utils_format_error_string(n));
   }
   
   return;
}

There are several key points:

A. hservstatus is a service_structure, which is very important and defines a lot of key data. Mainly:
(1) dwcontrolaccepted. Do not write it if it does not support pause or continue.

(2) The member dwwin32exitcode is easy to confuse. If the current service status is pending (start pending/pause pending/continue pending/Stop pending), you can set it to error_service_specific_error, which indicates that if an error occurs during pending, let windows SCM check dwservicespecificexitcode member to get the error code. However, if the status has been successfully converted to running/stopped, set dwwin32exitcode to no_error. In this case, Windows SCM considers that there is no error in service status conversion and the value of dwservicespecificexitcode is ignore. If dwwin32exitcode is error_service_specific_error, Windows SCM considers the service status conversion error and uses dwservicespecificexitcode as the error code to return the report. This is why the two member settings are required when the running and stopped statuses are converted in the above Code.

(3) dwcheckpoint is also used for pending status. In pending status, Windows SCM needs to know whether the service is alive, depending on the periodic check of this dwcheckpoint member. Therefore, we often use setservicestatus to update the dwcheckpoint when our service is in the pending state. Otherwise, Windows SCM considers the service down and forces the STOP service. In addition, after the service completes pending and switches to the new State, remember to reset dwcheckpoint to 0.

(4) dwwaithint: This member defines a time (in milliseconds). When the service is in the pending state, if the time defined here is passed, Windows SCM finds that the dwcheckpoint of the service is not updated, or if the service is not switched to a new non-pending state, Windows SCM forces the STOP service.

B. registerservicectrlhandlerex function. Registers the response function of the SCM action. That is, the response of the Stop/continue commands. Example:

Code: select all
/*   Control Handler Function */
#ifdef RegisterServiceCtrlHandlerEx
DWORD WINAPI ServerCtrlHandlerEx( DWORD dwControl, DWORD dwEventType,
                       LPVOID lpEventData, LPVOID lpContext)
#else
DWORD WINAPI ServerCtrlHandler( DWORD dwControl)
#endif
// requested control code
{
   DWORD n;

   switch (dwControl) {
   case SERVICE_CONTROL_SHUTDOWN:
   case SERVICE_CONTROL_STOP:
      service_terminate = TRUE;
      hServStatus.dwCheckPoint++;
      hServStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
      hServStatus.dwServiceSpecificExitCode = 0;
      hServStatus.dwCurrentState = SERVICE_STOP_PENDING;
      if (!SetServiceStatus(hSStat, &hServStatus)) {
         GET_ERROR_CODE(n);
         UTILS_RVIF_WITH_LOG(FALSE, NO_ERROR, LOG_LEVEL_ERROR, __SDFILE__, __LINE__,
            TEXT("Set service status failed. Reason: %s\n"),
            utils_format_error_string(n));
      }
      return NO_ERROR;
   case SERVICE_CONTROL_PAUSE:
      break;
   case SERVICE_CONTROL_CONTINUE:
      break;
   case SERVICE_CONTROL_INTERROGATE:
      return NO_ERROR;
   default:
      if (dwControl > 127 && dwControl < 256) /* User Defined */
      break;
   }
   return ERROR_CALL_NOT_IMPLEMENTED;
}

(1) For the control command without handle, return error_call_not_implemented. For handle, return no_error. The exception is that for service_control_interrogate, no_error is also returned, even if we do not have the handle control. For more information, see msdn.

(2) service_control_shutdown is sent to the service by SCM when the system is shut down. Like service_control_stop, you can set a pending state here. In servicemain, the pending state is actually stopped and the stopped state is set.

4. The key code of the service itself is this. The following describes how to create a service using SCM:

Code: select all
#define SVCNAME TEXT("EricTestService")

int _tmain(int argc, PTSTR argv[], PTSTR env[])
{
   DWORD errcode = 0;
   SC_HANDLE hManager = NULL;
   SC_HANDLE hService = NULL;

   setlocale(LC_ALL, "CHS");

   // Open SCManager
   hManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
   if (NULL == hManager) {
      errcode = GetLastError();
      _tprintf(TEXT("Open service manager failed. Reason: %s\n"), utils_format_error_string(errcode));
      goto failed;
   }

   // create service
   hService = CreateService(
        hManager,                  // SCM database
        SVCNAME,                   // name of service
        SVCNAME,                   // service name to display
        SERVICE_ALL_ACCESS,        // desired access
        SERVICE_WIN32_OWN_PROCESS, // service type
        SERVICE_AUTO_START,      // start type
        SERVICE_ERROR_NORMAL,      // error control type
      TEXT("C:\\ScheduleDownload\\Practise\\TestServ\\Debug\\TestServ.exe"),  // path to service's binary
        NULL,                      // no load ordering group
        NULL,                      // no tag identifier
        NULL,                      // no dependencies
        NULL,                      // LocalSystem account
        NULL);                     // no password

   if (NULL == hService) {
      errcode = GetLastError();
      _tprintf(TEXT("Create service failed. Reason: %s\n"), utils_format_error_string(errcode));
      CloseServiceHandle(hManager);
      goto failed;
   }

   CloseServiceHandle(hService);
   CloseServiceHandle(hManager);

   return 0;

failed:
   // wait key
   _getch();
   return 1;
}

This is relatively simple. Use openscmanager to open SCM and use createservice to create a service, which can be seen in Windows Services. In start type, set service_auto_start to start at startup (started without user logon and tested ). Other functions, such as startservice/deleteservice/queryservice... can be used to check msdn.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.