Develope/MFC2013. 1. 23. 14:18

서버같은 서비스 프로그램을 구현하다 보면,


어쩔 수 없이 로그에 많은 부분을 의지하게 된다.


파일이나 패킷을 통한 디버깅에도 한계가 있고,


그러다 보니 Windows의 시스템 로그를 많이 사용하게 된다.


Windows 시스템 로그란?


<시작-관리도구-이벤트 뷰어>


위와 같은 형태로 윈도우 시스템으로 저장하는 로그를 말한다.


우선, 서비스에 대한 소스를 등록하고,

   HANDLE RegisterEventSource(
  _In_  LPCTSTR lpUNCServerName,
  _In_  LPCTSTR lpSourceName
);

< 출처 : MSDN - RegisterEventSource Function (Windows) >


여기에 로그를 쌓는 API 함수는 다음과 같다.

BOOL ReportEvent(
  _In_  HANDLE hEventLog,
  _In_  WORD wType,
  _In_  WORD wCategory,
  _In_  DWORD dwEventID,
  _In_  PSID lpUserSid,
  _In_  WORD wNumStrings,
  _In_  DWORD dwDataSize,
  _In_  LPCTSTR *lpStrings,
  _In_  LPVOID lpRawData 
);

< 출처: MSDN - Report Event Function (Windows) >


위 함수를 사용하여 무작정 이벤트를 저장 할 경우, 한가지 문제가 생긴다.


Test Program 원본에서 이벤트 ID 0에 대한 설명을 찾을 수 없습니다. 이 이벤트를 발생시킨 구성 요소가 로컬 컴퓨터에 설치되어 있지 않거나 설치가 손상되었습니다. 로컬 컴퓨터에서 구성 요소를 설치 또는 복구할 수 있습니다.


이벤트가 다른 컴퓨터에서 시작된 경우 표시 정보를 이벤트와 함께 저장해야 합니다.


다음 정보가 이벤트와 함께 포함되었습니다.


이벤트 문자열


메시지 리소스가 있지만 문자열/메시지 테이블에서 메시지를 찾을 수 없습니다


바로 위와같이 이상한 오류같은 문자가 포함된다는 것 !!!


이런 현상이 발생하는 이유는 위의 RegisterEventSource 에서 사용된 이벤트 명이 이벤트 소스로 제대로 등록되어 있지 

않기 때문에 발생한다.


그런 고로, 다음과 같은 순서로 이벤트로그 저장 루틴을 구현하면 깔끔하게 해결 할 수 있다.


1. 이벤트 메시지 파일 생성

  - Message File을 다음과 같은 포멧으로 생성한다. (확장자 mc인 파일이며, ANSI로 저장하여도 상관 없다)

    링크 - Message Text File Format


2. 이벤트 메시지 파일을 mc.exe (Message Compiler) 를 이용하여 컴파일

  - 원래 기본적으로는 Windows SDK에 포함되어 있다고 하던데, 내 경우에는, Windows Kits 안에 있었다..

  - 컴파일 방법은 뭐.. 간단하게 mc 메시지파일.mc 로 수행한다. (난 VS2008에서 빌드이벤트로 안되서 커맨드로 수행 하였다)

  - 컴파일이 되면 다음과 같은 파일이 생성된다.

  메시지파일.rc

  메시지파일.h

  메시지파일_KOR.bin, 메시지파일_ENG.bin 등, 메시지 파일에 정의 된 언어의 갯수에 따라 생성 됨


3. 컴파일 된 리소스 파일 등록

  - 일단 VS 2008 기준으로, 기본 리소스 파일 (없을 시 기본으로 하나 생성하면 됨)의 리소스내용 에서 컴파일 타임 지시문

    항목에 추가 해준다.

    #include "메지시파일.rc"


4. 메시지 소스를 레지스트리에 등록

  - 메시지가 이제 실행파일에 포함되었으므로, 이벤트표시기에 정보를 등록하여야 한다.

    이때, 레지스트리를 이용한다.

    ● 레지스트리 경로 : HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\이벤트소스명

HKEY hKey = NULL; CString szKey; szKey.Format(_T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\%s"),m_szServiceName); // 레지스트리 생성 if (::RegCreateKey(HKEY_LOCAL_MACHINE, LPCTSTR(szKey), &hKey) != ERROR_SUCCESS) {     ::CloseServiceHandle(hService); ::CloseServiceHandle(hSCM); return FALSE; } // Add the Event ID message-file name to the 'EventMessageFile' subkey. ::RegSetValueEx(hKey,_T("EventMessageFile"),0,REG_EXPAND_SZ, (CONST BYTE*)szFilePath,_countof(szFilePath)); // Set the supported types flags. DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; ::RegSetValueEx(hKey, _T("TypesSupported"), 0, REG_DWORD, (CONST BYTE*)&dwData, sizeof(DWORD)); ::RegCloseKey(hKey);

     <예시>

5. 로그를 사용할 소스에서는 당연히, 메시지파일.h를 include 하여 사용하여야 한다.


위 단계에 따라 수행하게 되면, 깔끔(?)한 윈도우 이벤트 로그가 쌓이게 될 것이다.


내가 직접 구현하면서 당면했던 몇가지 상황에 대해서 추가로 정리를 해보자면,


case 1. mc.exe 컴파일러 파일 어딨지??

 - 조사에 의하면 windows sdk에 포함되었다고 하지만, 어느샌가 그게 빠졌다고 한다.

   찾아보니 Program Files의 Windows Kit 폴더 안에 있어서 그걸 사용했다.


case 2. 파일을 찾을 수 없다는 컴파일 에러 발생

 - rc 파일응 컴파일 하고 위치에 대한 bin 파일등에 대해서 다시 한번 점검 필요.

Posted by AsCarion