코딩하는 나귀

WTL로 프로그래밍하기#4 – Message Map(1)

 

저자 : hanburn

날짜 : 2007.08.17

환경 : WTL7.5, VS-2003

 

 

 

WTL에서 메시지 처리는 MFC와 유사하게 메시지 매크로를 이용하는데, 조금 다른 점이 라면 메시지 매크로가 헤더파일에 있다는 점입니다. 위치만 변경된 것이므로 뭐 크게 다를건 없구요..

메시지 맵은 BEGIN_MSG_MAP(클래스이름) / END_MSG_MAP() 매크로를 사용하여 MFC와 유사하게 처리합니다.

 

WTL에서는 메시지 종류에 따라서 크게 3가지로 분류하여 처리를 합니다.

 

WM_COMMAND

WM_NOTIFY

WM_계열의 일반 메시지

 

그럼 하나하나씩 알아보도록 하겠습니다. 먼저 일반 메시지 처리부터 살펴보도록 하죠.. WM_ 계열의 일반 메시지는 MESSAGE_HANDLER() 매크로를 이용하여 처리를 합니다. 함수 원형은 다음과 같다.

 

BEGIN_MSG_MAP(CMyDlg)

           MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

END_MSG_MAP()

 

LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

 

 

일반적인 메시지 핸들러 함수는 위에처럼 4개의 인자를 가지고 있는데, 첫번째 uMsg는 처리하는 메시지에 해당하구요, wParam, lParam은 각 메시지의 전달되는 값 그대로 이고, 마지막으로 bHandled는 해당 핸들러에서 메시지를 처리했는지 않했는지를 표시해준다. 예를 들어서 해당 메시지를 처리하고나서 ATL의 기본 WindowProc() 함수에서 해당 메시지를 처리하게 하려면 bHandled 값을 FALSE로 설정하면 된다. 이게 무슨 말인가?? 쉽게 설명하면 다음의 예를 보자

 

// MFC 버전

void CMyDialog::OnMouseMove(UINT nFlags, CPoint point)

{

        ...     // 어떤처리

        __super::OnMouseMove(nFlags, point);

}

// WTL 버전

LRESULT OnMouseMove (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

        ...     // 어떤처리

        bHandled = FALSE;

}

 

bHandle FALSE로 해주면 WM_MOUSEMOVE 메시지를 CMyDialog에서 처리를 않했다고 표시하는 것이다. 그래서 WM_MOUSEMOVE 메시지가 WindowProc으로 전달이 되는 것이다. MFC 에서는 WM_MOUSEMOVE 이벤트를 처리하고 베이스 클래스의 OnMouseMove를 호출해주는 것과 비요해서, WTL에서는 베이스 클래스의 가상함수를 호출하는 역활을 bHandled 변수가 담당하게 되는 것이다.

그럼 좀더 자세하게 MESSAGE_HANDLER의 정의부를 살펴보자.

 

#define MESSAGE_HANDLER(msg, func) \

if(uMsg == msg) \

{ \

        bHandled = TRUE; \

        lResult = func(uMsg, wParam, lParam, bHandled); \

        if(bHandled) \

               return TRUE; \

}

 

역시나 정의된 것을 직접 보는 게 제일 편하다. ㅋㅋ 해당 함수를 호출하기 전에 bHandled true로 설정하고 함수를 호출하고 나서 bHandled를 체크하여 return TRUE를 해주고 있구나. 그렇다면 WTL에서는 LRESULT 값이 무시가 되는 건가? 그렇다 현재 7.5 버전에서는 무시를 하고 있다. (END_MSG_MAP 정의 부분을 보면 알 수 있다.)

 

그리고 정의부를 본 김에 BEGIN_MSG_MAP과 함께 전체를 풀어보면 아래와 같다.

 

// BEGIN_MSG_MAP() 부분

public: \

BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) \

