본문 바로가기
C & C++/C & C++

[MFC] Message 처리

by izen8 2011. 4. 25.
반응형

메시지

중단점 설정: 마우스 오른쪽을 눌러서 중단점 중단점 삽입

F5키를 눌러서 실행할 때 프로그램을 멈춤

추적점 설정: 마우스 오른쪽을 눌러서 중단점 추적점 삽입

F5키를 눌러서 실행할 때 프로그램은 실행되고 추적점이 설정된 곳의 메시지를 출력

 

1. MFC 프로그램의 흐름

1) 프로그램의 수행

- App 클래스의 생성자 수행

전역 객체 theApp 를 만들기 때문에 가장 먼저 수행

이와 동시에 AfxWinMain 수행하고 InitInstance() 호출

 

- App 클래스의 InitInstance() 수행

응용 프로그램의 초기화에 관련된 코드를 삽입

 

- Doc 클래스의 생성자 수행

 

- MainFrame 클래스의 생성자 수행

 

- MainFrame 클래스의 LoadFrame() 수행

윈도우 생성

 

- MainFrame 클래스의 PreCreateWindow() 수행

 

- MainFrame 클래스의 PreCreateWindow() 수행

윈도우를 생성하기 바로 전에 수행

 

- MainFrame 클래스의 OnCreate() 수행

- MainFrame 클래스의 OnCreateClient() 수행

- View 클래스의 생성자 수행

- View 클래스의 Create() 수행

- View 클래스의 PreCreateWindow() 수행

- View 클래스의 OnCreate() 수행

- View 클래스의 OnShowWindow() 수행

- MainFrame 클래스의 OnCreateClient()로 리턴

 

- MainFrame 클래스의 OnCreate()로 리턴

 

- Doc 클래스의 OnNewDocument()

 

- View 클래스의 OnInitialUpdate()

 

- MainFrame 클래스의 OnActivateApp()

 

- MainFrame 클래스의 OnActivate()

 

- MainFrame 클래스의 OnShowWindow()

 

- View 클래스의 GetDocument()

- App 클래스의 Run() – 메시지 처리 함수에서 대기(WM_QUIT가 올 때까지 리턴되면서 수행)

2) MFC 프로그램의 종료

- App 클래스의 Run() – WM_QUIT 메시지가 발생하면

- MainFrame 클래스의 OnClose()

- MainFrame 클래스의 OnShowWindow()

- MainFrame 클래스의 OnActivate()

- MainFrame 클래스의 OnActivateApp()

- MainFrame 클래스의 DestroyWindow()

- MainFrame 클래스의 OnDestroy()

- View 클래스의 OnDestroy()

- View 클래스의 PostNcDestroy()

- View 클래스의 소멸자

- MainFrame 클래스의 OnNcDestroy()

        - MainFrame 클래스의 PostNcDestroy()

- MainFrame 클래스의 소멸자

- MainFrame 클래스의 OnNcDestroy() 리턴

- Doc 클래스의 소멸자

- MainFrame 클래스의 OnClose()리턴

- App 클래스의 ExitInstance()

- App 클래스의 Run() 리턴

 

 


 

2. MFC의 메시지

1) 메시지의 의미

프로그램은 사용자와의 상호작용(Interaction)을 해서 실행하게 됩니다.

메시지는 프로그램에 어떤 이벤트가 발생했을 때 프로그램이 이를 처리하도록 운영체제에서 보내는 신호라고 할 수 있습니다.

프로그램이 실행되는 동안 프로그램이 받아들이는 사용자의 명령이나 운영체제의 신호 등을 합쳐 메시지(Message)라고 표현합니다.

프로그램은 자신에게로 보내지는 메시지를 받아 그 의미를 해석하고 적절한 함수를 호출하여 메시지를 처리합니다.

MFC에서는 각 메시지에 대응하여 그 메시지를 처리하는 함수를 호출해서 처리하게 되는데

이를 메시지 핸들러(Message Handler) 라고 합니다.

OnDraw라는 함수는 OnPaint 핸들러에서 호출되는 가상함수입니다.

OnDraw함수도 일종의 메시지 핸들러입니다.

프로그램이 처음 생성되었거나 다른 프로그램에 의해 가려졌다 다시 나타난 경우 화면을 다시 그리라는 메시지(WM_PAINT)가 프로그램으로 보내지며 이 때 프레임워크는 이 메시지를 처리하는 핸들러인 OnDraw를 호출하며 OnDraw에 작성된 코드에 의해 화면에 다시 문자열이 출력됩니다.

 

