Создание многопоточной службы


Следующий типовой код демонстрирует, как простая служба может породить рабочие потоки, ответить Диспетчеру управления службами (SCM), уведомить потоки, чтобы они вышли из работы, сохранять Диспетчер управления службами (SCM), уведомляя его о состоянии и продвижениях в работе службы и сообщить Диспетчеру управления службами (SCM), что служба остановлена. Чтобы установить службу, встройте ее как консольное приложение и используйте утилиту SC, включенную в Платформу SDK. Используйте утилиту Service Control на Пульте управления, чтобы запустить и остановить службу.

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

HANDLE  hStopEvent;
HANDLE hThreads[3] = {NULL,NULL,NULL};
LPTSTR  lpszServiceName;
SERVICE_STATUS_HANDLE   ssh;

DWORD WINAPI ThreadProc(LPVOID lpParameter);
void  WINAPI  Service_Main(DWORD dwArgc, LPTSTR *lpszArgv);
void  WINAPI  Service_Ctrl(DWORD dwCtrlCode);
void  ErrorStopService(LPTSTR lpszAPI);
void  SetTheServiceStatus(DWORD dwCurrentState,DWORD dwWin32ExitCode,
                          DWORD dwCheckPoint,  DWORD dwWaitHint);

// Точка входа для службы. Вызываем StartServiceCtrlDispatcher,
// а затем блокируем до тех пор, пока функция ServiceMain не возвратит значение.

void _tmain(int argc, TCHAR *argv[])
{
   SERVICE_TABLE_ENTRY ste[] =
      {{TEXT(""),(LPSERVICE_MAIN_FUNCTION)Service_Main}, {NULL, NULL}};

   OutputDebugString(TEXT("Entered service code\n"));
   if (!StartServiceCtrlDispatcher(ste))
   {
      TCHAR error[256];

      wsprintf(error,
         TEXT("Error code for StartServiceCtrlDispatcher: %u.\n"),
         GetLastError());
      OutputDebugString(error);
   }
   else
      OutputDebugString(TEXT("StartServiceCtrlDispatcher returned!\n"));
}

// Вызванная диспетчером управления службами после вызова 
// StartServiceCtrlDispatcher.

void WINAPI Service_Main(DWORD dwArgc, LPTSTR *lpszArgv)
{
   DWORD ThreadId;
   DWORD t;
   DWORD dwWaitRes;

   // Получим имя службы.
   lpszServiceName = lpszArgv[0];

   // Зарегистрируем службу управления обработчиком.
   ssh = RegisterServiceCtrlHandler(lpszServiceName,
           (LPHANDLER_FUNCTION)Service_Ctrl);

   // Создаем событие, чтобы сообщить о службе об ее останове.
   hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (hStopEvent == NULL)
      ErrorStopService(TEXT("CreateEvent"));

   //
   // Вставьте одноразовую работу, которую Вы хотите завершить перед запуском.
   //

   for (t=0;t<3;t++)
   {
      hThreads[t] = CreateThread(NULL,0,ThreadProc,
                                (LPVOID)t,0,&ThreadId);
      if (hThreads[t] == INVALID_HANDLE_VALUE)
         ErrorStopService(TEXT("CreateThread"));
   }

   // Запускаем службу.
   SetTheServiceStatus(SERVICE_RUNNING, 0, 0, 0);
   OutputDebugString(TEXT("SetTheServiceStatus, SERVICE_RUNNING\n"));

   //
   // Главный цикл службы.
   // 

   while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
   {

/***************************************************************/ 
  // Главный цикл  службы.
/***************************************************************/ 
   }

   // Ожидаем потоки, чтобы выйти.
   for (t=1;TRUE;t++)
   {
      if ((dwWaitRes = WaitForMultipleObjects(3,hThreads,TRUE,1000))
           == WAIT_OBJECT_0)
         break;
      else if((dwWaitRes == WAIT_FAILED)||(dwWaitRes==WAIT_ABANDONED))
         ErrorStopService(TEXT("WaitForMultipleObjects"));
      else
         SetTheServiceStatus(SERVICE_STOP_PENDING, 0, t, 3000);
   }

   // Закроем дескриптор события и дескриптор потока.
   if (!CloseHandle(hStopEvent))
      ErrorStopService(TEXT("CloseHandle"));
   if (!CloseHandle(hThreads[0]))
      ErrorStopService(TEXT("CloseHandle"));
   if (!CloseHandle(hThreads[1]))
      ErrorStopService(TEXT("CloseHandle"));
   if (!CloseHandle(hThreads[2]))
      ErrorStopService(TEXT("CloseHandle"));

   // Остановим службу.
   OutputDebugString(TEXT("SetTheServiceStatus, SERVICE_STOPPED\n"));
   SetTheServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0);
}

