API 키보드

IT/개발공부 / / 2020. 9. 11. 21:30
728x90
반응형

 

키보드 메시지

전달되는 코드 값

윈도우 환경에서는 주로 마우스가 많이 사용되지만 문자를 입력받기 위한 가장 기본적인 입출력 장치는 키보드입니다. 키보드로부터 입력이 발생했을 경우 윈도우즈는 포커스를 가진 프로그램에게 키보드 메시지(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에는 반복 횟수, 스캔코드 및 여러 가지 추가 정보가 들어옵니다.

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)
728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기