메시지의 종류

메시지: 윈도우에 발생하는 이벤트 처리

이벤트: 커맨드 메시지

컨트롤 통보 메시지: 윈도우 상에 존재하는 컨트롤들이 발생시키는 메시지

 

2) 메시지 처리의 순서

PreTranslateMessage(): 윈도우 프로시저 함수나 메시지 핸들러 함수를 호출하기 전에 호출

이 함수를 재정의하여 모든 윈도우 메시지 처리 가능

이 함수는 메시지 큐에서 메시지를 꺼낸 직후 처리됩니다.

따라서 특정 메시지를 처리하고 싶지 않다면 이 함수에 작성하면 됩니다.

하지만 이 함수도 SendMessage로 발생하는 메시지는 처리할 수 없습니다.

PostMessage에 의해 발생된 메시지는 큐에 저장된 후 실행되지만 SendMessage로 발생하는 메시지는 큐에 저장되지 않고 바로 수행되기 때문입니다.

 

3. MFC의 메시지 시스템

메시지 시스템(Message System)이란 메시지가 입력 될 경우 어떤 함수를 호출할 것 인가를 결정하는 메커니즘을 의미합니다.

마우스 왼쪽 버튼을 누르면 WM_LBUTTONDOWN 메시지가 발생하고 이 메시지를 처리하는 OnLButtonDown 함수가 호출됩니다.

이런 식으로 메시지와 메시지 핸들러와의 대응 관계를 설정하는 것이 메시지 시스템입니다.

MFC는 메시지와 이에 대응하는 메시지 핸들러를 미리 등록을 해두는데 이를 등록해둔 것이 DECLARE_MESSAGE_MAP() 매크로와 BEGIN_MESSAGE_MAP() 매크로입니다.

DECLARE_MESSAGE_MAP 매크로는 메시지 맵을 선언하는 역할을 하고 있습니다.

DECLARE_MESSAGE_MAP()

메시지 맵을 정의하는 문장은 cpp 파일에 있으며 다음과 같습니다.

BEGIN_MESSAGE_MAP(CMessageView, CView)

        // 표준인쇄명령입니다.

        ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)

        ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)

        ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)

END_MESSAGE_MAP()


 

4. 마우스 메시지

1) 클라이언트 영역 메시지

- 메시지 종류

메시지

발생시점

WM_LBUTTONDOWN

마우스 왼쪽 버튼을 눌렀을 때

WM_LBUTTONUP

마우스 왼쪽 버튼을 놓을 때

WM_LBUTTONDBLCLK

마우스 왼쪽 버튼을 두 번 클릭할 때

WM_RBUTTONDOWN

마우스 오른쪽 버튼을 눌렀을 때

WM_RBUTTONUP

마우스 오른쪽 버튼을 놓을 때

WM_RBUTTONDBLCLK

마우스 오른쪽 버튼을 두 번 클릭할 때

WM_MBUTTONDOWN

마우스 가운데 버튼을 눌렀을 때

WM_MBUTTONUP

마우스 가운데 버튼을 놓을 때

WM_MBUTTONDBLCLK

마우스 가운데 버튼을 두 번 클릭할 때

WM_MOUSEMOVE

마우스를 움직일 때

 

- 메시지 발생시 생성되는 부가 정보

UINT nFlags: 마우스와 같이 누른 키 값이 전달 됩니다.

2개 이상의 키를 사용하고자 하는 경우에는 OR(|)를 이용하면 됩니다.

MK_CONTROLS

Ctrl

MK_SHIFT

Shift

MK_LBUTTON

마우스 왼쪽 버튼이 눌러졌을 때

MK_RBUTTON

마우스 오른쪽 버튼이 눌러졌을 때

MK_MBUTTON

마우스 가운데 버튼이 눌러졌을 때

 

CPoint point: 마우스의 커서 위치

 

WM_MOUSEWHEEL: 마우스의 휠을 움직일 때 발생하는 메시지

UINT nFlags: 마우스와 같이 누른 키 값이 전달 됨

short zDelta:  휠의 진행 방향과 속도(120 의 배수로 전달)

CPoint pt: 마우스의 좌표

 

WM_SIZE 메시지

윈도우의 창 크기가 변경된 후 발생하는 메시지

매개변수

UINT nType: 메시지가 발생하게 된 동작

int cx: 가로 크기

int cy: 세로 크기

 

MFC에서 OnDraw를 호출하는 함수

Invalidate(BOOL bErase): TRUE이면 전체 화면을 지우고 호출하고 FALSE이면 지우지 않고 호출

