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


145

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

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


임시 서비스 프로젝트를 만들어 서비스로 만드는 비트를 복사하지 않는 이유는 무엇입니까?
Gabe


여기에 설명 된 기술을 시도해 볼 수 있습니다. einaregilsson.com/2007/08/15/…
Joe

응? 잘 모르겠습니다. 이것에 대해서.

2
매우 간단한 상단 선반 대안 : runasservice.com
Luis Perez

답변:


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


3
ServiceInstaller 클래스를 사용합니다 ( msdn.microsoft.com/en-us/library/… 참조) .
VladV

2
서비스는 별도의 프로세스로 실행되므로 (작업 관리자에 표시됨)이 프로세스는 시스템에 의해 제어됩니다 (예 : 서비스 설정에 따라 시작, 중지, 다시 시작).
VladV

2
콘솔 앱으로 실행하면 서비스가 표시되지 않습니다. 이 코드의 전체 목적은 콘솔 앱이나 서비스로 실행할 수 있도록하는 것입니다. 서비스로 실행하려면 먼저 ServiceInstaller 클래스 (위의 MSDN 링크 또는 installuitil.exe 참조)를 사용하여 설치 한 다음 제어판에서 서비스를 실행해야합니다.
VladV

2
ServiceInstaller는 Windows 서비스 (installutil.exe 또는 sc.exe 유틸리티와 약간 비슷 함)를 처리하는 유틸리티 클래스 일뿐입니다. 서비스로 원하는 것을 설치하는 데 사용할 수 있으며 OS는 사용하는 프로젝트 유형에 신경 쓰지 않습니다.
VladV

5
프로젝트에서 System.ServiceProcess에 참조를 추가하면 위 코드를 사용할 수 있습니다
danimal

59

나는 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
안녕하세요, 나는 이것을 얻는다 :- " 'Topshelf 4.0.1'패키지를 설치할 수 없습니다. '.NETFramework, Version = v4.5'를 대상으로하는 프로젝트에이 패키지를 설치하려고하는데 패키지에 포함되어 있지 않습니다 해당 프레임 워크와 호환되는 어셈블리 참조 또는 콘텐츠 파일 " 여기서 무엇이 잘못 되었습니까?

3
클라이언트 프로파일이 아닌 전체 .NET 4.5.2 런타임을 대상으로하고 있는지 확인하십시오.
항해

myservice.exe에 대해 더 많은 정보를 알려 주시면 어떤 디렉토리에서 명령 프롬프트를 열 것
입니까

1
@Izuagbala myservice.exe는 코드 샘플에 표시된대로 TopShelf가 부트 스트랩 된 콘솔 응용 프로그램입니다.
saille

서비스로 설치 한 후 myservice.exe를 콘솔로 실행할 수 있습니까?. "콘솔 응용 프로그램을 만든 후 개발자는 단일 서비스 클래스를 만듭니다" docs.topshelf-project.com/en/latest/overview/…
Michael Freidgeim

27

다음은 전체 연습입니다.

  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);
        }
    }
}

1
64 비트 용 프로젝트를 컴파일하는 경우 64 비트 용 InstallUtil.exe를 사용해야합니다. C : \ windows \ Microsoft.NET \ Framework64 \ ... 32 비트 버전 (C : \ windows \ Microsoft.NET \ Framework)는 당신에게 BadImageFormatException을 던질 것입니다 ...
snytek

@snytek이 말했듯이 base 64를 사용하는 경우 올바른 디렉토리를 사용해야합니다. 또한 나와 같은 작업을 수행하고 서비스 이름을 "MyService"이외의 다른 이름으로 바꾸는 것을 잊어 버린 경우 코드를 변경하기 전에 서비스를 제거해야합니다.
dmoore1181

3

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

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

1
이것은 내가 몇 년 동안 해왔 던 방법입니다. 서비스는 거의 가지고 Start()있고 Stop()메소드가 있으며 콘솔 앱에는 루프가 있습니다. TopShelf 와 같은 프레임 워크를 사용하지 않는 것이 가장 좋은 방법입니다.
Basic

