API

윈도우 메시지 종류 및 큐잉, 비큐잉 메시지

디버그정 2008. 9. 1. 18:26

1 메시지 개요1.1 구조Windows는 메시지 기반의 운영체제이다. 메시지(Message)란 프로그램에 변화가 생겼을 때 Windows가 프로그램에게 알리는 정보이다. 메시지는 MSG로 정의되는 구조체인데 여기에는 윈도우 핸들, 메시지 식별 번호, 추가 정보, 시각, 커서 위치 등이 포함되어 있다.


typedef struct tagMSG {
        HWND            hwnd;      // 메시지가 발생한 윈도우 핸들
        UINT            message;   // message id
        WPARAM          wParam;     // 추가 정보
        LPARAM          lParam;    // 추가 정보
        DWORD           time;      // 메시지 발생 시간
        POINT           pt;        // 커서 위치 
} MSG;


1.2 식별 번호메세지 식별 번호(message id)는 0x0000에서 0xFFF 사이의 값을 가지며 유형에 따라 크게 4 부분으로 나누어져 있다.

유형  번호 
시스템 정의 메시지  0x0000 ~ 0x03FF 
사용자 정의 창 메시지  0x0400 ~ 0x7FFF 
사용자 정의 프로그램 메시지  0x8000 ~ 0xBFFF 
사용자 정의 문자열 메시지  0xC000 ~ 0xFFFF 

시스템 정의 메시지는 Windows에 의해 미리 예약(reserved)된 메시지이고 사용자 정의 메시지는 프로그램에서 추가로 정의해 사용할 수 있다.
'사용자 정의 창 메시지'는 동일한 application 내에 2개의 창이 교신할 때 사용되며 '사용자 정의 프로그램 메시지'는 서로 다른 application끼리 교신할 때 사용된다.
0x0400 과 0x8000 은 각각 WM_USER 와 WM_APP 로 정의 되어 있다. 따라서 사용자 정의 메시지를 정의할 때는 시스템 메시지와 충돌을 일으키지 않도록 다음과 같이 정의하도록 한다.


#define WM_USERWIDTH    (WM_USER + 100)
#define WM_APPWIDTH     (WM_APP + 200)


1.3 종류1.3.1 Window message창 메시지(window message)는 창에서 발생하는 대부분의 메시지(WM_LBUTTONDOWN과 같은)를 말한다. 창 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 형식은 다음과 같다.

ON_WindowMessage()
afx_msg ReturnType fn(Parameters);
 
여기서 메시지 핸들러 fn의 이름, 인자, 리턴형은 결정되어 있다.
창 메시지를 처리할 때 필요한 메시지 매크로와 매시지 핸들러의 예를 들어 보면 다음과 같다.

ON_WM_LBUTTONDOWN()
 
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);


1.3.2 Command message명령 메시지(Command message)는 메뉴항목, 엑셀러레이터 키, 도구모음 버튼(툴바)등과 같이 사용자 인터페이스 개체에서 발생하는 메시지(WM_COMMAND)를 말한다.
명령 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 형식은 다음과 같다.

ON_COMMAND(id, fn)
ON_UPDATE_COMMAND_UI(id, fn)

afx_msg void fn()
afx_msg void fn(CCmdUI *pCmdUI);


여기서 id는 메뉴항목, 엑셀러레이터 키, 도구모음 버튼의 식별 번호(id)가 될 수 있다.
메시지 핸들러 fn의 이름은 임의로 지을 수 있지만 인자와 리턴형은 결정되어 있다.


명령 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 예를 들어보면 다음과 같다.


ON_COMMAND(IDM_COLOR_WHITE, OnColorWhite)
ON_UPDATE_COMMAND_UI(IDM_COLOR_WHITE, OnUpdateColorWhite)
 
afx_msg void OnColorWhite();
afx_msg void OnUpdateColorWhite(CCmdUI* pCmdUI);


1.3.3 Notification message통지 메시지(Notification messate)는 컨트롤(button, edit, scroll bar, static, combo, list등)이 부모 창에게 알리는 메시지(BN_CLICKED, LBN_SELCHANGE, UDN_DELTAPOS)를 말한다. 통지 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 형식은 다음과 같다.


ON_NotifyCode(id, fn)
ON_CONTROL(NotifiyCode, id, fn)
ON_NOTIFY(NotifyCode, id, fn)

afx_msg void fn()
afx_msg void fn();
afx_msg void fn(NMHDR* pNMHDR, LRESULT* pResult);


여기서 id는 컨트롤 식별 번호(id)이며 NotifyCode는 통지 메시지이다. 메시지 핸들러 fn의 이름은 임의로 지을 수 있지만 파라미터와 리턴형은 결정되어 있다.


