자료 저장소

# 고전적인 메시지 처리

switch(iMessage) 
{
case 메시지1: 처리1; return0;
case 메시지2: 처리2; return0;
case 메시지3: 처리3; return0;
}
return DefWindowProc(...);
위의 형태는 구조가 단순하지만 메시지 종류가 많아지면 WndProc의 길이가 무한정 길어질수 있다는 점이 있다.
또한 WndProc이 한번 호출될 때마다 선언된 변수들로 인해 스택의 낭비가 심하고 여러 차례의 재진입이 발생시
불필요하게 메모리를 소모 하게 된다.

위와 같은 구조는 예제나 테스트 프로그램 정도에는 적합하지만 큰 규모의 프로그램에는 적합하지 않다.


# 개별 메시지 처리 함수

LRESULT OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam); 
LRESULT OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam);
LRESULT OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam);
LRESULT CALLBACK WndProc(...) 
{
switch(iMessage)
{
case WM_CREATE:
return OnCreate(hWnd,wParam,lParam);
case WM_PAINT:
return OnPaint(hWnd,wParam,lParam);
case DESTROY:
return OnDestroy(hWnd,wParam,lParam);
}
return DefWindowProc(...);
}
LRESULT OnCreate(HWND hWnd,WPARAM wParam,LPARAM lParam) 
{
/* 처리 */
}
LRESULT OnPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)
{
/* 처리 */
}
LRESULT OnDestroy(HWND hWnd,WPARAM wParam,LPARAM lParam);
{
/* 처리 */
}
개별적인 메시지를 처리하는 함수를 만들고 WndProc에서는 전달된 메시지에 따라 이 함수들을 호출하도록 변경.
메시지별로 함수가 작성되어 있어 가독성이 높고 함수 내부를 수정하기도 편리하며 코드의 재사용성도 높아진다.


# 메시지 크래커

각 메시지들은 wParam과 lParam을 전달받아 제 할일을 하는데 위의 예제는 모두 hWnd,wParam,lParam을 
인수로 전달 받았다. 
그런데 이왕이면 wParam,lParam에 포함된 추가 정보를 쓰기 편하게 추출해서 인수로 제공하면 함수 내부에서
훨씬 더 편하게 쓸 수 있는데 이런 서비스를 하는것이 바로 메시지 크래커 이다. 

메시지 크래커는 WndProc에 들어갈 함수 호출문과 메시지 처리 함수의 원형을 매크로로 잘 정의해 놓은것이다.
// 메시지 크래커를 사용하는 경우
#include <WindowsX.h>

// 함수 원형 정의, 아래는 Windowsx 헤더에 주석처리된 함수 원형이다.
LRESULT MsgCrk_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
/* BOOL Cls_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct) */
LRESULT MsgCrk_OnPaint(HWND hwnd);
/* void Cls_OnPaint(HWND hwnd) */
LRESULT MsgCrk_OnDestroy(HWND hwnd);
/* void Cls_OnDestroy(HWND hwnd) */

LRESULT CALLBACK WndProc(...)
{
switch(iMessage)
{
HANDLE_MSG(hWnd,WM_CREATE,MsgCrk_OnCreate);
HANDLE_MSG(hWnd,WM_PAINT,MsgCrk_OnPaint);
HANDLE_MSG(hWnd,WM_DESTROY,MsgCrk_OnDestroy);
}

return DefWindowProc(...);
}

LRESULT MsgCrk_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
{
/* 처리 */
}
LRESULT MsgCrk_OnPaint(HWND hwnd);
{
/* 처리 */
}
LRESULT MsgCrk_OnDestroy(HWND hwnd);
{
/* 처리 */
}
HANDLE_MSG(윈도우,메시지,처리함수)
이 함수는 윈도우에서 발생 하는 메시지와 처리 함수를 짝짓기 한다.

#define HANDLE_MSG(hwnd, message, fn) \ 
case (message): return HANDLE_##message((hwnd), (wParam), (lParam), (fn))
위처럼 매크로가 정의되어 있는데 일단 switch문에서 사용되기에 case (message): 으로 전달된 경우를 점검하고
HANDLE_##message로 정의된 매크로 함수를 다시 호출하며 이 함수로 hWnd,wParam,lParam을 전달한다.

