[CHAPTER 03] 메시지 처리

2023. 5. 14. 19:46기타(이론)/C++ MFC 윈도우 프로그래밍

1) 메시지 처리의 기본 개념

메시지를 처리한다 ↔ 프로그래밍 한다

<메시지의 종류>
1. 윈도우 메시지 : WM_로 시작(WM_COMMAND제외)
2. 컨트롤 통지 메시지 : Button, Combo Box와 같은 제어 객체나 자식 윈도우에서 부모 윈도우로 보내는 메시지
3. 명령 메시지 : WM_COMMAND

<메시지를 처리하기 위한 3단계>
1. 윈도우 클래스의 멤버 함수로 메시지 핸들러 함수를 선언한다.
2. 메시지 맵에 메시지와 메시지 핸들러 함수를 묶는 메시지의 매크로를 추가한다.
3. 메시지 핸들러 함수의 기능을 구현한다.

<메시지 핸들러 함수>
메시지 유형 발생 상황 메시지 핸들러 함수
WM_CREATE 윈도우가 생성될 때 OnCreate()
WM_ACTIVE 윈도우가 활성화 될 때 OnActive()
WM_PAINT 윈도우가 다시 그려져야 될 때 OnPaint()
WM_SIZE 윈도우가 생성될 때
윈도우가 크기가 변경될 때
OnSize()
WM_MOVE 윈도우가 움직일 떄 OnMove()
WM_TIMER 설정된 타이머 시간이 됐을 떄 OnTimer()
WM_DESTROY 윈도우가 종료될 때 OnDestroy()

2) 메시지 박스

int AfxMessageBox(LPCTSTR IpszText, UINT nType = MB_OK, UINT nIDHelp = 0);
  • IpszText : 출력하고자 하는 문자열
  • nType : 메시지 박스 출력 스타일
  • nIDHelp : 현재 상태에서 F1키를 눌러 도움말을 실행하였을 때의 도움말 ID
메시지 박스 버튼 스타일 사용 가능한 버튼 AfxMessageBox 함수의 반환 값
MB_OK  확인 IDOK
MB_KOCANCEL 확인 취소 IDOK, IDCANCEL
MB_YESNO 예 아니요 IDYES, IDNO
MB_YESNOCANCEL 예 아니요 취소 IDYES, IDNO, IDCANCEL
MB_RETRYCANCEL 다시시도 취소 IDRETRY, IDCANCEL
MB_ABORTRETRYIGNORE 중단 다시시도 무시 IDABORT, IDRETRY, IDIGNORE
int CPractice3aView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  여기에 특수화된 작성 코드를 추가합니다.
	//윈도우가 생성될 때 메시지 박스 출력
	AfxMessageBox(_T("윈도우가 생성되었습니다."), MB_OKCANCEL | MB_ICONINFORMATION);

	return 0;
}


void CPractice3aView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	//왼쪽 마우스를 더블 클릭할 때 메시지 박스 출력
	AfxMessageBox(_T("왼쪽 마우스를 더블클릭했습니까?"), MB_YESNO | MB_ICONQUESTION);

	CView::OnLButtonDblClk(nFlags, point);
}


void CPractice3aView::OnDestroy()
{
	CView::OnDestroy();

	// TODO: 여기에 메시지 처리기 코드를 추가합니다.
	//윈도우가 종료될 때 메시지 박스 출력
	AfxMessageBox(_T("윈도우가 종료되었습니다."), MB_OK | MB_ICONWARNING);
}​

3) 마우스 메시지

메시지 유형 발생 상황 메시지 핸들러 함수
WM_MOUSEMOVE 마우스를 이동 OnMouseMove()
WM_LBUTTONDBLCLK 왼쪽 마우스 버튼을 더블 클릭 OnLButtonDblClk()
WM_LBUTTONDOWN 왼쪽 마우스 버튼을 누름 OnLButtonDown()
WM_LBUTTONUP 왼쪽 마우스 버튼을 놓음 OnLButtonUp()
WM_RBUTTONDBLCLK 오른쪽 마우스 버튼을 더블 클릭 OnRButtonDblClk()
WM_RBUTTONDOWN 오른쪽 마우스 버튼을 누름 OnRButtonDown()
WM_RBUTTONUP 오른쪽 마우스 버튼을 놓음 OnRButtonUp()
WM_MOUSEWHEEL 마우스 휠을 움직임 OnMouseWheel()

