Додаток консолі .NET як служба Windows


145

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

Можливо, хтось може запропонувати бібліотеку класів або фрагмент коду, який міг би швидко і легко перетворити додаток c # console в сервіс?


Чому б вам просто не створити тимчасовий сервісний проект і не скопіювати біти, які роблять його послугою?
Гейб

4
Ви можете спробувати Topshelf topshelf-project.com
Артем Кошелєв,

Ви можете спробувати описану тут техніку: einaregilsson.com/2007/08/15/…
Joe

так? Я не впевнений. про це.

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звичайно справедливо для консольного додатка, а помилковим для послуги. Технічно можна запустити службу в інтерактивному режимі, щоб ви могли замість цього перевірити перемикач командного рядка.


3
Ви можете використовувати клас ServiceInstaller см msdn.microsoft.com/en-us/library / ... .
ВладВ

2
Це очікується - ваша служба запускатиметься як окремий процес (так це було б показано в диспетчері завдань), але цей процес буде контролюватися системою (наприклад, запущений, зупинений, перезапущений відповідно до налаштувань служби).
ВладВ

2
Якщо ви запускаєте його як консольний додаток, ви не побачите службу. Ціль цього коду полягає в тому, щоб ви могли запускати його або як консольний додаток, або як сервіс. Щоб запустити службу, потрібно спочатку встановити її (використовуючи клас ServiceInstaller - див. Посилання MSDN вище - або installuitil.exe) та запустити службу на панелі керування.
ВладВ

2
ServiceInstaller - це просто клас утиліти для роботи з послугами Windows (трохи схожими на утиліти installutil.exe або sc.exe). Ви можете використовувати його для установки всього, що ви хочете як послугу, ОС не хвилює тип проекту, який ви використовуєте.
ВладВ

5
Просто додайте до свого проекту посилання на System.ServiceProcess, і ви зможете скористатись кодом вище
danimal

59

Я мав великий успіх з TopShelf .

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

З консолі диспетчера пакунків (Nuget):

Встановити-пакетну верхню полицю

Для початку зверніться до зразків коду .

Приклад:

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 робить це все за вас.


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/…
Michael Freidgeim

27

Ось ось повний посібник:

  1. Створіть новий проект програми Console (наприклад, MyService)
  2. Додайте дві бібліотечні посилання: System.ServiceProcess та System.Configuration.Install
  3. Додайте три файли, надруковані нижче
  4. Створіть проект та запустіть "InstallUtil.exe c: \ path \ to \ MyService.exe"
  5. Тепер ви повинні побачити MyService у списку послуг (запустіть служби.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, якщо ви використовуєте базу 64, переконайтеся, що ви використовуєте правильний каталог. Крім того, якщо вам трапляється робити те саме, що я, і ви забули перейменувати службу на щось інше, ніж "MyService", переконайтеся, що ви видалили службу, перш ніж вносити зміни в код.
dmoore1181

3

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

  1. Одна бібліотечна збірка, яка виконує всю роботу. Тоді є два дуже тонкі / прості проекти:
  2. той, який є командним рядком
  3. той, що є сервісом Windows.

1
Ось як я це робив роками - Сервіс в значній мірі має Start()і Stop()методи, і консольний додаток має цикл. Окрім використання такої рамки, як TopShelf , це найкращий варіант
Basic

найбільше згоден з цією відповіддю. використання 3d-інструментів для простих рішень робить майбутнє технічне обслуговування непотрібним комплексом
tatigo

3

Ось новий спосіб, як перетворити додаток консолі в службу Windows як службу Worker на основі останньої .Net Core 3.1 .

Якщо ви створите службу Worker з 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"

2

Можна використовувати

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

І він з’явиться в списку послуг. Не знаю, чи працює це правильно. Службі зазвичай доводиться прослуховувати кілька подій.

Однак є декілька сервісних обгортків, які можуть запускати будь-яку програму як справжню послугу. Наприклад Microsofts SrvAny з набору ресурсів Win2003


Як ви кажете, сервісу exe потрібно буде спілкуватися з Windows. +1 для посилання на SrvAny
Jodrell

5
Я вважаю такий підхід небезпечним. У Windows є спеціальні бібліотеки та утиліти для управління сервісами, і вони швидше працюють послідовно в різних версіях ОС та середовищах. Для програми .NET створити інсталятор MSI у VS досить просто. Також можливо виконати інсталяцію прогрматично за допомогою методу ManagedInstallerClass.InstallHelper.
ВладВ

1
Немає необхідності у встановленні та іншому: просто використовуйте цей командний рядок: sc створити 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 я інстанціюю програму і закликаю функції запуску та зупинки, додані в межах 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 , це було дуже корисно для мене. Потім, використовуючи цей шаблон, додайте свій код до бажаних подій служби.

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

Удачі.


0

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

Як зрозуміло, під час запуску Windows безліч служб, що складають інфраструктуру, не (і не можуть безпосередньо) представляти користувачеві консольні вікна. Послуга повинна спілкуватися з користувачем не графічно: через SCM; у журналі подій, до якогось файлу журналу тощо. Службі також потрібно буде зв’язатися з Windows через SCM, інакше він вимкнеться.

Очевидно, було б прийнятно мати консольний додаток, який може спілкуватися зі службою, але послуга повинна працювати незалежно, не вимагаючи взаємодії з графічним інтерфейсом.

Заглушка консолі може бути дуже корисною для налагодження поведінки служби, але не повинна використовуватися у "виробничому" середовищі, що, зрештою, є метою створення служби.

Я не прочитав його повністю, але ця стаття, здається, пінтує в правильному напрямку.


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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.