4. 사용자 정의 메시지


윈도우즈에서의 사용자의 입력은 이벤트를 발생시키고 그 때마다 이벤트 통지 메시지가 메시지 큐로 들어가고 이벤트가 발생한 응용 프로그램에 메시지가 전달된다. 이런 윈도우즈 메시지 외에도 모든 차일드 콘트롤들도 각각의 상태에 따라 고유한 Notification 메시지를 발생시키고 부모 윈도우에 그 메시지가 전달된다. 그래서 윈도우즈는 이벤트와 메시지 중심의 운영체제라고 부르는 것이다.

윈도우에서 발생할 수 있는 사건들이 워낙 다양하다보니 각 사건들에 할당된 메시지의 수도 많을 수 밖에 없다. 모든 메시지는 컴퓨터가 인식할 수 있는 상수로 정의된 고유 ID를 가지고 있으며 윈도우즈는 이들 메시지 ID 상수의 범위를 체계적으로 구별해 놓고 있다. 다음은 메시지 ID로 사용되는 상수들의 범위이다.

 

메시지 범위
 #define WM_USER 0x0400 // 사용자 정의 메시지 
 0 ~ WM_USER - 1 윈도우즈에서 사용하기 위해 예약된 메시지들 
WM_USER ~ 0x7FFF 사적 윈도우 클래스에서 사용되는 정수 메시지들
WM_APP ~ 0xBFFF 미래의 사용을 위해 예약된 메시지들
0xC000 ~ 0xFFFF 응용 프로그램들에서 사용하는 문자열 메시지들
0xFFFF ~ 미래의 사용하기 위해 예약됨
 

위에서 보면 윈도우즈에서 사용되고 있는 메시지와 장차 사용하기 위해 예약해둔 메시지들이 포함되는 범위와 겹치지 않게 사용자가 자신을 위한 메시지를 만들어 사용할 수 있도록 일정 범위를 비워두고 있으며 그 범위는 0x0400 ~ 0x7FFF까지 임을 알 수 있다.

사용자 정의 메시지는 보통 응용 프로그램에서 다음 형태로 정의하게 되는데 'X'는 정수값이다.

        #define WM_MYMESSAGE     WM_USER + X

사용자 정의 메시지의 범위가 0x0400 ~ 0x7FFF이므로 'X'는 0 ~ 31743 까지 가능한데 이 얘기는 곧 사용자 정의 메시지를 3만개 이상 만들 수 있다는 뜻이 된다. (참고: 0x7FFF - 0x0400 = 31743 )

사용자 메시지는 응용 프로그램에서 공식적인 윈도우즈 메시지 외에 사적인 용도로 메시지를 사용키 위해 정의하게 되는데 윈도우즈 시스템과 응용 프로그램 또는 응용 프로그램과 응용 프로그램 간에 메시지를 주고 받아야 할 필요가 있을 때 사용한다.

ECR TRAY에서는 NOTIFYICONDATA 구조체를 설명할 때도 잠깐 언급했듯이 트레이 아이콘에서 마우스 클릭 이벤트가 발생했을 때 윈도우즈 시스템이 이를 ECR TRAY에 알려 주기 위해 이 메시지를 사용하게 된다.

 

※ 질문?: WM_LBUTTONUP등과 같은 마우스 클릭 메시지를 사용하면 안되는가?
궁극적으로 이 질문은 사용자 정의 메시지의 필요성을 더욱 확실하게 만든다. 물론 윈도우즈는 작업표시줄로부터 마우스 클릭 메시지를 받을 것이다. 이때 클릭 위치를 판단하여 특정 트레이 아이콘 영역 위라면 그 아이콘을 추가한 응용 프로그램에 메시지를 보내 주어야 하는데 만약 WM_LBUTTONUP 메시지를 그대로 보냈다고 한다면 응용 프로그램은 혼란에 빠진다. 자, 왼쪽 마우스 버튼이 눌렸다 놓였는데 이 메시지가 응용 프로그램의 창 내부에서 발생한 것인지 트레이 아이콘에서 발생한 것인지 어떻게 알 것인가?
따라서 사용자 정의 메시지를 새로 만들어 윈도우즈로 하여금 사건이 발생하면 그 메시지를 보내게 하고 응용 프로그램은 그 메시지를 처리하는 핸들러에서 관련 작업을 처리하면 간단해 지는 것이다.