{ \

        BOOL bHandled = TRUE; \

        (hWnd); \

        (uMsg); \

        (wParam); \

        (lParam); \

        (lResult); \

        (bHandled); \

        switch(dwMsgMapID) \

        { \

        case 0:

 

// MESSAGE_HANDLER()부분

if(uMsg == msg) \

{ \

                bHandled = TRUE; \

                lResult = func(uMsg, wParam, lParam, bHandled); \

                if(bHandled) \

                        return TRUE; \

}

 

// END_MSG_MAP 부분

        break; \

        default: \

        ATLTRACE(ATL::atlTraceWindowing, 0, _T("Invalid message map ID (%i)\n"), dwMsgMapID); \

        ATLASSERT(FALSE); \

        break; \

        } \

return FALSE; \

}

 

이렇게 메시지 메크로를 풀어보니까 그냥 메시지에 따라서 if문으로 구성되어 있는 간단한 처리방식인데, 사용하기 편하게 매크로로 정의를 한 것 뿐이다. 요건 MFC도 마찬가지다. ^^

 

WTL에서는 MFC와 다르게 메시지를 unpack을 않해주고 있기 때문에, 직접 WPARAM, LPARAM에서 원하는 정보를 추출하여 사용하여야 한다. 이것이 조금 불편한 점이다. <atlcrack.h> 헤더파일을 추가해주면 MFC와 비슷하게 전달되는 파라미터를 각각의 메시지에 맞게 변환해준 값을 전달해 주는데, 이것도 조금 불편하다. 그 이유를 설명하면, 다음의 unpack을 사용하는 WTL의 메시지 매크로 모습을 보자.

 

BEGIN_MSG_MAP_EX(CMiniNewsWnd)

        MSG_WM_CREATE(OnCreate)

        MSG_WM_LBUTTONUP(OnLButtonUp)

        MSG_WM_MOUSEMOVE(OnMouseMove)

END_MSG_MAP()

 

LRESULT        OnCreate(LPCREATESTRUCT lpcs);

void           OnLButtonUp(UINT nID, CPoint pt);

void           OnMouseMove(UINT nFlag, CPoint pt);

 

이것만 보면 어랏! MFC와 똑같네~’ 라고 할 수 있는데, 맞다 똑같다. 우리가 쓰던인자 그대로 뽑아서 넘겨준다. 그런데 왜 불편하다는 거야? 라고 물어보면 대답은 다음과 같다. MFC에서는 해당 메시지마다 위자드가 자동으로 핸들러를 추가해주지만, WTL에서는 위자드를 통해서 핸들러를 추가하게 되면 기본적인 MESSAGE_HANDLER 형식으로 추가를 해주기 때문이다. MSG_WM_ 형식은 수동으로 추가를 해야 하는데, 누가 각각의 메시지의 파라미터를 다 외우고 다니나? 그래서 추가를 하려면, 해당 MSG_WM_XXX의 정의를 보고 추가해주어야 한다. 이때 또 난감한 것이 있으니 다음과 같은 경우이다.

 

 

#define MSG_WM_SETCURSOR(func) \

if (uMsg == WM_SETCURSOR) \

{ \

        SetMsgHandled(TRUE); \

        lResult = (LRESULT)func((HWND)wParam, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam)); \

        if(IsMsgHandled()) \

               return TRUE; \

}

 

WM_SETCURSOR 핸들러를 추가하기 위해서 MSG_WM_SETCURSOR의 정의를 찾아본다. 하지만, 두번째, 세번째 인자는 lParam의 low/hi 워드를 표기 사는데 이것의 의미를 알기 위해서는 msdn을 참고해야 한다. , 각각 메시지 마다 파라미터의 의미를 대 외우지 않으면 msdn을 찾아야 하는 것은 필수이기 때문이다.

따라서 MESSAGE_HANDLER MSG_WM_XXX 중에서 맘에 드는 것을 선택적으로 사용하기 바란다. 참고로 필자는 두 개를 섞어서 사용한다.

 

그리고 MESSAGE_RANGE_HANDLER로 있다. 메시지의 범위를 처리하는 것인데, 사용자 정의 메시메시지 처리할 때 유용하다. 뭐 이건 MFC를 사용해 본 사람은 다 알고 있으리라 생각하고 넘어간다.

 

오늘은 여기서 짜르고 다음 시간에 나머지 부분에 대해서 알아보도록 하겠습니다.
읽어 주셔서 감사합니다~


PS: 이 글에대해서 링크 및 퍼가는 것을 허용합니다. 그러나 꼭 출처를 밝혀 주시기 바랍니다.