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