본문 바로가기

Windows/_Windows Programming

입력하기

1. 키보드 입력

WM_CHAR 메시지 : 문자키 메시지

키보드로부터 입력이 발생했을 경우 윈도우즈는 포커스를 가진 프로그램에게 키보드 메시지(WM_CHAR, WM_KEYDOWN)를 보내주며 프로그램은 이 메시지를 받아 키보드 입력을 처리한다.
(참조 : 포커스(Focus)란 입력초점이라는 뜻 즉 포커스를 가진 프로그램 = 활성화된 윈도우
오직 하나의 프로그램만이 활성화 될 수 있다)

키보드로부터 입력된 키값들을 화면으로 출력해주는 예제이다.
(윈도우 프로그램 기본틀에 수정을 하면 된다 http://dakuo.tistory.com/entry/1)

LRESULT CALLBACK WndProc(HWND hWnd, iMessage, WPARAM wParam,
                                             LPARAM lPara)
{
         HDC hdc;
         PAINTSTRUCT ps;
         static char str[256];
         int len;
         switch(iMessage)
         {
                 case WM_CHAR:
                         len = strlen(str);
                         str[len]=(TCHAR)wParam;
                         str[len+1]=0;
                         InvalidateRect(hWnd, NULL, FALSE);
                         return 0;
                 case WM_PAINT:
                         hdc=BeginPaint(hWnd, &ps);
                         TextOut(hdc, 100, 100, str, strlen(str));
                         EndPaint(hWnd, &ps);
                         return 0;
                 case WM_DESTORY:
                         PostQuitMessage(0);
                         return 0;
           }
           return(DefWindowProc(hWnd, iMessage, wParam, lParam);
}

키보드에서 키를 누르면 입력한 문자들이 화면 상단에 출력된다.
WndProc을 보면 str을 선언해 사용자가 입력한 문자들을 모은다.(지역변수로 선언되면 계속 초기화 되므로 static을 붙여 정적 변수로 선언한다)

WM_CHAR 메시지는 입력된 문자의 아스키 코드를 wParam으로 전달하며 wParam의 값을 읽어 사용자가 어떤 키를 눌렀는지를 알아내게 된다.
lParam에는 비트별로 다음과 같은 정보가 전달된다.


메시지 별로 필요한 추가 정보는 wParam, lParam 을 통해 넘어오는데 메지별로 사용법이 다르므로 추가 정보에 대한 사항은 메시지별로 알아야 한다.

len = strlen(str);
str[len]=(TCHAR)wParam;
str[len+1]=0;

문자열의 제일 끝 부분에 wParam을 써 넣고 뒤쪽에 0을 써넣어 문자열 끝을 표시한다.
즉 키 입력이 있을때마다 반복함으로써 str 문자 배열에 입력된 키값을 차곡차곡 쌓는다.
(이러는 이유는 문자열 뒤에는 항상 0이 와야되기 때문이다)

그런데 키보드 메시지에 의해 문자열이 다시 입력되더라도 화면상의 변화는 없으므로 WM_PAINT 메시지가 발생되지 않아 새로 입력한 글자가 출력되지 않는다. 따라서 강제로 WM_PAINT 메시지를 발생시키기 위해 InvalidateRect 함수를 호출한다. WM_CHAR에서 문자열을 조립 후 InvalidateRect 함수를 호출해 주어 키보드가 입력될 때마다 화면을 다시 그리도록 한다.


무효 영역 :

원래 그려져야 할 모습과는 다른 모습을 가지고 있다. 즉 다시 그려져야 할 필요가 있다.

WM_PAINT 메시지를 받아 다시 그려지면 무효영역은 없어진다. 운영체제는 윈도우의 일부가 지워졌을 때 지워진 작업영역을 무효로 만들어 윈도우가 다시 그려지도록 해준다.

단 내부적인 변화에 의해 화면이 다시 그려져야 할 때는 운영체제가 작업영역을 무효화하지 않는다 따라서 내부에서 다시 그려질수 있도록 해야 한다

BOOL InvalidateRect(HWND hWnd, CONST RECT *lpRect, BOOL bErase);

윈도우의 작업영역을 무효화시켜 운영체제로 하여금 WM_PAINT 메시지를 해당 윈도우로 보내도록 한다.
첫 번째 파라미터 hWnd 는 무효화 할 윈도우, 두 번째 파라미터 lpRect 는 무효화의 대상이 되는 사각영역(NULL일 경우 윈도우의 전 영역이 무효화되지만 실행속도가 느려진다)
세 번째 파라미터 bErase 는 무효화되기 전에 배경을 모두 지운 후 다시 그릴 것인지 아닐 것인지를 지정한다. TRUE 경우 배경을 지우고 FALSE 경우 배경을 지우지 않는다.
(참조 : 세 번째 파라미터를 FALSE로 줄경우 잔상이 남습니다. 즉 꼬리가 남습니다. 그래도 유효한 문자는 마지막에 출력된 것이며 다른 윈도우로 가리게 되면 잔상이 지워집니다) 


WM_KEYDOWN :

문자가 아닌 모든 키에 대해서 발생한다. (단 Alt 키, 윈도우 키, 한영 키 등의 특수 키 제외)
wParam 으로 가상 키코드라는 것을 전달해준다.

 가상키 코드  값  키
 VK_LBUTTON  1  
 VK_RBUTTON  2  
 VK_CANCEL  3  Ctrl-Break
 VK_MBUTTON  4  
 VK_BACK  8  Backspace
 VK_TAB  9  Tab
 VK_CLEAR  C  NumLock 꺼져있을때 5
 VK_RETURN  D  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_LWIN  5B  윈쪽 윈도우 키
 VK_RWIN  5C  오른쪽 윈도우 키
 VK_APP  5D  Application 키
 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~VK_F24  70~87  펑션키 F1~F24
 VK_NUMLOCK  90  Num Lock
 VK_SCROLL  91  Scroll Lock

WM_KEYDOWN 메시지 처리 루틴에서 wParam 값과 가상 키코드값을 비교해 봄으로써 어떤 키가 눌러졌는지 알수 있다.
(참고 : lParam 으로 전달되는 메시지는 WM_CHAR 와 동일하나 잘 사용되지 않는다)

가상키보드는 범용적인 코드이다. 현재 키보드에 없는 키값도 미리 정의되어 있으며(VK_SELECT, VK_EXECUTE, VK_HELP 등) Function 키도 F24까지 있다. 이렇게 한 이유는 다른 시스템으로 윈도우즈를 이식하더라도 키코드를 그대로 쓰기 위함이다.

WM_KEYUP 메시지는 키가 떨어질 때 발생한다. wParam, lParam의 의미는 WM_KEYDOWN과 동일하다. 키보드는 눌러질 때 입력된 것으로 간주하기에 잘 사용되지 않는다.


TranslateMessage :

키보드에서 'S'키를 눌렀다고 가정해보자. WM_KEYDOWN -> WM_CHAR -> WM_KEYUP
순서로 메시지가  발생한다. 그런데 이중에 WM_CHAR 메시지는 사용자에 의해 발생하는 메시지가 아니다. 메시지 루프에서

while(GetMessage(&Message, 0, 0, 0))
{
       TranslateMessage(&Message);
       DispatchMessage(&Message);
}

TranslateMessage 함수는 GetMessage 가 메시지 큐에서 꺼내온 메시지를 넘겨 받아 WM_KEYDOWN 메시지이면 눌러진 키가 문자키인지 검사한 후 조건이 맞을 경우 WM_CHAR 메시지를 만들어 메시지 큐에 덧붙인다. 조건이 맞지 않을 경우엔 DispatchMessage 함수에 의해 WndProc 으로 보내진다.

TranslateMessage 가 하는 일은 간단해 보이지만 실제 내부적으로 많은 계산을 한다.
문자키는 키보드 레이아웃에 따라 달라지기 때문에 키보드 디바이스 드라이버에 의해 결정된문자키로 입력키가 문자키인지를 검사한 후 Shift 키와 Caps Lock 키 상태 등을 종합 판단한 후에 최종 입력된 문자를 WM_CHAR 메시지로 보낸다.


WM_SYS :

앞에 SYS가 붙는 키보드 메시지는 모두 Alt키와 함께 눌러지는 키보드 메시지 들이며
(WM_SYSKEYDOWN, WM_SYSCHAR, WM_SYSKEYUP 등)이 메시지들은 시스템 내부적인 용도로 사용하므로 응용 프로그램에서 처리하는 경우는 드물다.

이 메시지를 받아 처리하기 위해서는 처리 한후 DefWindowProc 으로 리턴해줘야 한다.
(참고 : DefWindowProc로 리턴해주지 않으면 Alt키와 함께 동작하는 키보드 조합을 인식하지 못한다)



2. 마우스 입력

Mouse :

마우스 입력에 관한 메시지는 다음과 같은 종류가 있다.

 버튼   누름   놓음  더블클릭 
 좌측  WM_LBUTTONDOWN  WM_LBUTTONUP  WM_LBUTTONDBLCLK
 우측  WM_RBUTTONDOWN  WM_RBUTTONUP  WM_RBUTTONDBLCLK
 중앙  WM_MBUTTONDOWN  WM_MBUTTONUP  WM_MBUTTONDBLCLK

 값  의미 
 WM_MOUSEMOVE  마우스 이동
 WM_MOUSEWHEEL  휠조작

마우스 메시지는 lParam 의 상위 워드에 마우스 버튼이 눌러진 y좌표, 하위 워드에 x 좌표를 가지며 좌표값을 검출하기 위해 HIWORD, LOWORD 등의 매크로 함수를 사용한다.
(참조 : 마우스 메시지가 발생한 위치의 좌표는 (LOWORD(lParam), HIWORD(lParam)))
(참고 : 좌표값은 음수가 될수도 있는데 듀얼 모니터를 사용할 때 한쪽 모니터의 입장에서 볼때 좌표값이 음수가 되는 경우와 마우스를 캡처했을 때 음수 좌표가 전달되는 경우다.
이때는 (int)형으로 한번 더 캐스팅해서 부호를 제대로 변환해야 한다)

wParam 에는 마우스 버튼의 상태와 키보드 조합 키(Shift, Ctrl)의 상태가 전달된다. 조합키 상태는 비트 연산을 해보면 알 수 있다.

 값  의미
 MK_CONTROL  Ctrl 키가 눌러져 있다.
 MK_LBUTTON  마우스 왼쪽 버튼이 눌러져 있다.
 MK_RBUTTON  마우스 오른쪽 버튼이 눌러져 있다.
 MK_MBUTTON  마우스 중간 버튼(휠 버튼)이 눌러져 있다.
 MK_SHIFT  Shift 키가 눌러져 있다.

(참고 : Win16 에서 wParam은 16비트였고 lParam은 32비트였다. wParam의 w는 WORD,
lParam의 l은 LONG 이라는 뜻으로 인수의 길이를 나타내었지만 Win32 에서는 둘다 32비트이므로 w, l 문자는 단순한 이름이 되었다. 또 메시지가 전달할 수 있는 부가 정보는 총 64비트가 되었다(wParam(32비트) + lParam(32비트) = 64비트))


Double Click : 짧은 시간 안에 마우스 버튼을 두 번 빠르게 누르는 동작

단순히 더블클릭 한다고 해서 더블클릭 메시지가 발생하지 않는다. WM_LBUTTONDOWN 메시지와 WM_LBUTTONUP 메시지가 교대로 2번 발생할 뿐이다. 더블클릭 메시지를 받으려면
WndClass. style 멤버에 CS_DBLCLKS 플래그를 추가하면 이 윈도우는 더블클릭 메시지를 지원하게 된다.(WndClass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;)

많이 사용되는 더블클릭 메시지를 디폴트로 지원하지 않는 이유는 더블클릭을 검출하는데는 그만큼 실행시간의 감소가 요구되며 어떤 프로그램은 WM_LBUTTONDOWN 메시지를 두 번 반기를 원할 수도 있으며 또한 더블클릭으로 인정할 시간간격이나 마우스 포인터의 위치 따위의 규칙을 프로그램에서 자체적으로 만들어 쓸 수 있도록 하기 위해서이다.


비작업영역 마우스 메시지 : (참고 : 비작업영역이란 타이틀 바, 경계선, 메뉴, 스크롤 바 등)

이 메시지들은 작업영역 메시지의 이름앞에 NC(Non Client)가 덧붙여진다.
(예. WM_NCLBUTTONDOWN)

비작업영역 메시지는 시스템 내부적인 용도로 사용한다.
역시 메시지를 받아 처리하기 원한다면 처리한 후에 DefWindowProc으로 리턴해줘야 한다.
(참고 : DefWindowProc로 리턴해주지 않으면 표준 마우스 인터페이스가 동작하지 않는다)

'Windows > _Windows Programming' 카테고리의 다른 글

윈도우 관리 메시지  (0) 2009.12.23
타이머(Timer)  (0) 2009.12.23
출력하기  (2) 2009.11.28
윈도우 기초 프로그래밍  (0) 2009.11.27
윈도우즈(Windows) 프로그래밍의 기초  (0) 2009.11.27