'Page Up'이나 'Page Down'등을 눌렀을 때 응용 프로그램이 반응하려면 WM_KEYDOWN이나 WM_KEYUP 메시지를 처리한다.


인쇄 가능한 문자를 나타내는 키를 눌렀을 때는 (키누름이나 키해제 메시지를 무시하고) 키보드에서 입력된 문자를 나타내는 WM_CHAR 메시지를 처리하면 된다.


WM_KEYUP/DOWN 메시지 대신 WM_CHAR메시지를 사용하면 이벤트와 키 누름 주위의 상황(Shift가 눌러져 있는지 Caps Lock이 켜져 있는지)등에 대한 요인들을 Windows가 처리해주므로 쉽게 문자를 처리할 수 있다.



입력포커스


키보드로 부터 입력을 받는 창은 포커스를 갖고 있는 창이다.

Windows는 WM_SETFOCUS와 WM_KILLFOCUS 메시지를 통해 입력 포커스를 받거나 잃은 것에 대한 정보를 창에 알린다.


void CWnd::OnSetFocus(CWnd* pOldWnd)    // pOldWnd는 포커스를 잃은 창

void CWnd::OnKillFocus(CWnd* pNewWnd)   // pNewWnd는 포커스를 얻은 창


CWnd::SetFocus() 를 사용하면 입력 포커스를 다른 창으로 옮길 수 있다.


CWnd* pFocusWnd = CWnd::GetFocus() 를 사용하면 현재 누가 입력포커스를 갖고 있는지 알 수 있다.(다른 응용프로그램의 창 포인터는 얻을 수 없다. NULL이 리턴된다.)



키입력 메시지


키가 눌려지면 입력포커스가 있는 창에 가상 키코드와 함께 WM_KEYDOWN 메시지가 전달된다.

그리고, 키가 해제되면 WM_KEYUP 메시지가 전달된다.


'Alt'와 'F10'키를 제외한 모든 키는 WM_KEYDOWN/WM_KEYUP 메시지를 발생시킨다.

Alt 키를 누르면 WM_KEYDOWN/WM_KEYUP이 아니라 WM_SYSKEYDOWN/WM_SYSKEYUP 메시지가 발생한다.

F10 키를 누르면 메뉴 바로가기 처리 상태로 변환된다.


WM_KEYDOWN/WM_KEYUP, WM_SYSKEYDOWN/WM_SYSKEYUP은 각각 OnKeyDown/OnKeyUp, OnSysKeyDown/ONKeyUp 메시지 핸들러 함수와 연결된다.


위의 키입력 메시지 핸들러 함수의 원형은 다음과 같다.

afx_msg void OnMsgName( UINT nChar, UINT nRepCnt, UINT nFlags )


  nChar : 누르거나 뗀 키의 가상 키 코드

  nRepCnt : 키 누름 반복 횟수, 한 키를 계속 누르고 있는 경우, 한 메시지로 처리하기 위해..

  nFlags : 키의 스캔코드와 아래표에 정리한 비트 플래그의 조합

 

     0~7 : OEM 스켄코드 - 8비트 OEM 스캔코드

     8 : 확장 키 플래그 - 확장키는 1, 아니면 0

     9~10 : 예약됨 - N/A

     13 : 컨텍스트 코드 - Alt 키가 눌러져 있으면 1, 아니면 0

     14 : 이전 키의 상태 - 바로전에 키를 눌렀으면 1, 아니면 0

     15 : 변화 상태 - 키가 눌려 있으면 0, 아니면 1


** 컨텍스트코드는 메시지가 생성될 때 Alt 키가 눌려졌는지를 나타낸다.


일반적으로 WM_SYSKEYDOWN/WM_SYSKEYUP은 Windows가 처리하도록 한다.(::DefWindowProc)



가상 키 코드


Windows는 가상키코드를 통해 키를 식별하므로 프로그래머는 키보드마다 달라지는 하드코드값이나 OEM스캔코드값을 신경쓸 필요가 없다.


가상 키 코드는 Winuser.h에 정의되어 있다.


문자처리는 위의 키입력 메시지 핸들러에서 가상 키를 처리하는 것보다 WM_CHAR 메시지로 처리하는게 더 좋다.


** 가상키를 제공하지 않는.. 문자도 숫자도 아닌 키도 있고.. 이러한 키는 국가별로 다르기도 하고.. 등등..



Shift 키 상태와 토글


WM_KEYDOWN/WM_KEYUP, WM_SYSKEYDOWN/WM_SYSKEYUP 메시지를 처리할 때 Shift, Ctrl, Alt 의 상태를 확인해야 할 필요가 있을 것이다.

이때는 ::GetKeyState 함수를 이용한다. 이 함수는 원하는 키가 눌려져 있는지 보고한다.


::GetKeyState(VK_SHIFT)         // Shift 키가 눌려져 있으면 음수값을 리턴.

::GetKeyState(VK_CONTROL)    // Ctrl 키가 눌려져 있으면 음수값을 리턴.

..


// Ctrl + 왼쪽 화살표 일때만 ...

if ((nChar == VK_LEFT) && (::GetKeyState(VK_CONTROL) < 0))  {

   ...

}


** GetKeyState에서 Alt를 체크할 필요는 없을 것이다. Alt 키는 누르면 WM_KEYDOWN/UP 이 아니라 WM_SYSKEYDOWN/UP 메시지가 발생하니 해당 핸들러함수에서 처리하면 된다.


** VK_LBUTTON, VK_MBUTTON, VK_RBUTTON 식별자와 GetKeyState 함수를 결합하면 마우스 단추가 눌려 잇는지 알 수 있다.


