[C#] Windows 서비스로서의 .NET 콘솔 어플리케이션

콘솔 응용 프로그램이 있고 Windows 서비스로 실행하고 싶습니다. VS2010에는 콘솔 프로젝트를 연결하고 Windows 서비스를 빌드 할 수있는 프로젝트 템플릿이 있습니다. 별도의 서비스 프로젝트를 추가하지 않고 가능한 경우 서비스 응용 프로그램 코드를 콘솔 응용 프로그램에 통합하여 콘솔 응용 프로그램을 콘솔 응용 프로그램 또는 스위치를 사용하여 명령 줄에서 실행할 경우 Windows 서비스로 실행할 수있는 하나의 프로젝트로 유지합니다.

어쩌면 누군가가 C # 콘솔 응용 프로그램을 서비스로 빠르고 쉽게 변환 할 수있는 클래스 라이브러리 또는 코드 스 니펫을 제안 할 수 있습니까?



답변

나는 보통 다음 기술을 사용하여 콘솔 응용 프로그램이나 서비스와 동일한 응용 프로그램을 실행합니다.

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콘솔 앱의 경우 일반적으로 true이고 서비스의 경우 false입니다. 기술적으로 사용자 대화 형 모드에서 서비스를 실행할 수 있으므로 대신 명령 줄 스위치를 확인할 수 있습니다.


답변

나는 TopShelf로 큰 성공을 거두었습니다 .

TopShelf는 콘솔 앱 또는 Windows 서비스로 실행할 수있는 .NET Windows 앱을 쉽게 만들 수 있도록 설계된 Nuget 패키지입니다. 서비스 시작 및 중지 이벤트와 같은 이벤트를 빠르게 연결하고, 코드를 사용하여 실행 계정을 설정하고, 다른 서비스에 대한 종속성을 구성하고, 오류로부터 복구하는 방법을 구성 할 수 있습니다.

패키지 관리자 콘솔 (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. 새 콘솔 응용 프로그램 프로젝트 (예 : MyService)를 만듭니다.
  2. 두 개의 라이브러리 참조 (System.ServiceProcess 및 System.Configuration.Install)를 추가하십시오.
  3. 아래에 인쇄 된 3 개의 파일을 추가하십시오.
  4. 프로젝트를 빌드하고 “InstallUtil.exe c : \ path \ to \ MyService.exe”를 실행하십시오.
  5. 이제 서비스 목록에 MyService가 표시됩니다 (run 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);
        }
    }
}


답변

하나의 어셈블리가 반복되는 코드를 중지하기를 원한다고 주장하는 것이 들리지만, 가장 간단하고 코드 반복을 줄이고 향후에 다른 방법으로 코드를 쉽게 재사용 할 수있게하면 …. 3 개의 어셈블리로 나눕니다.

  1. 모든 작업을 수행하는 하나의 라이브러리 어셈블리. 그런 다음 두 가지 매우 슬림하고 간단한 프로젝트가 있습니다.
  2. 하나는 명령 줄
  3. 하나는 Windows 서비스입니다.

답변

다음은 최신 .Net Core 3.1을 기반으로 콘솔 응용 프로그램을 작업자 서비스로 Windows 서비스로 전환하는 새로운 방법입니다 .

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"


답변

당신이 사용할 수있는

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

그리고 그것은 서비스 목록에 나타납니다. 그래도 제대로 작동하는지 모르겠습니다. 서비스는 일반적으로 여러 이벤트를 청취해야합니다.

그러나 모든 서비스를 실제 서비스로 실행할 수있는 여러 서비스 랩퍼가 있습니다. 예 를 들어 Win2003 Resource Kit 의 Microsoft SrvAny


답변

먼저 콘솔 응용 프로그램 솔루션을 Windows 서비스 솔루션에 포함시키고 참조합니다.

그런 다음 콘솔 응용 프로그램 Program 클래스를 공개합니다

/// <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 서비스 하이브리드에도 사용할 수 있습니다