![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Попытка разобраться с сервисами [D7, XP]
Vlad © (2008-07-18 14:09:00)Пытаюсь разобраться с сервисами. Переписал пример, найденный в интернете. Запускаться - запускается, а вот ни инсталлироваться в качестве сервиса, ни работать, не хочет.
Подскажите, это автор что-то намудрил, или я? Где ошибка?
Вот код...
program SvcRunner;
uses
Windows,WinSvc,ShellApi;
{$R *.res}
const
ServiceName='SvcRunner';
ServiceDisplayName='SvcRunner';
var
DispatchTable : array [0..1] of _SERVICE_TABLE_ENTRYA;
ServiceStatus: SERVICE_STATUS;
ServiceStatusHandle:SERVICE_STATUS_HANDLE;
hThread:Cardinal;
procedure LogError(ErrorString:string);
begin
end;
procedure ServiceCtrlHandler(Opcode : Cardinal);stdcall;
var
Status : Cardinal;
begin
case Opcode of
SERVICE_CONTROL_PAUSE :
begin
ServiceStatus.dwCurrentState := SERVICE_PAUSED;
SuspendThread(hThread);
end;
SERVICE_CONTROL_CONTINUE :
begin
ServiceStatus.dwCurrentState := SERVICE_RUNNING;
ResumeThread(hThread);
end;
SERVICE_CONTROL_STOP :
begin
ServiceStatus.dwWin32ExitCode:=0;
ServiceStatus.dwCurrentState := SERVICE_STOPPED;
ServiceStatus.dwCheckPoint :=0;
ServiceStatus.dwWaitHint :=0;
if not SetServiceStatus (ServiceStatusHandle,ServiceStatus) then
begin
Status:=GetLastError;
LogError('SetServiceStatus');
Exit;
end;
exit;
end;
SERVICE_CONTROL_INTERROGATE : ;
end;
if not SetServiceStatus (ServiceStatusHandle, ServiceStatus) then
begin
Status := GetLastError;
LogError('SetServiceStatus');
Exit;
end;
end;
function ServiceInitialization(argc:DWORD; argv:array of PChar; specificError:DWORD):DWORD;stdcall;
begin
Result:=0;
end;
function MainServiceThread(p:Pointer):DWORD;stdcall;
var
h:HWND;
begin
h:=FindWindow(nil,'Bla-Bla-Bla');
if h<>0 then
PostQuitMessage(h);
end;
procedure ServiceProc(argc:DWORD; var argv:array of PChar);stdcall;
var
Status : DWORD;
SpecificError : DWORD;
ThId:Cardinal;
begin
ServiceStatus.dwServiceType := SERVICE_WIN32;
ServiceStatus.dwCurrentState := SERVICE_START_PENDING;
ServiceStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP
or SERVICE_ACCEPT_PAUSE_CONTINUE;
ServiceStatus.dwWin32ExitCode := 0;
ServiceStatus.dwServiceSpecificExitCode := 0;
ServiceStatus.dwCheckPoint := 0;
ServiceStatus.dwWaitHint := 0;
ServiceStatusHandle :=RegisterServiceCtrlHandler(ServiceName,@ServiceCtrlHandler);
if ServiceStatusHandle = 0 then WriteLn('RegisterServiceCtrlHandler Error');
Status :=ServiceInitialization(argc,argv,SpecificError);
if Status <> NO_ERROR then
begin
ServiceStatus.dwCurrentState := SERVICE_STOPPED;
ServiceStatus.dwCheckPoint := 0;
ServiceStatus.dwWaitHint := 0;
ServiceStatus.dwWin32ExitCode:=Status;
ServiceStatus.dwServiceSpecificExitCode:=SpecificError;
SetServiceStatus (ServiceStatusHandle, ServiceStatus);
LogError('ServiceInitialization');
exit;
end;
ServiceStatus.dwCurrentState :=SERVICE_RUNNING;
ServiceStatus.dwCheckPoint :=0;
ServiceStatus.dwWaitHint :=0;
if not SetServiceStatus (ServiceStatusHandle,ServiceStatus) then
begin
Status:=GetLastError;
LogError('SetServiceStatus');
exit;
end;
hThread:=CreateThread(nil,0,@MainServiceThread,nil,0,ThID);
WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread);
end;
begin
DispatchTable[0].lpServiceName:=ServiceName;
DispatchTable[0].lpServiceProc:=@ServiceProc;
DispatchTable[1].lpServiceName:=nil;
DispatchTable[1].lpServiceProc:=nil;
if not StartServiceCtrlDispatcher(DispatchTable[0]) then
LogError('StartServiceCtrlDispatcher Error');
end.
Vlad (2008-07-24 10:19:00) [1]
> Сергей М. © (22.07.08 20:26) [26]
Вариант (23.07.08 07:03) [27]
Спасибо за наводку и советы. Переделал с проверкой возвращаемых результатов CreateProcess, WaitForSingleObject и OpenProcess, который добавил для получения HANDLE.
Перенес код в консольное приложение, как и посоветовал Вариант (22.07.08 12:39) [24], отладил, затем перенес в сервис.
Все отлично работает.
Отдельное спасибо Rouse_ © и sniknik ©за примеры.
Вариант (2008-07-23 07:03:00) [2]
> Vlad (22.07.08 17:45) [25]
Да - косяк. ProcessID и HANDLE разные вещи. Если не разобрался еще в чем косяк, то - процесс надо открыть для получения HANDLE -см. OpenProcess или создать, если процесс не найден -см. CreateProcess. И анализируй возврат функций - WaitForSingleObject например может вернуть и WAIT_FAILED, что в твоем случае и было скорее всего.
Сергей М. (2008-07-22 20:26:00) [3]Косяк не только и не столько в этом.
Заимей приличие анализировать результаты WinAPI-вызовов)
Vlad (2008-07-22 17:45:00) [4]Подозреваю, что нашел свой косяк...
.............................
If Process32First(HSnapShot, pe) Then
repeat
if pe.szExeFile='aserv.exe' then
begin
AppRunning:=true;
hndl:=pe.th32ProcessID;
end;
until ( not Process32Next(HSnapShot,pe) );
closehandle(HSnapShot);
if AppRunning then
WaitForSingleObject( hndl , INFINITE)
......................
Вариант (2008-07-22 12:39:00) [5]
> Vlad (22.07.08 12:12) [23]
> Переделал по твоему варианту. Ничего не изменилось - та
> же 100% загруженность проца (((
Код твой не читал - нет времени, да и переделанного кода тут нет.
Могу только сказать, что WaitForSingleObject(HANDLE,INFINITE) при HANDLE в несигнальном состоянии не загружает процессор, а переводит поток в состояние ожидания события. Значит есть ошибка в логике выполнения программы. Поищи или код выложи сюда. Дам совет, напиши сперва обычную консольную или GUI задачу, с отладчиком продись по коду и найди возможные недоразумения. А потом уже рабочий и отлаженный код переноси в сервис.
Vlad (2008-07-22 12:12:00) [6]
> Сергей М. © (22.07.08 10:29) [21]
Спасибо. Но если честно, то мне кажется, что для меня это пока слишком сложно (((
> Вариант (22.07.08 10:31) [22]
Переделал по твоему варианту. Ничего не изменилось - та же 100% загруженность проца (((
Вариант (2008-07-22 10:31:00) [7]
> Vlad (22.07.08 10:04) [19]
если первоначально процесс запущен не тобой
Вариант -
1 Найди процесс в памяти (например CreateToolhelp32Snapshot) -> если нет процесса то 2, если есть процесс выполни 3
2 - создай процесс, получив его Handle, отслеживай по WaitFor...., при переходе handle в сигнальное состояние (процесс завершен) выполни 2
3 - открой процесс, получив его Handle, отслеживай по WaitFor (SingleObject или MultipleObjects)...., при переходе handle в сигнальное состояние (процесс завершен) выполни 2
Сергей М. (2008-07-22 10:29:00) [8]Неправильно понял.
http://msdn.microsoft.com/en-us/library/ms802952.aspx
Rouse_ (2008-07-22 10:15:00) [9]
> Когда я запускаю чуть переделанный сервис Rouse_ ©, то он
> отжирает все свободное процесорное время, то есть загрузка
> 100%. Как обычно этого избегают? Таймер?
Это потому что рабочий цикл сервиса пустой и крутится в холостую отьедая процессорное время :) Добавь полезную нагрузку и все будет нормально :)
Vlad (2008-07-22 10:04:00) [10]Мммм... Правильно ли я понял, что предлагается сделать глобальный хук на (например) CreateProcessA? И там уже смотреть, оно-не оно?
Сергей М. (2008-07-22 09:50:00) [11]
> Vlad (22.07.08 09:41) [17]
Чем, собссно, в этом "разрезе" сервис отличается от не-сервиса ?
Ничем. Обычное приложение решит ту же задачу с той же степенью кривизны логики. Так зачем же кривую логику тащить сразу в сервис ?
Мониторинг старт/стопа процессов должен осуществляться на уровне драйвера режима ядра, а не сервиса, никакие таймеры при этом не требуются - нотификация о событиях старт/стопа использует колбэк-механизм.
На wasm.ru имеется статья с примерами.
Vlad (2008-07-22 09:41:00) [12]
> Сергей М. © (21.07.08 19:21) [16]
Сударь, вы столь многословны )))
Шучу, спасибо за ответ.
А какие есть альтернативные пути решения? Код вверху.
Я не прошу написать мне код, хоть пример был бы желателен, но совершенно необязателен. Дайте направление. Если разбирусь сам, хорошо, если нет, задам вопрос уже в другой теме и хоть с какими то конкретными знаниями.
Сергей М. (2008-07-21 19:21:00) [13]
> вы считаете, это нормальное решение?
Это дерьмовое решение.
Хоть в сервисе, хоть не в сервисе.
Vlad (2008-07-21 16:44:00) [14]По поводу 100%-ой загрузки процессора...
Использование в сервисе SetTimer, KillTimer - это нормальная практика? Сам сервис призван делать только 1 вещь -мониторить запущен ли определенный процесс и если нет, запускать его... Думаю сделать опрос по таймеру, а не как сейчас... Как вы считаете, это нормальное решение?
Vlad (2008-07-21 14:00:00) [15]
> Сергей М. © (21.07.08 13:05) [12]
Я посчитал, что проще найти нормальный пример. Мне не трудно отформатировать, но лично для меня такой код - это признак плохого отношения к читателям. Но это естественно мое личное ИМХО.
> palva © (21.07.08 13:27) [13]
Буду иметь ввиду, спасибо.
А по моем двум вопросам, подскажите?
> Vlad (21.07.08 12:19) [9]
palva (2008-07-21 13:27:00) [16]
> Я не смог продраться через неотформатированный код и рваные
> объяснения. Может я и не прав.
Может быть, неправ в том, что искал статьи ориентированные на Delphi. API-программированием логичнее заниматься на си, и дельных материалов по программированию служб можно найти гораздо больше, если не ограничиваться языком Delphi. Даже документацию по API лучше читать в MSDN, а не в делфийском хэлпе.
Сергей М. (2008-07-21 13:05:00) [17]
> Vlad (21.07.08 12:33) [11]
Отформатировать сравнительно небольшой код ради благой идеи понять как он работает вовсе не сложно.
Vlad (2008-07-21 12:33:00) [18]
> Сергей М. © (21.07.08 12:24) [10]
Честно?
Просто пример с сервисом на WinApi был наиболее разжеван и, как мне показалось (но тут товарищи не согласны вроде), написан более знающим человеком, чем примеры с TServiceApplication. На самом деле, с последним я нашел только один пример, зато в куче мест.
Например вот тут http://www.realcoding.net/article/view/4164
Я не смог продраться через неотформатированный код и рваные объяснения. Может я и не прав.
Сергей М. (2008-07-21 12:24:00) [19]
> Vlad (18.07.08 15:40) [6]
>
>
> Мне показалось, что ничем не хуже, чем TServiceApplication
Совершенно нелогично)
Раз ты искал альтернативу классу TServiceApplication, значит он тебя чем-то не устраивает. Вот я и спрашиваю - чем он тебя не устраивает ?
Vlad (2008-07-21 12:19:00) [20]
> sniknik © (18.07.08 16:48) [7]
> Rouse_ © (18.07.08 19:31) [8]
Спасибо, оба примера очень информативны.
Если позволите, еще парочка ламерских вопроса...
1) Когда я запускаю чуть переделанный сервис Rouse_ ©, то он отжирает все свободное процесорное время, то есть загрузка 100%. Как обычно этого избегают? Таймер?
2) Эм... Мне стыдно, но я не пойму, почему он ругается на BM_CLICK. Неизвестный идентификатор. В Uses прописаны Windows, WinSvc, ShellApi;
Выполняющийся код внизу.
function ServiceThread(P: Pointer): DWORD; stdcall;
var
h,HW:HWND;
si: Tstartupinfo;
pr: Tprocessinformation;
begin
if not ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0) then begin
ErrorLog('ServiceThread SERVICE_RUNNING : '+GetErrosString);
result:= GetLastError;
exit;
end;
try
while (Status.dwCurrentState <> SERVICE_STOP_PENDING) do begin
h:=FindWindow(nil,'Сервер регистрации пользователей');
If h=0 then
begin
FillChar( Si, SizeOf( Si ) , 0 );
with Si do
begin
cb := SizeOf( Si);
dwFlags := startf_UseShowWindow;
wShowWindow := 4;
end;
Createprocess(nil, 'C:\MasterCash\aserv.exe', nil, nil,false, Create_default_error_mode, nil, nil, si, pr);
sleep(10000);
HW := FindWindowEx(FindWindow('TMessageForm', 'aserv'), 0, 'TButton', nil);
if HW<>0 then
//PostMessage (hw, BM_CLICK, 0, 0);
sleep(1000);
h:=FindWindow(nil,'Сервер регистрации пользователей');
ShowWindow(h,SW_MINIMIZE);
end;
end;
result:= 0;
finally
Status.dwCurrentState:= SERVICE_STOP;
end;
end;