Следующий типовой код демонстрирует, как простая служба может породить рабочие потоки, ответить Диспетчеру управления службами (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);
}
|