<nFlags가 가질 수 있는 값>

  • MK_CONTROL : ctrl 키가 눌림
  • MK_LBUTTON : 왼쪽 마우스 버튼 눌림
  • MK_MBUTTON : 가운데 마우스 버튼 눌림
  • MK_RBUTTON : 오른쪽 마우스 버튼 눌림
  • MK_SHIFT : shift 키가 눌림
CPractice3bView::CPractice3bView() noexcept
{
	// TODO: 여기에 생성 코드를 추가합니다.
	m_bTimerRun = false; //변수 초기화
	m_bTimerType = true;

}
int CPractice3bView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	if (CView::OnCreate(lpCreateStruct) == -1)
		return -1;

	// TODO:  여기에 특수화된 작성 코드를 추가합니다.
	SetTimer(0, 1000, NULL); //타이머 설정 //(타이머 ID, 메시지 발생시킬 간격, 메시지 발생시 실행되는 함수)
	m_bTimerRun = TRUE; //타이머 동작

	return 0;
}


void CPractice3bView::OnTimer(UINT_PTR nIDEvent)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	int hour;
	CString str;
	CTime timer;
	timer = CTime::GetCurrentTime();

	if (m_bTimerType)
	{
		m_strTimer.Format(_T("현재는 %d년 %d월 %d일 %d시 %d분 %d초"),
			timer.GetYear(), timer.GetMonth(), timer.GetDay(),
			timer.GetHour(), timer.GetMinute(), timer.GetSecond());
	}
	else
	{
		hour = timer.GetHour();
		if (hour >= 12)
		{
			str = _T("PM");
			if (hour >= 13)
				hour -= 12;
		}
		else
		{
			str = _T("AM");
		}
		m_strTimer.Format(_T("지금은 %s %d시 %d분 %d초"), str, hour,
			timer.GetMinute(), timer.GetSecond());
	}
	Invalidate();
	CView::OnTimer(nIDEvent);
}


void CPractice3bView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_bTimerType) //년, 월, 일, 시, 분, 초 형태로 출력 중
	{
		if (AfxMessageBox(_T("시,  분, 초 형태로 표시하겠습니까?"),
			MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			m_bTimerType = false;
		}
	}
	else //시, 분, 초 형태로 출력 중
	{
		if(AfxMessageBox(_T("년, 월, 일, 시, 분, 초형태로 표시하겠습니까?"),
			MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			m_bTimerType = true;
		}
	}
	CView::OnLButtonDown(nFlags, point);
}


void CPractice3bView::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_bTimerRun == false)
	{
		if (AfxMessageBox(_T("디지털시계를 동작시키겠습니까?"),
			MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			SetTimer(0, 1000, NULL); //타이머 설정
			m_bTimerRun = true; //타이머 동작 => true
		}
	}
	else //타이머가 동작 중일 때 메시지 박스 출력
	{
		if (AfxMessageBox(_T("정말로 디지털시계 동작을 멈추겠습니까?"),
			MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			KillTimer(0); //타이머 해제
			m_bTimerRun = false; //타이머 동작 => false
		}
	}

	CView::OnRButtonDown(nFlags, point);
}


void CPractice3bView::OnDestroy()
{
	CView::OnDestroy();

	// TODO: 여기에 메시지 처리기 코드를 추가합니다.
	if (m_bTimerRun)
		KillTimer(0); //타이머 해제
}

4) 키보드 메시지

메시지 유형 발생 상황 메시지 핸들러 함수
p147    
     
     
     
     
     

가상키 코드 대응되는 키보드 가상키 코드 대응되는 키보드
p148      
       
       
       
       
       
       
       
       
       
       
void CPractice3cView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	int nCharIndex;
	nCharIndex = m_strOutput.GetLength(); //입력된 데이터의 길이를 얻는다.

	if (nChar == VK_BACK) //백스페이스가 눌린경우
		m_strOutput.Delete(nCharIndex - 1, 1); //한 번에 하나씩 지운다
	else //백스페이스 이외의 키가 눌린 경우
		m_strOutput += (WCHAR)nChar; //키보드로 입력된 문자를 문자열에 추가
	
	Invalidate(); //화면갱신
	CView::OnChar(nChar, nRepCnt, nFlags);
}


