LISTORY

[윈도우즈 시스템 프로그래밍] 예외 핸들러 본문

IT/윈도우 프로그래밍

[윈도우즈 시스템 프로그래밍] 예외 핸들러

LiStoryTeller 2018. 10. 20. 23:25

뇌를 자극하는 윈도우즈 시스템 프로그래밍 책 관련 유투브 강의 정리이다.


이번 시간에 정리할 내용은 예외 핸들러이다.


YouTube 주소 : 예외 핸들러(Exception Handler)



예외 핸들러(Exception Handler)



⊙ 예외 핸들러와 필터



__try, __except 블럭이 있다.


이 두개는 이어서 등장해야 한다. 있는 위치는 다르지만 하나의 구문으로 이해해야한다.


__try 블럭에서 발생한 예외는 그 뒤에 나오는 __except에서 처리하겠다고 선언한 것이다.


그런데 그 예외를 처리하는 방식은 크게 세가지로 나뉘어진다. 이걸 필터라고 부른다.


예외 처리 필터

- EXCEPTION_EXECUTE_HANDLER

- EXCEPTION_CONTINUE_EXECUTION

- EXCEPTION_CONTINUE_SEARCH


이 필터가 위치하는 위치가 __except 괄호 부분이다.


언뜻 보면 __except에 인자로 전달하는 것처럼 보이지만, 인자로 전달하는 것은 아니고 __except의 인자전달의 위치에 예외처리 방식을 열거하는 것이다.


여기엔 예외 처리 필터 중 하나만 올 수 있다.


예를 들어 보자. 예외가 하나 발생했다.


그럼 실행이 윈도우즈에 의해 __except 블록으로 자동으로 옮겨진다(SEH 매커니즘)


그럼 가장 먼저 하는 것이 예외처리 방식이 무엇인지 확인하게 된다(이 또한 SEH 매커니즘)


그 방식, 즉 필터에 맞게 예외 처리가 진행된다.


예외가 처리되었다? __except 블럭에서 말하는 방식되로 처리되었다.



1. EXCEPTION_EXECUTE_HANDLER



p라는 포인터에 NULL을 대입시켰다. 오타가 발생했는데, 여기는 int *p이다. 즉, 이는 예외를 발생시킨다.


그럼 실행은 자동으로 __except 블럭으로 옮겨지고, 어떻게 처리할지 쳐다본다.


필터가 EXCEPTION_EXECUTE_HANDLER 이므로, 이에 언급하는 방식대로 처리되어야 한다.


EXCEPTION_EXECUTE_HANDLER 방식은 일단, 예외를 except 블럭 안에서 처리한다.


실행이 된 뒤, 예외가 발생된 지점으로 돌아가지 않고 예외가 발생된 시점부터 시작해서 그 나머지 try 블럭의 코드를 건너뛴다.


즉, 예외가 발생한 블럭의 코드는 모두 건너뛰고 그 다음 코드가 실행된다.


▶ 사례



case DIV: 코드 부분에서 예외가 생겼다고 가정해보자.


그럼 _tprintf 문을 실행하는 것이 의미가 없어진다.


그래서 만일 이 부분에 문제가 생긴다면 __except 부분에서 필터를 인지하고 블럭안에 있는 코드를 실행한다.


여기선 출력문을 썼지만 가급적으로 예외를 처리하는 코드를 넣는 것이 좋다.


그 후, 다시 __try 블럭으로 돌아가지 않고 다음 코드들을 실행한다.



⊙ 처리되지 않은 예외의 이동



그림에 써 있는 대로 int a에는 10, int b에 0이 들어갔다면, 예외가 발생한다. 


근데 Divide() 함수 부분은 __try 영역으로 감싸지지 않았다. 


그럼 이 코드를 감싸고 있는 함수가 전부 반환이 된다.


다시 말해보겠다.


_tmain 함수가 호출되고 _tmain 이 Calculator 함수를 호출한다.


또 Calculator 함수가 divide 함수를 호출했다. 즉, 그 순서대로 Stack에 쌓였다.


이 Divide 함수에 예외가 발생했고, 예외처리를 못했을 경우, 이 Stack이 반환되어 버린다.


그럼 다시 호출된 곳으로 돌아온다. 예외가 처리되지 않은 것이다.


그래서 이 돌아온 부분에서 예외를 처리할 방법을 찾는데, 이 부분은 __try로 묶여 있다.


그래서 여기서 예외를 처리할 수 있다.


하지만 가급적으로 예외는 발생한 위치해서 처리해주는 것이 좋다.


만일 여기에도 __except블럭이 없었다면 main으로 넘어갔을 것이다. main도 처리할 방법이 없다면 이는 운영체제로 넘어간다.


그럼 운영체제는 프로그램을 종료시켜 버린다.



예외를 구분하는 방법


GetExceptionCode


어떤 예외가 발생했는지 구분하고 싶을 수도 있다.



예외 A, B, C가 있다. 예외는 각각의 상황으로 인해 발생할 수 있다.


그런데 각기 예외를 처리하는 방식은 다르다. 따라서 __except블럭은 예외가 왜 발생했는지 알 수 있어야 한다.


그를 위해 제공되는 함수가 GetExceptionCode()이다. 이 함수에서 리턴되는 값은 왜 오류가 발생되었는지 알게 해준다.


이는 예외가 발생이 되야 의미가 있는 함수이므로 __except 블럭 내부나 예외필터 표현식 지정 위치 안에서만 호출할 수 있도록 제약되고 있다.


반환값은 다양하게 반환되므로 MSDN을 참조하는 것이 좋다.




2. EXCEPTION_CONTINUE_EXECUTION



위 코드로 설명해보겠다.


앞서 상황과 마찬가지로 num2에 0이 들어오면 예외가 발생이 된다.


그래서 이 부분을 try와 except로 묶고 있다.


근데 여기서 num2만 다시 값을 받고 싶을 경우가 있다. 그럴 경우에 EXCEPTION_CONTINUE_EXECUTION 를 사용한다.


EXCEPTION_CONTINUE_EXECUTION 는 예외를 처리한 뒤, 예외가 발생된 지점으로 다시 돌아간다.



근데 필터가 와야하는데 필터가 오지 않고 있다.


FilterFunction은 우리가 정의한 함수이고, 그 인자를 GetExceptionCode()로 얻게되는 값으로 하겠다고 명시해두었다.



우리가 정의한 FilterFunction 함수를 살펴보겠다.


여기서 0으로 인해 예외가 발생했으므로, EXCEPTION_INT_DIVIDE_BY_ZERO case가 실행된다.


코드를 보면 num 값을 다시 전달받고, 문제가 생긴 지점으로 다시 돌아갈 수 있도록 EXCEPTION_CONTINUE_EXECUTION 를 반환한다.




3. EXCEPTION_CONTINUE_SEARCH


이 필터는 잘 사용되지도 않고, 사용하기 권하지도 않는 필터이다.



이는 예외가 발생한 지점과, 예외를 처리하는 지점을 다르게 하고 싶을 때 사용한다.







Comments