Project Hub

1. 서비스 제어 관리자 (SCM : Service Control Manager) 구현 본문

Windows/Windows Service

1. 서비스 제어 관리자 (SCM : Service Control Manager) 구현

safy 2022. 7. 16. 12:48
728x90
반응형

참고 사항

더보기

Service 란?  

Windows 에서 실행되는 프로그램 종류 중 하나
백그라운드에서 실행되는 프로그램
사용자에게 보이지 않지만 시스템 유지, 데이터 제공, 하드웨어 관리 등의 기능 수행
UI 없음
서버 응용 프로그램이므로 NT, 서버급 windows 에서만 설치/실행 됨.
레지스트리에 등록 (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Service)

services.msc (서비스 애플릿)

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Service 해당 경로에 부팅 시 실행해야 하는 서비스 목록이 기록됨
이 레지스트리에 있는 정보를 서비스 데이터 베이스 라고 부름.
SCM을 GUI로 확인할 수 있음.

 

Service Control Manager(이하 SCM) 이란?

윈도우 서비스 프로세스와 시작과 정지 및 상호작용 하는 윈도우 NT 하의 특별한 시스템 프로세스.
서비스 DB를 관리하는 시스템 프로그램.

 

SCM 구현

  • service control 을 위한 기능을 클래스화하여 사용에 용이하도록 함.
#pragma once

#include <stdio.h>
#include <Windows.h>
#include <winsvc.h>

class CControlService 
{
public:
	CControlService();
	virtual ~CControlService();

public:
	DWORD CreateService(IN LPCTSTR lpServiceName,
		IN LPCTSTR lpDisplayName,
		IN DWORD dwServiceType,
		IN DWORD dwStartType,
		IN LPCTSTR lpBinaryPathName,
		IN LPCTSTR lpLoadOrderGroup,
		OUT LPDWORD lpdwTagId,
		IN LPCTSTR lpDependencies,
		IN LPCTSTR lpServiceStartName,
		IN LPCTSTR lpPassword);
	DWORD ChangeServiceConfig(LPCTSTR lpServiceName, DWORD dwStartType, LPCTSTR lpszDescription);
	DWORD StartService(LPCTSTR lpServiceName);
	DWORD StopService(LPCTSTR lpServiceName);
	DWORD PauseService(LPCTSTR lpServiceName);
	DWORD ContinueSerivce(LPCTSTR lpServiceName);
	DWORD ShutdownService(LPCTSTR lpServiceName);
	DWORD DeleteService(LPCTSTR lpServiceName);
	BOOL IsServiceInstalled(LPCTSTR lpServiceName);
	BOOL IsServiceRunning(LPCTSTR lpServiceName);
};
  • 함수 구현 내용
더보기
#include "ControlService.h"

CControlService::CControlService()
{

}

CControlService::~CControlService()
{

}