void CPractice3cView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	switch (nChar) //가상키 코드 값에 대해
	{
	case VK_LEFT: //왼쪽 화살표 키를 누를 때
		m_ptLocation.x--; //왼쪽으로 1픽셀 이동
		break;
	case VK_RIGHT: //오른쪽 화살표 키를 누를 때
		m_ptLocation.x++; //오른쪽으로 1픽셀 이동
		break;
	case VK_UP: //위쪽 화살표 키를 누를 때
		m_ptLocation.y--; //위쪽으로 1픽셀 이동
		break;
	case VK_DOWN: //아래쪽 화살표 키를 누를 때
		m_ptLocation.y++; //아래쪽으로 1픽셀 이동
		break;
	case VK_PRIOR: //Pg UP 키를 누를 때
		m_ptLocation.x--; //위쪽으로 1픽셀 이동
		break;
	case VK_NEXT: //Pg dn 키를 누를 때
		m_ptLocation.x--; //아래쪽으로 1픽셀 이동
		break;
	case VK_HOME: //Home 키를 누를 때
		m_ptLocation.x--; //처음 위치로 이동
		break;
	}

	if (m_ptLocation.x < 0) //왼쪽 경계선을 만나면
	{
		m_ptLocation.x = 0; //m_ptLocation.x = 0으로 초기화
		AfxMessageBox(_T("왼쪽으로 더 이상 이동할 수 없습니다."));
	}
	if (m_ptLocation.y < 0) //위쪽 경계선을 만나면
	{
		m_ptLocation.y = 0; //m_ptLocation.y = 0으로 초기화
		AfxMessageBox(_T("위쪽으로 더 이상 이동할 수 없습니다."));
	}
	if (m_ptLocation.x > m_ptClientSize.x) //오른쪽 경계선을 만나면
	{
		m_ptLocation.x = m_ptClientSize.x; //m_ptLocation.x = 윈도우 x 크기로 초기화
		AfxMessageBox(_T("오른쪽으로 더 이상 이동할 수 없습니다."));
	}
	if (m_ptLocation.y > m_ptClientSize.y) //아래쪽 경계선을 만나면
	{
		m_ptLocation.y = m_ptClientSize.y; //m_ptLocation.y = 윈도우 y 크기로 초기화
		AfxMessageBox(_T("아래쪽으로 더 이상 이동할 수 없습니다."));
	}
	Invalidate(); //화면 갱신


	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}


void CPractice3cView::OnSize(UINT nType, int cx, int cy)
{
	CView::OnSize(nType, cx, cy);

	// TODO: 여기에 메시지 처리기 코드를 추가합니다.
	m_ptClientSize.x = cx; //윈도우 영역의 가로(x) 길이를 얻음
	m_ptClientSize.y = cy; //윈도우 영역의 세로(y) 길이를 얻음

	Invalidate(); //화면 갱신
}


void CPractice3cView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (point.x >= m_ptLocation.x - 30 && point.x <= m_ptLocation.x + 30 && point.y > m_ptLocation.y - 30 && point.y <= m_ptLocation.y + 30)
	{
		m_bDrag = true;
	}
	m_ptLocation = point;
	Invalidate();

	CView::OnLButtonDown(nFlags, point);
}


void CPractice3cView::OnMouseMove(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_bDrag)
	{
		m_ptLocation = point;
	}
	Invalidate();

	CView::OnMouseMove(nFlags, point);
}


void CPractice3cView::OnLButtonUp(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_bDrag)
	{
		m_bDrag = false;
		m_ptLocation = point;
	}
	Invalidate();

	CView::OnLButtonUp(nFlags, point);
}


void CPractice3cView::OnRButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 여기에 메시지 처리기 코드를 추가 및/또는 기본값을 호출합니다.
	if (m_strOutput.IsEmpty() == false)
	{
		if (AfxMessageBox(_T("문자열을 모두 지우시겠습니까?"),
			MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			m_strOutput.Empty();
		}
	}
	else
	{
		AfxMessageBox(_T("문자열을 입력하시오."));
	}
	Invalidate();

	CView::OnRButtonDown(nFlags, point);
}​