InvalidateRect(LPCRECT lpRect, BOOL bErase): lpRECT 영역만큼을 bErase 옵션에 의해 지우고 호출하거나 지우지 않고 호출

RedrawWindow(): 윈도우를 다시 그리는 함수


 

예제1) 마우스를 이동할 때(WM_MOUSEMOVE) 마우스 커서의 위치를 화면에 표시하고 윈도우의 크기를 변경할 때(WM_SIZE) 윈도우의 크기를 화면에 표시해 주는 예제

예제2) 마우스를 드래그하면 선이 그려지는 예제(화면 복구 까지)

예제3) 문자열을 드래그 앤 드랍을 이용해서 움직이는 예제

예제)마우스가 현재 윈도우 영역에 있으면 있다고 출력하고 마우스의 좌표를 출력하고 윈도우 영역을 벗어나면 윈도우 영역 바깥에 있다고 출력하는 예제

2) 비 클라이언트 영역 메시지

- 메시지 종류

메시지

발생시점(클라이언트 바깥에서)

WM_NCLBUTTONDOWN

마우스 왼쪽 버튼을 눌렀을 때

WM_NCLBUTTONUP

마우스 왼쪽 버튼을 놓을 때

WM_NCLBUTTONDBLCLK

마우스 왼쪽 버튼을 두 번 클릭할 때

WM_NCRBUTTONDOWN

마우스 오른쪽 버튼을 눌렀을 때

WM_NCRBUTTONUP

마우스 오른쪽 버튼을 놓을 때

WM_NCRBUTTONDBLCLK

마우스 오른쪽 버튼을 두 번 클릭할 때

WM_NCMBUTTONDOWN

마우스 가운데 버튼을 눌렀을 때

WM_NCMBUTTONUP

마우스 가운데 버튼을 놓을 때

WM_NCMBUTTONDBLCLK

마우스 가운데 버튼을 두 번 클릭할 때

WM_NCMOUSEMOVE

마우스를 움직일 때

 

- 메시지 발생시 생성되는 부가 정보

UINT nHitTest: 마우스가 눌러진 위치

HTCAPTION

타이틀 바

HTHSCROLL

수평 스크롤 바

HTVSCROLL

수직 스크롤 바

HTTOP

상단 윈도우 경계

HTBOTTOM

하단 윈도우 경계

HTLEFT

좌측 윈도우 경계

HTRIGHT

우측 윈도우 경계

HTMINBUTTON

최소화 버튼

HTMAXBUTTON

최대화 버튼

HTCLOSE

종료 버튼

HTMENU

메뉴

HTSYSMENU

시스템 메뉴

HTCLIENT

클라이언트 영역

 

CPoint point: 마우스의 커서 위치


 

예제4) 마우스 메시지 연습

왼쪽 버튼 클릭: 마우스 왼쪽 버튼 클릭이라는 메시지를 커서 위치에 출력

오른쪽 버튼 클릭: 커서 위치의 문자 삭제

왼쪽 버튼 더블 클릭: 마우스 왼쪽 버튼 더블 클릭이라는 메시지를 커서 위치에 출력

마우스 휠: 마우스의 휠 정보를 출력

비 클라이언트 영역에서 왼쪽 버튼 클릭: 비 클라이언트 영역 별 정보를 출력

5. 키보드메시지

1) 키보드 메시지의 종류

메시지

발생 시점

WM_KEYDOWN

Alt, F10 등의 시스템 키를 제외한 키를 누를 때

WM_KEYUP

Alt, F10 등의 시스템 키를 제외한 키를 놓을 때

WM_CHAR

문자 키를 누를 때

WM_SYSKEYDOWN

Alt, F10 등의 시스템 키를 누를 때

WM_SYSKEYUP

Alt, F10 등의 시스템 키를 놓을 때

 

부가 정보

UINT nChar: 가상 키 코드

UINT nRepCnt: 반복 횟수

UINT nFlags: 기타 정보

 

nFlags

의미

0 ~ 7

키보드 스캔 코드로 제조사 별로 다름

8

Function 키나 오른쪽 키 패드의 키를 눌렀을 때 1로 설정

9 ~ 10

사용되지 않음

11 ~ 12

운영체제 내부적으로 사용

13

Alt 키가 눌러진 경우 1로 설정

14

다른 키가 눌러져 있었으면 1로 설정

15

키가 올라가면 1 눌러지면 0으로 설정

 