5. 사용자 정의 메시지 핸들러
트레이 아이콘에서 마우스 클릭 이벤트가 발생했을 때 미리 약속한 사용자 정의 메시지를 보내는 것은 윈도우즈가 알아서 할 일이다. 응용 프로그램에서는 단지 그 메시지 핸들러를 작성해 두고 핸들러가 호출되기만을 기다리면 된다.

그럼 사용자 정의 메시지 핸들러는 어떻게 추가할 것인가? 무작정 클래스위저드를 실행한다면 이내 실망하게 될 것이다. 사용자 정의 메시지란 윈도우즈 입장에서 보면 임의의 메시지이기 때문에 클래스위저드의 메시지 목록엔 나타나지 않는다.

MFC에는 몇가지 형태의 사용자 정의 핸들러를 만들 수 있는 매크로가 있는데 그 중 WM_USER ~ 0x7FFF 범위의 사용자 정의 메시지를 처리하기 위한 핸들러를 메시지 맵 안에서 정의하는 매크로가 ON_MESSAGE 이다. 이 매크로와 매크로에서 정의하는 핸들러 함수의 원형은 다음과 같다.

 

ON_MESSAGE 매크로와 사용자 정의 메시지 핸들러의 원형
 ON_MESSAGE( message, memberFxn )
 message : 사용자 정의 메시지 ID 
memberFxn : 사용자 정의 메시지 핸들러 함수의 이름, 원형은 아래와 같다.
 afx_msg LONG memberFxn( UINT, LONG ); 
 

메시지 맵에 이 매크로를 추가하는 일은 여러분이 직접 해야 하며 핸들러를 추가할 클래스의 소스파일을 약간 수정하면 된다. 수정하는 방법은 다음과 같다.

먼저 사용자 정의 메시지를 define 한다. 여기서는 메시지 id WM_TRAYNOTIFY를 'WM_USER + 100'으로 정의했다. '+100'의 정수는 겹치지 않고 범위내에만 든다면 어떤 값을 써도 좋다. 그리고 나서 핸들러를 추가할 클래스의 메시지 맵 내부에 아래처럼 매크로를 작성한다. 의미는 이미 알고 있듯이 WM_TRAYNOTIFY 메시지가 발생했을 때 OnTrayNotification() 핸들러가 호출되도록 한다는 것이다.

 

ON_MESSAGE 매크로 작성 (1) - cpp 파일의 메시지 맵 수정
 #define WM_TRAYNOTIFY WM_USER + 100 // 사용자 정의 메시지 
 BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 
//{{AFX_MSG_MAP(CMainFrame)
ON_MESSAGE( WM_TRAYNOTIFY, OnTrayNotification )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
 

메시지 정의와 매크로 작성이 끝났으면 헤더 파일에 핸들러의 원형을 추가해 준다. 이 원형은 위에서 ON_MESSAGE 매크로 설명시 그대로이며 단지 함수 이름만 메시지 맵에서 지정했던 이름으로 바꾸면 된다.

 

ON_MESSAGE 매크로 작성 (2) - 헤더파일의 수정
 class CMainFrame : public CFrameWnd 
{ ...
//{{AFX_MSG(CMainFrame)
afx_msg LRESULT OnTrayNotification( WPARAM wParam, LPARAM lParam );
//}}AFX_MSG ... };
 

마지막으로 핸들러 함수만 작성하면 된다. 위에서는 CMainFrame 클래스를 선택했으니 같은 클래스에 클래스위저드가 만들어 주는 핸들러 형태대로 직접 만들면 된다. 이렇게...

 LRESULT CMainFrame::OnTrayNotification( WPARAM wParam, LPARAM lParam ) 
{ ...
	// 여기에 코드 작성 
}
 
profile

J102.net 홈페이지 관리자 및 운영자 이재열입니다^^

(*.132.85.18)