> Подскажите, пожалуйста, как грамотно остановить сервис, > обеспечить корректное завершение рабочего потока? Остановить все потоки. Установить свой статус в STOPPED. Завершить выполнение ServiceMain.
> void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv) > { > <skip> > ss.dwCurrentState = SERVICE_RUNNING; > SetServiceStatus( hSS, &ss); > > hThread = _beginthreadex( NULL, 0, &SniffThread, > NULL, 0, &thID ); > if (! hThread) > err(_T("Error creating sniffer thread.")); > > } мм.. а дальше* ServiceMain должен завершаться только при завершении сервиса, а так висеть и к примеру ждать какого нить Mutex'а.
[Win32] Не могу корректно остановить сервис NT.23.05.04 23:43 Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 23.05.04 23:44 Количество правок: 1
Подскажите, пожалуйста, как грамотно остановить сервис, обеспечить корректное завершение рабочего потока?
Предполагается, что сервис останавливается из mmc SCM. При завершении работы необходимо, чтобы рабочий поток освободил все используемые ресурсы.
В call-back хендлере, собственно, сделана робкая попытка только установить некий глобальный флаг g_bStopSniff = TRUE. Предполагалось (по наивности ), что рабочий поток сможет проанализировать g_bStopSniff ==TRUE ;)))Самое смешное, что некоторые гуру так и делают, блин ;))
Т.е хендлер:
DWORD WINAPI ServiceCtrlHandler( DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext )
{
DWORD rc = NO_ERROR;
switch (dwControl) {
case SERVICE_CONTROL_STOP:
g_bStopSniff = TRUE;
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
break;
case SERVICE_CONTROL_PAUSE:
<skip>
default:
rc = ERROR_CALL_NOT_IMPLEMENTED;
break;
}
SetServiceStatus( hSS, &ss);
return rc;
}
Рабочим потоком может быть как, как собственно void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv), так и вторичный поток с функцией потока SniffThread:
void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv)
{
<skip>
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( hSS, &ss);
hThread = _beginthreadex( NULL, 0, &SniffThread, NULL, 0, &thID );
if (! hThread)
err(_T("Error creating sniffer thread."));
}
Понятно, что я хочу в этом примере. Если bStopSniff == TRUE, то я хочу остановить цикл while, прибить сокет snfSock и закрыть файл fl. Если там были выделены ещё какие-нить ресурсы, то хочу их освободить корректно.
В общем, что видится… Когда SCM вызывает хендлер ServiceCtrlHandler (SERVICE_CONTROL_STOP), то такое ощущение, что и ServiceMainProc и его вторичные потоки (если таковые были) – всё уже прибито SCM-ом ;( Поздно пить боржоми, чтобы чего-то там освободить ;((
Как быть, научите, плиз, недопрограммера ;)))
Остановить все потоки. установить свой статус в stopped...24.05.04 00:18 Автор: Killer{R} <Dmitry> Статус: Elderman
> Подскажите, пожалуйста, как грамотно остановить сервис, > обеспечить корректное завершение рабочего потока? Остановить все потоки. Установить свой статус в STOPPED. Завершить выполнение ServiceMain.
> void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv) > { > <skip> > ss.dwCurrentState = SERVICE_RUNNING; > SetServiceStatus( hSS, &ss); > > hThread = _beginthreadex( NULL, 0, &SniffThread, > NULL, 0, &thID ); > if (! hThread) > err(_T("Error creating sniffer thread.")); > > } мм.. а дальше* ServiceMain должен завершаться только при завершении сервиса, а так висеть и к примеру ждать какого нить Mutex'а.
Сорри, Вы наверное про это и постили - то, что в конце я там...24.05.04 02:07 Автор: void <Grebnev Valery> Статус: Elderman
> Остановить все потоки. Установить свой статус в STOPPED.
1) Ты не понял. Рабочие потоки я могу создать, к примеру, в ServiceMain. Дескрипторы этих потоков и всего, что в них создаётся ( сокетов, файлов и т.д.) я могу сделать, конечно глобальными. Но! Особождать ресурсы из другого потока (например, из которого вызывается хендлер SCM) и оттуда прибивать рабочие потоки при помощи BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode );
- ИМХО bad desing.
Потоки сами должны каким-то образом получить оповещение о том, что юзверь кликнул в остнастке SCM "stop". Они сами должны освободить ресурсы, которые ранее запрашивали, а затем функция потока должна завершиться, вернув 0.
2) Когда я пытаюсь что-то установить в хендлере сервиса (например, свой флаг STOP) , то подозреваю, что поток ServiceMain уже прибит силой SCM. Другими словами, когда в ServiceCtrlHandler я получаю dwControl = SERVICE_CONTROL_STOP, то все потоки рождённые от ServiceMain выглядят совершенно мёртвыми. В этих потоках невозможно по описанному сценарию проанализировать некую глобальную переменную STOP, и затем корректно самостоятельно завершить работу.
> Завершить выполнение ServiceMain. > > void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR > *lpszArgv) > > { > > <skip> > > ss.dwCurrentState = SERVICE_RUNNING; > > SetServiceStatus( hSS, &ss); > > > > hThread = _beginthreadex( NULL, 0, &SniffThread, > > NULL, 0, &thID ); > > if (! hThread) > > err(_T("Error creating sniffer thread.")); > > > > } > мм.. а дальше* ServiceMain должен завершаться только при > завершении сервиса, а так висеть и к примеру ждать какого > нить Mutex'а.
Ошибаешься ;)))) В той части, что ServiceMain умеет, что-то ждать (хоть там критической секции, хоть Mutex-ов;))), если в ServiceCtrlHandler ты получил dwControl = SERVICE_CONTROL_STOP и в ответ установил статус сервиса ss.dwCurrentState = SERVICE_STOPPED, то ServiceMain и все дочерние её потоки (если таковые были), будутнещадно_прибиты SCM. ;))
Ниже, совершенно рабочий код с_while(1)_в конце. ServiceMain ни в жисть не подвиснет там. ;)) Этот поток просто будет убиенным SCM-омо при остановке сервиса.
void WINAPI ServiceMainProc(DWORD dwArgc, LPTSTR *lpszArgv)
{
<skip>
ss.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( hSS, &ss);
while(1); // здесь поток не подвиснет. С ним расправится SCM ;)))
}
OOO !!! Вот я идиот. Но всё равно, подскажите24.05.04 02:04 Автор: void <Grebnev Valery> Статус: Elderman Отредактировано 24.05.04 02:05 Количество правок: 1
Что называется, бывает хуже, но реже, блин....Добавил Sleep(1000). Естественно заработало ;)))
Т.е. та функция хендлера добавлена одной строчкой с :
DWORD WINAPI ServiceCtrlHandler( DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext )
{
DWORD rc = NO_ERROR;
switch (dwControl) {
case SERVICE_CONTROL_STOP:
g_bStopSniff = TRUE;
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
// HERE ;))) OOOOOOO !!!!
Sleep(1000);
break;
case SERVICE_CONTROL_PAUSE:
<skip>
default:
rc = ERROR_CALL_NOT_IMPLEMENTED;
break;
}
SetServiceStatus( hSS, &ss);
return rc;
}
Это работает. Но всё же. Как делают программисты более грамотно. Может перед установкой ss.dwCurrentState = SERVICE_STOPPED делать так:
case SERVICE_CONTROL_STOP:
bStopSniff = TRUE;
ss.dwCurrentState = SERVICE_STOP_PENDING;
ss.dwWaitHint = 1000;
SetServiceStatus( hSS, &ss);
Sleep(1000);
ss.dwWin32ExitCode = 0;
ss.dwCurrentState = SERVICE_STOPPED;
ss.dwCheckPoint = 0;
ss.dwWaitHint = 0;
break;
Как думают программисты?
тут уже был целый постинг с примерным сервисом на делфи правда24.05.04 02:48 Автор: Killer{R} <Dmitry> Статус: Elderman
> Вот тут - > http://www.bugtraq.ru/cgi-bin/forum.mcgi?type=sb&b=2&m=1046 > 83 Да, внимательно не посмотрел вначале. Думал, что и так всё знаю. ;))) Чего смотреть ;))))
Конечно можно и так, при получении хендлером dwControl == SERVICE_CONTROL_STOP ответить только SetState(SERVICE_STOP_PENDING).
Далее дождаться эвента в рабочем потоке ServiceMain и там уже завершиться с установкой SetState(SERVICE_STOPPED).
Кстати, да. Так чисто получается ;)))
Ща мы тот код слямзим и отрехтуем в C ;)))