개발하는 두더지

[C/C++/MFC] 윈도우 서비스 생성/시작/중지/삭제 본문

C,C++

[C/C++/MFC] 윈도우 서비스 생성/시작/중지/삭제

덜지 2016. 7. 22. 03:27

# 빌드환경

 - Windows 7/10 64bit  + VS2008 sp1


# 동작 플랫폼

 - Windows xp / vista / 7 / 8 / 8.1 / 10



# 윈도우 서비스란?

윈도우 서비스는 사용자에게 보이지 않지만 (UI X) , 윈도우를 부팅한 후 또는 사용자 로그인 후( Flag 설정으로 변경 가능 ) 부터 항상 실행되는 프로그램.


# 서비스 생성 조건

Main 진입점이 있는 콘솔 프로그램으로 주로 제작 ( C++ 의 경우 )


속성

일반 : 표준 Windows 라이브러리 사용

C/C++ -> 코드 생성 -> 런타임 라이브러리 : 다중 스레드(/MT)  or 다중 스레드 디버그(/MTD , 용량 더 커짐)

정적 라이브러리에서 MFC 사용으로 설정했을 때와 같음 -> 재배포 패키지 없는 환경에서도 구동 가능


# 서비스 구현


- 생성

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
int CService::ServiceCreate(TCHAR * serviceName, TCHAR * displayServiceName
                            , TCHAR * exePath, TCHAR * description)
{
    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService   = NULL;
 
    //1. 서비스를 오픈한다.
    schSCManager = ::OpenSCManager(NULLNULL, SC_MANAGER_CREATE_SERVICE);
    if(schSCManager == NULL)
    {
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceCreate_OpenSCManager] : ")) ;
        return dwError;
    }
    
    //2. 오픈한 서비스 핸들로 서비스를 생성한다.
    schService = ::CreateService( 
        schSCManager,              // SCManager database 
        serviceName,               // name of service 
        displayServiceName,        // service name to display 
        SERVICE_ALL_ACCESS,        // desired access 
        SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type 
        SERVICE_AUTO_START,      // start type 
        SERVICE_ERROR_NORMAL,      // error control type 
        exePath,                   // service's binary 
        NULL,                      // no load ordering group 
        NULL,                      // no tag identifier 
        NULL,                      // no dependencies 
        NULL,                      // LocalSystem account 
        NULL);                     // no password 
 
    if(schService == NULL)
    {
        CloseServiceHandle(schSCManager);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceCreate_CreateService] : ")) ;
        return dwError;
    }
 
    SERVICE_DESCRIPTION Desc;
    Desc.lpDescription = description;
 
    //3. 생성된 서비스에 서비스 설명 내용을 변경해준다.
    BOOL bServiceConfig = ::ChangeServiceConfig2(schService, SERVICE_CONFIG_DESCRIPTION, &Desc);
    if(bServiceConfig == FALSE)
    {
        CloseServiceHandle(schSCManager);
        CloseServiceHandle(schService);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceCreate_OpenSCManager] : ")) ;
 
        return dwError;
    }
 
    //4. 서비스 핸들모두 닫아준다.
    CloseServiceHandle(schSCManager);
    CloseServiceHandle(schService);
 
    return ERROR_NONE;
}
cs


// SERVICE_INTERACTIVE_PROCESS

로 서비스를 실행하면

서비스 -> 자신의 서비스 속성 -> 로그온 탭 -> 로컬시스템계정 ( 서비스와 테스크톱 상호 작용 허용 ) 이 체크되어있는 상태와 같음


- 실행

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
int CService::ServiceStart(TCHAR * serviceName)
{
    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService   = NULL;
 
    //1. 서비스를 오픈한다.
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStart_OpenSCManager] : ")) ;
 
        return dwError;
    }
 
    //2. 서비스 이름으로 특정 서비스를 오픈한다.
    schService  = OpenService( 
        schSCManager,         // SCM database 
        serviceName,          // name of service 
        SERVICE_ALL_ACCESS);  // full access 
 
    if (schService == NULL)
    { 
        CloseServiceHandle(schSCManager);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStart_OpenService] : ")) ;
 
        return dwError;
    }    
 
    //3. 서비스를 시작한다.
    BOOL bStart = ::StartService(schService, 0NULL);
    if(bStart == FALSE)
    {
        CloseServiceHandle(schSCManager);
        CloseServiceHandle(schService);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStart_StartService] : ")) ;
 
        return dwError;
    }
 
    //4. 서비스 핸들은 모두 닫아준다.
    CloseServiceHandle(schSCManager);
    CloseServiceHandle(schService);
 
    return ERROR_NONE;
}
cs