그 대답에 가장 동의하십시오. 간단한 솔루션에 타사 도구를 사용하면 향후 유지 관리가 불필요 해집니다.
tatigo

3

다음은 최신 .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"

2

당신이 사용할 수있는

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

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

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


말했듯이 서비스 exe는 Windows와 통신해야합니다. SrvAny에 링크 +1
Jodrell

5
이 접근법은 안전하지 않다고 생각합니다. Windows에는 서비스를 관리하기위한 특수 라이브러리 및 유틸리티가 있으며 다른 OS 버전 및 환경에서 일관되게 작동 할 가능성이 높습니다. .NET 앱의 경우 VS에서 MSI 설치 프로그램을 만드는 것이 매우 쉽습니다. 또한 ManagedInstallerClass.InstallHelper 메소드를 사용하여 설치를 수행 적으로 수행하는 것도 가능합니다.
VladV

1
설치 프로그램 및 작업이 필요하지 않음 :이 명령 행을 사용하십시오. sc create MyServiceName binPath = "c : \ path \ to \ service \ file \ exe"
JDC

2

먼저 콘솔 응용 프로그램 솔루션을 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 서비스 하이브리드에도 사용할 수 있습니다


이것은 기본적으로 JonAlb가 이전 답변에서 말한 것입니다. 그러나 코드 예제에 감사드립니다
tatigo

0

내가 아는 한 필요한 것을 정의해야 할 수도 있지만 명령 줄을 사용하여 앱을 콘솔 또는 서비스로 동시에 실행할 수는 없습니다. 서비스가 설치되어 있고 서비스 관리자에서 서비스를 시작해야한다는 것을 기억하십시오. 서비스를 시작하거나 콘솔 앱을 실행하는 새 프로세스를 시작할 수있는 새 응용 프로그램을 만들 수 있습니다. 그러나 당신이 쓴대로

"콘솔 응용 프로그램을 하나의 프로젝트로 유지"

일단, 나는 당신의 입장에 있었고 콘솔 응용 프로그램을 서비스로 바 꾸었습니다. VS Express Edition으로 작업하는 경우 먼저 템플릿이 필요합니다. 다음은 첫 단계를 수행 할 수있는 링크입니다. C # Windows Service , 이것은 매우 도움이되었습니다. 그런 다음 해당 템플릿을 사용하여 원하는 서비스 이벤트에 코드를 추가하십시오.

서비스를 향상시키기 위해 할 수있는 또 다른 일이 있지만 이것은 빠르고 쉬운 일이 아니며 appdomains를 사용하고 dll을 생성하여로드 / 언로드하는 것입니다. 하나는 콘솔 응용 프로그램으로 새로운 프로세스를 시작할 수 있으며 다른 dll에서는 서비스가 수행하는 기능을 넣을 수 있습니다.

행운을 빕니다.


0

기능을 클래스로 분리하고 두 스텁 중 하나를 통해이를 시작해야합니다. 콘솔 스텁 또는 서비스 스텁

알다시피, 창을 실행할 때 인프라를 구성하는 무수한 서비스는 사용자에게 콘솔 창을 표시하지 않으며 직접적으로 표시 할 수 없습니다. 서비스는 그래픽이 아닌 방식으로 사용자와 통신해야합니다. SCM을 통해; 이벤트 로그, 일부 로그 파일 등. 서비스는 SCM을 통해 Windows와 통신해야합니다. 그렇지 않으면 종료됩니다.

서비스와 통신 할 수있는 일부 콘솔 앱을 사용하는 것은 당연하지만 GUI 상호 작용없이 서비스를 독립적으로 실행해야합니다.

콘솔 스텁은 서비스 비헤이비어 디버깅에 매우 유용 할 수 있지만 결국 서비스 생성을 목적으로하는 "제작 된"환경에서는 사용해서는 안됩니다.

나는 그것을 완전히 읽지 못했지만 이 기사 는 올바른 방향으로 파인 것 같습니다.


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);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.