Win32 API/기초

Win32 API 기초 : 핸들, DC, 윈도우 이벤트 (1)

시플마 2024. 6. 16. 11:37

윈도우에 어떤 메시지가 들어오면

해당 윈도우가 갖고 있는 함수, WndProc 함수를 통해

동작을 처리한다고 했습니다.

 

WndProc 함수에서 case WM_PAINT는

어떤 경우에 실행되는 걸까요? 

 

무효화 영역(Invaildate)이 발생했을 때입니다.

 

무효화 영역은 해당 윈도우가 사라졌다가 다시 등장하는 

영역을 말합니다. 이 영역을 발생시켜 WM_PAINT가

실행되도록 해 보죠. 일단 프로그램을 실행합니다.

 

이후 145번째 줄에 중단점을 찍은 후 윈도우를 

내렸다가 다시 윈도우를 띄워 봅니다.

 

그러자 중단점에 걸리는 모습을 볼 수 있습니다.

 

WM_PAINT 코드가 실행된 것이죠.

 

윈도우가 내려가면서 사라졌고 

이를 다시 클릭하여 열었을 때 

윈도우를 다시 그려야 하기 때문에 

WM_PAINT 코드가 실행되는 겁니다.

 

 

예전에는 아래 그림과 같이

 

윈도우(Client) 위에 또 다른 윈도우(계산기)를 띄우면

가려진 부분이 무효화 영역이 되어 

다시 윈도우(Client)를 클릭하는 순간 

WM_PAINT 코드가 실행되었지만,

이제는 윈도우(Client)가 화면에 그려졌던 그림 데이터를

비트맵 형태로 저장하고 있기 때문에 다른 윈도우에 의해 

가려졌다가 다시 앞으로 나오게 하여도

WM_PAINT 코드가 실행되지는 않습니다.

 

 

case 문 안의 코드를 보면 중괄호가 있습니다.

 

case 문 안에 지역 변수를 선언하려면 중괄호 안에

선언해야 오류가 발생하지 않기 때문입니다.

 

위와 같이 중괄호가 없으면 case 문 안에

지역 변수를 선언하였을 때 오류가 발생하죠.

 

 


 

 

WndProc 함수에서 148번째 줄을 보시면

 

BeginPaint 함수가 있습니다. 

 

해당 함수의 인자로 hWnd을 넘겨주네요.

 

hWnd은 InitInstance 함수에서

 

윈도우를 만들고

그 윈도우를 관리하는 핸들입니다. 

 

윈도우는 OS에서 제공되는 개념이기 때문에 

사용자가 직접 멤버에 접근할 수 없습니다. 

 

이렇게 메모리 공간에 할당되긴 하였으나

OS에서 관리하기 때문에 데이터에 함부로 접근하여

조작할 수 없는 객체를 Kernel Objects라고 합니다. 

 

 

윈도우의 크기를 바꾸고 싶다고 합시다.

 

만약 윈도우 객체가 사용자가 만든 클래스로 인해 

만들어졌다면 아마 윈도우 사이즈를 저장하는

멤버 Size가 있을 것이고, SetSize와 같은 함수를 만들어

Size를 조정할 수 있도록 했을 겁니다.

 

그러나 이런 상황이 아니기 때문에 

실제로 윈도우를 조정하기 위해서는

윈도우의 ID를 핸들에 저장하고

이 핸들과 함수를 이용하여 

윈도우를 제어하는 것이죠.

 

108 ~ 109번째 줄에 있는 코드가 바로 

윈도우의 ID를 통해 만든 핸들과 기본적으로

제공되는 함수를 통해서 윈도우를 제어하는 동작입니다.

 

 

108번째 줄에 있는 ShowWindow 함수는 

윈도우를 보여주는 함수인데 두 번째 인자를 false로 하고

프로그램을 실행하면 실행은 되지만 윈도우가 보이지 않습니다.

 

 

다시 WndProc 함수에서 WM_PAINT 코드로 돌아와서 

 

148번째 줄에 HDC형 객체 hdc가 보입니다.

 

여기서 DC는 Device Context로

마찬가지로 Kernel Object라고 생각하면 됩니다.

 

OS에서 관리하는 객체(윈도우)를 얻고

그 객체를 제어할 수 있는 함수에 

객체의 ID를 갖고 있는 핸들을 넣어 주는 것이죠.

 

 


 

 

HDC라는 자료형도 그렇고

HWND라는 자료형도 그렇고

모두 핸들입니다.

 

두 자료형이 무엇인지 확인해 보았더니

DECLARE_HANDLE이라는 전처리기로 처리가 되어 있습니다.

 

이것도 확인해 보니 

 

DECLARE_HANDLE이라는 전처리기에 이름을 인자로 주면

해당 이름을 통해 int형 정수 하나를 갖고 있는 구조체를 만듭니다.

 

즉 HDC나 HWND나 모두 똑같이

int형 정수 하나를 갖고 있는 구조체인 것이죠.

 

이것 말고도 아래 그림처럼

 

브러쉬 핸들과 펜 핸들이 존재하며

