Консольное приложение .NET как служба Windows

145

У меня есть консольное приложение, и я хотел бы запустить его как службу Windows. VS2010 имеет шаблон проекта, который позволяет присоединить консольный проект и построить сервис Windows. Я не хотел бы добавлять отдельный сервисный проект и, если возможно, интегрировать сервисный код в консольное приложение, чтобы консольное приложение оставалось как один проект, который мог бы работать как консольное приложение или как служба Windows, если он запускается, например, из командной строки с помощью переключателей.

Может быть, кто-то может предложить библиотеку классов или фрагмент кода, который может быстро и легко преобразовать консольное приложение c # в службу?

Tomas
источник
Почему бы вам просто не создать временный сервисный проект и не скопировать биты, которые делают его сервисным?
Гейб
4
Вы можете попробовать Topshelf topshelf-project.com
Артем Кошелев
Вы можете попробовать метод, описанный здесь: einaregilsson.com/2007/08/15/…
Джо,
да? Я не уверен. об этом.
2
Очень простая альтернатива для верхней полки: runasservice.com
Луис Перес,

Ответы:

185

Я обычно использую следующую технику для запуска того же приложения, что и консольное приложение или как служба:

public static class Program
{
    #region Nested classes to support running as service
    public const string ServiceName = "MyService";

    public class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
    #endregion

    static void Main(string[] args)
    {
        if (!Environment.UserInteractive)
            // running as service
            using (var service = new Service())
                ServiceBase.Run(service);
        else
        {
            // running as console app
            Start(args);

            Console.WriteLine("Press any key to stop...");
            Console.ReadKey(true);

            Stop();
        }
    }

    private static void Start(string[] args)
    {
        // onstart code here
    }

    private static void Stop()
    {
        // onstop code here
    }
}

Environment.UserInteractiveобычно верно для консольного приложения и false для службы. Технически, можно запустить службу в интерактивном режиме, так что вы можете вместо этого проверить переключатель командной строки.

VladV
источник
3
Вы используете класс ServiceInstaller , см. Msdn.microsoft.com/en-us/library/… .
VladV
2
Это ожидаемо - ваша служба будет работать как отдельный процесс (так будет отображаться в диспетчере задач), но этот процесс будет контролироваться системой (например, запускаться, останавливаться, перезапускаться в соответствии с настройками службы).
ВладВ
2
Если вы запустите его как консольное приложение, вы не увидите службу. Вся цель этого кода состоит в том, чтобы позволить вам запускать его либо как консольное приложение, либо как службу. Для запуска в качестве службы сначала необходимо установить ее (используя класс ServiceInstaller - см. Ссылку MSDN выше - или installuitil.exe), а затем запустить службу из панели управления.
VladV
2
ServiceInstaller - это просто служебный класс для работы со службами Windows (немного похожий на утилиты installutil.exe или sc.exe). Вы можете использовать его для установки того, что вы хотите в качестве службы, ОС не заботится о типе проекта, который вы используете.
VladV
5
Просто добавьте ссылку в свой проект в System.ServiceProcess, и вы сможете использовать приведенный выше код
danimal,
59

Я имел большой успех с TopShelf .

TopShelf - это пакет Nuget, разработанный для упрощения создания приложений .NET Windows, которые могут работать как консольные приложения или как службы Windows. Вы можете быстро подключить такие события, как события запуска и остановки вашей службы, настроить с помощью кода, например, настроить учетную запись, с которой она работает, настроить зависимости от других служб и настроить способ восстановления после ошибок.

Из консоли диспетчера пакетов (Nuget):

Install-Package Topshelf

Обратитесь к примерам кода, чтобы начать.

Пример:

HostFactory.Run(x =>                                 
{
    x.Service<TownCrier>(s =>                        
    {
       s.ConstructUsing(name=> new TownCrier());     
       s.WhenStarted(tc => tc.Start());              
       s.WhenStopped(tc => tc.Stop());               
    });
    x.RunAsLocalSystem();                            

    x.SetDescription("Sample Topshelf Host");        
    x.SetDisplayName("Stuff");                       
    x.SetServiceName("stuff");                       
}); 

