Страница 1 из 1

Работа в режиме службы

Добавлено: 10 Октябрь 2006, 12:22
Malex
Программа на Кларион, работает в трее на Windows 2003 server. Как организовать ее работу как служба Windows? Давно встречал статью про какуюто утилиту, кот. позволяла это делать. Может кто знает ?

Добавлено: 10 Октябрь 2006, 12:44
Admin
Смотри: http://www.authord.com/Clarion/
Там есть: http://www.passwordsmax.com/winapitk.zip
в нем смотри: Run an application as an NT Service (32-bit, NT).

Добавлено: 10 Октябрь 2006, 16:53
Malex
Спасибо! Попробовал на Win XP, работает как часы. Думаю на Win 2003 serv. проблем не будет.

Re: Работа в режиме службы

Добавлено: 25 Январь 2010, 13:43
seawolf
Вытащил на свет этот старый пост со старым кодом. Поскольку копнул службы решил покопаться внутри (пока есть вермя). InstallService и RemoveService здесь вроде все отрабатывает.
Пытаюсь запускать приложение как сервисы (как свое так и заведомо работающие).
Почему-то StartServiceCtrlDispatcher все время выдает 0. Его таблицу заполняем адресом присвоенного именем ServiceName. Почему-то сгенеренный код делает присваивание ServiceName='ServiceMain' и до вызова ServiceMain процедуры дело не доходит. В чем тут заковыка могет быть? Свое Приложение не в трее и не консоль, просто без окна.


StartServiceMain PROCEDURE

ServiceTable GROUP,PRE(LOC)
ServiceName UNSIGNED ! ADDRESS() of Service Name
ServiceProc UNSIGNED ! ADDRESS() of Main Service Proc
EndServiceName SIGNED(0) ! Null to indicate end
EndServiceProc SIGNED(0) ! Null to indicate end
END

CODE

ServiceName='ServiceMain'
LOC:ServiceName = ADDRESS(ServiceName)
LOC:ServiceProc = ADDRESS(ServiceMain)

IF StartServiceCtrlDispatcher(ADDRESS(ServiceTable))
ELSE
MESSAGE(GetAPIError() & '.<13>Cannot start service.','Error')
.


ServiceMain PROCEDURE(pParam1,pParam2)
LOC_ThreadID ULONG

CODE


hServiceStatus=RegisterServiceCtrlHandler(ADDRESS(ServiceName),ADDRESS(ServiceHandler))
IF ~hServiceStatus
MESSAGE(GetAPIError() & '.<13>Cannot register service handler.','Error')
.
CurrentState = SERVICE_RUNNING
SCResult = SendStatustoSCM(SERVICE_RUNNING, NO_ERROR, 0, 1, 1000)
ServiceRunning=1
GLO:ServiceStatus = ServiceRunning + 2 * ServicePaused
! Main - need to call as a new thread to operate properly.
IF ~CreateThread(0,0,ADDRESS(main),0,0,LOC_ThreadID)
DO ServiceStop
.
DO ServiceStop

Re: Работа в режиме службы

Добавлено: 29 Январь 2010, 12:46
seawolf
Весь этот старый пример прекрасно собирается и работает под clarion5. Под 6 нет!

Re: Работа в режиме службы

Добавлено: 05 Февраль 2010, 0:24
WadimZapara
Почему-то StartServiceCtrlDispatcher все время выдает 0
1) Интересно, а как происходит запуск ?
Ведь успех-то может быть только при запуске от SCM - именно как службы из диспетчера служб или там NET START... и т.д..
И что при этом говорит GetLastError() - а он-то многое расскажет!
2) А MESSAGE-то появляется ? А служба-то уже зарегистрирована в SCM ? И именно, как взаимодействующая с рабочим столом.
3) А как объявлена переменная ServiceName (я про атрибут STATIC и тип CSTRING)
4) А что у тебя ServiceHandler...
5) А что делает ServiceStop routine ...
...
В общем - мало информации об условиях неработоспособности :roll:

Re: Работа в режиме службы

