среда, 19 августа 2009 г.

Скелет службы (service) на Visual C++

Потребовалось мне написать службу, он же сервис, для Windows. Как показали поиски в интернете, самой используемой статьёй по этой теме оказалась Beginner's introductory guide to writing, installing, starting, stopping NT services аж 2001 года. В ней приводится скелет простой работующей службы. Этот скелет я встретил в нескольких широкоиспользуемых opensource программах, например FileZilla . Код рабочий, но есть в нем несколько моментов, которые не устраивают.

1. Управление службой идет из сторонних программ. Т.е. чтобы запустить или остановить службу, нужна дополнительная утилита.
2. Нет вывода произошедших ошибок.
3. Некоторые, найденые пользователями, ошибки. Ну даже не ошибки. Один момент проявился с выходом Windows XP. Другой момент - вызов своего потока до отправки команды "я работаю" проявляется редко. И то не понятно почему. Так что в мсдн появилась рекомендация поменять порядок вызова.
4. Во многих источниках, в том числе и в мсдн, приведен следующий код запуска программы ( не дословно, но по смылсу одинаков)
void __cdecl _tmain(int argc, TCHAR *argv[])
{
// If command-line parameter is "install", install the service.
// Otherwise, the service is probably being started by the SCM.

  if( lstrcmpi( argv[1], TEXT("install")) == 0 )
  {
    SvcInstall();
    return;
  }

// TO_DO: Add any additional services for the process to this table.
  SERVICE_TABLE_ENTRY DispatchTable[] =
  {
    { SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
    { NULL, NULL }
  };

// This call returns when the service has stopped.
// The process should simply terminate when the call returns.

  if (!StartServiceCtrlDispatcher( DispatchTable ))
  {
    SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
  }
}
Суть в том, что в мсдн не отображено, или плохо отображено, что служба должна стартовать только через service control manager. Т.е. если вы запустите этот код из консоли, то служба не запустится. Код ошибки ERROR_FAILED_SERVICE_CONTROLLER_CONNECT. Этот момент у меня вызвал продолжительные поиски. Я не мог понять почему у меня служба не запускается. Как показал поиск в  интернете я не одинок. Службу нужно запускать либо через консоль администрирования либо в своей программе через функцию StartService. А вышепреведенный код расчитан, что программа будет запускаться через service control manager.

В итоге я поправил код. Что мы имеем:
  • программа обрабатывает команды -[start|stop|install|uninstall]
  • для стандартных часто встречающихся ошибок выводит понятное сообщение в консоль, для остальных - только код ошибки
  • выводится подсказка по параметрам программы
  • исправлены ошибки пункта 3

Кстати, в консоль выводятся только ошибки, возникающие при выполнении команд [start|stop|install|uninstall]. Т.к. они и подаются большей частью из консоли. В идеале, для остальных случаев, еще бы крикрутить функцию для записи ошибок (событий) в системный журнал. Но это, думаю, будет позже.

Работоспособность проверял на Windows XP и Windows 2003. В идеале проверить бы еще на Windows 2000, Vista, 2008 и 7. Исходник на VC++ 2008 service.zip. Тут только модуль, без файлов проекта.

3 комментария:

  1. Александр Семенко14 июня 2010 г., 5:46

    Чувак, спасибо тебе, конечно, за _работающий_ сервис, но то, как это написано... Не позорься, оформи по нормальному (начни хотя-бы с CTRL+K+F в студии, что-ли).
    В общем, пользоваться _этим_ - страша-а-ашно...

    ОтветитьУдалить
  2. Хотелось бы более конструктивной критики. что не так , по вашему.
    CTRL+K+F и так был выполнен.

    ОтветитьУдалить
  3. Ты сам-то я зыком не лязгай попусту а возьми и продемонстрируй мастерство, только бы попи*деть =)

    ОтветитьУдалить