// Дескрипторы управляют сигналами от диспетчера управления службами.
void WINAPI Service_Ctrl(DWORD dwCtrlCode)
{
   DWORD dwState = SERVICE_RUNNING;

   switch(dwCtrlCode)
   {
      case SERVICE_CONTROL_STOP:
         dwState = SERVICE_STOP_PENDING;
         break;

      case SERVICE_CONTROL_SHUTDOWN:
         dwState = SERVICE_STOP_PENDING;
         break;

      case SERVICE_CONTROL_INTERROGATE:
         break;

      default:
         break;
   }

   // Установите состояние службы.
   SetTheServiceStatus(dwState, NO_ERROR, 0, 0);
   OutputDebugString(
       TEXT("SetTheServiceStatus, Service_Ctrl function\n"));

   // Укажем потоку service_main остановиться.
   if ((dwCtrlCode == SERVICE_CONTROL_STOP) ||
       (dwCtrlCode == SERVICE_CONTROL_SHUTDOWN))
   {
      if (!SetEvent(hStopEvent))
         ErrorStopService(TEXT("SetEvent"));
      else
         OutputDebugString(TEXT("Signal service_main thread\n"));
   }
}

// Процедура потока для всех трех рабочих потоков.

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
   INT nThreadNum = (INT)lpParameter;
   TCHAR szOutput[25];

   while(WaitForSingleObject(hStopEvent, 1000) != WAIT_OBJECT_0)
   {
   // Теперь сделаем так, чтобы каждую секунду посылался звуковой сигнал.
      Sleep(1000);
      wsprintf(szOutput,TEXT("\nThread %d says Beep\n"),nThreadNum);
      OutputDebugString(szOutput);     // Отправляем на визуальную отладку.
   }

   return 0;
}

//  Переход на SetServiceStatus.

void SetTheServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
                         DWORD dwCheckPoint,   DWORD dwWaitHint)
{
   SERVICE_STATUS ss;  // Текущий статус службы.

   // Отключаем запросы на управление до тех пор, пока служба не запустится.
   if (dwCurrentState == SERVICE_START_PENDING)
      ss.dwControlsAccepted = 0;
   else
      ss.dwControlsAccepted =
         SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
         // Другие флажки включающие SERVICE_ACCEPT_PAUSE_CONTINUE
         // и SERVICE_ACCEPT_SHUTDOWN.

   // Инициализируем структуру ss.
   ss.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
   ss.dwServiceSpecificExitCode = 0;
   ss.dwCurrentState            = dwCurrentState;
   ss.dwWin32ExitCode           = dwWin32ExitCode;
   ss.dwCheckPoint              = dwCheckPoint;
   ss.dwWaitHint                = dwWaitHint;

   // Отправляем статус службы в Service Controller.
   if (!SetServiceStatus(ssh, &ss))
      ErrorStopService(TEXT("SetServiceStatus"));
}

//  Обрабатываем ошибки API или другие проблемы при помощи завершения службы и
//  показа на экране сообщения об ошибках для отладчика.

void ErrorStopService(LPTSTR lpszAPI)
{
   INT t;
   TCHAR   buffer[256]  = TEXT("");
   TCHAR   error[1024]  = TEXT("");
   LPVOID lpvMessageBuffer;
   DWORD  dwWaitRes;

   wsprintf(buffer,TEXT("API = %s, "), lpszAPI);
   lstrcat(error, buffer);

   ZeroMemory(buffer, sizeof(buffer));
   wsprintf(buffer,TEXT("error code = %d, "), GetLastError());
   lstrcat(error, buffer);

   // Получим строку ошибки.
   FormatMessage(
      FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
      NULL, GetLastError(),
      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
      (LPTSTR)&lpvMessageBuffer, 0, NULL);

   ZeroMemory((LPVOID)buffer, (DWORD)sizeof(buffer));
   wsprintf(buffer,TEXT("message = %s"), (TCHAR *)lpvMessageBuffer);
   lstrcat(error, buffer);

   // Освободим буфер назначенный системой.
   LocalFree(lpvMessageBuffer);

   // Запишем строку ошибки отладчику.
   OutputDebugString(error);

   // Если имеете запущенный поток, прикажите ему остановиться. Кое-что пошло 
   // неправильно, и Вы должны остановить их так, чтобы смогли сообщить
   // Диспетчеру управления службами (SCM).
   SetEvent(hStopEvent);

   // Ждем потоки, которые останавливаются.
   for (t=1;TRUE;t++)
   {
      if ((dwWaitRes = WaitForMultipleObjects(3,hThreads,TRUE,1000))
           == WAIT_OBJECT_0)
         break;
      else if ((dwWaitRes == WAIT_FAILED)||
               (dwWaitRes == WAIT_ABANDONED))
         break; // Наше ожидание завершилось ошибкой
      else
      {
         SetTheServiceStatus(SERVICE_STOP_PENDING, 0, t, 3000);
      }
   }

   // Останавливаем службу
   SetTheServiceStatus(SERVICE_STOPPED, GetLastError(), 0, 0);
}

 
Назад в оглавление
На главную страницу

Hosted by uCoz