<고전적인 메시지 처리>
◈ 윈도우 프로시저의 switch-case문 문제점
- 메시지가 많아지면 case가 많아져 코드가 길어진다. 가독성이 떨어지고 자원이 낭비된다
- case에서 사용하는 변수가 많아지면 프로시저가 호출될 때마다 스택의 낭비가 심하고 불필요한 메모리를 소모한다
◈ 윈도우 프로시저의 switch-case문 문제점
- 메시지가 많아지면 case가 많아져 코드가 길어진다. 가독성이 떨어지고 자원이 낭비된다
- case에서 사용하는 변수가 많아지면 프로시저가 호출될 때마다 스택의 낭비가 심하고 불필요한 메모리를 소모한다
<개별 메시지 처리 함수>
- 메시지 처리 코드는 처리 함수를 만들고 WndProc안에서는 함수 호출만 한다
- 메시지 처리 함수를 메시지 핸들러라 한다
- 가독성이 높다
- 함수 내부 수정이 용이하며 다중 루프 탈출도 편하고 코드 재사용성이 높다
<메시지 크래커>
- WndProc에 들어갈 함수 호출문과 메시지 처리 함수의 원형을 매크로로 잘 정의해 놓은 것
<Windowsx.h>
- 메시지 크래커는 컴파일러가 지원하는 것도 아니고 C언어 명세에 있는 것도 아니다
- Windowsx.h 헤더 파일에 정의되어 있는 단순한 매크로 구문이다
HANDLE_MSG(윈도우, 메시지, 처리함수)
- 윈도우에서 발생하는 메시지와 처리 함수를 짝짓기한다
windowsx.h
#define HANDLE_MSG(hwnd, message, fn) case (message) : return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
- HANDLE_##message 매크로 함수는 메시지별로 정의되어 있다
CF)##은 치환후 주변의 공백과 함께 스스로 제거된다
- 윈도우에서 발생하는 메시지와 처리 함수를 짝짓기한다
windowsx.h
#define HANDLE_MSG(hwnd, message, fn) case (message) : return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
- HANDLE_##message 매크로 함수는 메시지별로 정의되어 있다
CF)##은 치환후 주변의 공백과 함께 스스로 제거된다
메시지 크래커 적용
- WndProc에서 처리하고자 하는 메시지에 대해 HANDLE_MSG 매크로 구문을 삽입한다. 처리하고자 하는 메시지와 함수명을 짝짖기 하면 된다
- 함수의 본체를 만든다. 메시지 처리 함수의 원형은 windowsx.h에 주석으로 기록되어 있다
- 핸들러 함수의 원형을 선언한다. windowsx.h를 포함시킨다
- WndProc에서 처리하고자 하는 메시지에 대해 HANDLE_MSG 매크로 구문을 삽입한다. 처리하고자 하는 메시지와 함수명을 짝짖기 하면 된다
- 함수의 본체를 만든다. 메시지 처리 함수의 원형은 windowsx.h에 주석으로 기록되어 있다
- 핸들러 함수의 원형을 선언한다. windowsx.h를 포함시킨다
=================================================
예제 코드
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd,WM_DESTROY, OnDestroy);
return DefWindowProc(hwnd, message, wParam, lParam);
}
void OnSize(HWND hwnd, UINT state, int cx, int cy)
{
g_cx = cx;
g_cy = cy;
}
void Onpaint(HWND hwnd)
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
char szString[30];
wsprintf(szString, "너비 : %d, 높이 : %d", g_cx, g_cy );
TextOut(hdc, 0, 0, szString, lstrlen(szString) );
EndPaint ( hwnd, &ps );
}
void OnDestroy(HWND hwnd)
{
PostQuitMessage(0);
}
갈색으로 된 부분을 보면 윈드프록 함수를 간결하게 그리고 메시지 처리를 함수로 빼어 내서
훨씬 알아보기도 쉽고 사용하기 편하게 보입니다.
HANDLE_MSG 매크로는 windowsx.h 파일에 정의 된 매크로인데
#define HANDLE_MSG(hwnd, message, fn) \ case (message)" return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
이렇게 정의 되어 있습니다
HANDLE_MSG(hwnd, WM_SIZE, OnSize); 함수가 호출 되면 'message' 에 WM_SIZE가 전달이
되어
case (WM_SIZE) : return HANDLE_##message((hwnd),(wParam), (lParam), (OnSize))
가 호출이 되게 됩니다.
##연산자는 #왼쪽과 ## 오른쪽을 연결해서 하나의 문자열로 만드는데 'message'에 WM_SIZE가 호출 되었으므로 결국
case (WM_SIZE): return HANDLE_WM_SIZE((hwnd), (wParam), (lParam), (OnSize))
가 되게 됩니다.
결국 HANDLE_MSG(hwnd, WM_SIZE, OnSize); 함수가 실행 되면
windowsx.h 파일내에 있는
#define HANDLE_WM_SIZE(hwnd, wParam, lParam, OnSize) \
OnSize(hwnd, wParam, LOWORD(lParam), HIWORD(lParam)), 0L
이 호출 됩니다.
좀복잡했지만 정리하면
HANDLE_MSG(hwnd, WM_SIZE, OnSize);
이 코드는 바뀌고 바껴서
case WM_SIZE:
OnSize(hwnd, wParam, LOWORD(lParam), HIWORD(lParam));
return 0;
이렇게 바뀌게 되는 겁니다. 결국 저 간단한 코드로 OnSize 함수를 실행 시키게 되는 겁니다.