2011년9월2일_winAPI_WndProc()에서 switch문으로 메시지를 구분하여 처리하지 않고 메시지맵(Message Map)을 사용해 해당 메시지에 대한 기능을 수행하고 각 기능별 함수들을 따로 모아 분할 컴파일(빌드)


오늘 날짜 0902_file_div프로젝트관련 파일들의 구조는 아래와 같고,

image

프로젝트명과 같은 0902_file_div.cpp에 main함수인 WinMain()가 있다.
MsgProc.cpp 파일에는 메시지처리함수들이 모여 있고,
MsgProc.h 파일에는 메시지처리함수들의 원형이 있다.


● 0902_file_div.cpp

   1: // 파일을 분할하여 빌드하고 메시지맵.
   2: // 2011년 9월 2일 작성 
   3: #include <windows.h>        // 모든 API함수들의 원형과 사용하는 상수들이 정의되어 있음.
   4: #include "MsgProc.h"    //add
   5:  
   6: LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    //메시지처리함수
   7: HINSTANCE g_hlnst;    //핸들 인스턴스
   8: LPCTSTR lpszClass = TEXT("파일 분할해서 빌드");        //윈도우 제목으로 사용되는 const char * (문자열)
   9:  
  10: //add
  11: typedef struct MESSAGEMAP {
  12:     UINT iMessage;
  13:     LRESULT (*lpfnMsgProc)(HWND, WPARAM, LPARAM);
  14: } MESSAGEMAP;
  15:  
  16: //시작함수.
  17: int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow)
  18: {
  19:     HWND hWnd;
  20:     MSG Message;
  21:     WNDCLASS WndClass;
  22:     g_hlnst = hInstance;
  23:  
  24:     WndClass.cbClsExtra = 0;
  25:     WndClass.cbWndExtra = 0;
  26:     WndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
  27:     WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  28:     WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  29:     WndClass.hInstance = hInstance;
  30:     WndClass.lpfnWndProc = WndProc;
  31:     WndClass.lpszClassName = lpszClass;
  32:     WndClass.lpszMenuName = NULL;
  33:     WndClass.style = CS_HREDRAW | CS_VREDRAW;
  34:     RegisterClass(&WndClass);
  35:  
  36:     
  37:     hWnd = CreateWindow(lpszClass, lpszClass, WS_OVERLAPPEDWINDOW,
  38:         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,        
  39:         NULL, (HMENU)NULL, hInstance, NULL);
  40:     
  41:     ShowWindow(hWnd, nCmdShow);
  42:  
  43:     while(GetMessage(&Message, NULL, 0, 0)) {
  44:         TranslateMessage(&Message);
  45:         DispatchMessage(&Message);
  46:     }
  47:  
  48:     return (int)Message.wParam;
  49: }
  50:  
  51: //운영체제가 호출하는 CALLBACK함수 (메시지 처리)
  52: LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam)
  53: {
  54:     //add
  55:     int i;
  56:  
  57:     static MESSAGEMAP MessageMaps[] = {
  58:         {WM_CREATE, OnCreate},
  59:         {WM_COMMAND, OnCommand},
  60:         {WM_LBUTTONDOWN, OnLButtonDown},
  61:         {WM_PAINT, OnPaint},
  62:         {WM_DESTROY, OnDestroy}
  63:     };
  64:  
  65:     for(i = 0 ; i < sizeof(MessageMaps) / sizeof(MessageMaps[0]) ; ++i)
  66:     {
  67:         if(MessageMaps[i].iMessage == iMessage)
  68:         {
  69:             return (*MessageMaps[i].lpfnMsgProc)(hWnd, wParam, lParam);
  70:         }
  71:     }
  72:     /////////////////////////////////
  73:  
  74:     return(DefWindowProc(hWnd, iMessage, wParam, lParam));
  75: }



● MsgProc.h

   1: //MsgProc.h
   2: #ifndef __MSGPROC_H_
   3: #define    __MSGPROC_H_
   4:  
   5: #include <windows.h>
   6:  
   7: LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
   8: LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam);
   9: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam);
  10: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam);
  11: LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam);
  12:  
  13: extern HINSTANCE g_hInst;
  14:  
  15: #endif



