とある科学の備忘録

とある科学の備忘録

CやPythonのプログラミング、Arduino等を使った電子工作をメインに書いています。また、木製CNCやドローンの自作製作記も更新中です。たまに機械学習とかもやってます。

【C++ Win32】キーボ―ド入力のすべて

サンプルプログラム

#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);

char szClassName[] = "keyboard";	// ウィンドウクラス
BOOL bStart = FALSE, bSeikai = TRUE;


int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,LPSTR lpsCmdLine, int nCmdShow){
	MSG msg;
	BOOL bRet;
	if (!InitApp(hCurInst))return FALSE;
	if (!InitInstance(hCurInst, nCmdShow))return FALSE;
	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
		if (bRet == -1) {
			break;
		}else {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}
	return (int)msg.wParam;
}

//ウィンドウ・クラスの登録

ATOM InitApp(HINSTANCE hInst){
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;	//プロシージャ名
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInst;//インスタンス
	wc.hIcon = NULL;
	wc.hCursor = (HCURSOR)LoadImage(NULL,MAKEINTRESOURCE(IDC_ARROW),IMAGE_CURSOR,0,0,LR_DEFAULTSIZE | LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = (LPCSTR)szClassName;
	wc.hIconSm = NULL;
	return (RegisterClassEx(&wc));
}

BOOL InitInstance(HINSTANCE hInst, int nCmdShow){
	HWND hWnd;

	hWnd = CreateWindow(szClassName,"KeyBoard", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,240,180,NULL,NULL, hInst, NULL);
	if (!hWnd) return FALSE;
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	HDC hdc;
	PAINTSTRUCT ps;
	MMTIME mm;

	char tcRe[32];
	char *str = "Please Press some key...";
	char *str2 = "--> A key was pressed!!";

	switch (msg) {
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		TextOut(hdc, 0, 0, str, (int)strlen(str));
		EndPaint(hWnd, &ps);
		break;

	case WM_CHAR:
		switch(wp){
			case VK_ESCAPE:
				PostQuitMessage(0);
			return 0;

			case 'A': //SHIFT + Aキーが押された時に実行される
				hdc = GetDC(hWnd);
				SetTextColor(hdc, RGB(30, 30, 30));  //文字の色を設定
				SetBkColor(hdc, RGB(255, 255, 0));  //文字の背景色を設定
				TextOut(hdc, 10, 40, str2, lstrlen(str2));
				ReleaseDC(hWnd, hdc);
				break;

			default:
				hdc = GetDC(hWnd);
				wsprintf(tcRe, "%d", (int)wp);
				SetTextColor(hdc, RGB(200, 200, 200));  //文字の色を設定
				SetBkColor(hdc, RGB(0, 0, 0));  //文字の背景色を設定
				TextOut(hdc, 10, 80, tcRe, lstrlen(tcRe));
				ReleaseDC(hWnd, hdc);
		}
		break;

	case WM_KEYUP:
		//何らかのキーが押されたときに発生するイベント
		break;

	case WM_KEYDOWN:
		switch (wp)	{
			//  エスケープキーの場合
		case VK_ESCAPE:
			// ウィンドウの閉じるメッセージ送信
			MessageBox(hWnd, "エスケープキーが押されたよ", "あああああ", MB_OK);
			PostQuitMessage(0);
			return 0;
		}
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return (DefWindowProc(hWnd, msg, wp, lp));
	}
	return 0;
}

  

実行結果

f:id:pythonjacascript:20200517174925j:plain
こんな感じです。
Shift+Aを押すと、「--> A key was pressed!!」と表示されます。
EscapeキーでWindowを終了します


解説

キーボードに関する処理は、↑のプログラムでは二つの方法があります。

  1. WndProc() 関数を使う
  2. GetKeyState()関数などによって、特定のキーが押されたかどうかを判定する

WndProc()を使って処理する方法

キーボードが押されたときに発生するメッセ―ジは以下のようなものがあります

メッセージ 説明
WM_CHAR 文字が入力されたときに発生する
WM_KEYDOWN 一般キーが押された
WM_KEYUP 一般キーが話された
WM_SYSKEYDOWN システムキーが押された
WM_SYSKEYUP システムキーが離された

システムキー」とは Alt と F10キーで、「一般キー」はシステムキー以外のキーです。

それぞれのメッセージにおいて、wParamには仮想キーコードが格納されています。
仮想キーコードにはこのサイトにまとめられています。

【Win32】仮想キーコード 一覧表 | 初心者のWindowsプログラミング日記


WM_CHARとWM_KEYUP(WM_KEYDOWN)の違い

  • WM_CHAR→一文字入力されたときに送られる。その時にUnicodeで入力された文字が送られてくる
  • WM_KEYUP(WM_KEYDOWN)→キーボードのいずれかのキーが押されたときに送られる。
  • 通常、WM_KEYDOWN→WM_CHAR→WM_KEYUPの順にメッセージが送られる

例えば、ユーザーが「A」を入力する間、

  1. Shiftキーを押す → WM_KEYDOWNメッセージ(Shiftを押したよ)
  2. 「a」キーを押す → WM_KEYDOWNメッセージ(aを押したよ)→ WM_CHARメッセージ(Unicode文字「A」が入力されたよ)
  3. 「a」キーを離す → WM_KEYUPメッセージ(aを離したよ)
  4. Shiftキーを離す→ WM_KEYUPメッセージ(Shiftを離したよ)

という一連のメッセージのやり取りがあります。


WM_CHARメッセージ

WM_CHARメッセージは文字が入力されると発生します。wParamにはその文字のUnicodeが入っています

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	HDC hdc;
	switch (msg) {
	case WM_CHAR:
		switch (wp) {
		case 0x08:  // backspace.
		case 0x09:  // tab.
		case 0x0A:  // linefeed.
		case 0x0D:  // carriage return.
		case 0x1B:  // escape.
		case 0x20:  // space.
			break;

			// Next, handle displayable characters by appending them to our string.
		default:
			hdc = GetDC(hWnd);
			
			SetTextColor(hdc, RGB(0, 0, 0));
			SetBkColor(hdc, RGB(255, 255, 255));
			TextOutW(hdc, 0,0, &(wchar_t)wp, 1);

			ReleaseDC(hWnd, hdc);
			break;
		}
(以下省略)

上のプログラムは押されたキーボードの文字を表示するプログラムです。aキーを押すと「a」と表示され、Shift+aキーだと「A」と表示されます。

特定の文字が入力されたのかどうかを調べるには、以下の様に行います

(WndProc関数内)
switch (msg) {
    case WM_CHAR:
	if (wp == 'A') {
              //Aが入力された(Shift+Aが押された)時にここが実行される
	}

 


WM_KEYDOWN(UP)メッセージ

一般キー(Altキー、F10キー以外)が押された時、キーボードフォーカスのあるウィンドウに送られます。
【Win32】仮想キーコード 一覧表 | 初心者のWindowsプログラミング日記

wParamには仮想キーコード(↓1つ下の節参照)が入っているため、下のように書くことができます

switch(msg){
    case WM_KEYDOWN:
        switch(WPARAM){
        case VK_ENTER:
            // エンターキーが押されたときの処理
            break;
            
        case 0x41: 
            //Aキーが押されたときの処理
            break;
        }
}



特定のキーが押されたかをチェック

GetKeyState()関数を使うことで、特定のキーが現在押されているかを調べることができます。

SHORT GetKeyState(int nVirtKey);

引数のnVirtKeyには、押されているかを調べる仮想キーコードを入力します
返り値は、そのキーが押されている時は負の数、押されていない時は0以上です

仮想キーコードは下のようなものがあります

仮想キーコード 説明
VK_RETURN エンター
VK_SHIFT シフト
VK_CONTROL コントロール
VK_MENU Alt
VK_PAUSE Pause
VK_CAPITAL Caps Lock
VK_ESCAPE Esc
VK_SPACE スペースキー
VK_PRIOR Pg UP
VK_NEXT Pg Down
VK_END End
VK_HOME Home
VK_LEFT 左方向キー
VK_UP 上方向キー
VK_RIGHT 右方向キー
VK_DOWN 下方向キー
VK_INSERT Insert
VK_DELETE Delete
VK_HELP ヘルプ
VK_LWIN Windowsキー
VK_RWIN Windowsキー

そして、通常のキーはasciiのキーコードをもとにしているようです。

  • A~Z:0x41~0x5A
  • 数字0~9:0x60~0x59

- ファンクションキー(F1~F24)は「VK_F○○」の形になります(例:F2キー→「VK_F2」)

使用例:

if (GetKeyState(VK_CONTROL) < 0) {
    // Ctrlキーが押されている
}else {
    // Ctrlキーが押されていない
}