더 많은 핸들이 존재하지만,

 

마찬가지로 모두 int형 정수 하나를 갖고 있는 구조체겠죠.

 

 

그렇다면 왜 굳이 이렇게 똑같은 형태를 하고 있는

자료형을 여러 개 만들어 놓은 것일까요?

 

핸들은 객체의 ID를 받아 해당 객체를 제어합니다.

즉 핸들은 객체에 대한 정보가 오직 번호뿐입니다.

 

그래서 자료형에서 구분해 주지 않으면

핸들이 갖고 있는 Kernel Object가 DC인지,

PEN인지, BRUSH인지 알 수가 없는 것이죠.

 

어차피 같은 구조를 가진 구조체이긴 하지만

이름을 다르게 하여 어떤 오브젝트의

핸들인지 파악할 수 있는 것이죠.

 

또한 HPEN에 HBRUSH를 넣으려고 하면

자료형이 맞지 않아 오류가 발생하겠죠?

문제가 발생할 수 있는 코드를 컴파일 과정에서 파악하기 용이해지죠.

덕분에 코드를 작성하는 과정에서 실수할 여지를 줄일 수 있습니다.

 

 

다시 WndProc 함수에서 WM_PAINT 코드로 돌아와서 

 

BeginPaint 함수는 Device Context를 만들고 

그 ID를 반환하는 함수임을 알 수 있습니다.

 

반환된 ID를 통해 hdc를 초기화하는 것이죠.

 

BeginPaint 함수는 hWnd, 즉 윈도우 핸들을 인자로 받아 

Device Context를 만듭니다.

 

이후 그리기 동작이 실행되면

인자로 받은 윈도우에 그림이 그려집니다.

 

이를 통해 Device Context라는 오브젝트를 만들 때에는

목적지인 윈도우도 알아야 한다는 것을 알 수 있습니다.

 

 

 


 

 

 

Device Context에 대해 더 알아봅시다.

 

Device Context를 제어하는 hdc는 

그리기가 수행될 목적지 윈도우와 펜, 브러쉬를 갖고 있습니다.

 

그래서 WndProc 함수의 WM_PAINT 코드를 보시면

151번째 줄에 있는 Rectangle 함수가 실행될 때 

hdc를 인자로 받죠? 그리고 인자로 넘겨준 좌표에 

사각형이 그려졌습니다. 이것이 가능한 이유가

hdc에는 그리기가 수행될 목적지 윈도우와 펜, 브러쉬를

갖고 있기 때문인 겁니다.

 

펜은 기본적으로 Black이고 

브러쉬는 기본적으로 White입니다.

 

그래서 따로 설정해 주지 않으면 

 

위와 같이 검은 펜으로 그림을 그리고 

그 안을 하얀색으로 채우죠.

(검은 펜으로 그림만 그린 것이 아니라

브러쉬를 통해 내부를 색칠했다는 점 주의해 주세요.)

 

 

우리가 색을 볼 수 있는 이유는 모니터라는 출력 장치가 있기 때문이죠.

메모리는 픽셀 한 칸을 단순히 0과 1로 표현하고 있습니다.

정확히는 R, G, B에 해당하는 총 3Byte를 한 픽셀로 보고 있죠.

 

모니터 해상도가 1920 x 1080이라면 

 

2,073,600개의 픽셀이 존재하는 것이고

 

1920 x 1080 x 3(Byte) = 6,220,800(Byte)가

메모리상에 할당되어 화면을 출력하고 있는 것입니다.

 

KByte로 표현하면 6,075KByte, 

MByte로 표현하면 5.93MByte로 대략 6MByte 정도네요.

 

 

한 픽셀을 검은색으로 표현하려면 RGB의 값을 모두 0으로, 

흰색을 표현하려면 모두 255로 해 주면 되고

RGB의 값을 조율하면 다양한 색을 낼 수가 있습니다.

 

RGB가 각각 0 ~ 255, 즉 256가지의 경우를 나타낼 수 있으므로

256 x 256 x 256 = 16,777,216의 색상 조합이 나옵니다.

 

 

 


 

 

 

정리해 보면 

윈도우를 띄운 후 윈도우를 내렸다가 다시 올리면

무효화 영역이 발생했다고 판단하여 메시지 큐에 

WM_PAINT가 들어오고 WM_PAINT 동작을 처리하기 위해서

WndProc 함수의 WM_PAINT case가 실행됩니다.

 

그러면서 Device Context가 만들어져 

ID가 hdc에 저장되고 hdc가 갖고 있는

목적지인 윈도우에 기본 펜과 기본 브러쉬로 

Rectangle 함수를 통해 사각형이 그려지는 것이죠.

 

 

이때 Rectangle 함수가 호출되기 전에

hdc에 펜을 직접 만들어서 넘기면

다른 색의 라인을 가진 사각형을 만들어 볼 수도 있겠죠.

 

 

 


강의 출처 : https://www.youtube.com/watch?v=xamOlK2poOo&list=PL4SIC1d_ab-ZLg4TvAO5R4nqlJTyJXsPK&index=3