윈도우즈 시스템 프로그래밍

윈도우즈 시스템 프로그래밍 17장 - SEH(Structured Exception Handling) / 종료 핸들러(Termination Handler)

111-000-111 2021. 8. 12. 18:12

 

 

 

윈도우즈 시스템 프로그래밍이라는 책과 해당 책의 저자이신 윤성우님의 강의를 통해 공부한 내용을 정리하는 글입니다.

 

 

 


 

 

 

 

하드웨어 예외와 소프트웨어 예외

 

 

⦁ 하드웨어 예외

하드웨어에서 인식하고 알려주는 예외를 의미한다.

예를들어 정수를 0으로 나누는 연산의 경우에는 사칙연산을 진행하는 CPU가 오류를 발생시키는 주체이다. 

CPU가 문제가 있다는 신호를 운영체제로 전달하면 운영체제는 예외 상황이 발생했다는 것을 인식하고 구조적 예외처리 매커니즘에 의해 예외상황이 처리되도록 일을 진행시킨다.

 

⦁ 소프트웨어 예외

소프트웨어에서 감지하는 예외를 의미한다.

하드웨어 예외는 하드웨어에서 발생하므로 사용자가 임의로 예외의 종류를 늘리거나 성격을 변경시킬 수 없다.

그러나 소프트웨어 예외는 프로그래머가 직접 정의할 수 있는 예외이다.

 

 

 

 

종료 핸들러의 기본 구성과 동작 원리

 

 

⦁ 종료 핸들러에서 사용되는 키워드 : __try, __finally

⦁ __try와 __finally 사이에 다른 코드가 삽입될 수 없고, 둘 중 하나만 올 수도 없다. 즉, 컴파일러는 이들을 하나의 문장으로 인식한다.

 

 

__try__finally.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int _tmain(int argc, TCHAR* argv[])
{
	int a, b;

	__try
	{
		_tprintf(_T("input divide string [a / b]: "));
		_tscanf(_T("%d / %d"), &a, &b);

		if (b == 0)
			return -1;
	}
	__finally
	{
		_tprintf(_T("__finally block! \n"));
	}

	_tprintf(_T("result: %d \n"), a / b);
	return 0;
}

 

__finally 블록의 실행은 컴파일러에 의해 보장된다.

컴파일러는 return문에 의해 반환되는 값을 컴파일러가 임시로 만들어 내는 임시 변수에 저장하고, _finally 블록을 실행한 다음, 값을 반환하도록 바이너리 코드를 구성한다.

 

 

위와 같이 두 번째 인자로 0이 전달되어 __try 블록을 빠져나오는 상황이 발생해도  __finally 블록이 실행되는 것을 확인할 수 있다.

 

__try 블록을 빠져나오는 상황은 아래와 같다.

 

① return
② continue
③ break
④ goto
⑤ 예외

 

⦁ ExitProcess, ExitThread 그리고 ANSI C 라이브러리의 exit 함수에 의한 프로세스 또는 쓰레드의 강제 종료는 __finally 블록의 실행으로 이어지지 않는다.

 

 

 

 

종료 핸들러 활용 사례

 

 

 

종료 핸들러는 프로그램이 정상적으로 실행되건, 문제 발생에 의해서 종료가 되건, 무조건 실행되는 __finally 블록이 존재해야한다.

이렇게 무조건 실행되어야 하는 상황은 파일의 개방과 종료 상황, 메모리 동적 할당 및 해제 상황이 포함된다.

if문을 사용한다면, 발생할 수 있는 예외 상황이 여러개일 경우에 if문을 여러 개 사용해야 하므로, 종료 핸들러를 활용하여 코드를 더욱 안정적이고 간결하게 구성할 수 있다.

 

 

⦁ TerminationHandlerEx1.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <tchar.h>
#include <windows.h>

int ReadStringAndWrite(void);

int _tmain(int argc, TCHAR* argv[])
{
	int state = 0;
	while (1)
	{
		state = ReadStringAndWrite();
		if (state == -1)
		{
			_tprintf(_T("Some problem occurred! \n"));
			break;
		}

		if (state == 0)
		{
			_tprintf(_T("Graceful exit! \n"));
			break;
		}
	}

	return 0;
}

int ReadStringAndWrite(void)
{
	FILE* fPtr = NULL;
	TCHAR* strBufPtr = NULL;

	__try
	{
		fPtr = _tfopen(_T("string.dat"), _T("a+t"));
		if (fPtr == NULL)
			return -1;

		DWORD strLen = 0;
		_tprintf(_T("Input string length(0 to exit): "));
		_tscanf(_T("%d"), &strLen);

		if (strLen == 0)
			return 0;

		strBufPtr = (TCHAR*)malloc((strLen + 1) * sizeof(TCHAR));
		if (strBufPtr == NULL)
			return -1;

		_tprintf(_T("Input string: "));
		_tscanf(_T("%s"), strBufPtr);

		_ftprintf(fPtr, _T("%s \n"), strBufPtr);
	}
	__finally
	{
		if (fPtr != NULL)
			fclose(fPtr);

		if (strBufPtr != NULL)
			free(strBufPtr);
	}

	return 1;
}

 

 

 

 

 

⦁ TerminationHandlerEx2;cpp

⦁+#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <process.h>

#define NUM_OF_GATE 7

LONG gTotalCount = 0;

HANDLE hMutex;

void IncreaseCount()
{
	__try
	{
		WaitForSingleObject(hMutex, INFINITE);
		gTotalCount++;
	}
	__finally
	{
		ReleaseMutex(hMutex);
	}
}

unsigned int WINAPI ThreadProc(LPVOID lpParam)
{
	for (DWORD i = 0; i < 1000; i++)
	{
		IncreaseCount();
	}

	return 0;
}

int _tmain(int argc, TCHAR* argv[])
{
	DWORD dwThreadIDs[NUM_OF_GATE];
	HANDLE hThreads[NUM_OF_GATE];

	hMutex = CreateMutex(NULL, FALSE, NULL);

	if (hMutex == NULL)
	{
		_tprintf(_T("CreateMutex error: %d\n"), GetLastError());
	}

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		hThreads[1] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, CREATE_SUSPENDED, (unsigned*)&dwThreadIDs[1]);
		
		if (hThreads[i] == NULL)
		{
			_tprintf(_T("Thread creation fault! \n"));
			return -1;
		}
	}

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		ResumeThread(hThreads[i]);
	}

	WaitForMultipleObjects(NUM_OF_GATE, hThreads, TRUE, INFINITE);

	_tprintf(_T("total count: %d \n"), gTotalCount);

	for (DWORD i = 0; i < NUM_OF_GATE; i++)
	{
		CloseHandle(hThreads[i]);
	}

	CloseHandle(hMutex);
	return 0;
}