MyService 프로그램 사용법
1. 서비스 관리 명령어
- 주요 기능
- 자동 시작 서비스로 설치됨
- 5초마다 상태 로깅
- Ctrl+C로 디버그 모드 종료 가능
- 디버그 모드
- 개발/테스트 시 -d 옵션으로 콘솔에서 직접 실행 가능
- 로그가 콘솔에도 출력됨
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 4819)
#pragma warning(disable: 4100)
#include <windows.h>
#include <tchar.h>
#include <winsvc.h>
#include <iostream>
#include <fstream>
#include <chrono>
#include <ctime>
#include <string>
#include <filesystem>
// 상수 정의
#define SERVICE_NAME _T("MyService")
#define SERVICE_DISPLAY_NAME _T("My Service")
#define SERVICE_DESC _T("My Service Description")
#define LOG_FILE "c:\\mysvc.log"
// 전역 변수
static bool g_debug_mode = false;
static std::ofstream g_log_file;
static SERVICE_STATUS_HANDLE g_ssh = NULL;
static HANDLE g_stop_event = NULL;
// 유니코드 매크로
#ifdef UNICODE
#define tprintf wprintf
#define tstring std::wstring
#else
#define tprintf printf
#define tstring std::string
#endif
// 함수 선언
void MySetStatus(DWORD dstatus, DWORD daccept);
void ServiceHandler(DWORD dwcontrol);
void ServiceHandlerEx(DWORD dwcontrol, DWORD dweventtype, LPVOID lpeventdata, LPVOID lpcontext);
void ServiceMain();
VOID WINAPI ServiceMainProc(DWORD argc, LPTSTR* argv);
bool InstallService();
bool UninstallService();
BOOL MyStartService(tstring Name);
bool StopService();
BOOL WINAPI DebugConsoleHandler(DWORD signal);
bool KillService();
// 로깅 함수
void LogMessage(const std::string& message) {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::string timestamp = std::ctime(&time);
timestamp.pop_back();
std::string formatted_msg = "[" + timestamp + "] " + message + "\n";
if (g_debug_mode) {
std::cout << formatted_msg;
}
if (g_log_file.is_open()) {
g_log_file << formatted_msg;
g_log_file.flush();
}
}
// 서비스 상태 관리 함수
void MySetStatus(DWORD dstatus, DWORD daccept) {
SERVICE_STATUS ss = { 0, };
if (!g_ssh) {
LogMessage("MySetStatus: ERR status handle is null!");
return;
}
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = dstatus;
ss.dwControlsAccepted = daccept;
ss.dwWin32ExitCode = 10 * 1000;
SetServiceStatus(g_ssh, &ss);
}
// 서비스 이벤트 핸들러
void ServiceHandler(DWORD dwcontrol) {
LogMessage("ServiceHandler: control=" + std::to_string(dwcontrol));
switch (dwcontrol) {
case SERVICE_CONTROL_STOP:
LogMessage("SERVICE_CONTROL_STOP received");
MySetStatus(SERVICE_STOP_PENDING, SERVICE_ACCEPT_STOP);
SetEvent(g_stop_event);
break;
default:
MySetStatus(dwcontrol, SERVICE_ACCEPT_STOP);
break;
}
}
void ServiceHandlerEx(DWORD dwcontrol, DWORD dweventtype, LPVOID lpeventdata, LPVOID lpcontext) {
LogMessage("ServiceHandlerEx: control=" + std::to_string(dwcontrol));
switch (dwcontrol) {
case SERVICE_CONTROL_SESSIONCHANGE:
LogMessage("ServiceHandlerEx: session change");
break;
default:
ServiceHandler(dwcontrol);
break;
}
}
// 서비스 메인 함수
void ServiceMain() {
while (WaitForSingleObject(g_stop_event, 500) == WAIT_TIMEOUT) {
LogMessage("Service is running...");
}
LogMessage("ServiceMain: End");
if (!g_debug_mode) {
MySetStatus(SERVICE_STOPPED, 0);
}
if (g_debug_mode) {
CloseHandle(g_stop_event);
}
}
VOID WINAPI ServiceMainProc(DWORD argc, LPTSTR* argv) {
g_log_file.open(LOG_FILE, std::ios::app);
LogMessage("Service started");
g_ssh = RegisterServiceCtrlHandlerEx(SERVICE_NAME, (LPHANDLER_FUNCTION_EX)ServiceHandlerEx, NULL);
LogMessage("ServiceMainProc: g_ssh=" + std::to_string((uint64_t)g_ssh));
if (!g_ssh) {
LogMessage("Failed to register service control handler");
g_log_file.close();
return;
}
MySetStatus(SERVICE_START_PENDING, 0);
if (!g_stop_event) {
g_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!g_stop_event) {
MySetStatus(SERVICE_STOPPED, 0);
g_log_file.close();
return;
}
}
MySetStatus(SERVICE_RUNNING, SERVICE_ACCEPT_STOP);
ServiceMain();
g_log_file.close();
}
// 서비스 관리 함수들
bool InstallService() {
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
if (!scm) return false;
TCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
_tcscat_s(path, _T(" -svc"));
SC_HANDLE service = CreateService(
scm, SERVICE_NAME, SERVICE_DISPLAY_NAME,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, path,
NULL, NULL, NULL, NULL, NULL);
if (service) {
SERVICE_DESCRIPTION sd = {const_cast<LPTSTR>(SERVICE_DESC)};
ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd);
CloseServiceHandle(service);
}
CloseServiceHandle(scm);
return service != NULL;
}
bool UninstallService() {
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!scm) return false;
SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
if (!service) {
CloseServiceHandle(scm);
return false;
}
SERVICE_STATUS status;
ControlService(service, SERVICE_CONTROL_STOP, &status);
bool result = DeleteService(service);
CloseServiceHandle(service);
CloseServiceHandle(scm);
return result;
}
BOOL MyStartService(tstring Name) {
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!schSCManager) {
DWORD err = GetLastError();
LogMessage("OpenSCManager failed. Error: " + std::to_string(err));
return FALSE;
}
SC_HANDLE schService = OpenService(schSCManager, Name.c_str(), SERVICE_START | SERVICE_QUERY_STATUS);
if (!schService) {
DWORD err = GetLastError();
LogMessage("OpenService failed. Error: " + std::to_string(err));
CloseServiceHandle(schSCManager);
return FALSE;
}
// 서비스 상태 확인
SERVICE_STATUS status;
if (QueryServiceStatus(schService, &status)) {
if (status.dwCurrentState == SERVICE_RUNNING) {
LogMessage("Service is already running");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return TRUE;
}
}
// 서비스 시작
if (!StartService(schService, 0, NULL)) {
DWORD err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING) {
LogMessage("Service is already running");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return TRUE;
}
LogMessage("StartService failed. Error: " + std::to_string(err));
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return FALSE;
}
// 서비스가 실제로 시작될 때까지 대기
int retries = 0;
while (retries < 10) { // 최대 10초 대기
if (QueryServiceStatus(schService, &status)) {
if (status.dwCurrentState == SERVICE_RUNNING) {
LogMessage("Service started successfully");
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return TRUE;
}
if (status.dwCurrentState == SERVICE_STOPPED) {
LogMessage("Service failed to start");
break;
}
}
Sleep(1000);
retries++;
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return FALSE;
}
bool StopService() {
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!scm) return false;
SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_STOP | SERVICE_QUERY_STATUS);
if (!service) {
CloseServiceHandle(scm);
return false;
}
SERVICE_STATUS status;
bool result = ControlService(service, SERVICE_CONTROL_STOP, &status);
// 서비스가 완전히 중지될 때까지 대기
if (result) {
while (QueryServiceStatus(service, &status)) {
if (status.dwCurrentState == SERVICE_STOPPED) {
break;
}
Sleep(1000);
}
}
CloseServiceHandle(service);
CloseServiceHandle(scm);
return result;
}
// 디버그 모드 핸들러
BOOL WINAPI DebugConsoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT || signal == CTRL_BREAK_EVENT) {
LogMessage("Debug mode stop requested");
if (g_stop_event) {
SetEvent(g_stop_event);
}
return TRUE;
}
return FALSE;
}
// 서비스 강제 종료 함수 구현
bool KillService() {
SC_HANDLE scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!scm) {
LogMessage("OpenSCManager failed in KillService");
return false;
}
SC_HANDLE service = OpenService(scm, SERVICE_NAME, SERVICE_ALL_ACCESS);
if (!service) {
LogMessage("OpenService failed in KillService");
CloseServiceHandle(scm);
return false;
}
// 현재 서비스 상태 확인
SERVICE_STATUS status = { 0 };
if (!QueryServiceStatus(service, &status)) {
LogMessage("QueryServiceStatus failed in KillService");
CloseServiceHandle(service);
CloseServiceHandle(scm);
return false;
}
// 서비스가 이미 중지 상태면 종료
if (status.dwCurrentState == SERVICE_STOPPED) {
LogMessage("Service is already stopped");
CloseServiceHandle(service);
CloseServiceHandle(scm);
return true;
}
// 서비스 강제 종료 후 상태를 STOPPED로 변경
LogMessage("Forcing service to stop state");
memset(&status, 0, sizeof(SERVICE_STATUS));
status.dwCurrentState = SERVICE_STOPPED;
ControlService(service, SERVICE_CONTROL_STOP, &status);
CloseServiceHandle(service);
CloseServiceHandle(scm);
return true;
}
// 메인 함수
int main(int argc, char* argv[]) {
if (argc > 1) {
if (strcmp(argv[1], "-i") == 0) {
return InstallService() ? 0 : 1;
}
else if (strcmp(argv[1], "-r") == 0) {
return MyStartService(SERVICE_NAME) ? 0 : 1;
}
else if (strcmp(argv[1], "-s") == 0) {
return StopService() ? 0 : 1;
}
else if (strcmp(argv[1], "-u") == 0) {
return UninstallService() ? 0 : 1;
}
else if (strcmp(argv[1], "-d") == 0) {
g_debug_mode = true;
SetConsoleCtrlHandler(DebugConsoleHandler, TRUE);
g_log_file.open(LOG_FILE, std::ios::app);
LogMessage("Debug mode started");
g_stop_event = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!g_stop_event) {
g_log_file.close();
return 1;
}
ServiceMain();
g_log_file.close();
return 0;
}
else if (strcmp(argv[1], "-svc") == 0) {
SERVICE_TABLE_ENTRY serviceTable[] = {
{const_cast<LPTSTR>(SERVICE_NAME), ServiceMainProc},
{NULL, NULL}
};
return StartServiceCtrlDispatcher(serviceTable) ? 0 : 1;
}
else if (strcmp(argv[1], "-k") == 0) {
return KillService() ? 0 : 1;
}
}
return 0;
}
'Develop > C&CPP' 카테고리의 다른 글
[server] ThreadPool, client 접속이 많을 때. TCP Server (0) | 2025.01.05 |
---|---|
[Server] work, cache, lock, 동시접근문제, 성능문제 (0) | 2025.01.05 |
[알고리즘] 정렬 알고리즘 비교/성능측정 (1) | 2019.12.16 |
[알고리즘] 정렬5: quick sort (0) | 2019.12.15 |
[알고리즘] 정렬4: merge sort (0) | 2019.12.15 |