Home | Info | Research | Blog | Repos | Messages | Contact Me

 


'2007/08'에 해당되는 글 3건

  1. 2007/08/27 PYRASIS.COM 4주년 (10)
  2. 2007/08/25 SEH(Structured Exception Handler) 활용하기
  3. 2007/08/03 Subversion svnserve Manager 1.1.1 릴리즈 (1)


오늘은 PYRASIS.COM(피라시스닷컴)이 문을 연지 4년째 되는 날입니다.

2, 3주년은 제가 계급을 달고 있던 시절이라 따로 챙기지 못하였습니다.

예나 지금이나 사이트의 겉모습은 거의 바뀐 것이 없지만 테터툴즈의 설치, WebSVN에서 ViewVC로 전환 등 기능적으로는 어느정도 발전이 있었습니다.

자주 글을 쓰지는 못하지만, 양질의 정보를 제공하기 위해 노력하겠습니다.

앞으로도 많은 방문 바랍니다. 감사합니다.



C언어를 사용하다 보면 예외처리 부분이 상당히 미흡하다는 것을 알 수 있습니다. C++은 언어 차원에서 try, catch라는 예외처리 문법을 제공해 주고 있습니다.

C언어는 C 표준 라이브러리에 있는 setjmp(), longjmp() 함수를 사용하여 예외처리를 할 수 있지만 상당히 구식이라 직관적이지도 못하고 사용하기도 불편합니다.

그래서 C언어에서는 Structured Exception Handler(SEH)라는 예외처리 방식을 사용할 수 있습니다. SEH는 Microsoft Visual C++ 컴파일러 차원에서 제공하는 예외처리 문법입니다. 물론 윈도우상에서만 사용할 수 있습니다.

void tryexcept()
{
    __try
    {
        int *world = NULL;

        *world = 8// 널 포인터에 값 대입

        printf("hello try %d\n", *world);
    } 
    __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
    {
        printf("EXCEPTION_ACCESS_VIOLATION\n");
    }
}


__try 부분에 예외를 검사할 코드를 넣습니다. 예외가 발생하면 __except 부분에서 지정한 코드가 실행되게 됩니다. 예외도 종류가 엄청 많은데, __except의 괄호 안에 예외 검사식을 넣어줘야 합니다.

위 예제의 경우 널 포인터에 값을 대입하고 있습니다. 이 코드를 실행하면 메모리 접근 위반 예외가 발생하고 __except 부분의 코드가 실행됩니다. 괄호안의 부분이 메모리 접근 위반 예외인지 판단하는 부분인데, GetExceptionCode() 함수로 현재 발생한 예외 코드를 구해옵니다. 그리고 그 예외가 EXCEPTION_ACCESS_VIOLATION (메모리 접근 위반 예외)인지 검사하고 맞으면 __except 블록 안의 코드를 실행하고 아니면, 상위 예외 처리 핸들러에게 넘깁니다.

  • EXCEPTION_EXECUTE_HANDLER (1)는 __except 블록 안의 코드를 실행합니다.
  • EXCEPTION_CONTINUE_SEARCH (0)는 __except 블록 안의 코드를 실행하지 않고 상위 예외 처리 핸들러에게 넘깁니다.
  • EXCEPTION_CONTINUE_EXECUTION (-1)은 예외를 무시하고 예외가 발생한 부분 부터 코드를 다시 실행합니다. 하지만 예외가 발생한 원인을 해결해 주지 못하면 무한루프에 빠집니다.

이 매크로들은 excpt.h에 정의되어 있습니다.

__except의 괄호 안에 모든 예외의 종류를 다 적어줄 순 없는 노릇입니다. 그래서 예외 코드를 판별하여 각각 처리해주는 함수를 만들어 사용할 수 있습니다. 여기서 예외의 원인을 수정하여 EXCEPTION_CONTINUE_EXECUTION를 리턴하면 코드를 계속 실행 할 수 있습니다.

int Value = 0;

DWORD ExceptionFilter(DWORD ExceptionCode)
{
    switch (ExceptionCode)
    {
    case EXCEPTION_INT_DIVIDE_BY_ZERO:
        {
            // 예외가 발생한 원인을 수정
            Value = 1;

            return EXCEPTION_CONTINUE_EXECUTION;
        }
    case EXCEPTION_ACCESS_VIOLATION:
        {
            // 예외가 발생한 원인을 수정

            return EXCEPTION_CONTINUE_EXECUTION;
        }
    // 다른 여러가지 예외 코드를 처리
    //case EXCEPTION_XXX
    default:
        return EXCEPTION_EXECUTE_HANDLER;
    }
}

void tryexceptfilter()
{
    __try
    {
        int world = 10;

        world = world / Value;
    } 
    __except (ExceptionFilter(GetExceptionCode()))
    {
        printf("EXCEPTION !\n");
    }
}


위 예제는 GetExceptionCode() 함수를 이용하여 예제코드를 ExceptionFilter() 함수에 넘기고 각 코드에 맞는 예외의 원인을 수정하도록 되어 있습니다.

world를 Value로 나누었는데 Value는 0입니다. 그래서 EXCEPTION_INT_DIVIDE_BY_ZERO예외가 발생하였고, ExceptionFilter() 함수에서는 이 예외 코드에 맞게 Value에 1을 대입하여 주었습니다. 그리고 EXCEPTION_CONTINUE_EXECUTION를 리턴하여 예외가 발생한 부분에서 코드를 계속 실행하게 됩니다.

이 ExceptionFilter()에 여러가지 다른 예외 코드를 추가해서 처리할 수 있습니다. 물론 여기에 추가되지 않은 예외가 발생하면 EXCEPTION_EXECUTE_HANDLER가 리턴되고 __except의 부분이 실행됩니다.

이번에는 __finally의 사용입니다. __finally 부분은 __try 부분이 실행에 성공하던 예외가 발생하던 무조건 실행됩니다.

void tryfinally()
{
    __try
    {
        printf("hello try\n");
    } 
    __finally
    {
        printf("hello finally\n");
    }
}


주로 사용되는 패턴은 __try 부분에서 동적 메모리 등을 할당했을때 __finally 부분에서 메모리가 할당 되었으면 해제하는 식입니다. __finally는 무조건 실행되기 때문에 메모리를 할당 했더라도 어떤 예외가 발생하여 프로그램이 중단되면 __finally 부분에서 할당한 메모리를 해제 할 수 있습니다.

비슷한 패턴으로는 크리티컬 섹션이나, 뮤텍스등 동기화 객체를 사용할 때, 객체를 획득한 상태에서 예외가 발생했더라도 __finally 부분에서 획득한 동기화 객체를 해제 할 수도 있습니다.

__try 안에서 return을 하더라도 __finally 부분이 실행되고 값을 리턴 합니다.

이 __finally는 __except와는 동시에 사용할 수 없습니다. 무조건 __try, __finally 혹은 __try, __except로만 사용할 수 있습니다.





SVNSERVE Manager 1.1.1이 릴리즈 되었습니다.

윈도우 비스타에서 트레이 아이콘이 16색으로 표시되는 것을 트루컬러로 표시되도록 수정하였습니다.

다운로드 및 버그 보고나 기능 추가 요청등은 SVNSERVEManager 페이지에서 할 수 있습니다.