Добавлено: 05 Февраль 2010, 14:10
seawolf
Ну собственно вот исходник практически не писаный самим (его сгенерил шаблон в C5). Имя сервиса а прописал.
GetLastError() возвращает 0. Запуск через sc - 1067. Сервис уже инсталлирован (InstallServcie). Sc query tgtmsvc отвечает что такой есть но пока не запущен. Попытка сделать start и вываливается MESSAGE(GetAPIError() & '.<13>Cannot start service.','Error'). Все тоже самое на C5 сходу работает.

Re: Работа в режиме службы

Добавлено: 08 Февраль 2010, 1:52
WadimZapara
Оформлено как DLL ?

Re: Работа в режиме службы

Добавлено: 08 Февраль 2010, 10:57
seawolf
Нет это просто по include затащен в проект. Сам проект прога exe. Без интерфейса, раз в 20 секунд делает опрос pop3 ящика и разбор чего там есть. Под 5 версией работает без проблем. Под 6 просто служба не стартует и все. Вот если руки дойдут под 7 посмотрю.

Re: Работа в режиме службы

Добавлено: 08 Февраль 2010, 22:53
WadimZapara
А что же у тебя является основным кодом EXE-шника ? Ответь для ясности. Ща смотрю твой пример. Соображения есть... Изложу чуть позже.
И ещё - условия испытаний одинаковы (Под одной и той же операционкой, с одинаковыми ли правами запускаешь) ?
Запускаешь сервис с помощью чего (NET START / SC.EXE / SEVICES.MSC) ?

P.S. GetLasError()=1067 - The process terminated unexpectedly. (Процесс завершился неожиданно)

Re: Работа в режиме службы

Добавлено: 09 Февраль 2010, 0:04
WadimZapara
Думаю, в основном - всё идёт от иной THREAD-модели в C6 по сравнению с C5. И поэтому - некоторые процедуры (см.ниже) завершаются, а система продолжает работать с их переменными, которые уже выгружены из памяти.
Вероятно основной код программы состоит из вызова (или после обработки параметров заканчивается вызовом) StartServiceMain

Объяви переменные глобально и попробуй:
ServiceTable GROUP из процедуры StartServiceMain
LOC:SERVICE_STATUS LIKE(SERVICE_STATUS) из функции SendStatusToSCM

ежели не будет изменений -
не завершай процедуру ServiceMain после запуска нового потока, а отправь его в спячку до возникновения какого-то события.
То есть после строк
IF ~CreateThread(0,0,ADDRESS(Main),0,0,LOC_ThreadID)
DO ServiceStop
.
допиши типа:

Код: Выделить всё

    !Создаём системное событие для сигнализации необходимости останова
    ghSvcStopEvent = CreateEvent(0,       | ! default security attributes
                                 TRUE,    | ! manual reset event
                                 FALSE,   | ! not signaled
                                 0)         ! no name
    If ghSvcStopEvent Then  ! успешно создан объект "событие"
      SendStatustoSCM( SERVICE_RUNNING, NO_ERROR, 0, 1, 1000 )  ! Отметить для SCM: "сервис в работе"
      Loop
        ! Вместо работы - просто ожидаем, когда нам скажут "STOP" - ждём возникновения события <ghSvcStopEvent>
        WaitForSingleObject(ghSvcStopEvent, INFINITE)
        Break
      End
    End
    SendStatustoSCM( SERVICE_STOPPED, NO_ERROR, 0, 1, 1000 )    ! Отметить для SCM: "сервис остановлен"
а в обработке controlCode = SERVICE_CONTROL_STOP в процедуре ServiceHandler укажи (взамен твоего), а также - если нужно при ином условии завершения, например, перед оператором RETURN процедуры MAIN:

Код: Выделить всё

      SendStatustoSCM( SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 1000)      ! изменить состояние на "начат процесс останова"
      SetEvent(ghSvcStopEvent)          ! взвести(активировать) событие <ghSvcStopEvent> (будет принято в ServiceMain)