** GetKeyState는 다음과 같이 Num Lock, Caps Lock, Scroll Lock 가 켜져 있는지 꺼져 있는지 확인할 수 있다.

  ::GetKeyState( VK_NUMLOCK ) & 0x01    // 켜져 있으면 0


::GetKeyState 는 키보드 메시지 핸들러에서만 사용해야 한다.

키보드 메시지 핸들러 외부에서 키나 마우스단추를 확인하려면 ::GetAsyncKeyState 를 사용한다.



문자 메시지


WM_KEYDOWN/UP등과 같은 키보드 메시지 핸들러 텍스트 편집기와 같은 응용 프로그램을 만든다면 어떻게 작업해야 할까...

프로그래머는 직접 가상 키코드값과 Shift, Caps Lock, Ctrl 등의 키 조합을 다 다루어야 할 것이다.


그러나 Windows는 ::TranslateMessage API 함수를 통해서 문자키를 갖는 키 입력 메시지를 WM_CHAR 메시지로 변환해준다.

따라서 문자가 들어가는 키입력은 WM_CHAR 메시지 핸들러에서 처리하면 된다.


** Alt가 눌려진 상태에서 문자가 들어간 키입력은 WM_SYSCHAR 메시지 핸들러로 처리한다.

** Alt 조합은 일반적으로 특별한 목적에 사용되기 때문에 대부분 WM_SYSCHAR는 무시한다.


afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )


  nChar : ANSI나 Unicode 문자 코드가 저장된다.

  nRepCnt, nFlags 는 키 입력 메시지와 동일하다.



데드 키 메시지


언급되지 않은 키보드 메시지가 2개 있다. 이 메시지는 거의 사용되지 않는다.


데드 키는 발음 부호를 나타낸다. 즉, 데드 키를 입력한 다음 문자를 입력하면 액센트 기호가 붙은 문자를 표현할 수 있다.


::TranslateMessage 는 데드키에 해당하는 WM_KEYUP를 WM_DEARCHAR로 변환하고 데드 키에 해당하는 WM_SYSKEYUP을 WM_SYSDEADCHAR 메시지로 변환한다.



캐럿


CWnd는 다음과 같이 7개의 캐럿 함수를 제공한다. 그리고 제공되지는 않지만 ::DestroyCaret 함수도 반드시 필요한 함수이다.


CreateCaret - 비트맵을 이용하여 캐럿을 만든다.

CreateSolidCaret - 실선 캐럿이나 블록 캐럿을 만든다.

CreateGrayCaret - 회색 선 캐럿이나 블록 캐럿을 만든다.

GetCaretPos - 캐럿의 위치를 얻는다.

SetCaretPos - 캐럿의 위치를 설정한다.

ShowCaret - 캐럿을 표시한다.

HideCaret - 캐럿을 감춘다.


** ::DestroyCaret 생성된 캐럿을 파괴한다.


캐럿은 동일한 스레드에서 실행되는 모든 창에 의해 공유되는 스레드 단위 자원이다.


캐럿을 사용하는 규칙은 다음과 같다.


  1. 입력 포커스를 받을 때 캐럿을 만들어야 하고 입력 포커스를 잃을 때는 삭제해야 한다.


  2. 캐럿이 만들어져 있다해도 ShowCaret를 해야 표시된다. HideCaret를 하면 숨길 수 있다.

    HIdeCaret를 두번연속 했다면 ShowCaret도 두번해야 캐럿을 다시 볼 수 있다.


  3. OnPaint 외부에서 캐럿을 포함하고 있는 창 영역을 그릴 때는 캐럿을 숨겨야 한다.

    (안그러면 화면이 깨진다.)

    OnPaint 안에서 캐럿을 숨기거나 다시 표시할 필요는 없다.

    ::BeginPain와 ::EndPaint가 알아서 해준다.


  4. SetFocusPos로 캐럿을 이동시킬 수 있다.

    즉, 윈도우가 자동으로 이동시켜주지 않는다. 프로그래머가 알아서 해야 한다.



캐럿의 폭을 찾 테두리의 폭에 맞추는 경우도 있다.

SM_CXBORDER 값과 함께 ::GetSystemMetrics를 호출하면 창 테두리의 폭을 알 수 있다.


고정폭 글꼴에서는 캐럿의 폭과 높이를 글자 하나의 크기와 같도록 할 수도 있다.


캐럿이동은 CWnd::SetCaretPos로 한다.


고정폭 글꼴은 문자의 위치에 한 문자의 폭을 곱하면 새로운 문자의 위치를 알 수 있다.


가변폭 글꼴은 CDC::GetTextExtent나 CDC::GetTabbedTextExtent 함수를 이용하여 논리적 단위로 가변폭 글꼴의 문자열 폭을 계산할 수 있다.


** CWnd나 CWnd를 상속받은 창에서 캐럿을 사용하려면 대단히 귀찮은 작업들을 많이 해줘야 한다.   따라서 웬만하면 CEdit와 같은 컨트롤로 해결하도록 한다.

그러나... CEdit와 같은 클래스로 할 수 없는 다양한 에디팅처리가 꼭 필요하다면 할수 없이 캐럿을 사용해야 한다.

첨부된 파일은 CWnd를 상속받은 창에서 캐럿을 처리하는 정석을 보여준다.

신고

'WindowsPrograming' 카테고리의 다른 글

WM_KEYDOWN 메시지  (0) 2007.04.09
[MFC]키보드로부터 입력  (1) 2007.04.09
DC (Device Context)  (0) 2007.04.05
SetWindowEXt() SetViewportExt()는  (2) 2007.04.04
Posted by Real_G