TN006: 메시지 맵
이 참고에서는 MFC 메시지 맵 기능을 설명합니다.
문제
Microsoft Windows는 메시징 기능을 사용하는 창 클래스에서 가상 함수를 구현합니다. 관련된 메시지 수가 많기 때문에 각 Windows 메시지에 별도의 가상 함수를 제공하면 엄청나게 큰 vtable이 생성됩니다.
시스템 정의 Windows 메시지의 수는 시간이 지남에 따라 변경되고 애플리케이션에서 자체 Windows 메시지를 정의할 수 있기 때문에 메시지 맵은 인터페이스 변경 내용이 기존 코드를 위반하지 않도록 하는 간접 참조 수준을 제공합니다.
개요
MFC는 기존 Windows 기반 프로그램에서 창으로 전송된 메시지를 처리하는 데 사용된 switch 문에 대한 대안을 제공합니다. 창에서 메시지를 받을 때 적절한 메서드가 자동으로 호출되도록 메시지에서 메서드로의 매핑을 정의할 수 있습니다. 이 메시지 맵 기능은 가상 함수와 유사하도록 설계되었지만 C++ 가상 함수에서는 사용할 수 없는 추가적인 이점이 있습니다.
메시지 맵 정의
DECLARE_MESSAGE_MAP 매크로는 클래스에 대해 세 개의 멤버를 선언합니다.
_messageEntries AFX_MSGMAP_ENTRY 항목의 프라이빗 배열입니다.
_messageEntries 배열을 가리키는 messageMap이라는 보호된 AFX_MSGMAP 구조체입니다.
messageMap의 주소를 반환하는 보호된 가상 함수입니다
GetMessageMap
.
이 매크로는 메시지 맵을 사용하여 클래스의 선언에 넣어야 합니다. 규칙에 따라 클래스 선언의 끝에 있습니다. 예시:
class CMyWnd : public CMyParentWndClass
{
// my stuff...
protected:
//{{AFX_MSG(CMyWnd)
afx_msg void OnPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
새 클래스를 만들 때 AppWizard 및 ClassWizard에서 생성되는 형식입니다. ClassWizard에는 //{{ 및 //}} 대괄호가 필요합니다.
메시지 맵의 테이블은 메시지 맵 항목으로 확장되는 매크로 집합을 사용하여 정의됩니다. 테이블은 이 메시지 맵에서 처리하는 클래스와 처리되지 않은 메시지가 전달되는 부모 클래스를 정의하는 BEGIN_MESSAGE_MAP 매크로 호출로 시작합니다. 테이블은 END_MESSAGE_MAP 매크로 호출로 끝납니다.
이러한 두 매크로 호출 사이에는 이 메시지 맵에서 처리할 각 메시지에 대한 항목이 있습니다. 모든 표준 Windows 메시지에는 해당 메시지에 대한 항목을 생성하는 ON_WM_MESSAGE_NAME 형식의 매크로가 있습니다.
각 Windows 메시지의 매개 변수의 압축을 풀고 형식 보안을 제공하기 위해 표준 함수 서명이 정의되었습니다. 이러한 서명은 CWnd 선언의 Afxwin.h 파일에서 찾을 수 있습니다. 각 키워드(keyword) afx_msg 표시되어 쉽게 식별할 수 있습니다.
참고 항목
ClassWizard를 사용하려면 메시지 맵 처리기 선언에서 afx_msg 키워드(keyword) 사용해야 합니다.
이러한 함수 서명은 간단한 규칙을 사용하여 파생되었습니다. 함수의 이름은 항상 "로 "On
시작합니다. 그 다음에는 "WM_"이 제거된 Windows 메시지의 이름과 각 단어의 첫 글자가 대문자로 표시됩니다. 매개 변수의 순서는 wParam 뒤에 (lParam) 다음HIWORD
(lParam)입니다.LOWORD
사용되지 않는 매개 변수는 전달되지 않습니다. MFC 클래스로 래핑되는 모든 핸들은 적절한 MFC 개체에 대한 포인터로 변환됩니다. 다음 예제에서는 WM_PAINT 메시지를 처리하고 함수를 CMyWnd::OnPaint
호출하는 방법을 보여줍니다.
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_WM_PAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
메시지 맵 테이블은 함수 또는 클래스 정의의 범위 밖에서 정의되어야 합니다. extern "C" 블록에 배치해서는 안 됩니다.
참고 항목
ClassWizard는 //{{ 및 //}} 주석 대괄호 사이에 발생하는 메시지 맵 항목을 수정합니다.
사용자 정의 Windows 메시지
사용자 정의 메시지는 ON_MESSAGE 매크로를 사용하여 메시지 맵에 포함될 수 있습니다. 이 매크로는 메시지 번호와 양식의 메서드를 허용합니다.
// inside the class declaration
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);
#define WM_MYMESSAGE (WM_USER + 100)
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)
END_MESSAGE_MAP()
이 예제에서는 사용자 정의 메시지에 대한 표준 WM_USER 기반에서 파생된 Windows 메시지 ID가 있는 사용자 지정 메시지에 대한 처리기를 설정합니다. 다음 예제에서는 이 처리기를 호출하는 방법을 보여줍니다.
CWnd* pWnd = ...;
pWnd->SendMessage(WM_MYMESSAGE);
이 방법을 사용하는 사용자 정의 메시지의 범위는 0x7fff WM_USER 범위에 있어야 합니다.
참고 항목
ClassWizard는 ClassWizard 사용자 인터페이스에서 ON_MESSAGE 처리기 루틴을 입력하는 것을 지원하지 않습니다. Visual C++ 편집기에서 수동으로 입력해야 합니다. ClassWizard는 이러한 항목을 구문 분석하고 다른 메시지 맵 항목과 마찬가지로 검색할 수 있도록 합니다.
등록된 Windows 메시지
RegisterWindowMessage 함수는 시스템 전체에서 고유하도록 보장되는 새 창 메시지를 정의하는 데 사용됩니다. 매크로 ON_REGISTERED_MESSAGE 이러한 메시지를 처리하는 데 사용됩니다. 이 매크로는 등록된 Windows 메시지 ID를 포함하는 UINT NEAR 변수의 이름을 허용합니다. 예를 들면 다음과 같습니다.
class CMyWnd : public CMyParentWndClass
{
public:
CMyWnd();
//{{AFX_MSG(CMyWnd)
afx_msg LRESULT OnFind(WPARAM wParam, LPARAM lParam);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
static UINT NEAR WM_FIND = RegisterWindowMessage("COMMDLG_FIND");
BEGIN_MESSAGE_MAP(CMyWnd, CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND, OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
등록된 Windows 메시지 ID 변수(이 예제의 WM_FIND)는 ON_REGISTERED_MESSAGE 구현되는 방식 때문에 NEAR 변수여야 합니다.
이 방법을 사용하는 사용자 정의 메시지의 범위는 0xFFFF 0xC000 범위에 있습니다.
참고 항목
ClassWizard는 ClassWizard 사용자 인터페이스에서 ON_REGISTERED_MESSAGE 처리기 루틴을 입력하는 것을 지원하지 않습니다. 텍스트 편집기에서 수동으로 입력해야 합니다. ClassWizard는 이러한 항목을 구문 분석하고 다른 메시지 맵 항목과 마찬가지로 검색할 수 있도록 합니다.
명령 메시지
메뉴 및 액셀러레이터의 명령 메시지는 ON_COMMAND 매크로를 사용하여 메시지 맵에서 처리됩니다. 이 매크로는 명령 ID와 메서드를 허용합니다. wParam이 지정된 명령 ID와 같은 특정 WM_COMMAND 메시지만 메시지 맵 항목에 지정된 메서드에 의해 처리됩니다. 명령 처리기 멤버 함수는 매개 변수를 사용하지 않고 반환 void
합니다. 매크로의 형식은 다음과 같습니다.
ON_COMMAND(id, memberFxn)
명령 업데이트 메시지는 동일한 메커니즘을 통해 라우팅되지만 대신 ON_UPDATE_COMMAND_UI 매크로를 사용합니다. 명령 업데이트 처리기 멤버 함수는 단일 매개 변수, CCmdUI 개체에 대한 포인터를 사용하여 반환void
합니다. 매크로에 양식이 있습니다.
ON_UPDATE_COMMAND_UI(id, memberFxn)
고급 사용자는 확장된 형태의 명령 메시지 처리기인 ON_COMMAND_EX 매크로를 사용할 수 있습니다. 매크로는 ON_COMMAND 기능의 상위 집합을 제공합니다. 확장 명령 처리기 멤버 함수는 명령 ID를 포함하는 UINT라는 단일 매개 변수를 사용하고 BOOL을 반환합니다. 명령이 처리되었음을 나타내려면 반환 값이 TRUE여야 합니다. 그렇지 않으면 라우팅이 다른 명령 대상 개체로 계속됩니다.
이러한 양식의 예:
Resource.h 내부(일반적으로 Visual C++에서 생성됨)
#define ID_MYCMD 100 #define ID_COMPLEX 101
클래스 선언 내부
afx_msg void OnMyCommand(); afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI); afx_msg BOOL OnComplexCommand(UINT nID);
메시지 맵 정의 내부
ON_COMMAND(ID_MYCMD, OnMyCommand) ON_UPDATE_COMMAND_UI(ID_MYCMD, OnUpdateMyCommand) ON_COMMAND_EX(ID_MYCMD, OnComplexCommand)
구현 파일에서
void CMyClass::OnMyCommand() { // handle the command } void CMyClass::OnUpdateMyCommand(CCmdUI* pCmdUI) { // set the UI state with pCmdUI } BOOL CMyClass::OnComplexCommand(UINT nID) { // handle the command return TRUE; }
고급 사용자는 단일 명령 처리기 (ON_COMMAND_RANGE 또는 ON_COMMAND_RANGE_EX )를 사용하여 명령 범위를 처리할 수 있습니다. 이러한 매크로에 대한 자세한 내용은 제품 설명서를 참조하세요.
참고 항목
ClassWizard는 ON_COMMAND 및 ON_UPDATE_COMMAND_UI 처리기 만들기를 지원하지만 ON_COMMAND_EX 또는 ON_COMMAND_RANGE 처리기 만들기는 지원하지 않습니다. 그러나 클래스 마법사를 구문 분석하고 네 가지 명령 처리기 변형을 모두 찾아볼 수 있습니다.
알림 메시지 제어
자식 컨트롤에서 창으로 전송되는 메시지에는 메시지 맵 항목에 컨트롤의 ID라는 추가 정보가 있습니다. 메시지 맵 항목에 지정된 메시지 처리기는 다음 조건이 true인 경우에만 호출됩니다.
컨트롤 알림 코드(lParam의 상위 단어)(예: BN_CLICKED)는 메시지 맵 항목에 지정된 알림 코드와 일치합니다.
컨트롤 ID(wParam)는 메시지 맵 항목에 지정된 컨트롤 ID와 일치합니다.
사용자 지정 컨트롤 알림 메시지는 ON_CONTROL 매크로를 사용하여 사용자 지정 알림 코드를 사용하여 메시지 맵 항목을 정의할 수 있습니다. 이 매크로에는 형식이 있습니다.
ON_CONTROL(wNotificationCode, id, memberFxn)
고급 사용 ON_CONTROL_RANGE 동일한 처리기를 사용 하는 컨트롤의 범위에서 특정 컨트롤 알림을 처리 하는 데 사용할 수 있습니다.
참고 항목
ClassWizard는 사용자 인터페이스에서 ON_CONTROL 또는 ON_CONTROL_RANGE 처리기를 만드는 것을 지원하지 않습니다. 텍스트 편집기를 사용하여 수동으로 입력해야 합니다. ClassWizard는 이러한 항목을 구문 분석하고 다른 메시지 맵 항목과 마찬가지로 검색할 수 있도록 합니다.
Windows 공용 컨트롤은 복잡한 컨트롤 알림에 더 강력한 WM_NOTIFY 사용합니다. 이 버전의 MFC는 ON_NOTIFY 및 ON_NOTIFY_RANGE 매크로를 사용하여 이 새 메시지를 직접 지원합니다. 이러한 매크로에 대한 자세한 내용은 제품 설명서를 참조하세요.