WM_SIZE에 대한 매크로문을 보면
#define HANDLE_WM_SIZE(hwnd, wParam, lParam, fn) \
((fn)((hwnd), (UINT)(wParam), (int)(short)LOWORD(lParam), (int)(short)HIWORD(lParam))
,0L)
이 매크로는 fn 함수를 호출하되 이때 인수로 hWnd,wParam,lParam의 하위,상위 워드 네개가 전달된다.
이 매크로에 대응되는 함수 fn은 다음 원형으로 WM_SIZE 매크로문 위에 주석 처리되어있다.
void Cls_OnSize(HWND hwnd, UINT state, int cx, int cy)
Cls는 메시지를 받을 윈도우의 이름으로 대체하는 것이 관례이고 매크로를 모두 전개 하면 다음과 같이 된다.
case WM_SIZE: 
return MsgCrk_OnSize(hWnd,wParam,LOWORD(lParam),HIWORD(lParam));
결국 이 구문을 만드는것이 메시지 크래커가 하는 일이다. wParam,lParam으로부터 정보를 추출하는 문장이
매크로 함수에 모두 작성되어 있으므로 메시지 크래커를 사용한다면 더 이상 wParam,lParam의 의미에 대해
신경쓸 필요없이 오로지 메시지 크래커가 전달해 주는 state,cx,cy 인수만 잘 사용하면 된다.
HANDLE_WM_SIZE 매크로의 제일 뒤에 있는 ,0은 리턴값을 무조건 0으로 한다는 뜻인데 여기서 콤마는
구두점이 아니라 좌변을 먼저 평가하고 우변을 리턴하는 연산자이다.

메시지 크래커를 실제 업무에 적용 시킬때는 아래의 순서대로 작업하도록 하자.

① WndProc에 처리하고자 하는 메시지에 대해 HANDLE_MSG 매크로 구문을 삽입한다. 처리하고자 하는 메시지
와 함수명을 짝짓기만 하면된다. 예)WM_SIZE = HANDLE_MSG(hWnd,WM_SIZE,MyPrg_OnSize)라고 적는다.

② 함수의 본체를 만든다. 함수의 원형은 각 매크로문 위에 주석 처리되어 있으므로 복사해서 Cls만 변경하면된다.

③ 핸들러 함수의 원형을 선언하면 작업이 완료된다. Windowsx.h를 가장 선두에 포함하는것을 잊지 말도록 하자.


# 컨트롤 제어

메시지 크래커의 또 다른 기능으로 컨트롤을 제어하는 매크로 구문들이 있는데 정의된 매크로에 따라 구문을
잘 해석하여 가져다 쓰면 된다.

#define ListBox_AddString(hwndCtl, lpsz) \ 
((int)(DWORD)SNDMSG((hwndCtl), LB_ADDSTRING, 0L, (LPARAM)(LPCTSTR)(lpsz)))
위 매크로는 리스트박스에 문자열을 추가하도록 하는 매크로 인데 SendMessage 함수를 직접 쓰는 것보다
ListBox_AddString(hList,"string"); 이라는 구문으로 사용하는게 훨씬 더 알기 쉽고 편하다는 것을 알 수 있다.
이 외에도 수많은 컨트롤 제어에 관한 메지시 크래커가 존재하니 필요할때 적절히 사용하도록 하자.

API 자체도 잘 모르면서 처음부터 편하자고 메시지 크래커로만 코드를 작성하는 것은 공부하는 자세가 아니라고
생각한다. 메시지 크래커는 API를 잘 아는 사람이 좀 더 편하게 쓰기 위해 존재하는 것일 뿐이다.
switch~case문이
다소 불편하다고 생각하면 메시지 크래커가 아닌 메시지 핸들러만 분리해도 상당히 편리함을 느낄 수 있다.

댓글 로드 중…

최근에 게시된 글