- 명령 프롬프트를 이용한 방법
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 |
#include <iostream> int main(void) { bool result = false; FILE* file; char output[300]; const char * command_1 = "sc query Audiosrv"; const char * command_2 = "sc query FontCache"; file = _popen(command_1, "r"); fread(output, 1, sizeof(output), file); fclose(file); if(strstr(output,": 4 RUNNING")) { result = true; } file = _popen(command_2, "r"); fread(output, 1, sizeof(output), file); fclose(file); if(strstr(output,": 4 RUNNING")) { result = true; } if(result){ printf("Service is running\n"); } return 0; } |
명령 프롬프트에서 sc.exe에 특정 서비스에 대한 쿼리를 날린 후 출력물에서 RUNNING이 들어있으면 실행 중이라고 판단하는 코드입니다. WinAPI를 이용하지 않고 이용할 수 있는 방법이지요. 간단하지만 코드의 측면에서는 수준이 정말 낮지요… 허점도 많고. 그 다음으로 짠 코드는 아래와 같습니다.
- ControlService를 이용한 방법
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 |
#include <iostream> using std::wcout; using std::endl; #include <windows.h> int main(void) { bool result = false; SERVICE_STATUS ss; SC_HANDLE hScm; SC_HANDLE hSvc; wchar_t * service_1 = L"Audiosrv"; wchar_t * service_2 = L"Fontcache"; hScm = OpenSCManagerW(NULL, NULL, GENERIC_READ); if(hScm == NULL) { wcout << "OpenSCManagerW failed with Error Code:" << GetLastError() << endl; } hSvc = OpenServiceW(hScm, service_1, SERVICE_INTERROGATE); if(hSvc == NULL) { cout << "OpenServiceW failed with Error Code:" << GetLastError() << endl; } ControlService(hSvc, SERVICE_CONTROL_INTERROGATE, &ss); if(ss.dwCurrentState == SERVICE_RUNNING) { result = true; } hSvc = OpenServiceW(hScm, service_2, SERVICE_INTERROGATE); if(hSvc == NULL) { cout << "According service not exists" << endl; } ControlService(hSvc, SERVICE_CONTROL_INTERROGATE, &ss); if(ss.dwCurrentState == SERVICE_RUNNING) { result = true; } if(result){ printf("Service is running\n"); } return 0; } |
잘 작동합니다. 다만 OpenServiceW의 인자에 있는 SERVICE_INTERROGATE와 ControlService의 인자에 있는 SERVICE_CONTROL_INTERROGATE는 권한 문제가 생길 수 있습니다.
- QueryServiceStatusEx를 이용한 방법(불완전)
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 |
#include <iostream> using std::wcout; using std::endl; #include <windows.h> int main(void) { bool result = false; wchar_t * service_1 = L"Audiosrv"; SERVICE_STATUS_PROCESS ssStatus; SC_HANDLE schSCManager; SC_HANDLE schService; DWORD dwBytesNeeded; // SC_MANAGER_ALL_ACCESS를 쓸려면 관리자 권한으로 실행된 상태여야함 schSCManager = OpenSCManagerW(NULL, NULL, GENERIC_READ); if(NULL == schSCManager) { wcout << "Failed to open Service Manager" << endl; } schService = OpenServiceW(schSCManager, service_1, GENERIC_READ); if(NULL == schService) { wcout << "OpenServiceW failed with Error Code: " << GetLastError() << endl; CloseServiceHandle(schSCManager); } if(!QueryServiceStatusEx( schService, SC_STATUS_PROCESS_INFO, (LPBYTE) &ssStatus, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)) { wcout << "QueryServiceStatusEx failed with Error Code: " << GetLastError() << endl; CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } if((ssStatus.dwCurrentState == SERVICE_STOPPED) || (ssStatus.dwCurrentState == SERVICE_RUNNING) || (ssStatus.dwCurrentState == SERVICE_PAUSED)) { result = true; } CloseServiceHandle(schService); CloseServiceHandle(schSCManager); if(result) { printf("Service is running\n"); } return 0; } |
ControlService는 상태 뿐만 아니라 다른 행동도 할 수 있습니다. 단순히 상태만 알고싶다면 QueryServiceStatusEx를 써도 됩니다.
참고로 GENERIC_READ로 들어간 인자를 SC_MANAGER_ALL_ACCESS 혹은 GENERIC_ALL_ACESS로 채워넣고 싶다면 관리자 권한으로 실행해야 합니다. 그렇지 않으면 핸들을 받아오지 못합니다.
불완전한 이유는 무엇일까요? 서비스가 존재하지 않을 때엔 쿼리를 보내지 않도록 해뒀어야 합니다.
- 예외 처리가 적용된 QueryServiceStatusEx (일부분)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
schService = OpenServiceW(schSCManager, service_1, GENERIC_READ); if((NULL == schService) && (GetLastError() == 0x424)) { //wcout << "Service not exists" << endl; } else if(NULL == schService) { wcout << "OpenServiceW failed with Error Code: " << GetLastError() << endl; CloseServiceHandle(schSCManager); } else { if(!QueryServiceStatusEx( schService, SC_STATUS_PROCESS_INFO, (LPBYTE) &ssStatus, sizeof(SERVICE_STATUS_PROCESS), &dwBytesNeeded)) { wcout << "QueryServiceStatusEx failed with Error Code: " << GetLastError() << endl; CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } if((ssStatus.dwCurrentState == SERVICE_STOPPED) || (ssStatus.dwCurrentState == SERVICE_RUNNING) || (ssStatus.dwCurrentState == SERVICE_PAUSED)) { result = true; } } |
좋은 정보 감사합니다. 혹시 시간 괜찮으시면 하나 여쭤보고싶습니다.
C#으로 개발중이기는 한데 C#의 ServiceController 라는 클래스로 외부 서버의 서비스 상태를 확인하는 프로그램을 만들었습니다. 서버가 여러개 있어서 각 서버의 서비스 상태를 확인해서 메일로 날리는 프로그램인데요. 이게 문제가 net use \\ip /user:ID PASS 로 각 서버의 서비스에 접근가능한 유저 권한을 주지 않으면 접근이 거부됩니다.이 글 보고 sc 커멘드로도 해봤는데 sc로 접근해도 거부되더라구요. 뭐 net use로 접근유저정보 기억시켜주면 해결될 문제이긴한데…서버 관리자가 좀 불안해 하는게 아무래도 Administrator권한을 쥐어주다보니…프로그램은 분명 status만 확인하는건 틀림없는데 혹시 진짜 혹시라도 뭐가 잘못되어서 서비스를 정지시켜버린다거나 하는 문제가 일어나는건 아닐까…라고 불안해하네요 ;;; 그래서 정말 그냥 서비스의 상태만 확인하고 싶을 뿐인데 net use로 유저권한 쥐어주지않고도 상태만 확인하는 방법이 있지않을까…하는 의문이 드는데 혹시 가능한지 불가능한지 알고 계시면 알려주실 수 있으실까요
안녕하세요.
우선 [구글에서 검색]해보니 몇 가지 방법이 보이네요.
[첫 번째 링크]
[두 번째 링크]
그리고 옵저버 패턴을 이용하는 방법을 누가 언급을 했는데 그것도 괜찮을 것 같긴 하네요. 확인할 서버에 상주하여 특정 포트로 특정 명령을 받으면 해당 서비스를 확인하는 프로그램 하나(혹은 서버가 여러 개면 각 서버에 모두 상주해야겠죠), 그리고 그 프로그램들에 특정 명령을 보낼 프로그램 하나 이렇게 개발하면 보안 측면으로도 안전할 수 있을 것 같네요.
또 궁금한게 생기시면 언제든 물어보세요. 메일 주소는 sappho192 at gmail.com입니다.
댓글을 달았던 것 같은데 뭘 잘못눌렀었는지 안달렸네요 ㅎㅎ;
답변 감사합니다. 구글링 스킬이 좀 딸렸었네요 둘다 못본 링크인데. 근데 결국 어느방법이든 외부에서 서비스 정보 보려면 접근가능한 유저정보는 필요한가 보네요. 저도 각 서버마다 프로그램 놓는게 가장 안전하다고 생각해서 관리자한테 전해는 뒀는데 일단 테스트서버에서 한동안 시험운영하면서 상태를 지켜보기로 했습니다 ㅎ 감사합니다!