ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Windows 서비스 프로그램 개발 - 1
    Windows 프로그래밍 2019. 1. 8. 13:22


    서비스 프로그램을 개발하게 되었는데 너무 길 것 같아서 나눠서 적어야겠다.

    서비스는 admin 권한이 필요한 프로그램이지만 사용자에게 허용 알림을 띄우지 않고도 작업 수행이 가능하게 하고 싶을 때, Windows가 부팅되자마자 사용자가 로그인 하기도 전에 자동으로 실행시키고 싶을 때 사용한다.


    일단 전체적인 큰 틀부터 잡아본다면 구조는 아래와 같다.


    서비스 실행 > 서비스.exe > 서비스.exe에서 원하는 프로그램 실행 > 내가 원하는 프로그램


    그럼 이제 서비스를 생성해보자. 생성을 해야 실행을 할테니 ㅎㅎㅎ


    Create 과정

    BOOL CService::CreateServiceProc(WCHAR *ServiceName, WCHAR *DisplayName, WCHAR *ExePath) { CString ErrorString; SC_HANDLE SCMhandle = NULL; SC_HANDLE CreateSChandle = NULL; SCMhandle = ::OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if (SCMhandle == NULL) { int Err = GetLastError(); ErrorString.Format(L"Create OpenSCManager : %d", Err); AfxMessageBox(ErrorString); return FALSE; } CreateSChandle = ::CreateService(SCMhandle, ServiceName, DisplayName, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, ExePath, NULL, NULL, NULL, NULL, NULL); int Err = GetLastError(); if (Err == 1073) { CloseServiceHandle(SCMhandle); return TRUE; } if (CreateSChandle == NULL) { CloseServiceHandle(SCMhandle); int Err = GetLastError(); ErrorString.Format(L"Create CreateService : %d", Err); AfxMessageBox(ErrorString); return FALSE; } CloseServiceHandle(SCMhandle); CloseServiceHandle(CreateSChandle); return TRUE; }

    코드는 이렇다. 복잡해 보일 수 있지만 알고보면 엄청 단순하다.


    코드를 설명하기 전에 함수에 넘어가야 할 인수들을 먼저 정리하고 넘어가자. ServiceName은 말 그대로 서비스의 이름이고, DisplayName은 사용자에게 보여질 이름이다. ExePath는 실행시킬 Exe 경로를 넣어주면 된다.


    자. 여기서 헷갈리면 안된다. 우린 지금 위에 크게 적어놓은 4가지 단계 중 첫 번째를 만들고 있는 것이고, ExePath에 들어가야 할 경로는 2번째 단계인 서비스 exe의 경로를 넣어주어야 한다. 그리고 지금은 서비스 exe를 만들고 있는 것이 아니고 첫 번째 단계를 만들고 있다.


    일단 SCManager라는 것을 Open한다. (이게 연다라고 적기는 이상한 것 같아서..)

    SCManager는 Windows의 서비스들을 제어 및 관리할 수 있게 해준다. 지금은 서비스를 생성하고 있으므로 생성용 SCManager가 필요하기 때문에 마지막 인자는 SC_MANAGER_CREATE_SERVICE로 준다.


    제대로 SCManager가 열렸는지 확인한 후 이제 CreateService 함수를 통해 서비스를 생성한다.

    1073번 에러는 이미 같은 서비스가 존재한다는 뜻이다. 여러 번 컴파일을 시도할 시 같은 서비스가 이미 만들어져 있을 수 있기 때문에 코드 상의 문제가 아니므로 TRUE를 리턴해준다.


    서비스가 제대로 생성되었는지 확인한 후 그렇지 않다면 SCManager 핸들을 닫은 후 FALSE를 리턴하고 생성되었다면 TRUE를 리턴한다.


    그럼 이제 Create가 끝났으니 Start로 넘어가 보자.


    Start 과정

    BOOL CService::StartServiceProc(WCHAR *ServiceName) {
    
    	CString ErrorString;
    
    	SC_HANDLE SCMhandle = NULL;
    	SC_HANDLE OpenSChandle = NULL;
    
    	SCMhandle = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
    	
    	if (SCMhandle == NULL) {
    		int Err = GetLastError();
    		ErrorString.Format(L"Start OpenSCManager : %d", Err);
    		AfxMessageBox(ErrorString);
    		return FALSE;
    	}
    
    	OpenSChandle = ::OpenService(SCMhandle, ServiceName, SC_MANAGER_ALL_ACCESS);
    	
    	if (OpenSChandle == NULL) {
    		CloseServiceHandle(SCMhandle);
    
    		int Err = GetLastError();
    		ErrorString.Format(L"Start OpenService : %d", Err);
    		AfxMessageBox(ErrorString);
    		return FALSE;
    	}
    
    	BOOL StartResult = ::StartService(OpenSChandle, NULL, NULL);
    	int Err = GetLastError();
    
    	if (StartResult == FALSE) {
    		CloseServiceHandle(SCMhandle);
    		CloseServiceHandle(OpenSChandle);
    
    		int Err = GetLastError();
    
    		if (Err == 1056) {
    			return TRUE;
    		}
    		ErrorString.Format(L"Start StartService : %d", Err);
    		AfxMessageBox(ErrorString);
    		return FALSE;
    	}
    
    	CloseServiceHandle(SCMhandle);
    	CloseServiceHandle(OpenSChandle);
    
    	return TRUE;
    }

    Create 과정과 비슷하다.


    먼저 SCManager를 열어준다. 생성하는 과정이 아니기 때문에 마지막 권한은 SC_MANAGER_ALL_ACCESS 준다. 그 다음 OpenService 함수를 이용해 앞에서 Create한 서비스를 연 후 StartService 함수를 통해 해당 서비스를 실행하면 된다. 에러처리 같은건 Create와 같아서 뺀다 ㅎㅎ...


    1056번 에러는 이미 실행중이라는 뜻이므로 TRUE를 리턴해주며 가볍게 무시해 준다.

    'Windows 프로그래밍' 카테고리의 다른 글

    Windows 서비스 프로그램 개발 - 3  (0) 2019.01.08
    Windows 서비스 프로그램 개발 - 2  (0) 2019.01.08
    Volatile 변수란?  (2) 2019.01.07
    Service 프로세스에서 winlogon.exe  (0) 2019.01.03
    OnTimer 함수  (0) 2019.01.03

    댓글

Designed by Tistory.