펌... 윈도우 메시지 처리에 대해 아주 정확하고 자세한 설명이 되어 있어서.... 퍼놓습니다. 절친 설명, 찰진 설명
출처 : http://anster.tistory.com/11
Notification Message
1. Background Knowledge
윈도우즈 프로그래밍시 프로그래머가 사용할 수 있는 컨트롤들은 여러가지가 있다. Static Box 부터 시작해서 List Box, List View, Button, Edit Control, Address Control 등등. 하지만 이러한 컨트롤들이 모두 처음부터 윈도우에 존재했던것은 아니다.
< 기본 컨트롤 >
윈도우 95 이전에 존재했던 컨트롤들을 기본(표준) 컨트롤이라 부른다. 기본 컨트롤은 16비트 윈도우의 경우 USER.EXE 에, 32비트 에서는 윈도우 XP 이전까지 USER32.DLL 에 의해서 구현되었으나 XP 이후 부터는 공통 컨트롤과 함께 COMCTL32.DLL 에 의해서 구현된다.
- Static Text
- Button
- Edit Control
- List Box
- Combo Box
< 공통 컨트롤 >
윈도우 95 이후부터 생긴 컨트롤들을 공통 컨트롤이라 부른다. 기본 컨트롤에 비해서 더 화려한 외관과 강력한 기능들을 제공한다. 16비트 윈도우에서는 COMMCTRL.DLL 에 의해서 구현되었고, 32비트에서는 COMCTL32.DLL 에 의해서 구현된다.
- Progress Bar
- List View Control
- Spin Control
- Command Bar Control
- Address Control
- 이외의 기본컨트롤을 제외한 모든 컨트롤
2. Notification Messge 란 무엇인가?
Notification Message 를 한글로 번역하면 통지 메시지 다. 무엇을 통지 해준다는 것일까? 부모 윈도우에게 자신의 상태가 변하였음을 통지해주는 것이다.
예를들어 다이얼로그가 있고, 그 안에 버튼이 존재한다고 하자. 사용자가 버튼을 눌렀다면 버튼의 눌려진 모양을 다시 그리도록 부모 윈도우에게 알려줘야 한다. 이때 자식 윈도우(버튼) 으로부터 부모 윈도우(다이얼로그) 에게 보내지는 메시지를 Notification Message 라 부른다.
우리가 프레임윈도우에 있는 메뉴를 선택했을때 발생하는 메시지인 WM_COMMAND 도 대표적인 Notification Message 다. 이 메시지가 발생했을때 전달되는 파라미터인 WPARAM 과 LPARAM 의 내용은 아래와 같다.
MESSAGE ID | WPARAM(32 Bit) |
LPARAM(32 Bit) |
WM_COMMAND | BN_CLICKED (High) Control ID(Low) | Control HWND |
WPARAM 의 상위 16비트에는 BN_CLICKED 와 같은 Notification Code 가, 하위 16비트에는 어떤 컨트롤에서 발생했는지 IDC_BUTTON1 과 같은 컨트롤의 ID 값이 들어간다.
LPARAM 의 32비트 값은 메시지가 발생한 컨트롤의 핸들이다. 이를테면 버튼이 눌렸을때 눌려진 버튼의 HWND 값이다.
2. 왜 WM_NOTIFY 가 생겨나게 되었는가?
메시지를 받고 보낼때 사용하는 파라미터는 WPARAM, LPARAM 두가지이므로 위의 WM_COMMAND 통지 메시지에서는 더이상의 추가적인 정보를 부모 윈도우에게 보낼 수 없다. 예를들면, 버튼이 눌려졌을때 커서의 위치가 어느지점이었는지 Point 구조체로 만들어 보내고 싶다고 해도 파라미터를 모두 사용했기에 보낼 수 없다.
이러한 이유에서 윈도우 3.x 에서는 추가적인 통지메시지들을 만들었다. 그 대표적인 예가 아래의 메시지들이다.
- WM_CTLCOLOR
- WM_VSCROLL
- WM_HSCROLL
- WM_DRAWITEM
- WM_MEASUREITEM
- WM_COMPAREITEM
- WM_DELETEITEM
- WM_CHARTOITEM
- WM_VKEYTOITEM
윈도우 버전이 올라감에 따라 더 강력한 기능을 가진 공통 컨트롤(COMCTL32.DLL)들이 추가되었고 이제 전혀 다른종류의 추가적인 데이터를 포함하는 통지 메시지가 필요해 졌다. 그렇다면 또 다시 WM_*과 같은 또다른 통지 메시지 만들어야 하는가? 그렇다면 수많은 컨트롤들이 포함되었을때 통지 메시지는 얼마나 많아지겠는가?
이러한 문제를 해결하기 위해 Win32 API 디자이너는 WM_*과 같은 수 많은 메시지를 추가하는 방법 대신에 WM_NOTIFY 단 하나의 메시지만 추가하는 방법을 선택했다. WM_NOTIFY 는 파라미터를 어떻게 사용하길래 추가적인 데이터를 메시지와 같이 보낼 수 있을까? 아래 그림을 확인 해 보자.
MESSAGE ID | WPARAM(32 Bit) |
LPARAM(32 Bit) |
WM_NOTIFY | Control ID | NMHDR * |
NMHDR 구조체를 살펴보자.
typedef struct tagNMHDR { HWND hwndFrom; // HWND of the Control UINT_PTR idFrom; // Control ID UINT code; // Notification Code } NMHDR; typedef NMHDR FAR * LPNMHDR;
WM_COMMAND 메시지의 LPARAM 에서 사용헀던 HWND 와 WPARAM 에서 사용헀던 Notification Code, Control ID 값이 있다. LPARAM 인자가 NMHDR * 이므로 윈도우는 처음 3개의 인자만 NMHDR 구조체와 같게 하거나, 아니면 NMHDR 구조체를 포함하는 구조체를 만들어 추가적인 데이터를 보내곤 한다.
typedef struct tagPointNMHDR { HWND hwndFrom; UINT_PTR idFrom; UINT code; Point point; } PointNMHDR; typedef struct tagPointNMHDR { NMHDR nmHdr; Point point; } PointNMHDR;
NMHDR 을 포함하는 구조체를 만들어 메시지 핸들러에서 인자로 사용함으로써 추가적인 데이터를 보낼 수 있다.
3. 어떻게 WM_NOTIFY 메시지를 사용하는가?
WM_NOTIFY 메시지는 메시지 맵에 ON_NOTIFY 혹은 ON_NOTIFY_RANGE 매크로를 등록함으로써 사용할 수 있다. ON_NOTIFY 매크로의 형식은 다음과 같다.
ON_NOTIFY ( wNotifyCode, ControlID, memberFxn )
|
ON_NOTIFY 매크로에서 사용하는 WM_NOTIFY 메시지의 핸들러는 아래와 같다.
afx msg void memberFxn ( NMHDR * pNotifyStruct, LRESULT * result )
|
만약, 다수의 컨트롤로 부터 오는 동일한 코드의 WM_NOTIFY 메시지를 하나의 핸들러에서 다루고 싶다면 ON_NOTIFY_RANGE 매크로를 사용하면 된다.
ON_NOTIFY_RANGE ( wNotifyCode, ControlIDFrom, ControlIDTo, memberFxn )
|
ON_NOTIFY_RAGNE 매크로는 Control ID From 부터 Control ID To 까지의 모든 윈도우로 부터 온 wNotifyCode 의 WM_NOTIFY 메시지를 memberFxn 핸들러에서 처리합니다. 이때 주의할점은 원하는 윈도우들의 컨트롤 ID가 순차적이어야 한다는 것.
4. 컨트롤 별 통지 메시지 처리법
컨트롤의 Notification Message 를 처리하는 방법은 컨트롤 마다 다르다. 기본 컨트롤의 경우 WM_COMMAND 와 같은 Notification Message 가 발생하고, 공통 컨트롤의 경우 WM_NOTIFY Notification Message 발생한다. 이러한 메시지를 처리하는 방법은 두가지가 있다.
< 가상 함수에서 Notification Message 처리 >
Notification Message 를 처리하는 가상함수는 어떠한 핸들러보다 먼저 호출된다.
< 기본 컨트롤 >
기본 컨트롤의 경우 CWnd 의 OnCommand 함수에서 통지 메시지인 WM_COMMAND 메시지를 처리하면 된다. 하지만 모든 WM_COMMAND 메시지가 이곳으로 오기때문에 특정 컨트롤만을 위한 작업을 이곳에 코딩하는 것은 비 효율적이다.
virtual BOOL OnCommand( WPARAM wParam, // High 16 : Code, Low 16 : Control ID LPARAM lParam // Control HWND );
< 공통 컨트롤 >
공통 컨트롤의 경우 CWnd 의 OnNotify 함수에서 통지 메시지인 WM_NOTIFY 를 처리한다.
virtual BOOL OnNotify( WPARAM wParam, // Control ID LPARAM lParam, // NMHDR * LRESULT* pResult );
< 메시지 핸들러에서 Notification Message 처리 >
< 기본 컨트롤 >
기본 컨트롤의 경우 ON_CONTROL 매크로를 사용하면 된다. 이때는 공통 컨트롤에서 WM_NOTIFY 를 위해 사용하는 NM_* 메시지를 사용하지 않는다. BN_CLICKED 나 LBN_SELCHANGE 와 같은 기본 컨트롤을 위한 Notification Code를 사용한다.
< 공통 컨트롤 >
공통 컨트롤의 경우 위해서 언급했듯이 ON_NOTIFY 매크로를 사용하면 된다.
참고 문헌
블로그 및 웹페이지
1. http://ko.wikipedia.org/wiki/%EC%9C%88%EB%8F%84_API (윈도우 API 위키백과)
2. http://www.tipssoft.com/bulletin/tb.php/FAQ/63 (팁스소프트 WM_NOTIFY 1)
3. http://www.tipssoft.com/bulletin/tb.php/FAQ/65 (팁스소프트 WM_NOTIFY 2)
4. http://msdn.microsoft.com/en-us/library/749htf6k%28v=VS.100%29.aspx (MSDN TN061 - WM_NOTIFY)
5. http://codemaker.egloos.com/1825628 (컨트롤의 종류에 따른 통지 메시지 처리법)
6. http://program.egloos.com/1555310 (윈도우 메시지)