[강좌/메시지] WM_NOTIFY 메시지에 대해서...(ON_NOTIFY) - 1편
출처: http://www.tipssoft.com/bulletin/tb.php/FAQ/63
WM_NOTIFY 메시지는 우리가 프로그램하면서 많이 사용하는 메시지 중에 하나입니다.:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
하지만 그 용법이나 의도를 제대로 이해하지 못하고 사용하는 사람들이 많아서
MSDN의 Technical note 61번의 내용을 근거로 간단한 소개글을 만들었습니다.
1. 통보(Notify) 메시지의 필요성
WM_NOTIFY 메시지는 일반적으로 자식윈도우(예, 컨트롤-버튼,에디트박스, ...)가 자신에게
일어난 여러가지 상황을 부모윈도우(예, 대화상자)에게 전달할때 사용합니다.
예를 들어, 대화상자에 버튼을 하나 만들었다고 하면 해당 버튼을 눌렀을때 WM_COMMAND 라는
메시지가 발생합니다. 이 메시지는 단순한 통보(notify)를 목적으로 구성되었기 때문에
WPARAM에 BN_CLICKED(HI WORD)와 해당 컨트롤의 ID(LOW WORD)가 저장되고
LPARAM에 해당 버튼 컨트롤의 핸들이 저장된 상태로 전달됩니다.
위 구성에서 본것처럼 이미 WPARAM, LPARAM을 모두 사용해버렸기 때문에 버튼 컨트롤 입장에서는
추가적인 정보(마우스 클릭 좌표와 같은 정보)를 전달하고 싶어도 전달할 방법이 없습니다.
컨트롤의 종류가 다양해지고 요구사항이 복잡해짐에 따라서 윈도우즈 시스템은 추가적인 정보를
포함할수 있는 메시지가 필요하게 되었고 아래와 같은 메시지가 추가되었습니다.
WM_CTLCOLOR, WM_VSCROLL, WM_HSCROLL, WM_DRAWITEM, WM_MEASUREITEM,
WM_COMPAREITEM, WM_DELETEITEM, WM_CHARTOITEM, WM_VKEYTOITEM, ...
2. Win32 시스템에서의 통보 메시지에 대하여...
Win32 시스템은 하위 시스템의 호환성을 유지하기 위해 기존 Windows 3.x에서 사용하던 메시지들을
대부분 수용하고 있습니다. 즉, 위에서 언급한 메시지들은 모두 사용할수 있습니다.
그리고 프로그램이 복잡해지고 컨트롤이 다양해짐에 따라, 이미 많은 메시지가 있음에도 불구하고,
메시지 추가에 대한 요구 사항이 늘어나게 되었고 윈도우즈 시스템은 필요할때마다 WM_ 메시지를
추가해서 문제를 해결하는데 한계가 있다는것 알고 통보 메시지에 대한 새로운 표준을 정했고
그것이 WM_NOTIFY 메시지로 구체화 되었습니다. WM_NOTIFY 메시지의 형식은 아래와 같습니다.
WM_NOTIFY 메시지의 LPARAM에 사용된 구조체의 형식은 아래와 같습니다.
typedef struct tagNMHDR {
HWND hwndFrom;
UINT idFrom;
UINT code;
} NMHDR;
NMHDR 구조체를 보시면 아시겠지만 항목이 너무 간단해서 다양한 정보를 수용할만한 구조가
아닙니다.
그렇다면 어떻게 컨트롤의 다양한 정보를 이 구조체를 이용해서 전달할수 있을까요?
그건 LPARAM에 전달되는 정보의 형식을 살펴보면 의문이 풀립니다.
LPARAM에 저장된 정보는 NMHDR 구조체의 주소입니다. 즉, 구조체의 크기가 한정되지 않다는
뜻입니다. WM_NOTIFY 메시지는 전달되는 정보의 시작형식을 NMHDR 구조체 형식으로 정한것이지
꼭 저 구조체를 사용하라는 뜻이 아닙니다.
예를 들어, 자신이 컨트롤을 하나 만들었고 해당 컨트롤이 x, y 좌표정보를 부모에게 통보해야 한다면
아래와 같이 구조체를 정의하고 사용하시면 됩니다.
typedef struct tagMyNotifyData {
HWND hwndFrom; // 변경불가
UINT idFrom; // 변경불가
UINT code; // 변경불가
// 자신이 추가하고 싶은 정보를 추가하면 됩니다.
int x_pos;
int y_pos;
} MyNotifyData;
위 구조체를 아래와 같이 선언해도 마찬가지 입니다.
typedef struct tagMyNotifyData {
NMHDR hdr; // 변경불가
// 자신이 추가하고 싶은 정보를 추가하면 됩니다.
int x_pos;
int y_pos;
} MyNotifyData;
WM_NOTIFY 관련 처리기는 LPARAM으로 전달되는 메시지의 시작형식만 NMHDR 구조를 만족하면
아무런 문제 없이 동작하도록 구성되어있기 때문에, 자신이 정의하는 구조체의 시작 부분만 정확하게
NMHDR 구조체 형식으로 구성해주시면 됩니다.
대부분의 통보 메시지를 사용하는 컨트롤들은 자신의 정보를 전달하기 위해서 NMHDR 구조체보다
더 복잡한 형태의 구조체를 사용할 것입니다. ( 툴팁 컨트롤이 통보하는 메시지 중에 TTN_SHOW,
TTN_POP 같은 메시지는 특별한 추가 정보가 없어서 NMHDR 구조를 그대로 사용하고 있습니다. )
WM_NOTIFY 메시지는 통보 메시지의 대표 메시지이고, 자신이 전달하고 싶은 통보 메시지 코드는
NMHDR 구조체의 code 항목에 저장해서 전달하면 된다. 따라서 받는 쪽에서는 해당 통보 메시지가
어떤 구조체인지는 모르지만 구조체의 앞부분이 NMHDR 구조체와 형식이 같기 때문에 일단 해당
주소를 NMHDR 구조체로 형변환(casting)을 한 후에 code 항목을 체크하여 어떤 통보 메시지인지를
구분하고 다시 정확한 구조체로 형변환(casting)을 해서 사용하게 됩니다.
예를들어, 자신이 위에서 이야기한 MyNotifyData 구조체를 사용했다고 가정하고 code에 MN_DATA
라는 메시지 코드를 넣어서 부모 윈도우로 WM_NOTIFY 형식으로 전달했다면 받는 쪽에서는 아래와
같이 처리할 것입니다.
// 통보 메시지는 일괄적으로 NM_NOTIFY 로 전달되기 때문에 어떤 통보메시지 인지를 확인해야
// 한다.
// 따라서 LPARAM 에 전달된 주소를 NMHDR로 형변환해서 code값을 확인해야 한다.
NMHDR *p_hdr = (NMHDR *)lParam;
if( p_hdr->code == MN_DATA ){
// 전달된 통보 메시지가 MN_DATA인 경우
MyNotifyData *p_my_data = (MyNotifyData *)lParam;
// p_my_data 를 사용...
} else {
// 다른 통보 메시지인지를 체크
}
3. WM_NOTIFY를 사용하는 컨트롤에서 공통적으로 사용되는 메시지들에 대하여..
통보 메시지 코드 | 통보 내용 |
NM_CLICK | 사용자가 해당 컨트롤에 마우스 왼쪽 버튼을 클릭한 경우 발생 |
NM_DBLCLK | 사용자가 해당 컨트롤에 마우스 왼쪽 버튼을 더블 클릭한 경우 발생 |
NM_RCLICK | 사용자가 해당 컨트롤에 마우스 오른쪽 버튼을 클릭한 경우 발생 |
NM_RDBLCLK | 사용자가 해당 컨트롤에 마우스 오른쪽 버튼을 더블 클릭한 경우 발생 |
NM_RETURN | 해당 컨트롤이 포커스를 가지고 있는 상태에서 ENTER 키를 누른 경우 발생 |
NM_SETFOCUS | 해당 컨트롤이 입력 포커스를 획득한 경우 발생 |
NM_KILLFOCUS | 해당 컨트롤이 입력 포커스를 잃어버린 경우 발생 |
NM_OUTOFMEMORY | 해당 컨트롤이 메모리 부족으로 명령 수행에 실패한 경우 발생 |
[ 2 편에서 계속하겠습니다. ]
'mfc & winAPI' 카테고리의 다른 글
마우스 이벤트 중복 클릭 방지 함수 (0) | 2017.05.29 |
---|---|
[강좌/메시지] WM_NOTIFY 메시지에 대해서...(ON_NOTIFY) - 2편 (0) | 2015.08.19 |
#import msado15.dll이 소스에서 OS등에 따라 컴파일이 되지 않을 때 (0) | 2014.04.11 |
BOOL PreTranslateMessage(MSG* pMsg) (0) | 2014.03.21 |
분할 윈도우 중 하나에 접근하는 방법 (0) | 2014.02.10 |