본문 바로가기

Windows

[ Windows ] 서비스 앱 샘플 작성하기

반응형

Windows 서비스 프로그램을 작성하려면, 서비스의 생명 주기 관리와 운영 체제와의 상호작용을 위해 Microsoft의 Windows API를 활용해야 합니다. 서비스 프로그램은 일반 응용 프로그램과 달리, Windows 서비스 제어 관리자(Services Control Manager, SCM)에 의해 실행되고 제어됩니다. 다음은 Windows 서비스 프로그램을 작성하는 방법과 전체적인 구조에 대한 설명입니다.


 

1. Windows 서비스의 기본 개념

  • 서비스: 백그라운드에서 실행되며 사용자 인터페이스(UI)가 없거나 최소한의 UI를 사용하는 프로그램.
  • SCM: 서비스를 관리하는 Windows 구성 요소로, 서비스의 시작, 중지, 일시 중지 등을 제어.
  • 서비스의 상태: 시작(Pending), 실행 중(Running), 일시 중지(Paused), 중지(Stopped) 등 다양한 상태를 가짐.

 

2. 작성 과정 요약

2.1 프로젝트 환경 설정

  • 언어: 주로 C++ 또는 C# 사용.
  • 도구: Visual Studio와 같은 IDE 권장.
  • 템플릿: C++에서는 빈 프로젝트를 생성하고 Windows API를 사용. C#에서는 Windows Service 프로젝트 템플릿을 사용 가능.

2.2 핵심 구성 요소

  1. 서비스 엔트리 포인트
    • main() 함수 또는 WinMain() 함수는 서비스를 초기화하고 StartServiceCtrlDispatcher API를 호출해 SCM과 연결.
  2. 서비스 메인 함수
    • 서비스는 하나 이상의 서비스 작업(Service Entry)을 처리하며, 이 작업은 ServiceMain 함수로 정의됨.
    • ServiceMain은 서비스의 상태를 관리하고 작업의 메인 루프를 실행.
  3. 서비스 제어 핸들러
    • Handler 또는 HandlerEx 함수는 SCM으로부터 들어오는 명령(시작, 중지, 일시 중지 등)을 처리.
  4. 서비스 등록
    • CreateService API를 사용해 서비스 등록.
    • PowerShell 또는 명령줄 유틸리티(sc.exe create)로 수동 등록 가능.

 

3. Windows 서비스 C++ 예제

다음은 간단한 Windows 서비스 프로그램의 구조를 보여주는 C++ 코드입니다.

#include <windows.h>
#include <tchar.h>

// 서비스 이름 정의
#define SERVICE_NAME _T("MyService")

// 서비스 상태 핸들 및 상태 변수
SERVICE_STATUS_HANDLE g_ServiceStatusHandle = NULL;
SERVICE_STATUS g_ServiceStatus;

// 서비스 메인 함수 선언
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
void WINAPI ServiceCtrlHandler(DWORD dwCtrlCode);
void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint);

// 서비스 초기화 작업
void ServiceInit()
{
    ReportServiceStatus(SERVICE_RUNNING, NO_ERROR, 0);

    // 서비스 작업 실행
    while (g_ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        // 실제 서비스 작업 수행
        Sleep(1000); // 1초 대기
    }

    ReportServiceStatus(SERVICE_STOPPED, NO_ERROR, 0);
}

int _tmain(int argc, TCHAR *argv[])
{
    SERVICE_TABLE_ENTRY ServiceTable[] = {
        {SERVICE_NAME, ServiceMain},
        {NULL, NULL} // 끝 표시
    };

    if (!StartServiceCtrlDispatcher(ServiceTable))
    {
        return GetLastError();
    }

    return 0;
}

// 서비스 메인 함수 구현
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv)
{
    g_ServiceStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, ServiceCtrlHandler);

    if (!g_ServiceStatusHandle)
    {
        return;
    }

    ReportServiceStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
    ServiceInit();
}

// 서비스 제어 핸들러
void WINAPI ServiceCtrlHandler(DWORD dwCtrlCode)
{
    switch (dwCtrlCode)
    {
    case SERVICE_CONTROL_STOP:
        ReportServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
        g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        return;
    }
}

// 서비스 상태 보고
void ReportServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
    g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    g_ServiceStatus.dwCurrentState = dwCurrentState;
    g_ServiceStatus.dwWin32ExitCode = dwWin32ExitCode;
    g_ServiceStatus.dwWaitHint = dwWaitHint;

    SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
}

 

4. 서비스 설치 및 실행

4.1 서비스 등록

  • 명령어를 사용하여 서비스를 등록:
  sc create MyService binPath= "C:\Path\To\Service.exe"

4.2 서비스 실행

  • Windows 서비스 관리 도구나 명령줄에서 실행:
  net start MyService

4.3 서비스 삭제

  • 명령어를 사용하여 삭제:
  sc delete MyService

 

5. 개발 시 고려 사항

  1. 에러 처리: 서비스 초기화 및 실행 중 적절한 에러 로그 기록.
  2. 디버깅: 서비스 디버깅이 어려우므로 별도 실행 모드(예: 콘솔 모드) 추가.
  3. 보안: 서비스 권한 설정과 계정 사용에 주의.

위 코드를 기반으로 추가 기능을 구현하거나, C#과 같은 고수준 언어를 사용하면 더 쉽게 서비스를 개발할 수 있습니다. 서비스의 구체적인 요구 사항에 따라 작업 내용을 조정하세요.

반응형