01. SDK 프로그램 기본 구조
① Window Class를 정의(초기화)하고 OS에 등록
② Window를 생성하고 화면에 나타낸다.
③ Message Loop를 구동한다.
④ Window Procedure에서 메시지를 처리한다.
01-1. SDK 예제
: 윈도우를 최소화 혹은 최대화할 수 있으며, 마우스로 테두리 부분을 드래그하면 크기가 변경되는 프로그램. 화면에 표시된 Hello, SDK 문자열이 지워지지 않고 항상 클라이언트 영역의 일정한 위치에 표시. 또한 클라이언트 영역에서 마우스 왼쪽 버튼을 누르면 간단한 메시지 상자가 뜬다.

: 코드
#include <windows.h>
// WinMain 함수에서 참조하므로 함수 원형을 선언한다.
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wndclass;
HWND hwnd;
MSG msg;
// 윈도우 클래스를 초기화하고 운영체제에 등록한다.
wndclass.style = CS_HREDRAW | CS_VREDRAW; // 스타일 지정
wndclass.lpfnWndProc = WndProc; // 윈도우 프로시저 이름
wndclass.cbClsExtra = 0; // 여분 메모리(0바이트)
wndclass.cbWndExtra = 0; // 여분 메모리(0바이트)
wndclass.hInstance = hInstance; // 인스턴스 핸들
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 아이콘 모양
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); // 커서 모양
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 배경(흰색)
wndclass.lpszMenuName = NULL; // 메뉴(NULL->메뉴 없음)
wndclass.lpszClassName = TEXT("HelloClass"); // 윈도우 클래스 이름
if (!RegisterClass(&wndclass)) return 1;
// 윈도우를 생성하고 화면에 나타낸다.
hwnd = CreateWindow(TEXT("HelloClass"), TEXT("HelloSDK"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
// 메시지 큐에서 메시지를 하나씩 꺼내서 처리한다.
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
const TCHAR* str = TEXT("Hello, SDK");
// 발생한 메시지의 종류에 따라 적절히 처리한다.
switch (message) {
case WM_CREATE:
return 0;
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("마우스 클릭!"), TEXT("마우스 메시지"), MB_OK);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 100, 100, str, lstrlen(str));
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// 응용 프로그램이 처리하지 않은 메시지는 운영체제가 처리한다.
return DefWindowProc(hwnd, message, wParam, lParam);
}
01-2. SDK 예제 코드 분석
① Header File
[1] Windows.h : 여러 헤더 파일을 포함하는 헤더 파일. 윈도우 API 함수 원형, 데이터 타입, 구조체, 매크로 상수 등이 여기에 선언. HINSTANCE, LPSTR, HWND 같은 생소한 데이터 타입의 정의는 해당 헤더 파일 보면 알 수 있다.
② Main Function
: WinMain() 함수는 C 프로그램의 main() 함수에 해당. 프로그램을 실행하는 시작점.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
[1] hInstance : 메모리에 로드된 실행 파일의 위치를 나타내는 주소값. 이 값은 실행 파일에 포함된 리소스(비트맵, 아이콘 ~)에 접근할 때 종종 사용.
(참고: MFC는 프로그램에서 hInstance 값에 직접 접근할 수 있도록 AfxGetInstanceHandle() 함수를 제공)
[2] hPrevInstance: 16비트 윈도우의 잔재. 항상 NULL 값
[3] lpCmdLine: 명령행 인자를 담고 있는 문자열.
ex) 'HelloSDK TEST1.TXT TEST2.TXT'와 같이 프로그램을 실행하면 lpCmdLine은 'TEST1.TXT TEST2.TXT' 값을 가진다.
[4] nCmdShow: 프로그램이 시작할 때 보여줄 윈도우 모양 결정. 이 값에 따라 윈도우는 최소화, 최대화 혹은 보통 상태로 시작.
③ Window Class Initialization / Upload
WNDCLASS wndclass;
// 윈도우 클래스를 초기화하고 운영체제에 등록한다.
wndclass.style = CS_HREDRAW | CS_VREDRAW; // 스타일 지정
wndclass.lpfnWndProc = WndProc; // 윈도우 프로시저 이름
wndclass.cbClsExtra = 0; // 여분 메모리(0바이트)
wndclass.cbWndExtra = 0; // 여분 메모리(0바이트)
wndclass.hInstance = hInstance; // 인스턴스 핸들
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // 아이콘 모양
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW); // 커서 모양
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); // 배경(흰색)
wndclass.lpszMenuName = NULL; // 메뉴(NULL->메뉴 없음)
wndclass.lpszClassName = TEXT("HelloClass"); // 윈도우 클래스 이름
if (!RegisterClass(&wndclass)) return 1;
: WNDCLASS 윈도우 클래스는 윈도우를 생성하는 데 필요한 다양한 정보를 담고 있는 구조체. 윈도우를 생성하기 전에 윈도우 클래스를 초기화하고 운영체제에 등록하는 과정이 필요.
: 윈도우 클래스는 생성할 윈도우의 특성을 나타내는 다양한 요소로 이루어져 있는데, 이들 중 가장 중요한 것은 lpfnWndProc. 이는 일종의 함수 포인터로, 응용 프로그램의 윈도우가 OS로부터 받은 메시지를 처리할 사용자 정의 함수(=윈도우 프로시저)를 가리킨다.
: 따라서, 윈도우 클래스를 등록하고 나면 응용 프로그램은 특성이 동일한 윈도우를 여러 개 생성할 수 있고, 이렇게 생성된 윈도우에서 발생한 메시지는 하나의 윈도우 프로시저에서 처리한다.
④ Window Create
// 윈도우를 생성하고 화면에 나타낸다.
hwnd = CreateWindow(TEXT("HelloClass"), TEXT("HelloSDK"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);
: CreateWindow() 함수는 위에서 등록한 윈도우 클래스를 기반으로 실제 윈도우 생성. 응용 프로그램의 최상위 윈도우(메인 윈도우) 생성.
[1] 클래스 이름) TEXT("HelloClass")
: OS가 윈도우를 생성하는 데 필요한 정보를 제공하는 데, 특히 Window Procedure의 주소(=위치)를 OS에 알려주는 중요한 역할 제공.
[2] 윈도우 이름) TEXT("HelloSDK")
[3] 윈도우 스타일) WS_OVERLAPPEDWINDOW
[4] x좌표 / y좌표 / 윈도우의 폭 / 윈도우의 높이) CW_USEDEFAULT : 기본값 사용
[5] 부모 윈도우 핸들 / 메뉴 핸들 / 옵션 데이터) NULL
[6] 인스턴스 핸들) hInstance
[7] 리턴값 = 윈도우 핸들(hwnd). OS 내부에서 윈도우를 유지하는 데 필요한 데이터 구조체를 가리키는 일종의 포인터. 해당 핸들값의 숫자 자체는 중요하지 않으며, 이 핸들을 가지고 있다면, 윈도우 API를 호출함으로써 해당 윈도우를 다양한 방식으로 제어 가능.
: ShowWindow() 함수는 생성한 윈도우를 화면에 표시. nCmdShow에 따라 최소화, 최대화, 혹은 보통 상태로 시작. 해당 함수의 첫번째 인자로 Window Handle 전달에 주목. SDK 프로그래밍에서는 윈도우, 메뉴, 비트맵 같은 사용자 인터페이스 구성 요소를 다룰 때 핸들 주로 사용.
(MFC는 내부에 핸들을 숨기고 있기에 핸들을 직접 다룰 일이 많지 않다. 그러나 특별한 기능을 구현하기 위해 윈도우 API를 직접 호출하는 경우에는 핸들이 필요한데, 이 경우 핸들값이 MFC 클래스의 public 멤버로 공개된 경우가 많다)
⑤ Message Loop
// 메시지 큐에서 메시지를 하나씩 꺼내서 처리한다.
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
: 메시지 큐에서 메시지를 하나씩 꺼내어 처리하는 반복문
: GetMessage() 함수는 message queue에서 message 하나를 꺼내 msg 변수에 저장. 꺼낸 메시지가 WM_QUIT이면 0을 리턴하면서 while문 종료
: TranslateMessage() 함수는 msg 변수에 키보드 메시지가 들어 있을 경우 키에 대응하는 문자를 만들어내는 역할
: DispatchMessage() 함수는 message를 window procedure로 보낸다.(위의 경우 WndProc)
⑥ Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
const TCHAR* str = TEXT("Hello, SDK");
// 발생한 메시지의 종류에 따라 적절히 처리한다.
switch (message) {
case WM_CREATE:
return 0;
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("마우스 클릭!"), TEXT("마우스 메시지"), MB_OK);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 100, 100, str, lstrlen(str));
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
// 응용 프로그램이 처리하지 않은 메시지는 운영체제가 처리한다.
return DefWindowProc(hwnd, message, wParam, lParam);
}
: Window Message를 처리하는 핵심 함수.
: 첫번째 인자) 1개의 윈도우 클래스를 기반으로 윈도우를 여러 개 생성한 경우, 모든 window가 window procedure를 공유하므로 어느 window에서 message가 발생했는 지 구분할 필요 있다. → 첫번째 인자 hwnd가 이 역할을 한다.
: 두번째 인자) UINT message: 발생한 메시지 종류를 나타내며, WM_으로 시작하는 상숫값
| message name | message evoked time |
| WM_CREATE | CreateWindow() 함수 호출할 때 |
| WM_LBUTTONDOWN | client 영역에서 마우스 왼쪽 버튼을 누를 때 |
| WM_PAINT | client 영역의 일부 또는 전체를 다시 그릴 필요가 있을 때 |
| WM_DESTROY | window 종료 버튼을 클릭할 때 |
(1) WM_CREATE message) 윈도우가 생성되면서 한 번만 발생하므로, 메시지 발생 시 각종 초기화 작업 수행.
(2) WM_DESTROY
case WM_DESTROY:
PostQuitMessage(0);
return 0;
: message) 메시지 처리의 마지막 단계로, PostQuitMessage() 함수 호출. 해당 함수는 응용 프로그램 message queue에 WM_QUIT message를 집어넣는다.
(3) WM_LBUTTONDOWN
case WM_LBUTTONDOWN:
MessageBox(hwnd, TEXT("마우스 클릭!"), TEXT("마우스 메시지"), MB_OK);
return 0;
: message) client 영역에서 마우스 왼쪽 버튼을 누를 때 발생. wParam & lParam
: 세번째 / 네번째 인자) wParam / lParam: 각각 마우스/키보드 버튼의 상태 & 마우스 커서의 위치 저장.
(4) WM_PAINT
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 100, 100, str, lstrlen(str));
EndPaint(hwnd, &ps);
return 0;
: 윈도우 응용 프로그램이 화면에 무언가를 표시할 때 주로 윈도우의 client 영역에 출력. 출력 내용) OS가 자동으로 저장/복원하지 않으므로 다른 윈도우가 가렸다가 다시 드러나면 지워진다. OS는 client 영역의 일부 또는 전체를 다시 그릴 필요가 있음을 응용 프로그램에 알리기 위해 WM_PAINT 메시지 발생시킨다. → 응용 프로그램은 WM_PAINT 메시지에 응답하여 자신의 client 영역 화면을 다시 그린다(복원한다)
: WM_PAINT 메시지 처리 시 BeginPaint() / EndPaint() 시작과 끝부분에서 호출(두 함수 사용 필수)
(두 함수: 응용 프로그램이 WM_PAINT 메시지에 응답하여 윈도우의 client 영역을 다시 그렸음을 OS에 알려준다. / WM_PAINT 메시지를 받았을 때 화면을 다시 그려주지 않으면(즉, BeginPaint() / EndPaint() 함수 사용하지 않으면), 윈도우 최소화/최대화를 비롯한 크기 변경, 다른 윈도우가 client 영역을 가렸다가 다시 보이게 되는 경우 등이 발생할 때 화면의 내용이 지워지는 현상 발생)
: 만약 BeginPaint() / EndPaint() 함수를 호출하지 않으면(즉 생략하면; 주석 처리) 윈도우의 크기를 약간 변경한 후(이 때, WM_PAINT 메시지 발생) 작업 관리자 살펴봤을 때 HelloSDK 프로그램의 CPU 사용률이 높다.
(BeginPaint() / EndPaint() 함수 호출 생략했으므로 OS가 끊임없이 WM_PAINT 메시지 발생시키기 때문)
: BeginPaint() 함수의 리턴값 = Device Context(디바이스 컨텍스트 핸들).
: TextOut() 함수로 클라이언트 영역의 지정된 위치에 문자열 출력. 이 때, 첫번째 인자로 hdc가 사용됨.
(5) DefWindowProc()
: switch-case문에 해당하지 않는 처리되지 않은 메시지는 해당 함수 DefWindowProc() 함수를 호출하여 OS가 자동으로 처리되게 한다.
⑦ SDK 프로그램 구조 최종 정리
[1] OS는 각 응용 프로그램에 message queue 할당. program 외부 / 내부 요인으로 발생하는 message가 system message queue를 거쳐 응용 프로그램 message queue에 저장된다.
[2] WinMain() 함수에 있는 message loop는 응용 프로그램 message queue에 저장된 message를 하나씩 꺼내서 window procedure에 전달한다.
[3] Window Procedure는 전달받는 메시지 종류에 따라 적절하게 처리하되 처리하지 않은 message는 DefWindowProc() 함수에 넘겨서 OS가 자동으로 처리하게 한다.
: Window Application Program의 핵심은 Window Procedure. Window Procedure는 message 처리 코드 집합이므로, 결국 message 처리 코드를 어떻게 작성하느냐가 전체 응용 프로그램의 동작 결정(MFC의 경우 message handler).

★ 헝가리 표기법 ★
: SDK나 MFC 프로그래밍 진행 시 헝가리 표기법(Hungarian Notation)을 사용해 변수를 표기. 헝가리 표기법은 변수 타입을 짐작할 수 있도록 Prefix를 덧붙여 변수 이름을 정한다. ex) hInstance는 첫글자인 h만으로 핸들 타입임을 알 수 있다. 데이터 타입에 따른 Prefix 꼭 알기!
| Prefix | Data Type | Prefix | Data Type |
| b / f | BOOL(b=bool, f=flag) | l | LONG |
| c / ch | char | sz | string ends with 0 |
| b | BYTE | h | handle |
| n / i | int | p | pointer |
| w | WORD | lp | long pointer |
| dw | DWORD | fn | function |
* 출처) 쉽게 배우는 MFC 윈도우 프로그래밍(한빛미디어)
'C, C++ Language > MFC Window Programming' 카테고리의 다른 글
| 🔥Ch01. Window Programming Fundamentals🔥(3/3) - MFC (0) | 2025.10.08 |
|---|---|
| 🔥Ch01. Window Programming Fundamentals🔥(1/3) - Intro (0) | 2025.10.06 |
댓글