윈도우에서 WM_ KEYDOWN이 발생하면 이 때 누른 키 값을 조사합니다.

그 후 누른 키 값이 ASCII 값이면 WM_CHAR이 발생합니다.

이 메시지에서 주의할 점은 가상 키 코드는 키 별로 값이 할당되어 있기 때문에 대소문자를 구별할 수 없습니다.

a라는 문자를 누르면 WM_KEYDOWN은 무조건 65라는 값이 전달됩니다.

따라서 대소문자를 구분하여 처리해야 하는 경우는 WM_CHAR 메시지에서 처리해야 합니다.

윈도우의 가상키 코드는 다음과 같습니다.

VK_LBUTTON

01

VK_RBUTTON

02

VK_CANCEL

03

Ctrl-Break

VK_MBUTTON

04

 

VK_BACK

08

Backspace

VK_TAB

09

Tab

VK_CLEAR

0C

NumLock 가 꺼져 있을 때의 5

VK_RETURN

0D

Enter

VK_SHIFT

10

Shift

VK_CONTROL

11

Ctrl

VK_MENU

12

Alt

VK_PAUSE

13

Pause

VK_CAPITAL

14

Caps Lock

VK_ESCAPE

1B

Esc

VK_SPACE

20

스페이스

VK_PRIOR

21

PgUp

VK_NEXT

22

PgDn

VK_END

23

End

VK_HOME

24

Home

VK_LEFT

25

좌측 커서 이동키

VK_UP

26

위쪽 커서 이동키

VK_RIGHT

27

오른쪽 커서 이동키

VK_DOWN

28

아래쪽 커서 이동키

VK_SELECT

29

 

VK_PRINT

2A

 

VK_EXECUTE

2B

 

VK_SNAPSHOT

2C

Print Screen

VK_INSERT

2D

Insert

VK_DELETE

2E

Delete

VK_HELP

2F

 

 

30~39

숫자 키0~9

 

41~5A

영문자A~Z

VK_NUMPAD0~

60~69

숫자패드의0~9

VK_NUMPAD9

 

 

VK_MULTIPLY

6A

숫자패드의*

VK_ADD

6B

숫자패드의+

VK_SEPARATOR

6C

 

VK_SUBTRACT

6D

숫자패드의-

VK_DECIMAL

6E

숫자패드의.

VK_DIVIDE

6F

숫자패드의/

VK_F1~VKF16

70~7F

평션키F1~F16

VK_NUMLOCK

90

Num Lock

VK_SCROLL

91

Scroll Lock

 

 


 

2)WM_CHAR 메시지

WM_CHAR 메시지는 문자 키가 눌러졌을 때 발생하는 메시지입니다.

이번에는 키보드 입력을 받아 처리하는 예제를 만들어보겠습니다.

마우스 버튼이 눌러지면 마우스 메시지가 발생하는 것과 마찬가지로 사용자가 프로그램 실행 중에 키보드의 키를 누르면 키보드 메시지가 발생합니다.

키보드의 메시지를 받아 입력된 문자를 그대로 화면으로 출력해주는 프로그램을 만들 것 입니다.

 

예제1) 입력한 문자를 화면에 출력하는 예제

1. SDI 옵션으로 프로젝트 생성

 

2. 키보드로부터 입력된 문자를 저장할 수 있는 변수를 만듭니다.

Doc 클래스에 CString 형으로 m_str로 변수 선언

 

3. View 클래스의 WM_CHAR 메시지에 작성

C프로젝트명Doc* pDoc = GetDocument();

CString temp;

temp.Format(TEXT("%c"), nChar);

pDoc->m_str = pDoc->m_str + temp;

Invalidate (FALSE);

 

4.View 클래스의 OnDraw()에 작성

pDC->TextOut (0,0,pDoc->m_str);

3) WM_KEYDOWN

WM_CHAR 메시지는 키보드에 의해 발생하지만 키보드 메시지는 아닙니다.

키보드 키가 눌러짐에 따라 문자가 입력되면 WM_CHAR 메시지가 발생합니다.

그래서 WM_CHAR 메시지는 문자가 아닌 키에 대한 입력도 모두 문자로 인식합니다.

Enter키도 문자로 하나의 문자로 처리합니다.

키보드에 의해 직접적으로 발생하는 메시지는 WM_KEYDOWN 메시지와 WM_KEYUP 메시지이며 WM_KEYDOWN 메시지는 키보드에서 키가 눌러질 때 발생하며 반대로 WM_KEYUP은 키보드에서 눌러진 키가 떨어질 때 발생합니다.

