MFC 메시지 맵 3 : 메시지맵을 사용해 윈도우 프로시저(WndProc) 자동화 MFC 마을


지난시간에는 WIn32 API 를 이용한 윈도우 응용프로그램을 클래스화 했다.

MFC 메시지 맵 2 : 클래스를 사용해 프로그램을 구조화




하지만, 이 프로그램의 문제점은 사용되는 윈도우 메시지가 추가 될 때마다 윈도우 프로시저(WndProc)에 case by case 로 코딩해 주어야 한다는 것.

이러한 문제점을 이번시간에는 MFC 에서 사용하는 메시지 맵 구조를 사용해 해결 해 보자.


우선, 기본적인 변수들 즉, HWND, MSG, WNDCLASSEX 를 포함하고 있고, InitInstance, Run, ExitInstance

등의 멤버 함수들을 가지고 있어 이 클래스만으로도 응용프로그램을 구성 할 수 있는 CObject 클래스를 가정하자.

윈도우 메시지에 대응되는 핸들러는 이 클래스를 상속받은 CView 클래스 내에서 구현한다.


( 누르면 크게보인다. )


CView 클래스는 static 멤버로 메시지맵, 즉 어떤 윈도우 메시지와, 어떤 핸들러가(WM_CREATE - OnCreate) 대응되는지에 대한 맵을 가지고 있다.


아래는 CObject.h

01: 
02: #include <windows.h>
03: #include <tchar.h>
04:
05: #ifndef SIMPLE_COBJECT_H
06: #define SIMPLE_COBJECT_H
07:
08: LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);
09:
10: class CObject
11: {
12: protected:
13: static TCHAR szAppName[];
14: HWND m_hwnd;
15: MSG m_msg;
16: WNDCLASSEX m_wndclass;
17:
18: public:
19: void InitInstance(HINSTANCE hInstance, LPSTR szCmdLine, int iCmdShow);
20: void Run(void);
21: WPARAM ExitInstance(void);
22: };
23:
24:
25:
26: #endif

아래는 CObject.cpp


01: 
02: #include "CObject.h"
03: #include "CView.h"
04:
05: TCHAR CObject::szAppName[] = _T("Sample Program");
06:
07: extern CView app;
08:
09: static CViewFuncPointer fpCViewGlobal;
10:
11:
12: void CObject::InitInstance(HINSTANCE hInstance, LPSTR szCmdLine, int iCmdShow)
13: {
14: m_wndclass.cbSize = sizeof(m_wndclass);
15: m_wndclass.style = CS_HREDRAW | CS_VREDRAW;
16: m_wndclass.lpfnWndProc = WndProc;
17: m_wndclass.cbClsExtra = 0;
18: m_wndclass.cbWndExtra = 0;
19: m_wndclass.hInstance = hInstance;
20: m_wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
21: m_wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
22: m_wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
23: m_wndclass.lpszMenuName = NULL;
24: m_wndclass.lpszClassName = szAppName;
25: m_wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
26:
27: RegisterClassEx ( &m_wndclass );
28:
29: m_hwnd = CreateWindow ( szAppName, _T("Simple"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
30: CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
31:
32: ShowWindow(m_hwnd, iCmdShow);
33: UpdateWindow(m_hwnd);
34: }
35:
36: void CObject::Run(void)
37: {
38: while ( GetMessage(&m_msg, NULL, 0, 0))
39: {
40: TranslateMessage(&m_msg);
41: DispatchMessage(&m_msg);
42: }
43: }
44:
45: WPARAM CObject::ExitInstance(void)
46: {
47: return m_msg.wParam;
48: }
49:
50: int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in LPSTR lpCmdLine, __in int nShowCmd )
51: {
52: app.InitInstance(hInstance, lpCmdLine, nShowCmd);
53: app.Run();
54: return app.ExitInstance();
55: }
56:
57: LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
58: {
59: UINT uMessage = 0;
60:
61: while ( CView::messageMap[uMessage].iMsg != 0 )
62: {
63: if ( iMsg == CView::messageMap[uMessage].iMsg )
64: {
65: fpCViewGlobal = CView::messageMap[uMessage].fp;
66: (app.*fpCViewGlobal)();
67: return 0;
68: }
69:
70: uMessage++;
71: }
72:
73: return DefWindowProc(hwnd, iMsg, wParam, lParam);
74: }

CObject.cpp 내부에 WinMainWndProc 를 넣고, CView.cpp 에 전역으로 선언된 CView app; 인스턴스를 extern 키워드를
이용해서 얻었다. 주의해야할 부분은 WndProc 인데, CView 내부에 존재하는 메시지맵을 사용해 WndProc 를 자동화 함으로써

프로그램에 이벤트 핸들러가 추가되더라도 WndProc 를 건드릴 필요가 없어졌다.

아래는 CView.h 의 소스다.


01: 
02: #ifndef SIMPLE_CVIEW_H
03: #define SIMPLE_CVIEW_H
04:
05: #include <windows.h>
06: #include <tchar.h>
07:
08: class CView;
09:
10: typedef void (CView::*CViewFuncPointer)();
11: typedef struct tagMessageMap {
12: UINT iMsg;
13: CViewFuncPointer fp;
14: } MessageMap;
15:
16: class CView : public CObject
17: {
18:
19: public:
20: //Message Map
21: static MessageMap messageMap[];
22:
23: public:
24: //Event Handler
25: void OnCreate(void);
26: void OnDraw(void);
27: void OnDestroy(void);
28: void OnLButtonDown(void);
29: };
30:
31:
32: #endif







아래는 CView.cpp 의 소스


01: #include "CObject.h"
02: #include "CView.h"
03:
04: CView app;
05:
06: MessageMap CView::messageMap[] = {
07: {WM_CREATE, &CView::OnCreate},
08: {WM_PAINT, &CView::OnDraw},
09: {WM_DESTROY, &CView::OnDestroy},
10: {WM_LBUTTONDOWN, &CView::OnLButtonDown},
11: {0, NULL }
12: };
13:
14: void CView::OnCreate()
15: {
16:
17: }
18:
19: void CView::OnDraw()
20: {
21: PAINTSTRUCT ps;
22: RECT rect;
23: HDC hdc;
24:
25: hdc = BeginPaint(m_hwnd, &ps);
26: GetClientRect(m_hwnd, &rect);
27: DrawText(hdc, _T("Hello Windows!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
28: EndPaint(m_hwnd, &ps);
29: }
30:
31: void CView::OnDestroy()
32: {
33: PostQuitMessage(0);
34: }
35:
36: void CView::OnLButtonDown(void)
37: {
38: MessageBox(m_hwnd, _T("Button Down!"), _T("Hello"), MB_OK);
39: }

CObject.cpp 를 보면 알겠지만, 윈도우 프로시저와 메인 함수가 CObject 내부에 존재하므로 더이상 WndProc WinMain 신경쓸 필요가 없다.

사용자는 단지 CView.h 에 원하는 핸들러를 선언하고, CView.cpp 메시지맵에 메시지와 핸들러를 추가한 후 CView.cpp 에 구현하기만 하면 된다.


다음시간에는 매크로를 이용해서 메시지맵을 더욱 더 간단하게 표현해 보자.



참고 - MFC 구조와 원리 p.218 ~ 223

덧글

댓글 입력 영역


시계

라운드 시계

위키피디아