Картинка блога

Службы Windows это удобный способ запускать и контролировать программы, выполняемые на заднем плане. Они часто используются в больших приложениях или как альтернатива консоли или Form приложения, которое можно спрятать в трей. Сегодня я попробую объединить все идеи и наработки касательно сервисов и консольных приложений, используемые мной за время моего «молчания» лета.

Для начала, о консоли

  1. Не пишите логику в методе Main — рано или поздно ее все равно нужно будет перенести в собственный класс.
  2. Не надо парсить входящие параметры в ручную. Не изобретайте велосипед, используйте готовое, например NDesk.Options.
  3. Если вы делаете консоль, вместо Console.ReadLine используйте getline.cs от Мигеля де Икаса. При наличии достаточно интереса я покажу как красиво объединить его с номером два.

Теперь о службе

  1. Есть достаточно техник послать консоль на задний фон, возможно вам и не нужна служба.
  2. Вы все еще можете читать параметры командной строки с помощью Environment.CommandLine. Используйте его, например для задания имени службы.
  3. Только для Windows: Свойство Environment.UserInteractive всегда false в службах и true — в консольных приложениях. Готовый пример тут.
  4. С помощью ServiecInstaller класса можно настроить автостарт службы и учетную запись, под которой он должен работать.

И еще,

Не используйте Console.WriteLine в коде

Дело в том, что когда вы начнете переносить приложение в Формы или Службу, от этого потока нужно будет избавится или перенаправить в другое место.

  1. Создать собственное статическое свойство типа ITextWriter в возвращать Console.Out. Это простой но не действенный способ, так как если возвращать поток пишущий в файл, он не позволяет контролировать открытие и закрытие файла а также сброс буфера.
  2. Сделать свой интерфейс (ILoggger или IMyWriter) с нужными методами. Это, наверно, самый популярный способ. Требует немного больше времени чем первый, но позволяет контролировать процесс на нужном уровне.
  3. Использовать готовую библиотеку — Будь то log4net или System.Diagnostics. Тут, думаю, все понятно. Настроить такие библиотеки обычно можно из конфигурации, также, есть возможность контролировать уровень важности и фильтр.

Что, если запустить службу в Linux?

Mono окружение обладает рядом особенностей работы со службой. Несмотря на то, что «запускалка» сервисов в Mono есть, я вообще отказался от служб в кросс-платформанных приложениях. Дело в том, что в mono-service нет одной мелочи, переключения на новый сборщик мусора. А это ведет к утечкам, при использовании .NET 4.0. Тут есть выход, так как в Linux еще больше способов послать консоль в фон, чем в Windows. 🙂

Как лочить выполнение с возможностью остановки?

Как вариант, можно сделать lock на основе файла:

#region FileLock
private static FileSystemWatcher watcher;
private static string watchFilename;
public static void LockByFile(string filename = "lock")
{
    if (File.Exists(filename)) File.Delete(filename);
    File.WriteAllText(filename, "");

    watchFilename = filename;
    watcher = new FileSystemWatcher(Environment.CurrentDirectory);
    watcher.Deleted += watcher_Deleted;
    watcher.Renamed += watcher_Renamed;
    watcher.EnableRaisingEvents = true;

    log.Warn("to unlock: Delete file - " + filename);

    while (!String.IsNullOrEmpty(watchFilename)) Thread.Sleep(800);
}

private static void watcher_Renamed(object sender, RenamedEventArgs e)
{
    if (!File.Exists(watchFilename)) watchFilename = null;
}

static void watcher_Deleted(object sender, FileSystemEventArgs e)
{
    if (e.Name == watchFilename) watchFilename = null;
}
#endregion

Скрипт для запуска службы

Вот так выглядит .bat файл, который я даю вместе с программой, которая будет запускаться на Windows в качестве службы:

::
@ECHO OFF
echo :: This script will install and start
echo :: [YourProgram] service

rem set UTIL=C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe
set UTIL=C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe
set NAME=Program.exe
cd %~dp0
%UTIL% %NAME% /ShowCallStack
echo :: If service started: (if not, you are probably not under Administrator rights)
echo ::   Press [Enter] to stop and uninstall it.
echo ::   Press [Ctrl+C] to leave it in the service table.
pause
%UTIL% %NAME% /u BioStarSimpleConnector.exe

Метки:, ,