아래의 내용은 soen.kr 사이트에 오픈되어 있는 강의 내용입니다.
Win32 API
#include <windows.h>
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
LPSTR lpszClass="First";
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
,LPSTR lpszCmdParam,int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst=hInstance;
WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=(WNDPROC)WndProc;
WndClass.lpszClassName=lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass);
hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL,(HMENU)NULL,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
while(GetMessage(&Message,0,0,0)) {
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
위의 코드는 Win32 API를 이용한 기본 틀이다.
(참고 > http://soen.kr/lecture/win32api/lec2/lec2-1-1.htm )
위의 코드를 실행하게 된다면, 아래와 같이 결과물이 나오게 된다.
WinMain
HINSTANCE g_hInst;
LPSTR lpszClass="First";
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
위의 코드는 Win32 API에서의 main() 함수이다.
C 언어로 Dos 환경에서 결과물을 출력할 때 main() 함수가 필수적인데, Win32 API 에서는 위의 WinMain() 함수가 필수이다.
즉, Win32 API으로 만들어진 프로그램은 프로그램의 시작점인 Entry Point가 WinMain() 인 것이다.
Dos 에서의 main 함수는 인수 사용 여부에 따라 여러 가지 원형이 있을 수 있지만, WinMain의 원형은 위와 같이 고정이다.
APIENTRY 지정자는 __stdcall형 호출 규약을 사용한다는 뜻인데 일단은 딱히 중요하지 않다.
위의 4개의 인수의 의미는 아래와 같다.
인수 | 의미 |
hInstance | 프로그램의 인스턴스 핸들 |
hPrevInstance | 바로 앞에 실행된 현재 프로그램의 인스턴스 핸들. 없을 경우는 NULL이 되며 Win32에서는 항상 NULL이다. 호환성을 위해서만 존재하는 인수이므로 신경쓰지 않아도 된다. |
lpCmdLine | 명령행으로 입력된 프로그램 인수이다. 도스의 argv 인수에 해당한다. |
nCmdShow | 프로그램이 실행될 형태이며 최소화, 보통모양 등이 전달된다. |
위의 4개의 인수 중 hInstance 이외에는 잘 사용되지 않는다.
인스턴스(Instance)라는 말은 클래스가 메모리에 실제로 구현된 실체를 의미한다.
Windows 용 프로그램은 여러 개의 프로그램이 동시에 실행되는 멀티태스킹 시스템일 뿐만 아니라 하나의 프로그램이 여러 번 실행될 수도 있고, 이때 실행되고 있는 각각의 프로그램을 프로그램 인스턴스라고 하는데, 이를 줄여서 인스턴스라고 한다.
예시로 notepad.exe를 두 번 실행하면 두 개의 창이 뜨는데, 운영체제는 각각 다른 메모리를 사용하는 다른 프로그램으로 인식하는데, 이 각각의 메모장은 서로 다른 인스턴스 핸들을 가지고 있고, 운영체제는 이 인스턴스 핸들값으로 두 개의 메모장을 구별한다.
hInstance는 프로그램 자체를 일컫는 정수값이다.
그리고 위의 전역 변수들 중 lpszClass는 문자열이 정의되어 있는데 이 문자열은 윈도우 클래스를 정의하는 데 사용된다.
메시지 처리 함수 WndProc 원형
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
WinMain과 더불어 중요한 함수가 하나 더 있는데 바로 WndProc() 이다.
Dos에서는 main 함수만으로도 프로그램을 작성할 수 있었지만, Windows에서는 특별한 경우를 제외하고는 대부분 WinMain과 WndProc() 함수가 모두 있어야 한다.
WinMain에서는 윈도우 창을 만들고 화면에 출력하기만 할 뿐 대부분의 일처리들은 WndProc에서 이루어진다.
WinMain의 모양은 대체로 일정하고 특별한 일을 하지 않지만, WndProc는 프로그램에 따라 천차만별로 달라지므로 WinMain보다는 WndProc 부분을 주의깊게 봐야 한다.
위의 코드는 WndProc의 원형이다.
윈도우 클래스
// windows.h
typedef struct tagWNDCLASS
{
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASS;
WinMain 함수가 하는 것 중 가장 중요한 건 윈도우 창을 만드는 것이다.
윈도우가 있어야 입/출력 작업을 할 수 있기 때문인데, 이 윈도우를 만들려면 윈도우 클래스를 먼저 등록한 후에 CreateWindow 함수를 호출해야 한다.
모든 윈도우는 윈도우 클래스를 기반으로 하여 만들어지고, 윈도우 클래스는 만들어질 윈도우의 여러가지 특성을 정의한다.
위의 구조체는 윈도우 클래스인데, 10개나 되는 멤버를 가지고 있고, WndClass의 각 멤버들의 의미는 아래와 같다.
style | 윈도우의 스타일 즉, 어떤 형태를 가질 것인지를 지정한다. 지정할 수 있는 값은 엄청 많지만 CS_HREDRAW와 CS_VREDRAW가 많이 쓰이는 값이고, 윈도우의 수직 또는 수평 크기가 변하면 윈도우를 다시 그린다는 의미이다. 값들은 OR 연산자(|)로 연결하여 사용한다. |
lpfnWndProc | 윈도우의 메시지 처리 함수를 지정한다. 메시지가 발생할 때마다 여기서 지정한 함수가 호출되며 이 함수가 모든 메시지를 처리하는 것이다. 메시지 처리 함수의 이름은 WndProc으로 되어 있지만, 마음대로 정할 수 있다. (WinMain 함수는 이름이 고정적이지만, WndProc는 사용자가 임의로 이름을 정할 수 있는 함수이고, 관습적으로 WndProc이라고 사용할 뿐이다.) |
cbClsExtra | 예약 영역으로, Windows가 내부족으로 사용하며 아주 특수한 목적에 사용되는 여분의 공간이기 때문에 사용하지 않을 경우는 0으로 지정한다. |
cbWndExtra | |
hInstance | 이 윈도우 클래스를 사용하는 프로그램의 번호이며, 이 값은 WinMain의 인수로 전달된 hInstance 값을 그대로 대입해주면 된다. |
hIcon | 이 윈도우가 사용할 마우스 커서와 최소화 되었을 경우 출력될 아이콘을 지정하는데, LoadCursor()와 LoadIcon()를 사용하여 지정한다. 사용자가 직접 아이콘과 커서를 만들어 사용할 수도 있지만, Windows가 기본적으로 제공하는 아이콘과 커서를 사용한다. |
hCursor | |
hbrBackground | 윈도우의 배경 색상을 지정한다. 더 정확한 표현은 윈도우의 배경 색상을 채색할 브러시를 지정하는 멤버로, GetStockObject() 함수를 사용해 윈도우에서 기본적으로 제공하는 브러시를 지정한다. 여러가지 브러시들이 있지만, 가장 일반적인 흰색 배경(WHITE_BRUSH)이 많이 사용된다. |
lpszMenuName | 이 프로그램이 사용할 메뉴를 지정하는데, 메뉴는 코드가 아니라 리소스 에디터에 의해 별도로 만들어진 후 링크 할 때 합쳐지므로 메뉴를 사용하지 않을 경우 이 멤버에 NULL을 대입해 주면 된다. |
lpszClassName | 윈도우 클래스의 이름을 정의한다. 여기서 지정한 이름은 CreateWindow() 함수에 전달되며, CreateWindow() 함수는 윈도우 클래스에서 정의한 특성값을 참조하여 윈도우를 만든다. 윈도우 클래스의 이름은 보통 실행 파일의 이름과 일치시켜 작성한다. |
ATOM RegisterClass( CONST WNDCLASS *lpWndClass);
RegisterClass 함수의 인수로 WndClass 구조체의 주소를 넘겨주면, 이러한 특성을 가진 윈도우 창을 앞으로 사용하겠다는 의미로 등록이 된다.
WNDCLASS WndClass;
WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=(WNDPROC)WndProc;
WndClass.lpszClassName=lpszClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass);
위의 코드를 보면 WNDCLASS 구조체 변수 WndClass를 선언하고, 각 멤버에 값을 대입한 후 RegisterClass() 함수로 윈도우 클래스를 등록한다.
HWND CreateWindow(lpszClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, hwndParent, hmenu, hinst, lpvParam)
이제 윈도우 클래스를 등록했다면 등록한 윈도우 클래스를 바탕으로 실제 윈도우 창을 생성해야 하는데, CreateWindow() 함수를 사용하면 된다.
각 인수의 의미는 아래와 같다.
lpszClassName | 생성하고자 하는 윈도우의 클래스를 지정하는 문자열이다. 앞에서 정의한 WndClass 구조체의 lpszClassName 멤버의 이름을 여기에 기입해준다. |
lpszWindowName | 윈도우의 타이틀 바에 나타날 문자열이다. 여기서 지정한 문자열이 윈도우의 타이틀 바에 나타나는데, 프로그래머가 마음대로 지정할 수 있다. |
dwStyle | 만들고자 하는 윈도우의 형태를 지정하는 인수이다. 일종의 비트 필드값이며 수십 개의 매크로 상수들이 정의되어 있고, 이 상수들을 OR 연산자로 연결하여 윈도우의 다양한 형태를 지정한다. 윈도우의 세세한 부분들을 지정할 수 있는데 WS_OVERLAPPEDWINDOW를 사용하면 가장 무난한 윈도우 설정 상태가 된다. 즉, 시스템 메뉴, 최대 및 최소 버튼, 타이틀 바, 경계선을 가진 윈도우를 만들어 준다는 것이다. |
x | 윈도우의 크기와 위치를 지정하며 픽셀 단위를 사용한다. x와 y 좌표는 메인 윈도우의 경우는 전체 화면을 기준으로 하고, Child 윈도우의 경우는 부모 윈도우의 좌상단을 기준으로 한다. 정수값을 바로 지정해도 되고, CW_USEDEFAULT를 사용하면 윈도우즈가 알아서 적당한 크기와 위치를 설정해준다. |
y | |
nWidth | |
nHeight | |
hwndParent | 부모 윈도우가 있을 경우 부모 윈도우의 핸들을 지정해주고, 부모 윈도우가 없을 경우에는 NULL로 지정한다. MDI 프로그램이나 팝업 윈도우는 윈도우끼리 수직적인 상하관계를 가져 부자(Parent-child) 관계가 성립되는데 이 관계를 지정해 주는 인수이다. |
hmenu | 윈도우에서 사용할 메뉴의 핸들을 지정한다. WndClass에도 메뉴를 지정하는 멤버가 있는데 윈도우 클래스의 메뉴는 그 윈도우 클래스를 기반으로 하는 모든 윈도우에서 사용되는 반면, 이 인수로 지정된 메뉴는 현재 CreateWindow() 함수로 만들어지는 윈도우에서만 사용된다. 만약 WndClass에서 지정한 메뉴를 그대로 사용하려면 이 인수에 NULL을 주면 되고, WndClass에서 지정한 메뉴 대신 다른 메뉴를 사용하려면 이 인수에 원하는 메뉴 핸들을 주면 된다. |
hinst | 윈도우를 만드는 주체, 즉 프로그램의 핸들을 지정한다. WinMain() 함수의 인수로 전달된 hInstance를 대입해 주면 되는 것이다. |
lpvParam | CREATESTRUCT라는 구조체의 주소이며 특수한 목적에 사용되므로 보통은 NULL 값을 사용한다. |
CreateWindows 함수는 윈도우에 관한 모든 정보를 메모리에 만든 후 윈도우 핸들을 Retrun 값으로 넘겨준다.
넘겨지는 윈도우 핸들은 hWnd라는 지역 변수에 저장되었다가 윈도우를 참조하는 모든 함수의 인수로 사용된다.
CreateWindows 함수로 만든 윈도우는 메모리상에서만 있을 뿐이며 아직까지 화면에 출력되지는 않는다.
메모리에 만들어진 윈도우를 화면으로 보이게 하려면 아래의 ShowWindow함수를 사용해야 한다.
BOOL ShowWindow(hWnd, nCmdShow);
hWnd 인수는 화면으로 출력하고자 하는 윈도우의 핸들이며, CreateWindow 함수가 리턴한 핸들을 그대로 넘겨주면 된다.
nCmdShow는 윈도우를 화면에 출력하는 방법을 지정하는 것이며, 아래와 같은 매크로 상수들이 정의되어 있다.
매크로 상수 | 의미 |
SW_HIDE | 윈도우를 숨긴다. |
SW_MINIMIZE | 윈도우를 최소화시키고 활성화시키지 않는다. |
SW_RESTORE | 윈도우를 활성화시킨다. |
SW_SHOW | 윈도우를 활성화시켜 보여준다. |
SW_SHOWNORMAL | 윈도우를 활성화시켜 보여준다. |
nCmdShow 인수에는 WinMain 함수의 인수로 전달된 nCmdShow를 그대로 넘겨주기만 하면 된다.
ShowWindow(hWnd, nCmdShow)
그래서 위와 같이 호출 형식이 거의 정해져 있다.
hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, (HMENU)NULL, hInstance, NULL);
ShowWindow(hWnd,nCmdShow);
사실상 윈도우를 만들고 화면에 출력하는 코드는 위의 두 줄이 전부이다.
위의 코드가 실행되면 화면에 윈도우가 출력되고, 이후부터는 메시지 루프가 시작되며 프로그램이 사용자와 윈도우즈, 그리고 다른 프로그램과 상호 정보를 교환하며 실행한다.
최종적으로 WinMain() 함수 내에서의 프로세스는 위와 같다.
메시지 루프
Windows를 메시지 구동 시스템(Message Driven System)이라고 하며 이 점이 Dos와 Windows의 가장 뚜렷한 차이점이다.
Dos에서는 프로그래머에 의해 미리 입력된 일련의 명령들을 순서대로 실행하는 순차적 실행방법을 사용했다면
Windows에서는 프로그램의 실행 순서가 명확하게 정해져 있지 않고, 어떤 메시지가 주어졌는지에 따라 실행 순서가 달라진다.
Windows에서 말하는 메시지란 사용자나 시스템 내부적인 동작에 의해 발생된 일체의 변화에 대한 정보를 말하는데, 예를 들자면 사용자가 마우스의 버튼을 눌렀다거나 키보드를 눌렀다거나 윈도우가 최소화되었다던가 하는 변화에 대한 정보들을 말한다.
메시지가 발생하면 프로그램에서는 메시지가 어떤 정보를 담고 있는가를 분석해 어떤 루틴을 호출할 것인가를 결정한다.
즉, 순서를 따르지 않고 주어진 메시지에 대한 반응을 정의하는 방식으로 프로그램이 실행한다는 것이다.
while(GetMessage(&Message, 0, 0, 0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
Windows 프로그램에서 메시지를 처리하는 부분을 메시지 루프라고 하며 보통 WinMain 함수의 끝에 위와 같은 형식으로 존재한다.
메시지 루프는 3개의 함수 호출로 이루어져 있고, 전체 루프는 While 문으로 싸여져 있다.
참고)
https://learn.microsoft.com/ko-kr/windows/win32/api/winuser/nf-winuser-getmessage
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
위의 URL에 따르면 GetMessage()의 반환값이 0이거나 0 또는 -1이 될 수 있으므로 -1이 반환됐을 때 무한루프가 되기 때문에 위와 같이 작성하라고 한다.
BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax);
GetMessage 함수는 시스템이 유지하는 메시지 큐에서 메시지를 읽어들인다.
읽어들인 메시지는 첫 번째 인수가 지정하는 MSG 구조체에 저장되는데, 이 함수는 읽어들인 메시지가 프로그램을 종료하라는 WM_QUIT일 경우 FALSE를 반환하고 이외의 메시지는 True를 반환한다.
그렇기 때문에 프로그램을 종료하라는 WM_QUIT 메시지가 읽히기 전까지 while문이 계속 실행된다.
나머지 3개의 인수는 읽어들일 메시지의 범위를 지정하는데, 잘 사용되지 않는다.
BOOL TranslateMessage( CONST MSG *lpMsg);
키보드 입력 메시지를 가공하여 프로그램에서 쉽게 사용할 수 있도록 해준다.
Windows는 키보드의 어떠한 키가 눌려지거나 떼어질 때 키보드 메시지를 발생시키는데 이 함수는 키보드의 눌림(WM_KEYDOWN)과 떨어짐(WM_KEYUP)이 연속적으로 발생할 때 문자가 입력되었다는 메시지 (WM_CHAR)를 만드는 역할을 한다.
예를 들어 A 키를 누른 후 다시 A 키를 떼면 A 문자가 입력되었다는 메시지를 만들어 낸다는 것이다.
LONG DispatchMessage( CONST MSG *lpmsg);
시스템 메시지 큐에서 꺼낸 메시지를 프로그램의 메시지 처리 함수(WndProc)로 전달한다.
이 함수에 의해 메시지가 프로그램으로 전달되며 프로그램에서는 전달된 메시지를 점검하여 다음 동작을 결정하게 된다.
typedef struct tagMSG
{
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG;
메시지 루프에서 하는 일은 메시지를 꺼내고, 필요한 경우 약간의 가공을 거친 뒤 응용 프로그램에게 전달하는 것 뿐이다.
이 과정은 WM_QUIT 메시지가 전달되기 전까지 계속 반복한다.
결국 메시지 루프가 하는 일은 메시지 큐에서 메시지를 꺼내 메시지 처리 함수로 보내주는 것이 전부이다.
실제 메시지 처리는 별도의 메시지 처리 함수(WndProc)에서 수행하는데, 메시지는 시스템의 변화에 대한 정보이며 MSG 라는 구조체에 보관된다고 했다.
MSG 구조체의 원형은 위와 같고, 각 멤버의 의미는 아래와 같다.
멤버 | 의미 |
hwnd | 메시지를 받을 윈도우 핸들이다. |
message | 어떤 종류의 메시지인가를 나타내는 것으로, 가장 중요하다. |
wParam | 전달된 메시지에 대한 부가적인 정보를 가지고, 32bit 값이며, 어떤 의미를 가지는가는 메시지별로 다르다. |
lParam | |
time | 메시지가 발생한 시간이다. |
pt | 메시지가 발생했을 때의 마우스 위치이다. |
message 멤버를 읽음으로써 메시지의 종류를 파악하며, message 값에 따라 프로그램의 반응이 달라진다.
wParam, lParam은 메시지에 대한 부가적인 정보를 가지되 메시지별로 의미가 달라진다.
GetMessage 함수는 읽은 메시지를 MSG 형의 구조체에 대입해주며, 이 구조체는 DispatchMessage 함수에 의해 응용 프로그램의 메시지 처리 함수(WndProc)로 전달된다.
메시지는 실제로 하나의 정수값으로 표현되는데, 메시지의 종류가 엄청 많아서 메시지의 번호를 다 외울 수 없으므로 windows.h에 메시지 별로 매크로 상수를 정의해 두었고, 접두어가 "WM_"으로 시작한다.
아래는 가장 자주 사용되는 메시지이다.
메시지 | 의미 |
WM_QUIT | 프로그램이 종료될 때 발생하는 메시지 |
WM_LBUTTONDOWN | 마우스의 왼쪽 버튼이 눌렸을 대 발생한다. |
WM_CHAR | 키보드로부터 문자가 입력될 때 발생한다. |
WM_PAINT | 화면을 다시 그려야 할 필요가 있을 때 발생한다. |
WM_DESTROY | 윈도우가 메모리에서 파괴될 때 발생한다. |
WM_CREATE | 윈도우가 처음 만들어질 때 발생한다. |
메시지 루프가 종료되면 프로그램은 마지막으로 Message, wParam을 반환하고 종료한다.
이 값은 WM_QUIT 메시지로부터 전달된 탈출 코드(Exit Code)이다.
하지만, 사용되는 경우가 거의 없다.
WndProc
WndProc은 메시지 처리 함수이고, 메시지 처리 함수는 메시지가 발생할 때 프로그램의 반응을 처리하는 일을 하며, WinMain 함수와는 별도로 WndProc이라는 이름으로 존재하는데, 윈도우 프로시져(Window Procedure)라는 뜻이지만, 읽을 때는 "윈드프록" 또는 "윈프록"이라고 읽는다.
WndProc은 WinMain에서 호출하는 것이 아니라 윈도우즈에 의해 호출된다.
WinMain 내의 메시지 루프는 메시지를 메시지 처리 함수로 보내주기만 할 뿐이고, WndProc은 메시지가 입력되면 Windows에 의해 호출되어 메시지를 처리하는데, 운영체제에 의해 호출되는 WndProc 같은 응용 프로그램 내의 함수를 콜백(CallBack) 함수라고 한다.
WndProc의 인수는 모두 4개인데, MSG 구조체의 멤버 4개와 동일하다.
hWnd는 메시지를 받을 윈도우의 핸들이고, iMessage는 어떤 변화가 발생하여 전달된 메시지인지에 관한 정보를 가진다.
iMessage가 WM_MOVE이면, 윈도우으 위치가 변경되었음을 알리고 WM_DESTROY이면 윈도우가 파괴되었음을 알리는 것이다.
wParam과 lParam은 iMessage의 메시지에 따른 부가적인 정보를 가지는데, 메시지 별로 정보들이 다르다.
switch(iMessage)
{
case Msg1:
처리1;
return 0;
case Msg2:
처리2;
return 0;
case Msg3:
처리3;
return 0;
default:
return DefWindowProc(...);
}
WndProc의 구조는 대체로 위와 같은 형태를 가진다.
메시지의 종류에 따라 다중 분기하여 메시지별로 처리를 진행한다.
case문은 프로그램이 처리할 메시지의 수만큼 반복될 것이고, 제일 끝에 있는 DefWindowProc 함수는 WndProc에서 처리하지 않은 다른 메시지에 관한 처리를 해준다.
예를 들어 시스템 메뉴를 더블 클릭하면 프로그램이 종료되는데 이런 처리는 별도로 해 주지 않아도 DefWindowProc 함수에서 해준다.
그래서 윈도우의 이동이나 크기 변경같은 처리는 직접 해 줄 필요없이 DefWindowProc으로 넘어가게 하면 된다.
LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
switch(iMessage) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
위의 코드는 이 글 맨 위에 있는 예제 코드 중 일부분인데, WM_DESTROY 메시지만을 처리하고 있고, 다른 메시지들은 DefWindowProc에게 맡기고 있다.
WM_DESTROY 메시지는 사용자가 시스템 메뉴를 더블 클릭하거나 Alt+F4(강제 종료)를 눌러 프로그램을 끝내려고 하면 발생되는 메시지이다.
WndProc()에서 이 메시지가 발생하면 PostQuitMessage() 함수를 호출하여 WM_QUIT 메시지를 보내는데, WM_QUIT 메시지가 입력되면 메시지 루프의 GetMessage 함수 리턴 값이 False가 되어 프로그램이 종료되는 것이다.
DefWindowProc 함수로 메시지가 전달되면 DefWindowProc 함수에서는 해당 메시지에 대한 기본적인 처리를 수행해준다.
WndProc은 메시지를 처리했을 경우반드시 0을 리턴해줘야 하고, DefWindowsProc 함수가 메시지를 처리했을 경우 이 함수가 리턴한 값을 WndProc 함수가 다시 리턴해줘야 한다.
위의 사진은 메시지 처리 함수의 전체적인 순서도이다.