DWORD CControlService::CreateService(IN LPCTSTR lpServiceName, 
									 IN LPCTSTR lpDisplayName, 
									 IN DWORD dwServiceType, 
									 IN DWORD dwStartType, 
									 IN LPCTSTR lpBinaryPathName, 
									 IN LPCTSTR lpLoadOrderGroup, 
									 OUT LPDWORD lpdwTagId, 
									 IN LPCTSTR lpDependencies, 
									 IN LPCTSTR lpServiceStartName, 
									 IN LPCTSTR lpPassword)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);  //Required to call the CreateService function to create a service object and add it to the database.
	if (schSCManager)
	{
		schService = ::CreateService(schSCManager,		
			lpServiceName,							// "TEST_Service"
			lpDisplayName,							// "TEST_Service"
			SERVICE_ALL_ACCESS,
			dwServiceType,							// SERVICE_WIN32_OWN_PROCESS
			dwStartType,							// SERVICE_AUTO_START
			SERVICE_ERROR_NORMAL,
			lpBinaryPathName,						// "Service program path" -> "" 이 없을 경우 시스템에 따라서 서비스 시작시 193 에러가 반환됨.
			lpLoadOrderGroup,						// NULL
			lpdwTagId,								// NULL
			lpDependencies,							// "\0"
			lpServiceStartName,						// NULL
			lpPassword);							// NULL

		if (schService)
			::CloseServiceHandle(schService);
		// 이미 서비스가 있는 경우, ERROR_SERVICE_EXISTS 리턴
		else if (ERROR_SERVICE_EXISTS != GetLastError())
			dwRet = GetLastError();

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::ChangeServiceConfig(LPCTSTR lpServiceName, DWORD dwStartType, LPCTSTR lpszDescription)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_CHANGE_CONFIG);

		if (schService)
		{
			if (!::ChangeServiceConfig(schService,	// handle of service 
				SERVICE_NO_CHANGE,					// service type: no change 
				dwStartType,						// change service start type 
				SERVICE_NO_CHANGE,					// error control: no change 
				NULL,								// binary path: no change 
				NULL,								// load order group: no change
				NULL,								// tag ID: no change 
				NULL,								// dependencies: no change 
				NULL,								// account name: no change 
				NULL,								// password: no change 
				lpszDescription))					// display name
			{
				dwRet = GetLastError();
			}

			//:) NT4.0에서는 ChangeServiceConfig2()가 없다.
#if _MSC_VER >= 1500
			SERVICE_DESCRIPTION sd = {0};
			sd.lpDescription = const_cast<LPWSTR>(lpszDescription);

			if( !::ChangeServiceConfig2(
				schService,					// handle to service
				SERVICE_CONFIG_DESCRIPTION,	// change: description
				&sd) )						// new description
			{
				dwRet = GetLastError();
			}
#endif
			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::StartService(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); // local computer, SERVICES_ACTIVE_DATABASE
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_ALL_ACCESS);

		if (schService)
		{
			if (!::StartService(schService, 0, NULL))
			{
				if (ERROR_SERVICE_ALREADY_RUNNING != GetLastError())
					dwRet = GetLastError();
			}

			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::StopService(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;
	SERVICE_STATUS ss;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_QUERY_STATUS | SERVICE_STOP); // SERVICE_QUERY_STATUS?
		if (schService)
		{
			::ControlService(schService, SERVICE_CONTROL_STOP, &ss);
			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::PauseService(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_ALL_ACCESS);
		if (schService)
		{
			if (NULL == ::ControlService(schService, SERVICE_CONTROL_PAUSE, NULL))
			{			
				dwRet = GetLastError();			
			}		
			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::ContinueSerivce(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_ALL_ACCESS);
		if (schService)
		{
			if (NULL == ::ControlService(schService, SERVICE_CONTROL_CONTINUE, NULL))
			{			
				dwRet = GetLastError();			
			}		
			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::ShutdownService(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_ALL_ACCESS);
		if (schService)
		{
			if (NULL == ::ControlService(schService, SERVICE_CONTROL_SHUTDOWN, NULL))
			{			
				dwRet = GetLastError();			
			}		
			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

DWORD CControlService::DeleteService(LPCTSTR lpServiceName)
{
	DWORD dwRet = ERROR_SUCCESS;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); // local computer, SERVICES_ACTIVE_DATABASE
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, DELETE);

		if (schService)
		{
			if (!::DeleteService(schService))
				dwRet = GetLastError();

			::CloseServiceHandle(schService);
		}
		else
		{
			dwRet = GetLastError();
		}

		::CloseServiceHandle(schSCManager);
	}
	else
	{
		dwRet = GetLastError();
	}

	return dwRet;
}

BOOL CControlService::IsServiceInstalled(LPCTSTR lpServiceName)
{
	BOOL bRet = FALSE;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_QUERY_CONFIG);
		if (schService)
		{
			bRet = TRUE;
			::CloseServiceHandle(schService);
		}
		
		::CloseServiceHandle(schSCManager);
	}

	return bRet;
}

BOOL CControlService::IsServiceRunning(LPCTSTR lpServiceName)
{
	BOOL bRet = FALSE;
	SC_HANDLE schSCManager = NULL;
	SC_HANDLE schService = NULL;
	SERVICE_STATUS ss;

	schSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
	if (schSCManager)
	{
		schService = ::OpenService(schSCManager, lpServiceName, SERVICE_QUERY_CONFIG|SERVICE_QUERY_STATUS);
		if (schService)
		{
			if (::QueryServiceStatus(schService, &ss))
			{
				if (SERVICE_RUNNING == ss.dwCurrentState)
					bRet = TRUE;
			}
			::CloseServiceHandle(schService);
		}

		::CloseServiceHandle(schSCManager);
	}

	return bRet;
}

 

향후 진행 & 추가 확인해야 할 내용

  • service 프로그램 구현하여 SCM을 활용해 제어하는 과정 확인.
  • SCM도 singleton 필요?
  • 서비스 속성 제어에 대한 부분도 추가 확인 필요.

 

 

이미지 출처 : https://www.flaticon.com/free-icons/control

728x90
반응형

'Windows > Windows Service' 카테고리의 다른 글

2. ServiceApp 구현 및 서비스 실행  (1) 2022.08.06
Comments