'Socket Programming'에 해당되는 글 1건

  1. 2009.04.22 메시지 크래커(Message Cracker)

<고전적인 메시지 처리>
◈ 윈도우 프로시저의 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)##은 치환후 주변의 공백과 함께 스스로 제거된다
메시지 크래커 적용
- 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 함수를 실행 시키게 되는 겁니다.
Posted by 마블(이환문)