2018년 3월 20일 화요일

초간단 try , catch 사용하기

2008. 9. 30. 12:23

오늘의 주제는 try - catch 로 예외 처리 하기입니다.
추가로 try - catch를 이용해서 예외 처리를 하면 좋은것은 구문 안에서 객체 생성자와 소멸자를 넣을수있다는 것입니다.
__try - __except - __finally 의 경우는 구문안에 new , delete , 객체 생성자 소멸자가 있으면 컴파일 오류를 발생시키지요.

try - catch구문 안에 new 등을 넣을수 있다는 거지요. 

try - catch의 기본적인 사용법을 봅시다.

// 아래 내용은 함수안에 들어갈 내용이겠지요.
    try
    {
        char *p=NULL;
        *p = 10;               // <-- 오류 발생
    }
    catch (...)
    {
        printf( "ERROR Found. \n");
    }
    printf("END! \n");

널 포인터에 값을 넣어서 오류가 발생되겠지요.

하지만 여기서는 다운된 위치가 어디인지 대략적인것은 알수있으나 어느 라인에서 어떤 오류로 인해 다운되었는지 알수있는 방법이 없습니다. 
(EIP값이 구해지면 크래쉬 파인더 등으로 위치를 찾는것은 기본적으로 알고 있겠지요 ??)

아래와 같이 구성을 해보지요.

class SDException 
{
    protected:
        int                 m_nErr;     // 에러 코드
        int                 m_nEIP;     // EIP 주소
        PEXCEPTION_RECORD   m_pER;      // 오류와 관련된 여러가지 레코드셋
    public:
        inline SDException(int nErr,int nEIP,PEXCEPTION_RECORD pER) 
        { 
            m_nErr  = nErr; 
            m_nEIP  = nEIP; 
            m_pER   = pER;
        };
        ~SDException() {};
        int                 getErrCode() { return m_nErr; };
        int                 getErrEIP() { return m_nEIP; };
        PEXCEPTION_RECORD   getErrRecord() { return m_pER; };
};

void ExceptionFilterFunc( unsigned int nErr, _EXCEPTION_POINTERS * pEXP)
{
    throw SDException(nErr,(int)pEXP->ExceptionRecord->ExceptionAddress , pEXP->ExceptionRecord);
};

    // 아래 내용은 함수안에 들어갈 내용이겠지요.
    _set_se_translator( ExceptionFilterFunc );
    try
    {
        char *p=NULL;
        *p = 10;
    }
    //catch (...)
    catch (SDException E)
    {
        //printf( "ERROR Found. \n");
        printf( "ERR_CODE = 0x%x , ERR_EIP = 0x%x \n", E.getErrCode() , E.getErrEIP() );
    }

이렇게하면 원하는 에러코드와 주소를 구할수있습니다.

_set_se_translator() 이 함수가 중요한데요. 이것은 WIN API에 있는 SetUnhandledExceptionFilter()와 유사한 역할을 하는 함수입니다.

이 함수들은 프로그램에 비정상적 오류가 생겼을때 윈도우즈에서 보여주는 오류 대화상자전에 우리가 코딩한 함수쪽으로 먼저 알려주는 역할을 하게 되는것입니다. 

ExceptionFilterFunc()를 보면 SDException개체에 값을 넣어서 throw 시켜주는것을 볼수있습니다. (필요하다면 ExceptionRecord에 있는 다른 값들을 이용해도 좋겠지요).
throw를 한다는것은 오류가 생겼을때 catch 할수있도록 던져준다는 의미로 받아들이면 됩니다. (catch 구문으로 던져 줍니다.)

여하튼 위의 방법을 이용하면 생성자와 소멸자가 있을때 __try - __except - __finally 구문으로 사용못하던 문제를 해결할수있습니다. (__try - __except - __finally을 해도 생성자 소멸자를 넣을수는 있지요. 그것은 다음 강좌에...)

