-
Добрый вечер. Подскажите, пож-та, как сделать консольное приложение, чтобы его можно было запустить из cmd и установить как сервис Windows. Например, с oracle.exe можно так делать. И видел еще примеры таких программ. Спасибо
-
Через ключи командной строки, где определяйся как работать будешь, либо как консоль, либо как сервис через вызов StartServiceCtrlDispatcher().
-
Т.е. пишу консольное приложение. При запуске смотрю список параметров переданных в прогу? Например, если параметр -service то делать, что вы написали выше. Так? Можете поподробнее описать. Спасибо.
-
-
FindCmdLineSwitch('service', ['-', '\', '/'], True)
-
Из вашего примера. Мне нужен только один сервис, так что все, что связано со вторым можно удалить. Еще вопрос: приложение в режиме сервиса должно будет слушать порт TCP. Что-то нужно будет писать в FirstMainProc?
-
> Через ключи командной строки, где определяйся как работать > будешь, либо как консоль, либо как сервис
- помимо ключей можно проверить, что приложение запущено не под SCM - причем для консольного все гораздо проще - например GetConsoleWindow() <> 0, да и StartServiceCtrlDispatcher практически мгновенно ERROR_FAILED_SERVICE_CONTROLLER_CONNECT возвращает... А в запущенных detached - больше минуты SCM по сусекам ищет, приходится по жетону SID-ы проверять( http://win32.mvps.org/security/is_svc.txt)...
-
> так что все, что связано со вторым можно удалить.
угу
> Что-то нужно будет писать в FirstMainProc?
ну да, это считай тот-же begin..end который у консоли, в который ты тоже будеш писать код...
-
> - помимо ключей можно проверить, что приложение запущено > не под SCM - причем для консольного все гораздо проще - > например GetConsoleWindow() <> 0, да и StartServiceCtrlDispatcher > практически мгновенно ERROR_FAILED_SERVICE_CONTROLLER_CONNECT > возвращает...
Это посмотрю. Спс.
> Что-то нужно будет писать в FirstMainProc? > > ну да, это считай тот-же begin..end который у консоли, в > который ты тоже будеш писать код...
Вы немного не поняли, или я неправильно выразился.
Для прослушки порта буду использовать Indy 10 (idTCPServer). При запуске сервиса начинаю слушать порт, при остановке перестаю слушать порт. Получается, что в коде FirstMainProc надо только извещение о том, что сервис работает, т.к. в Indy есть событие OnExecute.
Верно?
-
Эмм, грубо, как только произойдет выход из функции FirstMainProc работа сервиса завершится. Как ты будешь делать - решать тебе. Я обычно делаю отдельный модуль, в который выношу саму логику работы, а в основном цикле создаю пайп для управления сервисом и вишу на нем ожидая управляющих команд.
-
Стал потихоньку переделывать Ваш пример. В самом начале добавил: {$APPTYPE CONSOLE} и все ShowMsg заменил на WriteLn. Главную процедуру изменил на: procedure MainProc(ArgCount: DWORD; var Args: array of PChar); stdcall;
var
Context: DWORD;
begin
Context := Context;
StatusHandle := RegisterServiceCtrlHandlerEx(Name, @ServicesCtrlHandler, @Context);
if (StatusHandle <> 0) and Initialize and NotifyIsRunning then
try
except
end;
ExitThread(0);
end;
Дает 99% загрузки проца. Служба устанавливается и запускается (процесс в диспетчере задач видно). Проблема с остановкой. Нажимаю остановить процесс убивается и долго-долго винда пытается его остановить, после чего сообщение: Не удалось остановить службу TSvcTest на Локальный компьютер. Ошибка 1053: Служба не ответила на запрос своевременно. Что я сделал не так?
-
Странно, загрузки быть не должно, а по поводу остановки, попробуй SERVICE_STOP заменить на SERVICE_STOP_PENDING в данном коде. ЗЫ: вообще по хорошему вопрос у тебя интересный, надо демку написать как время освободится...
Завтра я болею - послезавтра свяжись со мной по аське в профиле, если не получится реализовать как надо. Попробуем разрулить ситуацию...
-
Я использую примерно такой код для создания программ, которые я называю гибридными, т.е. которые могут запускаться как сервис или как консоль: http://dvmuratov.narod.ru/uServices.pasКонсоль создается классом http://dvmuratov.narod.ru/uConsole.pasКласс TSCManager просто управляет сервисами Чаще всего я консоль использую только для вывода логов, поэтому ее функционал по вводу данных минимальный, она просто создается и в ней надо нажать CTRL+Q для выхода в случае по-умолчанию. Как использовать THybridApplication:
type
TMyService = class (THybridApplication)
private
FServiceMonitor: TServiceMonitorThread;
public
procedure Init; override;
procedure Done; override;
function GetServiceController: TServiceController; override;
function GetServiceMainProc: TServiceMainProc; override;
end;
var
MyApp: TMyService;
implementation
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
MyApp.Controller(CtrlCode);
end;
procedure ServiceMainProc(ArgCount: DWORD; var Args: array of PChar); stdcall;
begin
MyApp.MainProc(ArgCount, Args);
end;
function TMyService.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
function TMyService.GetServiceMainProc: TServiceMainProc;
begin
Result := ServiceMainProc;
end;
procedure TMyService.Init;
begin
StartLog;
try
except
on e: Exception do
Logger.Error(e.ClassName + ' : ' + e.Message);
end;
end;
procedure TMyService.Done;
begin
try
except
on e: Exception do
Logger.Error(e.ClassName + ' : ' + e.Message);
end;
StopLog;
end;
-
Ну а в файле проекта так:
MyApp := TMyService.Create(rsServiceName,
rsServiceDisplayName,
rsServiceDesc);
try
MyApp .Run;
finally
MyApp .Free;
end;
-
Попробовал следующее: В RAD 2009 создал новый проект Service Application. И сделал код такой: program myprog;
uses
SysUtils,
SvcMgr,
SvcUnit in 'SvcUnit.pas' ,
Console in 'Console.pas';
begin
if FindCmdLineSwitch('service', ['-'], True) then
begin
if not Application.DelayInitialize or Application.Installing then
Application.Initialize;
Application.CreateForm(TListener, Listener);
Application.Run;
end
else
begin
Work('MYPROG');
end;
end. unit Console;
interface
uses
SysUtils;
procedure Work(const Invit: String);
implementation
procedure Work(const Invit: String);
var
Terminated: Boolean;
Input: String;
begin
Terminated := False;
while not Terminated do
begin
Write(Invit + '> ');
ReadLn(Input);
if ANSILowerCase(Input) = 'exit' then
Terminated := True;
end;
end;
end. Вроде работает и так и так.
-
Работает на Win XP и 2003. На Win 7 не устанавливается как сервис.
-
> Андрей2711 (29.11.12 21:17) [15]
> На Win 7 не устанавливается как сервис.
а если {$APPTYPE CONSOLE} убрать?
-
Мне на Win 7 не очень то и надо, это я так просто для инфы скинул. Мне кажется, что если убрать {APPTYPE CONSOLE}, то работать как консольное приложение не будет. Проверю вечером.
-
> Андрей2711 (30.11.12 14:04) [17]
> Мне кажется, что если убрать {APPTYPE CONSOLE}, то работать > как консольное приложение не будет.
Конечно не будет. Консоль надо вручную создавать, смотри, что я выше писал.
-
program myprog;
uses
Windows;
begin
WriteLn('Test');
ReadLn;
end.
-
> anatoly podgoretsky © (30.11.12 15:31) [19]
EInOutError будет, даже проверять не надо.
-
Так выдает ошибку на WriteLn project myprog.exe raised exception class EInOutError with message 'I/O error 105'.
-
> Андрей2711 (30.11.12 21:39) [21]
Я ж тебе дал практически готовый рабочий вариант, простых путей не будет.
-
> Я ж тебе дал практически готовый рабочий вариант, простых > путей не будет.
Спасибо. Но ввиду того, что работает на Xp и 2003, то меня устроит вариант, который я написал выше. Ибо на Win 7 Vista и пр. не пофигу :)
Просто так получается по проще. :)
-
Кстати, создал пустой сервис и попытался установить на Win7. Получаю: Exception EOSError in module Project1.exe at 000112A5. System Error. Code: 5. Отказано в доступе.
-
> Андрей2711 (02.12.12 22:43) [24]
> System Error. Code: 5. Отказано в доступе.
А подумать? Запускай от имени администратора консоль и оттуда запускай установку с ключом /install (или какой там у тебя).
-
Вот блин. Спасибо.
|