TopShelf также заботится об установке службы, которая может сэкономить много времени и удаляет стандартный код из вашего решения. Чтобы установить .exe в качестве службы, просто выполните следующее из командной строки:

myservice.exe install -servicename "MyService" -displayname "My Service" -description "This is my service."

Вам не нужно подключать ServiceInstaller и все такое - TopShelf сделает все за вас.

Saille
источник
1
Привет, я получаю это: - "Не удалось установить пакет 'Topshelf 4.0.1'. Вы пытаетесь установить этот пакет в проект, который нацелен на '.NETFramework, Version = v4.5', но пакет не содержит никаких ссылки на сборки или файлы содержимого, совместимые с этой платформой. " что здесь не так?
3
Убедитесь, что вы нацелены на полную среду выполнения .NET 4.5.2, а не на профиль клиента.
saille
пожалуйста, вы можете пролить больше света на myservice.exe и из какого каталога вы собираетесь открыть командную строку
Izuagbala
1
@Izuagbala myservice.exe - это созданное вами консольное приложение, в которое загружается TopShelf, как показано в примере кода.
saille
Можно ли запустить myservice.exe в качестве консоли после его установки в качестве службы? Документация не ясна: «Как только консольное приложение создано, разработчик создает один класс обслуживания» docs.topshelf-project.com/en/latest/overview/…
Майкл
28

Итак, вот полное прохождение:

  1. Создать новый проект консольного приложения (например, MyService)
  2. Добавьте две ссылки на библиотеку: System.ServiceProcess и System.Configuration.Install
  3. Добавьте три файла, напечатанные ниже
  4. Создайте проект и запустите «InstallUtil.exe c: \ path \ to \ MyService.exe»
  5. Теперь вы должны увидеть MyService в списке сервисов (запустите services.msc)

* InstallUtil.exe обычно можно найти здесь: C: \ windows \ Microsoft.NET \ Framework \ v4.0.30319 \ InstallUtil.ex‌ e

Program.cs

using System;
using System.IO;
using System.ServiceProcess;

namespace MyService
{
    class Program
    {
        public const string ServiceName = "MyService";

        static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                // running as console app
                Start(args);

                Console.WriteLine("Press any key to stop...");
                Console.ReadKey(true);

                Stop();
            }
            else
            {
                // running as service
                using (var service = new Service())
                {
                    ServiceBase.Run(service);
                }
            }
        }

        public static void Start(string[] args)
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} started{1}", DateTime.Now, Environment.NewLine));
        }

        public static void Stop()
        {
            File.AppendAllText(@"c:\temp\MyService.txt", String.Format("{0} stopped{1}", DateTime.Now, Environment.NewLine));
        }
    }
}

MyService.cs

using System.ServiceProcess;

namespace MyService
{
    class Service : ServiceBase
    {
        public Service()
        {
            ServiceName = Program.ServiceName;
        }

        protected override void OnStart(string[] args)
        {
            Program.Start(args);
        }

        protected override void OnStop()
        {
            Program.Stop();
        }
    }
}

MyServiceInstaller.cs

using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;

namespace MyService
{
    [RunInstaller(true)]
    public class MyServiceInstaller : Installer
    {
        public MyServiceInstaller()
        {
            var spi = new ServiceProcessInstaller();
            var si = new ServiceInstaller();

            spi.Account = ServiceAccount.LocalSystem;
            spi.Username = null;
            spi.Password = null;

            si.DisplayName = Program.ServiceName;
            si.ServiceName = Program.ServiceName;
            si.StartType = ServiceStartMode.Automatic;

            Installers.Add(spi);
            Installers.Add(si);
        }
    }
}
Николай Куделия
источник
1
Если вы компилируете свой проект для 64-битной версии, вы должны использовать InstallUtil.exe для 64-битной версии, который можно найти здесь: C: \ windows \ Microsoft.NET \ Framework64 \ ... Версия для 32-битной версии (C: \ windows \ Microsoft.NET \ Framework) создаст для вас
исключение BadImageFormatException
Это работает очень хорошо, обратите внимание, что, как говорит @snytek, если вы используете base 64, убедитесь, что вы используете правильный каталог. Кроме того, если вы делаете то же самое, что и я, и забыли переименовать службу в нечто иное, чем «MyService», обязательно удалите службу, прежде чем вносить изменения в код.
dmoore1181
3

