Windows 와 C++ MFC Message Map HCI Programming 2 (321190) 2008년가을학기 10/14/2008 박경신 C++ 의다형성 (Polymorphsim) 기반클래스의어떤멤버함수를파생클래스에서재정의 (overriding) 하기위해서는기반클래스의그멤버함수가가상함수 (virtual function) 로정의되어야함 MFC 의 CWnd 에서메시지핸들러함수는가상함수로정의되어있지않고메시지맵을사용 모든메시지핸들러함수가가상함수로되어있어동적바인딩을하게된다면메모리를많이차지하게되는자원낭비의문제발생 윈도우메시지의수와종류가바뀌고있으므로메시지핸들러함수를가상함수로사용하는것은메시지가변경될때코드를쓸모없게만듬 그래서, 효율적이고, 확장가능하고, 컴파일러에의존적이지않은메시지처리방법인메시지맵을개발 메시지맵이란일종의매크로로, 간단하게윈도우메시지와명령어를클래스의멤버함수와연결 (map) 시켜주는것임 2 Message Map Message Driven Programming MFC에서는 SDK의 switch문을대체하는방법으로메시지맵이란방법을사용 메시지맵이란특정메시지와그메시지가발생했을경우취해야할동작을하나의쌍으로연결시켜놓은테이블 미리정의된메시지핸들러를이용하면특정메시지를받을때마다그메시지에연결된핸들러가호출 메시지는자신을나타내는식별자와함께추가적인정보를메시지핸들러의인자로전달 Windows 시스템 이벤트발생 메시지전송 Application 메시지처리 화면에출력 3 4
Message 메시지는프로그램에변화가생겼을때 Windows 가프로그램에게알리는정보 메시지는 MSG 로정의되는구조체 윈도우핸들, 메시지식별번호, 추가정보, 시간, 커서위치등포함 typedef struct tagmsg HWND hwnd; UINT message; WPARAM wparam; LPARAM lparam; DWORD time; POINT pt; MSG; // 메시지가발생한윈도우핸들 // message id <WINDEF.H> // 추가정보 // 추가정보 // 메시지발생시간 // 커서위치 Message 종류 메시지발생주체에따라 사용자에의해발생되는메시지 사용자가하는동작 (e.g. 마우스클릭, 마우스이동, 키보드누름등 ) 을윈도우가이해할수있는메시지로만들어서응용프로그램에전달 시스템에의해발생되는메시지 윈도우에의해서메시지가발생되는경우 (e.g. 특정윈도우의클라이언트영역을지울필요가있다는사실을발견하게되면 WM_PAINT 메시지활성 ) 6 Message 종류 메시지처리주체에따라 윈도우 (Window) 메시지 WM_COMMAND를제외한 WM_ 로시작하는모든메시지 통지 (Notification) 메시지 자식윈도우로부터부모윈도우를향해알리는메시지 명령 (Command) 메시지 사용자인터페이스에서발생되는 WM_COMMAND 메시지 사용자정의메시지 Message 종류 윈도우메시지 (Window Message) WM_ 로시작하는메시지 (WM_COMMAND 는제외 ) 매개변수를통하여메시지를어떻게처리할것인지를결정 윈도우관리메시지 : 윈도우의상태가바뀔때발생 WM_PAINT, WM_ACTIVE, WM_SIZE, WM_MOVE, WM_CREATE, WM_DESTORY 초기화메시지 : 응용프로그램이대화상자를시작할때발생 WM_INITDIALOG 입력메시지 : 마우스, 키보드로입력할때발생 WM_KEYDOWN, WM_CHAR WM_MOUSEMOVE, WM_LBUTTONDOWN, 7 8
Message 종류 컨트롤통지메시지 (Control Notification Message) Button, Combo Box와같은컨트롤객체나자식윈도우에서부모윈도우로보내는메시지 BN_CLICKED, EN_CHANGE, CBN_SELCHANGE, LBN_SELCHANGE, 명령메시지 (Command Message) 메뉴, 툴바, 엑셀레이터키와같은사용자인터페이스객체로부터발생되는 WM_COMMAND 메시지 명령메시지는윈도우뿐만아니라도큐먼트, 도큐먼트템플렛, 뷰, 다른애플리케이션객체에의해서도발생가능 어떤메뉴가눌렸는지구별하기위해서메뉴의 ID가윈도우메시지의 WPARAM을통해전달 Message 처리방식 SDK 프로그램 들어온메시지를 switch 문을사용하여처리 MFC 프로그램 메시지처리를위해메시지맵을사용 메시지핸들러함수구현 메시지맵 메시지번호와메시지가발생하였을때호출되는함수의포인터등의정보를갖고있는테이블 프로그램에전달된메시지와메시지핸들러함수를연결하는데사용 파생클래스의메시지핸들러함수가우선 9 10 Message Map 메시지처리단계 1. 윈도우클래스의멤버함수로메시지핸들러함수를선언한다 2. 메시지맵에메시지와메시지핸들러함수를묶는메시지맵매크로를추가한다 3. 메시지핸들러함수의기능을구현한다 11 //(1) 메시지핸들러함수선언 (CWinmsgView.h) class CWinmsgView : public CView protected: //AFX_MSG(CWinmsgView) afx_msg void OnLButtonDown(UINT nflags, CPoint point); //AFX_MSG DECLARE_MESSAGE_MAP() //(2) 메시지맵매크로 (CWinmsgView.cpp) BEGIN_MESSAGE_MAP(CWinmsgView, CView) //AFX_MSG_MAP(CWinmsgView) ON_WM_LBUTTONDOWN() //AFX_MSG_MAP END_MESSAGE_MAP() //(3) 메시지처리함수구현 ((CWinmsgView.cpp) void CWinmsgView::OnLButtonDown(UINT nflags, CPoint point) AfxMessageBox( 마우스왼쪽버튼누름 ") ; // 메시지처리루틴 CView::OnLButtonDown(nFlags, point); 12
Message Handler 윈도우로부터애플리케이션에메시지가전달될때해당메시지를처리하는멤버함수 함수이름 메시지핸들러는윈도우메시지의 WM_ 대신에 On을붙여시작 예 함수선언시 afx_msg 는메시지핸들러함수표시 //AFX_MSG(CMyView) afx_msg void OnKeyDown(UINT nflags, CPoint point); afx_msg void OnLButtonDown(UINT nflags, CPoint point); //AFX_MSG DECLARE_MESSAGE_MAP() DECLARE_MESSAGE_MAP DECLARE_MESSAGE_MAP 매크로 afxwin.h #define DECLARE_MESSAGE_MAP() \ private: \ static const AFX_MSGMAP_ENTRY _messageentries[]; \ protected: \ static AFX_DATA const AFX_MSGMAP messagemap; \ virtual const AFX_MSGMAP* GetMessageMap() const; \ 13 14 DECLARE_MESSAGE_MAP struct AFX_MSGMAP_ENTRY // 메시지맵항목을정의하기위해사용 UINT nmessage; // 시스템을통해들어오는윈도우메시지 ID UINT ncode; // 제어코드나 WM_NOTIFY 코드를나타냄 UINT nid; // 메시지를생성한콘트롤 ID UINT nlastid; // 콘트롤식별자의범위를나타내는엔트리를 // 위해서사용 UINT nsig; // 메시지를다루는핸들러함수의 signature를표시 AFX_PMSG pfn; // 메시지를처리하는핸들러함수를가리킴 ; Message Map Macro 메시지맵은 BEGIN_MESSAGE_MAP 과 END_MESSAGE_ MAP 사이에메시지엔트리들로구성 BEGIN_MESSAGE_MAP(CMainWnd, CFrameWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() ON_WM_KEYDOWN() 기반클래스 END_MESSAGE_MAP() 파생클래스 struct AFX_MSGMAP // 메시지맵에등록된메시지핸들러호출 const AFX_MSGMAP* pbasemap; // base class의메시지맵 const AFX_MSGMAP_ENTRY* lpentries; // 링크리스트 ; 15 16
Message Map Macro BEGIN_MESSAGE_MAP(CDerivedClass, CBaseClass) 만약 CDerivedClass 의메시지맵에서일치하는것을발견하지못하면 CBaseClass 에지정된기반클래스의메시지맵에서찾게됨 기반클래스의메시지맵에서일치하는메시지핸들러가발견되지않는다면메시지에대한디폴트처리가수행됨 만약윈도우메시지라면메시지는연결된디폴트윈도우메시지프로시져로보내짐 기반클래스에서제공된메시지핸들러도결국파생클래스에의해상속됨 바로이점이메시지핸들러를가상함수로만들지않고도가상함수와유사한기능을하게만듬 BEGIN/END_MESSAGE_MAP BEGIN_MESSAGE_MAP & END_MESSAGE_MAP 매크로 #define BEGIN_MESSAGE_MAP(theClass, baseclass) \ const AFX_MSGMAP* theclass::getmessagemap() const \ \ return &theclass::messagemap; \ AFX_DATADEF const AFX_MSGMAP theclass::messagemap = \ &baseclass::messagemap, &theclass::_messageentries[0] ; \ const AFX_MSGMAP_ENTRY theclass::_messageentries[] = \ \ _messageentries[] 에메시지맵항목을넣을수있게준비 17 #define END_MESSAGE_MAP() \ 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 \ ; 메시지엔트리배열닫기 18 Message Map Entries Message Map Entries 메시지매크로들은 ON_ 으로시작하는것을제외하고는표준윈도우메시지의이름과동일 WM_LBUTTONDOWN 메시지에해당하는메시지매크로는 ON_WM_LBUTTONDOWN() 임 한가지예외로는 WM_COMMAND 메시지는 ON_COMMAND라는매크로를사용 윈도우메시지명령메시지명령범위메시지사용자정의메시지 ON_WM_XXXX ON_COMMAND ON_COMMAND_RANGE ON_MESSAGE #define ON_WM_LBUTTONDOWN() \ WM_LBUTTONDOWN, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonDown, #define ON_WM_LBUTTONUP() \ WM_LBUTTONUP, 0, 0, 0, AfxSig_vwp, \ (AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, CPoint))&OnLButtonUp, 통지메시지 일반적인컨트롤 버튼에디트리스트 ON_CONTROL ON_BN_XXX ON_EN_XXX ON_LBN_XXX 콤보박스 ON_CBN_XXX 19 20
윈도우관리메시지와메시지핸들러 메시지유형 발생상황 메시지핸들러함수 WM_CREATE 윈도우가생성될때 OnCreate() WM_ACTIVE 윈도우가활성화될때 OnActive() WM_PAINT 윈도우가다시그려질때 OnPaint(), OnDraw() WM_SIZE 윈도우크기가변경될때 OnSize() WM_MOVE 윈도우가움직일때 OnMove() WM_TIMER 설정된타이머시간이됐을때 OnTimer() WM_DESTROY 윈도우가종료될때 OnDestroy() 21 // 메시지핸들러함수선언 ( CWinmsgView.h) class CWinmsgView : public CView protected: //AFX_MSG(CWinmsgView) afx_msg int OnCreate(LPCREATESTRUCT lpcreatestruct); afx_msg void OnSize(UINT ntype, int cx, int cy); afx_msg void OnDestroy(); //AFX_MSG DECLARE_MESSAGE_MAP() // 메시지맵매크로 (CWinmsgView.cpp) BEGIN_MESSAGE_MAP(CWinmsgView, CView) //AFX_MSG_MAP(CWinmsgView) ON_WM_CREATE() ON_WM_SIZE() ON_WM_DESTROY() //AFX_MSG_MAP END_MESSAGE_MAP() // 메시지핸들러 (CWinmsgView.cpp) void CWinmsgView::OnDraw(CDC* pdc) CWinmsgDoc* pdoc = GetDocument(); ASSERT_VALID(pDoc); // 윈도우크기를나타내는문자열을윈도우중앙에출력 CRect rectview; GetClientRect(&rectView); pdc->drawtext(m_strwindowsize, rectview, DT_SINGLELINE DT_CENTER 22 DT_VCENTER); // 메시지핸들러 (CWinmsgView.cpp) int CWinmsgView::OnCreate(LPCREATESTRUCT lpcreatestruct) if (CView::OnCreate(lpCreateStruct) == -1) return -1; // 윈도우가생성될때메시지박스출력 AfxMessageBox( 뷰윈도우가생성되었습니다."); return 0; void CWinmsgView::OnSize(UINT ntype, int cx, int cy) CView::OnSize(nType, cx, cy); // 윈도우크기가변경될때윈도우크기를나타내는문자열생성 m_strwindowsize.format(" 윈도우크기는넓이 %d, 높이 %d 입니다.", cx, cy); Invalidate(); // 화면갱신 void CWinmsgView::OnDestroy() CView::OnDestroy(); // 윈도우가종료될때메시지박스출력 AfxMessageBox( 뷰윈도우가종료되었습니다."); 23 Timer 메시지 지정된시간마다 WM_TIMER 메시지를발생 타이머설정함수 - SetTimer UINT SetTimer( UINT nidevent, UINT nelapse, void (CALLBACK EXPORT* lpfntimer) (HWND, UINT, UINT, WORD) ); 예 nidevent : 타이머 ID nelapse : WM_TIMER 메시지를발생시킬시간간격 ( 단위 : mili second = 1/1000 초, 1 초 = 1000 ms) lpfntimer() : 설정된시간마다호출되는함수명, NULL 로설정되면 OnTimer() 함수가호출 SetTimer(0, 1000, NULL); // 1 초당 WM_TIMER 메시지발생 // 메시지처리를위해 OnTimer() 함수호출 24
Timer 메시지 타이머해제함수 - KillTimer UINT KillTimer( UINT nidevent) nidevent : SetTimer 에서설정된 Timer 의 ID. 타이머 message handler - OnTimer WM_TIMER 메시지처리함수 // WM_TIMER 메시지발생에대한처리내용 void CWinmsgView::OnTimer(UINT nidevent) CClientDC dc(this); // 현재윈도우의클라이언트영역의 DC얻음 CTime timer; // 시간객체생성 timer = CTime::GetCurrentTime(); // 현재시간얻음 CString strtimer; // 출력문자열선언 strtimer = timer.format(" 현재시간 < %H:%M:%S >"); // 현재시간을 " 시 : 분 : 초 " 형식으로문자열에대입 dc.textout(10, 10, strtimer); // 클라이언트영역에현재시간을출력 CView::OnTimer(nIDEvent); 25 사용자정의 Message MFC 가제공하지않는윈도우메시지 만약 WM_MYMESSAGE라는새로운메시지를만들고자한다면 #define문을이용하여 WM_USER이후의값을선언함 #define WM_MYMESSAGE WM_USER+1 해당메시지에대한메시지핸들러의원형을헤더부에설정함 afx_msg void OnMyMessage(WPARAM wparam, LPARAM lparam); BEGIN_MESSAGE_MAP/END_MESSAGE_MAP 사이에 ON_MESSAGE 매크로를이용하여정의한메시지명과함수명을연결 ON_MESSAGE(WM_MYMESSAGE, OnMyMessage) 메시지핸들러함수를구현함 void CMyView::OnMyMessage(WPARAM wparam, LPARAM lparam) CString p = (LPCSTR) lparam; //.. 26 사용자정의 Message 자주사용되는 Message Handler SendMessage 를사용해서사용자정의메시지를발생 사용자가만든메시지는사용자가직접메시지를발생시킴 WM_MYMESSAGE를생성하기위해서 SendMessage() 를사용함 SendMessage(WM_MYMESSAGE, 0, 0); LRESULT SendMessage ( UINT message, WPARAM wparam = 0, LPARAM lparam = 0); Message : 보내고자하는 Message ID wparam : 메시지정보와함께보내주는 WPARAM 인자 lparam : 메시지정보와함께보내주는 lparam 인자 void CUserTestView::OnLButtonDown(UINT nflags, Cpoint point) CString temp = 사용자메시지전송 ; SendMessage(WM_MYMESSAGE, 0, (LPARAM)(LPCSTR) temp); CView::OnLButtonDown(nFlags, point); 27 메시지 WM_CREATE WM_CLOSE WM_CHAR WM_KEYDOWN WM_LBUTTONDOWN WM_MOUSEMOVE WM_MOVE WM_PAINT WM_SETFOCUS WM_SIZE WM_TIMER WM_ERASEBKGND WM_HSCROLL 메시지핸들러 afx_msg int OnCreate(LPCREATESTRUCT); afx_msg void OnClose(); afx_msg void OnChar(UINT, UINT, UINT); afx_msg void OnKeyDown(UINT, UINT, UINT); afx_msg void OnLButtonDown(UINT, CPoint); afx_msg void OnMouseMove(UINT, CPoint); afx_msg void OnMove(int, int); afx_msg void OnPaint(); afx_msg void OnSetFocus(CWnd*); afx_msg void OnSize(UINT, int, int); afx_msg void OnTimer(UINT); afx_msg BOOL OnEraseBkgnd(CDC*); afx_msg void OnHScroll(UINT, UINT, 28CWnd*);
메시지박스 AfxMessageBox() 함수 사용자에게간단한메시지를출력하는데사용되는대화상자 함수원형 Int AfxMessageBox(LPCTSTR lpsztext, UINT ntype = MB_OK, UINT nidhelp = 0) lpsztext : 출력하고자하는문자열 ntype : 대화상자에설정되는버튼 nidhelp : 도움말 (F1) 을실행하였을때의도움말 ID 디폴트메시지박스스타일과아이콘 메시지박스 : MB_OK 아이콘 : MB_ICONEXCLAMATION 메시지박스 메시지박스스타일과반환값 메시지박스스타일 사용가능한버튼 MB_OK 확인 IDOK 반환값 MB_OKCANCEL 확인, 취소 IDOK,IDCANCEL MB_YESNO 예, 아니오 IDYES,IDNO MB_YESNOCANCEL 예, 아니오, 취소 IDYES,IDNO, IDCANCEL MB_RETRYCANCEL 재시도, 취소 IDRETRY,IDCANCEL MB_ABORTRETRYIGNORE 취소, 재시도, 무시 IDABORT,IDRETRY,IDIGNORE 29 30 메시지박스 아이콘스타일 [ 위험 ] MB_ICONHAND 또는 MB_ICONSTOP [ 정보 ] MB_ICONINFORMATION 또는 MB_ICONASTERISK 메시지박스 // 메시지박스사용예 int rv; rv = AfxMessageBox(" 확인하시겠습니까?", MB_YESNOCANCEL MB_ICONQUESTION); switch(rv) case IDYES: AfxMessageBox(" 예를눌렀습니다."); break; case IDNO: AfxMessageBox(" 아니오를눌렀습니다."); break; case IDCANCEL: AfxMessageBox(" 취소를눌렀습니다."); break; [ 물음 ] MB_ICONQUESTION[ 경고 ] MB_ICONEXCLAMATION 31 32