아래는 간략하게 만든 소스 입니다.

  • #include <stdio.h>
    
    #include <time.h>
    #include <windows.h>
    #include <eh.h>             // _set_se_translator 사용
    
    class SDException 
    {
        protected:
            int                 m_nErr;     // 에러 코드
            int                 m_nEIP;     // EIP 주소
            PEXCEPTION_RECORD   m_pER;      // 오류와 관련된 여러가지 레코드셋
    
        public:
            inline SDException(int nErr,int nEIP,PEXCEPTION_RECORD pER) 
            { 
                m_nErr  = nErr; 
                m_nEIP  = nEIP; 
                m_pER   = pER;
            };
    
            ~SDException() {};
    
            int                 getErrCode() { return m_nErr; };
            int                 getErrEIP() { return m_nEIP; };
            PEXCEPTION_RECORD   getErrRecord() { return m_pER; };
    };
    
    class SDExceptionFilter
    {
        protected :
            static void ExceptionFilterFunc( unsigned int nErr, _EXCEPTION_POINTERS * pEXP)
            {
                throw SDException(nErr,(int)pEXP->ExceptionRecord->ExceptionAddress , pEXP->ExceptionRecord);
            };
    
        public :
            inline void Init()
            {
                _set_se_translator( ExceptionFilterFunc );
            };
    };
    
    void ExceptionFilterFunc( unsigned int nErr, _EXCEPTION_POINTERS * pEXP)
    {
        throw SDException(nErr,(int)pEXP->ExceptionRecord->ExceptionAddress , pEXP->ExceptionRecord);
    };
    
    LONG WINAPI SDUnhandledExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo )
    {
        throw SDException(pExceptionInfo->ExceptionRecord->ExceptionCode,(int)pExceptionInfo->ExceptionRecord->ExceptionAddress, pExceptionInfo->ExceptionRecord);
    
        FILE * fp;
    
        fp = fopen("error.log","at");
        fprintf(fp,"FUOUND! \n");
        fclose(fp);
    
        return TRUE;
    }
    
    // 순수한 예외 처리 , 하지만 아무런 정보를 기록하지 않기때문에 시스템이 죽지않게만 한다는것 외에는 의미 없음
    void try_main1()
    {
        printf( "\n Case 1. \n");
    
        try
        {
            char *p=NULL;
            *p = 10;
        }
        catch (...)
        {
            printf( "ERROR Found. \n");
        }
    
    }
    
    // _set_se_translator 을 이용한 SDExceptionFilter 통해 전역 처리자 셋팅
    void try_main2()
    {
        printf( "\n Case 2. \n");
    
        SDExceptionFilter SEF;
    
        SEF.Init();
    
        try
        {
            char *p=NULL;
            *p = 10;
        }
        catch (SDException E)
        {
            printf( "ERR_CODE = 0x%x , ERR_EIP = 0x%x \n", E.getErrCode() , E.getErrEIP() );
        }
    }
    
    // _set_se_translator 대신에 SetUnhandledExceptionFilter으로 전역 예외처리자 설정
    void try_main3()
    {
        printf( "\n Case 3. \n");
    
        LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
    
        m_previousFilter = SetUnhandledExceptionFilter(SDUnhandledExceptionFilter);
    
        try
        {
            char *p=NULL;
            *p = 10;
        }
        catch (SDException E)
        {
            printf( "ERR_CODE = 0x%x , ERR_EIP = 0x%x \n", E.getErrCode() , E.getErrEIP() );
            
        }
    
        printf("END! \n");
    
        SetUnhandledExceptionFilter( m_previousFilter );
    }
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // Func    : main
    // Author  : SEXYMAN ( sexyman@innoco.com )
    // Explain : 메인
    ///////////////////////////////////////////////////////////////////////////////////////
    void main()
    {
        try_main1();
    
        try_main2();
        
        try_main3();
        
        getchar();
    }

댓글 없음:

댓글 쓰기