где ghSvcStopEvent - глобально объявлено, как:
ghSvcStopEvent HANDLE ! Handle события StopEvent
---------------------------------------------------
И ещё, для подключения к Service Control Manager
MSDN ПИШЕТ:
The OpenSCManager function establishes a connection to the service control manager on the specified computer and opens the specified service control manager database.
SC_HANDLE OpenSCManager(
LPCTSTR lpMachineName,
LPCTSTR lpDatabaseName,
DWORD dwDesiredAccess
);
Parameters
lpMachineName [in] Pointer to a null-terminated string that specifies the name of the target computer. If the pointer is NULL or points to an empty string, the function connects to the service control manager on the local computer.
lpDatabaseName [in] Pointer to a null-terminated string that specifies the name of the service control manager database to open. This parameter should be set to SERVICES_ACTIVE_DATABASE. If it is NULL, the SERVICES_ACTIVE_DATABASE database is opened by default.
dwDesiredAccess [in] Access to the service control manager. For a list of access rights, see Service Security and Access Rights.
Before granting the requested access rights, the system checks the access token of the calling process against the discretionary access-control list of the security descriptor associated with the service control manager.
The SC_MANAGER_CONNECT access right is implicitly specified by calling this function.

ПРИ ЭТОМ, СОДЕРЖИТСЯ в advapi32.dll В ДВУХ ВАРИАНТАХ: OpenSCManagerA, OpenSCManagerW
-----------------------------------
ПОЭТОМУ МОЁ ОБЪЯВЛЕНИЕ:
OpenSCManager( <*CString lpMachineName>, <*CString lpDatabaseName>, DWORD dwDesiredAccess ), SC_HANDLE, Pascal, Raw, Dll, Name('OpenSCManagerA')
И МОИ ВЫЗОВЫ:
hSCM = OpenSCManager( , , STANDARD_RIGHTS_WRITE + SC_MANAGER_CONNECT + SC_MANAGER_CREATE_SERVICE ) ! для инсталляции сервиса
hSCM = OpenSCManager( , , STANDARD_RIGHTS_READ + SC_MANAGER_CONNECT + SC_MANAGER_ENUMERATE_SERVICE ) ! для перечисления сервисов
hSCM = OpenSCManager( , , STANDARD_RIGHTS_READ + SC_MANAGER_CONNECT ) ! для подключения

Последнее поможет на случай, если ServicesDatabaseName='ServicesActive' для SCM непонятно.

Re: Работа в режиме службы

Добавлено: 15 Февраль 2010, 10:27
seawolf
Прошу прощение за задержку, только вернулся из поездки. Основной код exe это как и предполагалось вызов StratMainService. Переобъявление в голабальные переменные очереди Loc не помогло. Остальное буду смотреть, после чего отпишусь.

Re: Работа в режиме службы

Добавлено: 18 Февраль 2010, 0:14
WadimZapara
Да, HANDLE и SC_HANDLE - это UNSIGNED.

Кроме того, нужно проверить и иметь в виду следующее:
1. Переменные, которые используются в разных потоках обязательно должны использоваться через систему семафора/мьютекса/события, чтобы более одного THREAD'а не имело возможности доступа к переменной.
2. К таким же переменным относятся и области данных, адреса которых передаются в CALLBACK-функции Windows.
3. Есть такой нюанс (я натыкался):
- в теле CLARION-процедуры делаешь какой-то вызов Windows-функции, предавая ей в качестве параметров локальные переменные этой CLARION-процедуры
- после это завершаешь CLARION-процедуру
- РЕЗУЛЬТАТ ОШИБКА ДОСТУПА К ПАМЯТИ
= Причина: Windows-функция ещё в процессе исполнения, а кларион уже чистит выделенную твоей процедуре память (и под код, и под переменные).
(у меня это была передача файлов по FTP с CallBack-процедурой для отслеживания процесса; когда я выяснял, что все байты файла уже переданы - я давал команду по отключению CallBack-процедуры, закрывал все Handle-ы и завершал свою Кларион-процедуру, а в это время - как выяснилось, в CallBack-процедуру, а точнее в то место памяти, где она была ранее распределена кларионом, который уже провёл "зачистку", - продолжали поступать управляющие данные протокола FTP).
Выход: А) нужно, по-возможности, убеждаться в завершении работы Windows-функции, Б) такие переменные должны быть STATIC (если не получается А)