키보드 메시지
전달되는 코드 값
윈도우 환경에서는 주로 마우스가 많이 사용되지만 문자를 입력받기 위한 가장 기본적인 입출력 장치는 키보드입니다. 키보드로부터 입력이 발생했을 경우 윈도우즈는 포커스를 가진 프로그램에게 키보드 메시지(WM_CHAR, WM_KEYDOWN 등)를 보내주며 프로그램이 메시지를 받아 키보드 입력을 처리합니다.
여기서 포커스란 입력 초점이라는 뜻이며 키보드 입력을 받아들일 수 있는 상태라는 뜻입니다. 포커스를 가진 프로그램이란 활성화되어 있는 윈도우를 말하며 한 번에 오직 하나의 프로그램만 활성화됩니다.
만약 키보드 입력이 발생하게 되면 입력된 키 입력 정보가 전달되는데 이 때 전달되는 정보를 아래와 같이 구분할 수 있습니다.
스캔 코드
- 키보드를 누를 때 마다 하드웨어적으로 발생되는 키보드의 고유 번호입니다.
- 장치에 종속적입니다, 따라서 하드웨어에 따라 키 값이 틀립니다.
- 보통 자판 배열 순서를 갖습니다. 예를 들어 Q는 16, W는 17, E는 18 등 입니다.
가상 키 코드
- 키보드 종류에 상관없이 각 키에 부여된 고유한 코드 값입니다.
- 일반적으로 알파벳 순서로 코드 키 값이 부여되어 있습니다.
- 장치에 독립적입니다. 따라서 하드웨어에 상관없이 동일 값이 생성됩니다.
- 대 소문자 구별이 안됩니다. 예를 들어 'A' 와 'a' 는 둘 다 65라는 키 값으로 전달됩니다.
아스키 코드
- 각 문자에 부여되는 코드값입니다.
- 대소문자가 구별됩니다.
키보드 메시지가 전달되는 과정
아래의 그림을 살펴 보면 사용자가 키보드로 키 입력을 하면 이벤트가 발생합니다. 발생한 스캔 코드는 키보드 디바이스 드라이버를 거쳐 시스템에서 발생하는 모든 메시지를 담는 System Message Queue에 저장됩니다.
저장된 메시지는 모든 애플리케이션이 독립적으로 가지고 있는 Message Queue에 전달되고 API 코드에서 작성된 메시지 루프에서 해당 메시지를 읽어 메시지 프로시저에 최종 전달되게 됩니다.
Foreground Thread
시스템 메시지 큐와 Foreground Thread 메시지 큐 사이에는 RIT라는 메시지 분배 역할을 하는 객체가 존재합니다. RIT는 내부적으로 각 어플리케이션의 큐에 연결되어 있는 스레드의 ID를 보관하고 있습니다.
이와 같이 RIT와 연결되어 있는 스레드를 Foreground Thread 라고 합니다. RIT는 시스템 메시지 큐에서 키보드 메시지를 꺼낼 경우 무조건 자신과 연결된 Foreground Thread 의 메시지 큐로 메시지를 전달합니다.
Keyboard Focus
한 개의 스레드가 여러 개의 윈도우를 만들 경우 스레드의 메시지 큐에 전달된 메시지는 입력 포커스를 지닌 윈도우에게 전달됩니다. 아래 함수로 윈도우의 입력 포커스를 변경할 수 있습니다.
HWND SetFocus( HWND );
키보드 메시지 처리하기
WM_KEYDOWN, WM_KEYUP
사용자가 키보드를 누를 때 WM_KEYDOWN, WM_KEYUP 메시지가 발생합니다. 이 때 wParam에는 가상 키 코드가 Iparam에는 반복 횟수, 스캔코드 및 여러 가지 추가 정보가 들어옵니다.
#define MBox(x) MessageBox( 0, x, TEXT(""), MB_OK)
#define GetScanCode(x) ( ( x >> 16) & 0x00FF)
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_KEYDOWN:
{
TCHAR temp[128];
wsprintf(temp, TEXT("Scan Code : %d\nVirtual Key Code : %d\n"), GetScanCode(lParam), wParam);
MBox( temp );
}
}
}
위의 예제는 키를 입력했을때 스캔 코드값과 가상키 코드 값을 출력하는 예제입니다.
가상 키 코드 값은 아래와 같이 얻을 수 있습니다.
int vCode = (int)wParam;
스캔 코드값은 IParam 의 16~23 비트에 있습니다. 따라서, 아래와 같이 얻을 수 있습니다.
int sCode = ( lParam & 0x00FF0000) >> 16;
아래는 가상키 코드표입니다. 즉, 가상키 코드란 시스템에 장착된 키보드의 종류에 상관없이 키를 입력받기 위해 만들어진 코드 값인 것을 알 수 있습니다.
가상키 코드 | 값 | 키 |
VK_BACK | 08 | BackSpace |
VK_TAB | 09 | Tab |
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 | 위쪽 커서 이동키 |
이하생략 | 이하생략 |
위의 예제를 돌려보면 확인 할 수 있는게 숫자 및 영문자의 가상 키 코드값은 아스키 코드와 동일합니다. 따라서 매크로 상수는 정의 되어 있지 않으므로 아스키 코드와 wParam을 바로 비교하면 됩니다.
static POINTS pt = {100, 100};
switch( msg )
{
case WM_KEYDOWN:
{
switch(wParam){
case VK_LEFT: pt.x -= 10; break;
case VK_RIGHT:pt.x += 10; break;
case VK_UP: pt.y -= 10; break;
case VK_DOWN: pt.y += 10; break;
}
HDC hdc = GetDC(hwnd);
TextOut(hdc, pt.x, pt.y, TEXT("#"),1);
ReleaseDC(hwnd, hdc);
}
return 0;
위의 예제는 WM_KEYDOWN으로 방향키 제어하는 코드입니다.
WM_SYSKEYDOWN, WM_SYSKEYUP
사용자가 ALT 키와 다른 키보드를 같이 누를 때 WM_SYSKEYDOWN, WM_SYSKEYUP 메시지가 발생합니다. 이 메시지는 반드시 DefWindowProc()으로 전달해야 합니다. 만약 전달하지 않을 경우 ALT+F4 같은 키보드 명령이 동작하지 않습니다.
// ALT+F4 등의 시스템 명령을 금지 시킨다.
case WM_SYSKEYDOWN: return 0;
WM_CHAR
WM_KEYDOWN 메시지가 발생할 때 wParam, IParam 을 통해서 많은 정보를 얻을 수 있습니다. 하지만 문자 코드는 얻을 수 없습니다.
만약 WM_KEYDOWN 메시지를 아래 함수로 전달하면 아래 함수가 WM_CHAR 메시지를 생성해 줍니다. 보통 아래 함수는 메시지 루프에 작성합니다.
BOOL TranslateMessage(const MSG *lpMsg);
이 때 WM_CHAR 메시지의 wParam 에는 가상 키 코드가 아닌 문자키 코드가 들어있습니다. 단 Cap, Num, 방향키 등은 문자키가 아니므로 WM_CHAR 메시지가 발생하지 않습니다.
키보드 상태 조사하기
아래 함수로 키보드의 상태를 조사할 수 있습니다.
현재 메시지가 발생한 순간의 특정 키보드 상태를 조사합니다.
SHORT GetKeyState(int nVirtKey)
현재 키보드의 상태를 조사합니다.
SHORT GetAsynckKeyState(int vKey);
모든 가상 키의 상태를 얻어옵니다.
BOOL GetKeyboardState( PBYTE lpKeyState)
최근댓글