Я слышал, что вы хотели, чтобы одна сборка остановила повторяющийся код, но это было бы проще и уменьшило бы повторение кода, а также упростило бы повторное использование вашего кода другими способами в будущем, если ...... вам нужно разбить его на 3 сборки.

  1. Одна сборка библиотеки, которая делает всю работу. Тогда есть два очень очень тонких / простых проекта:
  2. тот, который является командной строкой
  3. тот, который является службой Windows.
JonAlb
источник
1
Это, как я делал это в течение многих лет - служба в значительной степени имеет Start()и Stop()методы , и приложение консоли имеет петлю. Если не считать фреймворк , такой как TopShelf , это лучший вариант
Basic
согласен с этим ответом наиболее. использование инструментов 3D-вечеринок для простых решений делает ненужным обслуживание будущего
tatigo
3

Вот новый способ преобразования консольного приложения в службу Windows в качестве рабочей службы на основе новейшей версии .Net Core 3.1. .

Если вы создадите рабочую службу из Visual Studio 2019, она предоставит вам практически все, что вам нужно для создания службы Windows из коробки, что также необходимо изменить в консольном приложении, чтобы преобразовать его в службу Windows.

Вот изменения, которые вам нужно сделать:

Установите следующие пакеты NuGet

Install-Package Microsoft.Extensions.Hosting.WindowsServices -Version 3.1.0
Install-Package Microsoft.Extensions.Configuration.Abstractions -Version 3.1.0

Измените Program.cs для реализации, как показано ниже:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ConsoleApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).UseWindowsService().Build().Run();
        }

        private static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddHostedService<Worker>();
                });
    }
}

и добавьте Worker.cs, куда вы поместите код, который будет выполняться операциями службы:

using Microsoft.Extensions.Hosting;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp
{
    public class Worker : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            //do some operation
        }

        public override Task StartAsync(CancellationToken cancellationToken)
        {
            return base.StartAsync(cancellationToken);
        }

        public override Task StopAsync(CancellationToken cancellationToken)
        {
            return base.StopAsync(cancellationToken);
        }
    }
}

Когда все готово и приложение успешно собрано , вы можете использовать sc.exe для установки exe своего консольного приложения в качестве службы Windows с помощью следующей команды:

sc.exe create DemoService binpath= "path/to/your/file.exe"
meJustAndrew
источник
2

Ты можешь использовать

reg add HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run /v ServiceName /d "c:\path\to\service\file\exe"

И это появится в списке услуг. Я не знаю, правильно ли это работает, хотя. Служба обычно должна прослушивать несколько событий.

Однако есть несколько сервисных оболочек, которые могут запускать любое приложение как реальный сервис. Например, Microsoft SrvAny из комплекта ресурсов Win2003

Darcara
источник
Как вы говорите, служба exe должна будет общаться с Windows. +1 за ссылку на SrvAny
Джодрелл
5
Я бы посчитал такой подход небезопасным. В Windows есть специальные библиотеки и утилиты для управления службами, и они с большей вероятностью будут работать согласованно в разных версиях ОС и средах. Для приложения .NET довольно легко создать установщик MSI в VS. Также возможно выполнить установку программно, используя метод ManagedInstallerClass.InstallHelper.
VladV
1
Нет необходимости в установщиках и прочем: просто используйте эту командную строку: sc create MyServiceName binPath = "c: \ path \ to \ service \ file \ exe"
JDC
2

Сначала я встраиваю консольное приложение в решение для службы Windows и ссылаюсь на него.

Затем я делаю консольный класс программы приложения общедоступным.

/// <summary>
/// Hybrid service/console application
/// </summary>
public class Program
{
}

Затем я создаю две функции в консольном приложении

    /// <summary>
    /// Used to start as a service
    /// </summary>
    public void Start()
    {
        Main();
    }

    /// <summary>
    /// Used to stop the service
    /// </summary>
    public void Stop()
    {
       if (Application.MessageLoop)
            Application.Exit();   //windows app
        else
            Environment.Exit(1);  //console app
    }

Затем в самой службе Windows я создаю экземпляр Программы и вызываю функции Start и Stop, добавленные в OnStart и OnStop. Увидеть ниже