StartService 하면 밑의 내용이 포함된 exe가 실행되어야한다. (자신 프로세스도 가능)


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int APIENTRY _tWinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);
 
     // TODO: 여기에 코드를 입력합니다.
    WCHAR wzBINARY[MAX_PATH] = {0};
    ::GetModuleFileName(NULL, wzBINARY, MAX_PATH);
 
    if(lpCmdLine[0== 'i')
    {
        ServiceInstall();
        return 0;
    }
    else if(lpCmdLine[0== 'u')
    {
        ServiceUninstall();
        return 0;
    }
    else if(lpCmdLine[0== 's')
    {
        ServiceStart(_TEXT("KJH"));
        return 0;
    }
 
    SERVICE_TABLE_ENTRY STE[] =
    {
        {(WCHAR*)wzNAME, (LPSERVICE_MAIN_FUNCTION)_tmain_service},
        {NULL,NULL}
    };
 
    // 서비스를 위해서 특별히 만들어진 구조의 함수 시작 부분을 시스템에 전달해야 한다.
    // 콘솔 프로그램과 다른 부분은 이렇게 등록시킨 함수가 콘솔의 main 처럼 동작한다는 점이다.
    // 일종의 콜백함수 포인터를 등록하면, 서비스 매니저가 이걸 호출해주는 방식이다.
    if(StartServiceCtrlDispatcher(STE) == FALSE)
        return -1;
    
 
    return 0;
}
cs


-중지

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int CService::ServiceStop(TCHAR * serviceName)
{
    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService   = NULL;
 
    //1. 서비스를 오픈한다.
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStop_OpenSCManager] : ")) ;
 
        return dwError;
    }
 
    //2. 서비스 이름을 이용해서 특정 서비스를 오픈한다.
    schService  = OpenService( 
        schSCManager,         // SCM database 
        serviceName,          // name of service 
        SERVICE_ALL_ACCESS);  // full access 
 
    if (schService == NULL)
    { 
        CloseServiceHandle(schSCManager);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStop_OpenService] : ")) ;
 
        return dwError;
    }    
 
    SERVICE_STATUS ss;
 
    //3. 서비스의 상태를 요청한다.
    BOOL bQuery = ::QueryServiceStatus(schService, &ss);
    if(bQuery == FALSE)
    {
        CloseServiceHandle(schSCManager);
        CloseServiceHandle(schService);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceStop_QueryServiceStatus] : ")) ;
 
        return dwError;
    }
 
    //4. 서비스 상태가 STOP 상태가 아니라면 STOP을 요청한다.
    if(ss.dwCurrentState != SERVICE_STOPPED)
    {
        BOOL bControl = ::ControlService(schService, SERVICE_CONTROL_STOP, &ss);
        if(bControl == FALSE)
        {
            CloseServiceHandle(schSCManager);
            CloseServiceHandle(schService);
 
            DWORD  dwError       = ::GetLastError();
            ErrorMsg(dwError,_TEXT("[CService::ServiceStop_ControlService] : ")) ;
 
            return dwError;
 
        }
        ::Sleep(2000); //서비스가 2초안에 종료 되어야 한다.
    }
 
    //5. 서비스 핸들은 모두 닫아준다.
    CloseServiceHandle(schSCManager);
    CloseServiceHandle(schService);
 
    return ERROR_NONE;
 
}
cs


- 삭제

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int CService::ServiceDelete(TCHAR * serviceName)
{
    SC_HANDLE schSCManager = NULL;
    SC_HANDLE schService   = NULL;
 
    //1. 서비스를 오픈한다.
    schSCManager = OpenSCManager( 
        NULL,                    // local computer
        NULL,                    // servicesActive database 
        SC_MANAGER_ALL_ACCESS);  // full access rights 
 
    if (NULL == schSCManager) 
    {
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceDelete_OpenSCManager] : ")) ;
 
        return dwError;
    }
 
    //2. 서비스 이름을 이용해서 특정 서비스를 오픈한다.
    schService  = OpenService( 
        schSCManager,         // SCM database 
        serviceName,          // name of service 
        SERVICE_ALL_ACCESS);  // full access 
 
    if (schService == NULL)
    { 
        CloseServiceHandle(schSCManager);
 
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceDelete_OpenService] : ")) ;
 
        return dwError;
    }    
 
    //3. 특정 서비스를 삭제한다.
    BOOL bDelete = ::DeleteService(schService);
    if(bDelete == FALSE)
    {
        CloseServiceHandle(schSCManager);
        CloseServiceHandle(schService);
        DWORD  dwError       = ::GetLastError();
        ErrorMsg(dwError,_TEXT("[CService::ServiceDelete_OpenService] : ")) ;
 
        return dwError;
    }
 
    //4. 서비스 핸들은 모두 닫아준다.
    CloseServiceHandle(schSCManager);
    CloseServiceHandle(schService);
    return ERROR_NONE;
 
}
cs


Comments