통지 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 예를 들어보면 다음과 같다.


ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)
ON_CONTROL(LBN_SELCHANGE, IDC_LIST_SEND, OnSelChangedListSend)
ON_NOTIFY(UDN_DELTAPOS, IDC_SPIN_WIDTH, OnDeltaposSpinWidth)
 
afx_msg void OnBtnSend();
afx_msg void OnSelchangedListSend();
afx_msg void OnDeltaposSpinWidth(NMHDR* pNMHDR, LRESULT* pResult);


1.3.4 User-defined message사용자 정의 메시지(User-defined message)는 MFC가 기본적으로 지원하지 않거나 사용자가 정의한 메시지(예:WM_USERWIDTH)를 말한다. 사용자 정의 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 형식은 다음과 같다.


ON_MESSAGE(UserMessage, fn)

afx_msg LRESULT fn(WPARAM wParam, LPARAM lParam);



여기서 UserMessage는 사용자 정의 메시지이다. 메시지 핸들러 fn의 이름은 임의로 지을 수 있지만 파라미터와 리턴형은 결정되어 있다.
사용자 정의 메시지를 처리할 때 필요한 메시지 매크로와 메시지 핸들러의 예를 들어보면 다음과 같다.


ON_MESSAGE(WM_USERWIDTH, OnUserWidth)
 
afx_msg LRESULT OnUserWidth(WPARAM wParam, LPARAM lParam
);


메시지 큐(message queue)는 메시지를 순서대로 보관해 둘 수 있는 메모리 영역으로 system message queue와 thread message queue가 있다. system message queue는 마우스와 키보드 입력 메시지를 보관해 두기 위한 곳으로 윈도우즈 시스템에 하나만 존재한다. thread message queue는 system message queue로 부터 전달받은 메시지와 프로그램으로부터 전달받은 메시지를 보관해 두기 위한 곳으로 스레드마다 하나씩 존재한다.


윈도우 메시지는 윈도우 프로시저에게 전달되어 처리된다. 메시지가 윈도우 프로시저에게 전달되는 방식에는 메시지 큐를 경유하는 방식과 메시지 큐를 경유하지 않는 방식이 있다.


2.1 메시지 큐를 경유하는 방식메시지 큐를 경유하는 메시지들은 일반적으로 사건, event에 대한 결과로 발생하는 메시지들이다.
입력 메시지들(WM_LBUTTONDOWN, WM_KEYDOWN, WM_SYSTEMDOWN, WM_TIMER)은 system message queue와 thread message queue를 경유하여 메시지 루프를 통해 윈도우 프로시저에게 전달된다.

message --> message queue --> message loop --> window procedure


급하게 처리될 필요가 있는 몇몇 메시지들(WM_PAINT, WM_QUIT)은 메시지 큐를 경유하지 않고 메시지 루프를 통해 윈도우 프로시저에게 전달된다.
메시지 큐를 경유하는 메시지들은 event를 통지하는 성격의 메시지들이기 때문에 return값이 불필요한 경유가 많다.


2.2 메시지 큐를 경유하지 않는 방식입력 메시지를 제외한 대부분의 메시지들(WM_CREATE, WM_SIZE, WM_CLOSE, WM_DESTROY)은 메시지 큐와 메시지 루프를 경유하지 않고 직접 윈도우 프로시저에게 전달된다.

message --> window procedure


위의 그림에서는 표현하지 않았지만 이런 메시지들은 질문, 의뢰의 성격이 많기 때문에 메시지에 대한 응답, 즉 return값이 필요한 경우가 많다.


3 메시지 전달 함수함수를 통해 명시적으로 메시지를 전달하는 것도 가능하다. 창에 메시지를 전달할 때는 CWnd::SendMessage와 CWnd::PostMessage가 사용된다.

CWnd::SendMessage는 동기적으로 메시지를 전달하는 반면, CWnd::PostMessage는 비동기적으로 메시지를 전달한다. 이게 무슨 말인고 하면, CWnd::SendMessage는 윈도우 프로시저에게 지접 메시지를 전달하고 나서 그 메시지가 처리될 때까지 기다린다. 반며에 CWnd::PostMessage는 메시지 큐에 메시지를 넣고나서 즉시 리턴해 버린다.


MSDN에서 CWnd::SendMessage에 대한 설명을 보면, CWnd::SendMessage는 전달할 메시지를 message를 메시지 큐에 넣지 않고 직접 윈도우 프로시저에게 전달한다고 설명되어 있다. CWnd::PostMessage는 전달 메시지를 메시지 큐에 넣고 그냥 돌아오기 때문에 CWnd::SendMessage와는 달리 윈도우 프로시저로부터 리턴값을 받을 수 없다.
그외 두 함수의 원형과 사용방법은 MSDN 도움말을 참고하자.