CAFE

강좌 & 팁

MFC 메세지맵을 대체하는 이벤트맵 만들기

작성자저격수|작성시간06.09.22|조회수812 목록 댓글 0
현재 모든 프로그래밍 언어들 중에 Windows 메세지 처리에 가장 강력한 언어를 들자면 아마도 C#을 꼽을 수 있을 것입니다. 가령 C#에서 이벤트 매핑을 처리하는 코드는 다음 과 같이 작성될 것입니다. EventClass ec = new EventClass(); ec.myevent += new MyDelegate(EventHandler); 이렇게 C# 언어에서는 MFC와는 달리 메세지 맵 매크로를 작성하는 대신에 += 연사자를 사용하여 이벤트 매핑을 하게 됩니다. 단연 불안정한 매크로에 의존하는 MFC보다는 C# 의 처리가 훨씬 간결하고 안정적이라는 것은 의심할 여지가 없습니다. 바로 이 점에 착안하여 MFC에서도 메세지를 매핑시키는 연산자를 오버로딩하여 C#처럼 구현할 방법이 없을까?함 강구해 봤습니다요~^^;; 물론 이러한 방법은 MFC의 모든 메세 지 맵을 대체하지는 않습니다 그 이유는 만약 굳이 이러한 모든 메세지까지 처리하고자 한다면 불가능한 것은 아니지만 메세지 처리 속도가 늦어지고 MFC의 메세지 크랙의 효과를 볼 수 없게되어 사실상 실속이 없 어지게 되기 때문입니다 하지만 C#이나 비쥬얼 베이직의 Windows Form 기반 프레임에 해당하는 MFC 다이얼로그 프로젝트에서 차일드 메세지 의 처리에서는 상당한 위력을 발휘하게 됩니다 즉, WM_COMMAND 메세지로 처리되는 메뉴, 악셀레이터 그리고 무엇보다도 다이얼로그에 부착한 차일드 컨트롤의 메세지를 대단히 성공적으로 처리하게 됩니다. 하지만 다이얼 로그 자신의 고유한 메세지는 기존의 메세지 매핑에 의존해야 함은 당연합니다 따라서 이들 WM_COMMAND 계열의 콘트롤 메세지는 따로이 가령 eventmap.cpp 파일로 분리하여 EventMap 클래스의 오버로딩 연산자를 사용하여 이벤트 매핑과 이벤트 핸들러를 함께 작성해 주면 훨씬 코드 관리가 수월해지게 됩니다 이상 설명한 효과를 가능케 하는 EventMap 클래스의 구현 원리는 일단 struct EVENT { WPARAM wParam; // WM_COMMAND의 wParam 값을 저장. int (T::*)(void) OnEvent; // 해당 메세지 처리 함수의 포인터. }; 로 구성되는 inner 클래스를 선언하여 이 구조체를 저장하는 이벤트 테이블을 생성하여 여기에 오버로딩 연산자를 사용할 때마다 해당 이벤트를 탑재 시킵니다. 그리고 실행시 에는 이 이벤트 테이블을 마치 메세지 큐를 읽는 것처럼 읽어서 해당 이벤트 핸들러의 함수 포인터를 호출하여 가동시킵니다. 물론 이렇게 하면 약간의 속도 저하를 초래할수 있습니다 하지만 우리가 알다시피 WM_COMMAND 메세지로 처 리되는 차일드 콘트롤은 아무리 빨리 클릭한다 해봐야 일초에 몇번 밖에 메세지가 발생하지 않습니다 그렇지만 다이얼로그 자신의 메세지는 초당 수십 수백번 씩 발생하는 경우도 있기에 위에서 지적한 것처럼 눈물을 머금고 구 현을 포기할 수 밖에 없는 것입니다. 일단 이렇게 구현하게 되면 가령 m_eventMap[IDC_MY_BUTTON] + BN_CLICKED = OnMyButton_Clicked; 이러한 코드를 OnInitDialog()에서 메세지 맵 대신 작성하면 됩니다. 물론 위 표현식에 서 r-value인 OnMyButton_Clicked 는 함수 포인터이며 따라서 메세지를 처리하는 함 수명을 그대로 써주면 그만인 것입니다. 자세한 것은 소스를 참고하시길... http://myhome.naver.net/ziralist/Data/EventMap.h //////////////////////////////////////////////////////////////////// // // 작성자: 저격수 // CopyLeft(C++) 2006 All Rights Dedicated To Gotomaki. // // ※불펌금지: 이 소스는 모닝구 무스메의 슈퍼스타 고토마키를 // 사랑하거나 C++을 사랑하는 모든 프로그래머들이 지맘대로 아 // 무런 제약없이 사용하실 수 있습니다요~^^;; ㅋㅋ // //////////////////////////////////////////////////////////////////// /*/////////// 소스 사용법(Copy and Paste 신공 필수) /////////////////// 1. class T 의 선언부에 아래 석줄을 밀어 넣으셈~ KGB::CEventMap m_eventMap; void MappingCommandEvent(void); virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); 2. class T 함수 정의에서 가상함수 WindowProc() 오버라이딩을 추가. LRESULT T::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { int nResult = m_eventMap.CommandProcess(message, wParam); return KGB_EVENT_MAP(CDialog, message, wParam, lParam, nResult); } 3. T() 생성자 함수에서 CEventMap를 객체 T에 Attach() 시킴. m_eventMap.Attach(this); 4. T::OnInitDialog()에다 아래 한줄을 넣는다. this->MappingCommandEvent(); ////////////////////////////////////////////////////////////////////*/ #pragma once #include #define KGB_MSG #define KGB_EVENT_MAP(TBase, message, wParam, lParam, nResult) \ (KGB::MFC == nResult) ? TBase ## ::WindowProc(message, wParam, lParam): \ (KGB::WND == nResult) ? TBase ## ::DefWindowProc(message, wParam, lParam): \ (KGB::API == nResult) ? \ ::DefWindowProc(this->m_hWnd, message, wParam, lParam): KGB::END; namespace KGB { enum MAP_FLAG { NOT_INDEX = -1, END, MFC, WND, API }; template class CEventMap { typedef int (T::* ONEVENT)(void); struct EVENT { WPARAM wParam; ONEVENT OnEvent; }; public: // Mapping Operator Overloading CEventMap& operator[] (WPARAM wID) { ASSERT(m_pThis); ASSERT(!m_event.wParam); ASSERT(!m_event.OnEvent); m_event.wParam |= wID; return *this; } CEventMap& operator+ (WPARAM wCode) { ASSERT(m_pThis); ASSERT(!m_event.OnEvent); m_event.wParam |= wCode << 16; return *this; } void operator= (ONEVENT OnEvent) { ASSERT(m_pThis); ASSERT(m_event.wParam); ASSERT(!m_event.OnEvent); m_event.OnEvent = OnEvent; int nFind = ScanExcludingTable(m_event); if(NOT_INDEX == nFind) m_eventTable.Add(m_event); else m_eventTable[nFind] = m_event; ::ZeroMemory(&m_event, sizeof(m_event)); } protected: int ScanExcludingTable(const EVENT& event) { ASSERT(m_event.wParam); ASSERT(m_event.OnEvent); int nFind = NOT_INDEX; for(int i = 0; i < m_eventTable.GetCount(); i++) { if(m_eventTable[i].wParam == event.wParam) { if(NOT_INDEX == nFind) nFind = i; else m_eventTable.RemoveAt(i); // remove other same events. } } return nFind; } public: // Other Fundamental Operations int CommandProcess(UINT message, WPARAM wParam) const { ASSERT(m_pThis); return (WM_COMMAND == message) ? ExecuteCommand(wParam): MFC; } int ExecuteCommand(WPARAM wParam) const { ASSERT(m_pThis); for(int i = 0; i < m_eventTable.GetCount(); i++) { if (m_eventTable[i].wParam == wParam) return (m_pThis->*m_eventTable[i].OnEvent)(); } return MFC; } void Attach(T* pThis) { m_pThis = pThis; } CEventMap() { m_pThis = NULL; m_event.wParam = NULL; m_event.OnEvent = NULL; } virtual ~CEventMap() {} protected: T* m_pThis; EVENT m_event; CArray m_eventTable; }; } ////////////////////////////////////////////////////////////////////////////// // 그럼 이제부터 EventMap 클래스의 사용 방법을 설명하겠습니다. 캬캬~ ////////////////////////////////////////////////////////////////////////////// 1. 다운받은 EventMap.h 파일을 다이얼로그 프로젝트에 추가시킨다. 2. CXXXDialog() 클래스 선언부에 CEventMap m_eventMap 멤버 객체를 추가한다. 그리고 CXXXDialog() 생성자에다 m_eventMap.Attach(this) 를 처밀어 넣는다~ ㅡㅡ;; 3. 그리고 CDialog::WindowProc() 가상함수를 오버라이딩 한다. ※ CXXXDialog 클래스 선언에 추가. virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); ※ CXXXDialog 클래스 정의 코드는 천편일률적으로 다음과 같이 작성함. LRESULT CXXXDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { int nResult = m_eventMap.CommandProcess(message, wParam); return KGB_EVENT_MAP(CDialog, message, wParam, lParam, nResult); } 4. 직접 OnInitdialog() 대신에 따로이 eventMap.cpp 파일에 MappingCommandEvent() 라는 임의의 함수를 작성하여 그 속에 일률적으로 밀어 넣으면 편리하다는...ㅡ_ㅡ;; 일단 CXXXDialog 선언부에 아래의 코드를 추가한다 void MappingCommandEvent(void); 그리고 CXXXDialog::OnInitDialog() 에다 아래 한줄을 넣는다. this->MappingCommandEvent(); 5. 그런 다음에 eventMap.cpp 파일에다 MappingCommandEvent()함수 속에 일률적 으로 이벤트 매핑 표현식을 작성한다. 일케 다이얼로그 자신의 메세지와 분리하여, 메뉴, 악셀레이터 그리고 차일드 콘트롤 이벤트 핸들러를 eventMap.cpp 파일에다 일률적으로 처모아 작성하면 코드 관리가 훨씬 쉬워질지도 모른다는...^^;; 참~ 그리고 일케 표현식으로 매핑하게 되면 런타임시에 메세지 매핑을 실시간으로 바 꿀 수 있게됩니다. 궁금하신 분은 같이 아래 예제 소스를 참조하시길...ㅡ.ㅡ;; http://myhome.naver.net/ziralist/Data/Example.zip
다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