코딩하는 나귀

저자 : hanburn

날짜 : 2007.08.18

환경 : WTL7.5, VS-2003

 

 

지난시간에 WM_XXX 메시지 핸들러를 알아 보았는데, 이번에는 나머지에 대해서 알아보자.

일단 WM_COMMAND 메시지에 해당하는 핸들러로 COMMAND_HANDLER 가 있다.

 

 

BEGIN_MSG_MAP(CListDlg)

        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel)

        COMMAND_HANDLER(IDOK, BN_CLICKED, OnBnClickedOk)    

END_MSG_MAP()

 

 

COMMAND_HANDLER 3개의 인자를 받는데, 각각 id, code, func 이다. Id 어떤 컨트롤(혹은 매뉴 id)에서 발생된 것인지를 나타내고, code 명령의 세부사항이다. code 각각 컨트롤 마다 다르기 때문에 이것또한 (MFC 비해서)번거로운 일이다. 일일이 msdn 참고해야 하기 때문이다. Win32 코딩을 했던 사람들은 익숙하겠지만, MFC 개발자에게는 낯설 수가 있다.

COMMAND_HANDLER 정의를 한번 보도록 하자.

 

#define COMMAND_HANDLER(id, code, func) \

if(uMsg == WM_COMMAND && id == LOWORD(wParam) && code == HIWORD(wParam)) \

{ \

        bHandled = TRUE; \

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

        if(bHandled) \

               return TRUE; \

}

 

별로 특별한 것인 없다. ㅋㅋ

 

유사한 것으로는 COMMAND_ID_HANDLER COMMAND_CODE_HANDLER가 있는데, 차이점은 이름에서 알 수 있듯이 전자는 ID에 매칭이 되고, 후자는 CODE에 매칭이 되는 핸들러 이다. 각각의 매크로 정의를 보면 쉽게 알 수 있다.

 

 

#define COMMAND_ID_HANDLER(id, func) \

if(uMsg == WM_COMMAND && id == LOWORD(wParam)) \

{ \

        bHandled = TRUE; \

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

        if(bHandled) \

               return TRUE; \

}

 

#define COMMAND_CODE_HANDLER(code, func) \

if(uMsg == WM_COMMAND && code == HIWORD(wParam)) \

{ \

        bHandled = TRUE; \

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

        if(bHandled) \

               return TRUE; \

}

 

쉽게 설명하면 COMMAND_ID_HANDLER 는 코드는 무시하고 ID가 매칭되면 해당 핸들러를 호출해주는 것이고 COMMAND_CODE_HANDLER ID는 무시하고 코드가 매칭되면 해당 핸들러를 호출해주는 것이다. 3개를 섞어서 사용하다 보면 이상한 경우가 생길수가 있다. 아래의 예를 보면..

 

BEGIN_MSG_MAP(CListDlg)

        COMMAND_HANDLER(IDCANCEL, BN_CLICKED, OnBnClickedCancel)

        COMMAND_ID_HANDLER(IDOK, OnCommandOK)

        COMMAND_CODE_HANDLER(BN_CLICKED, OnCommandBnClicked)

ENG_MSG_MAP

 

위의 설명에 앞서서 메시지 맵에 들어있는 매크로의 순서대로 위에서부터 검사를 한다는 것을 알아야 한다. 위에서 cancel 버튼을 클릭한다고 하면 위에서부터 검사해서 첫번째에 걸리므로 OnBnClickedCancel이 호출되고, OK 버튼을 클릭했을때는 두번째에 걸리므로 OnCommandOK가 호출이 되는 것이다. 다른 버튼이 있다고 할 때, 그 버튼을 클릭하면 세 번째에 걸리므로 OnCommandClicked가 호출이 되는 것이다.

 

COMMAND 메크로 중에서 범위를 지정하는 것은 2개밖에 없다. COMMAND_RANGE_HANDLER COMMAND_RANGE_CODE_HANDLER 이렇게 두개가 있는데, 전자는 ID 범위를 받고, 후자는 코드 범위를 받는다.

 

 

그럼 이제 NOTIFY에 대해서 알아보도록 하자. NOTIFY 핸들러는 WM_NOTIFY메시지를 처리하는데 WM_COMMAND에서 NOTIFY로만 변경된거 외에는 메시지 핸들러는 똑같다. 똑같이 NOTIFY_HANDLER, NOTIFY_ID_HANDLER, NOTIFY_CODE_HANDLER, NOTIFY_RANGE_HANDLER, NOTIFY_RANGE_CODE_HANLDER 가 존재한다. 다른점은 핸들러 함수의 파라미터가 다르다.

 

BEGIN_MSG_MAP(CListDlg)

NOTIFY_HANDLER(IDC_FILE_LIST, NM_CLICK, OnNMClickFileList)

END_MSG_MAP()

 

LRESULT OnNMClickFileList(int /*idCtrl*/, LPNMHDR pNMHDR, BOOL& /*bHandled*/);

 


두번째 파라미터가 각각의 상황별로 포인터 변형되어 사용되는 것을 다 알 것 이니까,
이제는 MFC와 좀 다른 것(?) 들을 알아보도록 하자.

 

BEGIN_MSG_MAP(CListDlg)

REFLECT_NOTIFICATIONS()

        CHAIN_MSG_MAP(CBaseClass)

END_MSG_MAP()

 

위와 나온 매크로도 자주 쓰이는 것들이니까, 알아 두어야 한다. 먼저 CHAIN_MSG_MAP은 말그대로 메시지 맵에 체인을 걸어주는 것이다. 무슨말인고 하면,  CListDlg가 다른 클래스로부터 상속된 클래스라고 하면 베이스 클래스로 메시지 체인을 걸어주게 되는 것이다. CHAIN_MSG_MAP() 매크로가 맨 위에 있다면 메시지들은 일단 베이스 클래스에서 해당 메시지의 핸들러가 있는지 확인을 하고 없으면, CListDlg에서 처리를 하게되고 반대로 CHAIN_MSG_MAP 매크로가 메시지 맵의 마지막에 있으면, CListDlg에서 처리하지 않은 메시지 들에 대해서 베이스 클래스로 전달을 하는 역할을 하게 된다. 이 매크로는 상속관계에서 많이 쓰이고, Mix-in Template 클래스를 사용할 때도 사용해야 한다.

 

다음으로 REFLECT_NOTIFICATIONS() 매크로는 이름에서도 알수 있듯이 NOTIFY reflet 해주는 역할이다. 차일드 컨트롤 윈도우에서 발생하는 notify 메시지에 대해서 차일드 윈도으로 다시 보내주는 역할을 하는 것이다. MFC에서는 =NM_ 계열의 핸들러라가 있는 것을 알것이다. 이것이 바로 notify 메시지를 차이드가 자체적으로 처리하는대, WTL에서는 부모에서 위의 메크로를 써주어야 차일드에서 notify를 처리할 수 있는 것이다.

읽어 주셔서 감사합니다~