WM_CHAR 메시지는 WM_KEYDOWN 메시지가 발생한 후 추가로 발생하는 메시지이며 WM_CHAR 메시지 다음에 WM_KEYUP 메시지가 발생합니다.

넘어오는 매개변수는 WM_CHAR와 동일합니다.

 

2개 이상의 키를 눌렀을 때 발생하는 메시지를 처리하고 싶다면 GetKeyState(가상키)를 이용하면 됩니다.

이 함수는 API 함수로 2개의 바이트를 리턴합니다.

상위 바이트의 첫번째 비트는 매개변수로 입력한 키가 눌러져 있다면 1로 셋팅되고 토글키인 경우 하위 바이트의 마지막 비트가 1로 셋팅됩니다.

 

예제2) 키 값을 알아보는 예제

예제3) 방향키를 누르면 도형이 이동하는 예제

예제4) 자식 윈도우를 생성하고 방향키를 누르면 움직이는 예제

6. 타이머메시지

사용자와는 전혀 상관없이 시스템의 내부적인 사정에 의해 발생하는 메시지도 있습니다.

그 대표적인 메시지가 타이머 메시지입니다.

타이머 메시지를 이용한 시계 프로그램을 만들어 보겠습니다.

타이머 메시지는 일정한 시간 간격으로 계속적으로 발생하는 메시지이며 규칙적인 작업을

계속 수행하는 프로그램에 적합한 메시지입니다.

원하는 시간 간격으로 타이머를 설정해 두면 지정한 시간마다 WM_TIMER 메시지를 발생시켜줍니다.

 

타이머의 생성

UINT_PTR SetTimer(UINT_PTR nIDEvent, UINT nElapse, void (CALLBACK* lpfnTimer));

nIDEvent: 타이머를 식별하기 위한 번호

nElapse: 타이머의 주기

lpfnTimer: 타이머를 처리하기 위한 메시지 처리 함수 NULL이면 WM_TIMER 메시지 발생

 

타이머의 종료

KillTimer(타이머 ID);

 

WM_TIMER의 메시지 처리함수는 OnTimer입니다.

OnTimer(UINT_PTR nIDEvent)

nIDEvent는 타이머의 식별 번호입니다.

여러 개의 타이머를 생성해서 사용하는 경우 타이머를 식별하기 위해 사용합니다.

 

 

예제) 타이머를 이용한 시계

예제) 바운스(원을 주기적으로 화면에 표시)

7. 기타 메시지

1) WM_CLOSE: 창 종료 버튼을 눌렀을 때 발생

 

2) WM_CONTEXTMENU: 마우스 오른쪽을 눌렀을 때 발생하는 메시지

CWnd* pWnd: 윈도우 핸들

CPoint point: 좌표

 

3) WM_CREATE: 윈도우가 만들어 질 때 발생하는 메시지

lpCreateStruct: 윈도우가 만들어 질 때의 구성요소에 대한 데이터

 

4) WM_DESTROY: 윈도우가 파괴될 때 발생하는 메시지

 

5) WM_KILLFOCUS: 윈도우가 포커스를 잃어 버렸을 때 발생

pNewWnd: 키보드 포커스를 얻은 윈도우의 주소가 전달

포커스를 얻은 윈도우가 없으면 NULL

 

6) WM_MOVE: 윈도우의 위치가 변경된 후 발생하는 메시지

x: 변경된 후의 x좌표

y: 변경된 후의 y좌표

 

7) WM_PAINT: 윈도우 화면을 다시 그릴 때 발생하는 메시지

- 윈도우가 처음 생성되었을 때

- 윈도우의 위치가 이동되었을 때

- 윈도우의 크기가 변경되었을 때

- 다른 윈도우에 가려져 있다가 드러날 때

- 스크롤 될 때

- InvalidateRect(), Invalidate()를 호출할 때

View 클래스에서 OnDraw()를 자동으로 호출합니다.

 

8) WM_FOCUS: 윈도우가 포커스를 받았을 때 발생하는 메시지

pOldWnd: 포커스를 잃은 윈도우의 주소가 전달

포커스를 잃은 윈도우가 없으면 NULL

반응형

'C & C++ > C & C++' 카테고리의 다른 글

[MFC] 파일 입출력  (0) 2011.04.25
[MFC] 대화 상자  (0) 2011.04.25
[MFC] 계층적 구조  (0) 2011.04.25
[MFC] 주요 클래스  (0) 2011.04.25
[MFC] 기본 구조  (0) 2011.04.25

댓글