Development note/C#

[C#] 프로그램을 서비스(Services.msc)에 등록하는 방법

v명월v 2021. 1. 22. 17:12

안녕하세요. 명월입니다.


이 글은 C#으로 작성한 프로그램을 서비스(Services.msc)에 등록하는 방법에 대한 글입니다.


우리가 C#으로 프로그램을 작성하면 보통 콘솔이나 윈도우, 그리고 라이브러리(DLL)파일을 작성하게 됩니다.

그중 콘솔이나 윈도우 프로그램은 우리가 윈도우 운영체제에서 실행을 할 수 있습니다. 여기서 우리가 콘솔이나 윈도우를 실행하는 데 항상 유저가 프로그램을 실행해야 한다는 문제가 있습니다.즉, 서버에서 운영된다고 하면 서버를 재부팅을 할 때마다 프로그램을 수동으로 실행을 시켜야 합니다.


그러나 운영체제에는 그런 수동적인 조작이 아닌 부팅이 되면 자동으로 실행하게 하는 방법이 있습니다. 그 중 가장 많이 사용되는 방법이 윈도우 스케줄러에 등록해서 실행하는 방법과 서비스에 등록하는 방법입니다.

이 방법은 서로 간의 장단점이 있는데 스케줄러에 등록하는 것은 실행 창에 taskschd.msc 명령어를 넣고 실행하면 스케줄러가 나옵니다.

스케줄러 다루는 방법에 대해서는 다른 글에서 소개하도록 하겠습니다.


이 스케줄러에 등록하는 방법에 대한 단점이 있습니다. 그 문제는 세션을 유지해야 한다는 점입니다.

세션이란 OS에서 유저가 로그인 해서 접속하는 상태를 말하는데, 즉 서버에서 사용할 경우 재부팅이 된다고 해도 로그인을 해서 로그아웃을 하면 안된다는 뜻입니다. 세션을 유지해야 합니다. 여러모로 불편한 점이 있습니다.


그 다음에 서비스에 등록하는 것인데, 이건 OS측에서 기동하는 것으로 세션과는 관계가 없습니다. 즉, OS가 부팅되면 바로 실행되는 것입니다.

이 서비스는 services.msc로 확인하면 서비스 화면이 보이고 현재 실행 중인 프로그램등을 확인할 수 있습니다.

(제가 OS가 일본판이라.....ㅜㅜ)

대충 이런 화면이 보이게 됩니다.

여기서 프로그램을 등록하기 위해서는 일반 프로그램으로는 안되고 서비스 프로그램으로 새로 개발해야 합니다.


먼저 콘솔 프로젝트에서 내장 라이브러리(System.Configuration, System.Configuration.Install, System.ServiceProcess)를 추가해야 합니다.

using System;
using System.IO;
using System.Threading;
using System.Text;
using System.Configuration.Install;
using System.ServiceProcess;
using System.ComponentModel;

namespace Example
{
  // InstallUtil의 설정 어트리뷰트
  [RunInstallerAttribute(true)]
  // 서비스를 등록할 때의 설정 클래스
  public class MyProjectInstaller : Installer
  {
    //생성자
    public MyProjectInstaller()
    {
      // ServiceProcessInstaller는 권한에 대한 설정, 여기서는 LocalSystem 권한이지만 특정 권한으로 작성할 수 있고 Username과 Password도 넣을 수 있다.
      Installers.Add(new ServiceProcessInstaller()
      {
        // 접속 권한
        Account = ServiceAccount.LocalSystem
      });
      // 등록시의 설정 값
      Installers.Add(new ServiceInstaller()
      {
        // 부팅시의 형태.. 자동 실행인데, 수동 실행등의 설정도 가능하다.
        StartType = ServiceStartMode.Automatic,
        // 서비스에 등록되는 이름
        ServiceName = "nowonbun service"
      });
    }
  }
  // 서비스에서 처리되는 클래스
  public class Service : ServiceBase
  {
    // Program 인스턴스 생성
    private Program p = new Program();
    // Start 버튼을 누르면(시작되면)
    protected override void OnStart(string[] args)
    {
      // Program.Start함수 호출
      p.Start();
    }
    // Stop 버튼을 누르면(종료되면)
    protected override void OnStop()
    {
      // Program.Dispose함수 호출
      p.Dispose();
    }
    // 서비스가 시작될 때 호출되는 실행함수
    [STAThread]
    static void Main(string[] args)
    {
      ServiceBase.Run(new Service());
    }
  }
  // 서비스에 실행할 클래스
  public class Program : IDisposable
  {
    // 스레드 생성
    private Thread thread;
    // 생성자
    public Program()
    {
      // 스레드 인스턴스 생성
      thread = new Thread(ThreadStart);
    }
    // 시작 함수
    public void Start()
    {
      // 스레드 시작
      thread.Start();
    }
    // 종료 함수
    public void Stop()
    {
      // 스레드 종료
      thread.Interrupt();
    }
    // IDisposable의 재정의 함수
    public void Dispose()
    {
      // 스레드 종료
      this.Stop();
    }
    // 스레드 함수
    private void ThreadStart()
    {
      // 무한 루프
      while (true)
      {
        try
        {
          // 시간단위로 d:\work\service.txt파일에 시간을 작성한다.
          using (var stream = new FileStream(@"d:\work\service.txt", FileMode.Append, FileAccess.Write))
          {
            // 시:분:초\r\n를 binary로 변환
            var data = Encoding.UTF8.GetBytes(DateTime.Now.ToString("HH:mm:ss") + "\r\n");
            // IO에 쓰기
            stream.Write(data, 0, data.Length);
          }
        }
        catch
        {
          // silent
        }
        // 1초 대기
        Thread.Sleep(1000);
      }
    }
  }
}

위 소스를 디버그해서 일반 콘솔처럼 실행하면 에러가 발생합니다.

내용은 installutil를 이용해서 등록해서 사용하라고 나오네요.


그럼 Visual studio 유틸이 전부 path되어 있는 커맨드(cmd)창을 실행합니다. 이 커맨드(cmd)창은 반드시 관리자 권한으로 실행해야 합니다.

저는 아래와 같이 준비를 했습니다.

installutil Example.exe

무어라무어라 이야기는 하는데 마지막에 The transacted install has completed.가 나오는 것보니 등록이 된 것 같습니다.

이번에는 services.msc로 확인해 보겠습니다.

등록이 잘 되었습니다. 이름도 nowonbun service라고 등록이 되었네요. 그럼 서비스 설정 내용을 확인해 보겠습니다.

시작 옵션은 자동으로 되어있습니다. 그리고 접속 권한도 LocalSystem으로 되어 있습니다.

위 소스 설정대로 되어 있습니다. 그럼 이제 서비스를 개시해보겠습니다.(자동이라 되어 있으니 컴퓨터를 재부팅해도 확인이 가능합니다.)

위 소스는 서비스가 실행되면 c:\work에 service.txt파일에 초당 시분초가 작성되기로 했으니 확인해 봅시다.

파일은 제대로 생겼네요.

초당 String 문자열이 텍스트 파일에 제대로 작성이 됩니다.


이제 이 서비스를 해제하는 방법입니다.

우선 서비스를 멈추도록 합니다.


그리고 아까 Example.exe를 등록시킨 곳에서 installutil에 -u 옵션을 주어서 해제합니다.

installutil -u Example.exe

이번에도 uninstall이 잘 되었다고 나옵니다.

다시 서비스(services.msc)로 가서 확인해 보겠습니다.

nowonbun service라는 항목이 없어졌습니다.


우리가 서버에 프로그램을 작성한다고 하면 소켓 통신 서버가 가장 많이 있겠습니다.

통신 소켓 서버는 따로 윈도우 폼이나 콘솔의 화면이 필요없고 재대로 대기(LISTEN)하고 그에 대한 결과를 클라이언트에게 제대로 전송해주면 되는 것입니다.

물론 사양에 따라서 서버라고 해도 화면이 필요할 때가 있습니다. 보통 우리가 Linux(CentOS)에 서버를 구축한다고 해도 화면은 잘 만들지 않습니다.

그리고 서버라는게 물론 업데이트 옵션과 재부팅 옵션을 모두 OFF를 시켜놓겠지만, 만에 하나 서버가 재부팅이 된다고 하면 자동으로 이 서버 프로그램들이 기동이 되어야 합니다.

스케줄러를 이용하는 방법도 있지만, 세션이 필요하다는 단점이 존재하기 때문에 결국에는 유저가 접속을 해야합니다. 새벽 2~3시 서버 다운이 일어나면 꾸역꾸역 일어나서 프로그램 기동시켜야 합니다.

그러나 이 서비스를 이용하면 이런 문제점이 많이 해소가 됩니다.


여기까지 C#으로 작성한 프로그램을 서비스(Services.msc)에 등록하는 방법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.