● MsgProc.cpp

   1: //MsgProc.cpp
   2:  
   3: #include "MsgProc.h"
   4:  
   5: LRESULT OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
   6: {
   7:     return 0;
   8: }
   9:  
  10: LRESULT OnCommand(HWND hWnd, WPARAM wParam, LPARAM lParam)
  11: {
  12:     return 0;
  13: }
  14:  
  15: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
  16: {
  17:     HDC hdc;
  18:     static x = 50, y = 50;
  19:     
  20:     hdc = GetDC(hWnd);
  21:     TextOut(hdc, x, y, TEXT("클릭"), lstrlen(TEXT("클릭")));
  22:  
  23:     x = x + 50;
  24:     if(600 < x)
  25:     {    
  26:         x = 0;
  27:         y = y + 20;
  28:     }
  29:     ReleaseDC(hWnd, hdc);
  30:  
  31:     return 0;
  32: }
  33:  
  34: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
  35: {
  36:     HDC hdc;
  37:     PAINTSTRUCT ps;
  38:  
  39:     hdc = BeginPaint(hWnd, &ps);
  40:  
  41:     EndPaint(hWnd, &ps);
  42:     return 0;
  43: }
  44:  
  45: LRESULT OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
  46: {
  47:     PostQuitMessage(0);
  48:     return 0;
  49: }
  50:  



● 메시지맵

  11: typedef struct MESSAGEMAP {
  12:     UINT iMessage;
  13:     LRESULT (*lpfnMsgProc)(HWND, WPARAM, LPARAM);
  14: } MESSAGEMAP;

메시지맵의 구조체는 상기와 같고,
WndProc에서 인자로 받은 iMessage와 같은 자료형에 같은 이름을 가진 멤버변수와,
반환값이 LRESULT이고 인자가 HWND형, WPARAM형, LPARAM형인 함수포인터 멤버변수가 있다.
함수포인터를 만드는 법은 MsgProc.cpp에서 설명하겠다.


  57:     static MESSAGEMAP MessageMaps[] = {
  58:         {WM_CREATE, OnCreate},
  59:         {WM_COMMAND, OnCommand},
  60:         {WM_LBUTTONDOWN, OnLButtonDown},
  61:         {WM_PAINT, OnPaint},
  62:         {WM_DESTROY, OnDestroy}
  63:     };

WndProc()내부에서,
MessageMaps라는 이름의 MESSAGEMAP 자료형으로 구조체배열을 선언하고 바로 초기화한다.
메시지는 자주 발생하니 구조체배열을 메시지가 발생할 때마다 초기화할 필요는 없다.
어짜피 각 메시지별 기능은 동일할테니 말이다.
MessageMaps[0] 부터 [5]까지 메시지와 함수의 주소를 넣었으니 메시지를 검사하여 호출하는 일만 남았다.

  65:     for(i = 0 ; i < sizeof(MessageMaps) / sizeof(MessageMaps[0]) ; ++i)
  66:     {
  67:         if(MessageMaps[i].iMessage == iMessage)
  69:             return (*MessageMaps[i].lpfnMsgProc)(hWnd, wParam, lParam);


0번 인덱스 부터 5번 인덱스까지(메시지맵 전체의 크기에서 각 원소의 크기를 나눈 값 = 원소의 갯수) 반복하여,
메시지맵에 담긴 메시지값과 WndProc() 호출시 받은 메시지 인자(iMessage)의 값을 비교하여 일치하면,
해당메시지의 기능을 수행하는 함수를 호출한다.
이 때 WndProc() 호출시 받은 hWnd(윈도우 핸들), wParam, lParam을 인자로 건네준다.

  15: LRESULT OnLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam)
  21:     TextOut(hdc, x, y, TEXT("클릭"), lstrlen(TEXT("클릭")));

왼쪽 마우스버튼을 눌렀을 때,
”클릭”이라는 문자열을 윈도우에 표시하고 커서를 이동시킨다.

  34: LRESULT OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
  39:     hdc = BeginPaint(hWnd, &ps);
  41:     EndPaint(hWnd, &ps);

OnPaint()는 WM_PAINT메시지가 발생되었을 때 호출되는 함수로 윈도우가 지워지거나 변경되는 등 변화가 일어나면,
윈도우에 그려진 문자, 그림, 사진등등을 다시 그리는 일을 수행해야 한다.
여기서는 아무 일도 하지 않는다.


<빌드>

 image

Rebuild All을 수행하니 프로젝트내의 모든 소스파일들이 재컴파일 & 링크되었다.
헤더파일은 소스파일이 아니니 포함이 되지 않고 참조만 할 뿐이다.
이렇게 파일을 나누고 switch문 대신 메시지맵 구조체배열을 사용하니 소스코드가 더 간결해 보인다.
파일을 분할하면 여럿이 작업할 수 있으니 꼭 분할컴파일은 알아둬야 할 것이다.


<실행결과>

image 

아쉽게도 OnPaint()에서 윈도우를 다시 그려주지 않으니 사이즈를 변경하거나 가리면 다시 그려주지 않는다.