class WinService : ServiceBase
{
    readonly Program _application = new Program();

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        ServiceBase[] servicesToRun = { new WinService() };
        Run(servicesToRun);
    }

    /// <summary>
    /// Set things in motion so your service can do its work.
    /// </summary>
    protected override void OnStart(string[] args)
    {
        Thread thread = new Thread(() => _application.Start());
        thread.Start();
    }

    /// <summary>
    /// Stop this service.
    /// </summary>
    protected override void OnStop()
    {
        Thread thread = new Thread(() => _application.Stop());
        thread.Start();
    }
}

Этот подход также можно использовать для гибридного приложения Windows / службы Windows.

Патрик Рейнольдс
источник
это в основном то, что сказал JonAlb в предыдущем ответе, но спасибо за пример кода
tatigo
0

Возможно, вам следует определить, что вам нужно, насколько я знаю, вы не можете одновременно запускать ваше приложение как консоль или службу с командной строкой. Помните, что служба установлена, и вы должны запустить ее в диспетчере служб, вы можете создать новое приложение, которое запустит службу или запустит новый процесс, выполняющий ваше консольное приложение. Но как вы написали

"сохранить консольное приложение как один проект"

Однажды я оказался на вашем месте, превратив консольное приложение в сервис. Сначала вам нужен шаблон, если вы работаете с VS Express Edition. Вот ссылка, где вы можете сделать свои первые шаги: C # Windows Service , это было очень полезно для меня. Затем, используя этот шаблон, добавьте свой код к нужным событиям сервиса.

Чтобы улучшить свой сервис, есть еще одна вещь, которую вы можете сделать, но это не быстро и / или легко, это использование доменов приложений и создание библиотек DLL для загрузки / выгрузки. В одном вы можете запустить новый процесс с помощью консольного приложения, а в другом dll вы можете просто добавить функциональность, которую должен выполнять сервис.

Удачи.

BlackCath
источник
0

Вы должны разделить функциональность на класс или классы и запустить их через одну из двух заглушек. Заглушка консоли или служебная заглушка.

Как видно, при запуске окон множество сервисов, составляющих инфраструктуру, не (и не могут напрямую) предоставлять пользователю консольные окна. Служба должна общаться с пользователем не графическим способом: через SCM; в журнале событий, в некоторый файл журнала и т. д. Служба также должна будет обмениваться данными с окнами через SCM, в противном случае она отключится.

Очевидно, было бы приемлемо иметь какое-то консольное приложение, которое может взаимодействовать со службой, но служба должна работать независимо, без необходимости взаимодействия с графическим интерфейсом.

Заглушка консоли может быть очень полезна для отладки поведения службы, но ее не следует использовать в «производственной» среде, которая, в конце концов, является целью создания службы.

Я не прочитал это полностью, но эта статья, кажется, сдать в правильном направлении.

Jodrell
источник
0

Я использую класс обслуживания, который следует стандартному шаблону, предписанному ServiceBase, и использую помощников для легкой отладки F5. Благодаря этому данные сервиса определяются в сервисе, что облегчает их поиск и управление жизненным циклом.

Я обычно создаю приложение для Windows со структурой ниже. Я не создаю консольное приложение; таким образом, я не получаю большой черный ящик каждый раз, когда я запускаю приложение. Я остаюсь в отладчике, где все действия. я используюDebug.WriteLine так, чтобы сообщения отправлялись в окно вывода, которое хорошо стыкуется и остается видимым после завершения работы приложения.

Я обычно не пытаюсь добавить код отладки для остановки; Я просто использую отладчик вместо этого. Если мне нужно отладить остановку, я делаю проект консольным приложением, добавляю Stopметод пересылки и вызываю его после вызова Console.ReadKey.

public class Service : ServiceBase
{
    protected override void OnStart(string[] args)
    {
        // Start logic here.
    }

    protected override void OnStop()
    {
        // Stop logic here.
    }

    static void Main(string[] args)
    {
        using (var service = new Service()) {
            if (Environment.UserInteractive) {
                service.Start();
                Thread.Sleep(Timeout.Infinite);
            } else
                Run(service);
        }
    }
    public void Start() => OnStart(null);
}
Эдвард Брей
источник