「Windows API/入力」の版間の差分

出典: フリー教科書『ウィキブックス(Wikibooks)』
削除された内容 追加された内容
fix lint error (use script)、シンボルに{{Microsoft Docs Search}}wo
3 行 3 行
キー入力に反応させるには、単に、関数
キー入力に反応させるには、単に、関数


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
LRESULT CALLBACK {{Microsoft Docs Search|WndProc}}(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)


の関数ブロック内に、下記のように case WM_KEYDOWN: を作成し、
の関数ブロック内に、下記のように case WM_KEYDOWN: を作成し、


<source lang=c>
<syntaxhighlight lang=c>
case WM_KEYDOWN:
case WM_KEYDOWN:
switch (wParam)
switch (wParam)
18 行 18 行
break;
break;
}
}
</syntaxhighlight>
</source>


のように、すればいい。上記のコードはZボタンとXボタンにだけ反応する。
のように、すればいい。上記のコードはZボタンとXボタンにだけ反応する。
45 行 45 行
まず、入力欄を作らなければいけない。そのためには、「エディット ボックス」というのを使うと便利である。
まず、入力欄を作らなければいけない。そのためには、「エディット ボックス」というのを使うと便利である。


「エディット ボックス」を呼び出せる関数は2つあり、CreateWindow() 関数と、CreateWindowEX() 関数である。
「エディット ボックス」を呼び出せる関数は2つあり、{{Microsoft Docs Search|CreateWindow}}() 関数と、{{Microsoft Docs Search|CreateWindowEX}}() 関数である。


==== CreateWindow() 関数の使い方 ====
==== CreateWindow() 関数の使い方 ====
56 行 56 行
CreateWindow() 関数を使う。
CreateWindow() 関数を使う。


<source lang=c>
<syntaxhighlight lang=c>
case WM_CREATE:
case WM_CREATE:
CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
10, 10, 200, 30, hWnd, (HMENU)1,((LPCREATESTRUCT)(lParam))->hInstance, NULL);
10, 10, 200, 30, hWnd, (HMENU)1,((LPCREATESTRUCT)(lParam))->hInstance, NULL);
</syntaxhighlight>
</source>
と書く。
と書く。


69 行 69 行
最近のVisual Studio では、これを単に hInst と置き換えて
最近のVisual Studio では、これを単に hInst と置き換えて


<source lang=c>
<syntaxhighlight lang=c>
case WM_CREATE:
case WM_CREATE:
CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
10, 10, 200, 30, hWnd, (HMENU)1, hInst, NULL);
10, 10, 200, 30, hWnd, (HMENU)1, hInst, NULL);
</syntaxhighlight>
</source>


と書いても、コンパイルできて、エディットボックスを実行できる。(Visual Studio 2017 で動作確認してある。)
と書いても、コンパイルできて、エディットボックスを実行できる。(Visual Studio 2017 で動作確認してある。)
101 行 101 行
というのがあり、その下に、
というのがあり、その下に、


<source lang=c>
<syntaxhighlight lang=c>
if (!hWnd)
if (!hWnd)
{
{
return FALSE;
return FALSE;
}
}
</syntaxhighlight>
</source>


というのがあるので、その下でCreateWindowEX() 関数を使う。
というのがあるので、その下でCreateWindowEX() 関数を使う。
112 行 112 行




<source lang=c>
<syntaxhighlight lang=c>
if (!hWnd)
if (!hWnd)
{
{
121 行 121 行
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
0, 0, 150, 30, hWnd, (HMENU)1, hInst, NULL);
0, 0, 150, 30, hWnd, (HMENU)1, hInst, NULL);
</syntaxhighlight>
</source>


なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。
なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。
132 行 132 行
というのがあり、その下に、
というのがあり、その下に、


<source lang=c>
<syntaxhighlight lang=c>
if (!hWnd)
if (!hWnd)
{
{
return FALSE;
return FALSE;
}
}
</syntaxhighlight>
</source>


というのがあるので、その下でCreateWindowEX() 関数を使う。
というのがあるので、その下でCreateWindowEX() 関数を使う。


最終的に、次のようなコードになる。
最終的に、次のようなコードになる。
<source lang=c>
<syntaxhighlight lang=c>
if (!hWnd)
if (!hWnd)
{
{
151 行 151 行
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
10, 10, 200, 30, hWnd, (HMENU)1, hInstance, NULL);
10, 10, 200, 30, hWnd, (HMENU)1, hInstance, NULL);
</syntaxhighlight>
</source>


なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。
なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。
166 行 166 行
ボタンコントロールもまた、CreateWindow()関数で作成できます。
ボタンコントロールもまた、CreateWindow()関数で作成できます。


<source lang=c>
<syntaxhighlight lang=c>
CreateWindow(
CreateWindow(
TEXT("BUTTON"), TEXT("決定"),
TEXT("BUTTON"), TEXT("決定"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
110, 70, 100, 50, hWnd, NULL, hInst, NULL);
110, 70, 100, 50, hWnd, NULL, hInst, NULL);
</syntaxhighlight>
</source>


なお<code>BS_PUSHBUTTON,</code>の次の
なお<code>BS_PUSHBUTTON,</code>の次の
187 行 187 行


例えば、
例えば、
<source lang=c>
<syntaxhighlight lang=c>
CreateWindow(
CreateWindow(
TEXT("BUTTON"), TEXT("決定"),
TEXT("BUTTON"), TEXT("決定"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
110, 70, 100, 50, hWnd, (HMENU)BUTTON1_ID, hInst, NULL);
110, 70, 100, 50, hWnd, (HMENU)BUTTON1_ID, hInst, NULL);
</syntaxhighlight>
</source>
と書いたら、
と書いたら、
さらにソースファイルの冒頭の、defineマクロのある箇所に、
さらにソースファイルの冒頭の、defineマクロのある箇所に、


<source lang=c>
<syntaxhighlight lang=c>
#define BUTTON1_ID 10
#define BUTTON1_ID 10
</syntaxhighlight>
</source>


のように定義して、BUTTON1_ID に任意の数字を与えます。
のように定義して、BUTTON1_ID に任意の数字を与えます。
209 行 209 行
まず、ボタンコントロールが押されたときの処理は、
まず、ボタンコントロールが押されたときの処理は、


<source lang=c>
<syntaxhighlight lang=c>
case WM_COMMAND:
case WM_COMMAND:
</syntaxhighlight>
</source>


の段落に書きます。この段落は、既にウィザードによって自動作成されています。
の段落に書きます。この段落は、既にウィザードによって自動作成されています。
217 行 217 行
既に、
既に、


<source lang=c>
<syntaxhighlight lang=c>
case WM_COMMAND:
case WM_COMMAND:
{
{
236 行 236 行
}
}
break;
break;
</syntaxhighlight>
</source>


というようなコードが自動生成されているので、
というようなコードが自動生成されているので、
242 行 242 行




<source lang=c>
<syntaxhighlight lang=c>
case WM_COMMAND:
case WM_COMMAND:
{
{
265 行 265 行
}
}
break;
break;
</syntaxhighlight>
</source>


のように、case ボタンID を追加するだけです。
のように、case ボタンID を追加するだけです。


さて、われわれが行いたいのは、エディットボックスの文字列の取得ですから、上述の MessageBox() の代わりにエディットボックスの文字列を取得するコードを記述する必要があります。
さて、われわれが行いたいのは、エディットボックスの文字列の取得ですから、上述の {{Microsoft Docs Search|MessageBox}}() の代わりにエディットボックスの文字列を取得するコードを記述する必要があります。


まず、エディットボックスに名前をつける必要があります。どのエディットボックスから文字を読みこむか指定するためです。
まず、エディットボックスに名前をつける必要があります。どのエディットボックスから文字を読みこむか指定するためです。
277 行 277 行
エディットボックスに名前をつけるには、エディットボックスの作成時に、下記のように代入する必要があります。
エディットボックスに名前をつけるには、エディットボックスの作成時に、下記のように代入する必要があります。


<source lang=c>
<syntaxhighlight lang=c>
case WM_CREATE:
case WM_CREATE:
editbox1 = CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"), // この行の冒頭が変わった。
editbox1 = CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"), // この行の冒頭が変わった。
287 行 287 行
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
110, 70, 100, 50, hWnd, (HMENU)BUTTON_ID1, hInst, NULL);
110, 70, 100, 50, hWnd, (HMENU)BUTTON_ID1, hInst, NULL);
</syntaxhighlight>
</source>




294 行 294 行
GetWindowText() 関数の第1引数で、どのコントロールから取得するかを指定するので、そこに 先ほど名づけたエディットボックスの名前を入れます。また、取得した変数を代入するための変数として、事前に文字列型を定義しておきます。下記のコードでは、エディットボックスから取得した文字列を、 変数 kakunou に格納します。
GetWindowText() 関数の第1引数で、どのコントロールから取得するかを指定するので、そこに 先ほど名づけたエディットボックスの名前を入れます。また、取得した変数を代入するための変数として、事前に文字列型を定義しておきます。下記のコードでは、エディットボックスから取得した文字列を、 変数 kakunou に格納します。


<source lang=c>
<syntaxhighlight lang=c>
case BUTTON1_ID:
case BUTTON1_ID:
GetWindowText(editbox1, kakunou, 100);
GetWindowText(editbox1, kakunou, 100);
300 行 300 行
InvalidateRect(hWnd, NULL, FALSE);
InvalidateRect(hWnd, NULL, FALSE);
UpdateWindow(hWnd);
UpdateWindow(hWnd);
</syntaxhighlight>
</source>


これだけだとエラーになってしまいますので、次のようなコードをソース冒頭のグローバル領域に追加します。
これだけだとエラーになってしまいますので、次のようなコードをソース冒頭のグローバル領域に追加します。


<source lang=c>
<syntaxhighlight lang=c>
static HWND editbox1;
static HWND editbox1;
TCHAR kakunou[256];
TCHAR kakunou[256];
</syntaxhighlight>
</source>




313 行 313 行
文字を取得しただけでは、まだ画面の描画内容の更新は行われません。
文字を取得しただけでは、まだ画面の描画内容の更新は行われません。


ボタンコントロールを押した場合にかぎらず、一般にwin32APIで画面の更新をするには、さらに下記の InvalidateRect 命令と UpdateWindow 命令が必要です。
ボタンコントロールを押した場合にかぎらず、一般にwin32APIで画面の更新をするには、さらに下記の {{Microsoft Docs Search|InvalidateRect}} 命令と {{Microsoft Docs Search|UpdateWindow}} 命令が必要です。


<source lang=c>
<syntaxhighlight lang=c>
InvalidateRect(hWnd, NULL, TRUE);
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
UpdateWindow(hWnd);
</syntaxhighlight>
</source>
は決まり文句ですので、鵜呑みにしてください。
は決まり文句ですので、鵜呑みにしてください。


325 行 325 行


たとえば case WM_PAINT: に、
たとえば case WM_PAINT: に、
<source lang=c>
<syntaxhighlight lang=c>
TextOut(hdc, 200, 200, kakunou, lstrlen(kakunou));
TextOut(hdc, 200, 200, kakunou, lstrlen(kakunou));
</syntaxhighlight>
</source>
のコードを追加したりします。
のコードを追加したりします。

2021年4月13日 (火) 11:28時点における版

入力

キー入力

キー入力に反応させるには、単に、関数

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

の関数ブロック内に、下記のように case WM_KEYDOWN: を作成し、

	case WM_KEYDOWN:
		switch (wParam)
		{
		case 'Z':
			MessageBox(NULL, TEXT("Zが押されました。"), TEXT("キーテスト"), MB_OK);
			break;
		case 'X':
			MessageBox(NULL, TEXT("Xが押されました。"), TEXT("キーテスト"), MB_OK);
			break;
		}

のように、すればいい。上記のコードはZボタンとXボタンにだけ反応する。


入力後の共通処理がほしい場合

case が複数通りある場合、break文をすると、switchブロックの外に出てしまいます。

上記のコードでは、単にメッセージボックスが出るだけなのに、画面更新の命令は不要です。

ですが、一般的には、そういう命令とはかぎらず、また、なにかの入力をプアプリが受け付けたことをユーザーに知らせるために、画面更新の命令が必要です。

このため、たとえば case 'Z': と case 'X': の入力検出後の、画面更新のようにどのブロックのあとでも必要になる処理などを、まとめてコードに書きたいと思うでしょう。

しかし、break文によってswitchブロックの外側に出てしまいます。


べつに、switch文の抜けたあとの場所に書くこともできるので、画面更新などの共通処理をコードを記述できる場所もあります。


しかし、実際に書いてみると分かるのですが、WindowsAPIではカッコ { } 記号がコード中に多かったりするので、よく、共通処理を書くべき場所を間違えてしまいます。

なので、慣れない内は、もし検出後の共通処理があまり長くない処理なら、それぞれのcase ブロックに処理を記述してしまうほうが簡単だし、見た目でも処理内容が分かりやすくなるでしょう。

文字入力

まず、入力欄を作らなければいけない。そのためには、「エディット ボックス」というのを使うと便利である。

「エディット ボックス」を呼び出せる関数は2つあり、CreateWindow() 関数と、CreateWindowEX() 関数である。

CreateWindow() 関数の使い方

  • 方法1

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

の関数ブロック内に、下記のように case WM_CREATE: を作成し、

CreateWindow() 関数を使う。

	case WM_CREATE:
		CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
			WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
			10, 10, 200, 30, hWnd, (HMENU)1,((LPCREATESTRUCT)(lParam))->hInstance, NULL);

と書く。

((LPCREATESTRUCT)(lParam))->hInstance について

最近のVisual Studio では、これを単に hInst と置き換えて

	case WM_CREATE:
		CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
			WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
			10, 10, 200, 30, hWnd, (HMENU)1, hInst, NULL);

と書いても、コンパイルできて、エディットボックスを実行できる。(Visual Studio 2017 で動作確認してある。)


この CreateWindow() 関数は、名前はあたかもウィンドウ作成の関数っぽくて、まぎらわしいが、ウィンドウだけでなく、ボタンなどのコントロールも作成できる。

そして、作成できるコントロールの中には、文字入力欄を簡単に作れるエディットボックスがあるので、それを使う。

第1引数の TEXT("EDIT") により、エディットボックスを使うことを宣言している。第2引数は、ボックス内に表示される文字を指定している。アプリ起動後にエディットボックス内に表示される この文字(上記の場合は「ここに名前を入力」)は、エディットボックスDeleteキーなどで消去できる。

アプリ起動後に、ボックス内をマウスクリックすれば、文字入力カーソルがボックス内に表示され、ボックス内の文字を編集できるようになる。


これによって、文字列の入力欄を作れるが、しかし、まだ入力欄を作っただけなので、入力してみてエンターを押しても、まだ何も起こらない。


よって、入力後の処理をコードに追記していく必要がある。

  • 方法2

または、ウィザードで作ったテンプレートの100行目あたりに

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

というのがあり、その下に、

   if (!hWnd)
   {
      return FALSE;
   }

というのがあるので、その下でCreateWindowEX() 関数を使う。


   if (!hWnd)
   {
      return FALSE;
   }

   CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"),
	   WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
	   0, 0, 150, 30, hWnd, (HMENU)1, hInst, NULL);

なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。

CreateWindowEX() 関数の使い方

ウィザードで作ったテンプレートの100行目あたりに

BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)

というのがあり、その下に、

   if (!hWnd)
   {
      return FALSE;
   }

というのがあるので、その下でCreateWindowEX() 関数を使う。

最終的に、次のようなコードになる。

   if (!hWnd)
   {
      return FALSE;
   }

   CreateWindowEx(0, TEXT("EDIT"), TEXT("ここに名前を入力"), 
	   WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
	   10, 10, 200, 30, hWnd, (HMENU)1, hInstance, NULL);

なお、この位置は、lParam の効果が及ばない位置なのでエラーになので、それを使わないように引数を書く必要がある。


確定ボタンの配置

エディットボックスに文字を打ち込んだら、文字列を確定したことをOSに知らせる処理が必要です。(さらにその後、確定した文字列を変数に代入する処理が必要になります。)


まず、どうやって文字列を確定したことを宣言するか、考える必要があります。

実用的には、決定ボタンなどのコントロールをアプリウィンドウ上に配置するのが、簡単でしょう。

ボタンコントロールもまた、CreateWindow()関数で作成できます。

		CreateWindow(
			TEXT("BUTTON"), TEXT("決定"),
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			110, 70, 100, 50, hWnd, NULL, hInst, NULL);

なおBS_PUSHBUTTON,の次の

110, 70, 100, 50,  

について、これは

ボタンの左上のx座標110, y座標70, 幅 100, 高さ 50

という意味です。


後の処理のために、このボタンを呼び出すためのIDをつける必要があります。

後ろから3番目の引数(hWnd, NULL, hInst のNULL の部分)がIDです。IDは数字でなければ、なりません。

通常、 define マクロで定義するのが慣例です。

例えば、

		CreateWindow(
			TEXT("BUTTON"), TEXT("決定"),
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			110, 70, 100, 50, hWnd, (HMENU)BUTTON1_ID, hInst, NULL);

と書いたら、 さらにソースファイルの冒頭の、defineマクロのある箇所に、

#define BUTTON1_ID 10

のように定義して、BUTTON1_ID に任意の数字を与えます。

なお HMENU は型名であり、ハンドルの識別番号をあらわす型です。

文字列の取得

上述のように、ウィンドウ上に、ボタンコントロールで作成した決定ボタンを配置したとしましょう。

まず、ボタンコントロールが押されたときの処理は、

    case WM_COMMAND:

の段落に書きます。この段落は、既にウィザードによって自動作成されています。

既に、

case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
		break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

というようなコードが自動生成されているので、


case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 選択されたメニューの解析:
            switch (wmId)
            {
            case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;

            case BUTTON1_ID: // このcaseが追加された
                MessageBox(hWnd, TEXT("決定ボタンが押されました。"), TEXT("テスト"), MB_OK); 
                break;

            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

のように、case ボタンID を追加するだけです。

さて、われわれが行いたいのは、エディットボックスの文字列の取得ですから、上述の MessageBox() の代わりにエディットボックスの文字列を取得するコードを記述する必要があります。

まず、エディットボックスに名前をつける必要があります。どのエディットボックスから文字を読みこむか指定するためです。

とりあえず editbox1 という名前をつけたとしましょう。

エディットボックスに名前をつけるには、エディットボックスの作成時に、下記のように代入する必要があります。

	case WM_CREATE:
		editbox1 = CreateWindow(TEXT("EDIT"), TEXT("ここに名前を入力"), // この行の冒頭が変わった。
			WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT,
			10, 10, 200, 40, hWnd, (HMENU)1, hInst, NULL);

		CreateWindow(
			TEXT("BUTTON"), TEXT("決定"),
			WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
			110, 70, 100, 50, hWnd, (HMENU)BUTTON_ID1, hInst, NULL);


エディットボックスにある文字列を取得するには、GetWindowText() 関数を使います。

GetWindowText() 関数の第1引数で、どのコントロールから取得するかを指定するので、そこに 先ほど名づけたエディットボックスの名前を入れます。また、取得した変数を代入するための変数として、事前に文字列型を定義しておきます。下記のコードでは、エディットボックスから取得した文字列を、 変数 kakunou に格納します。

			case BUTTON1_ID:
				GetWindowText(editbox1, kakunou, 100);
				
				InvalidateRect(hWnd, NULL, FALSE);
				UpdateWindow(hWnd);

これだけだとエラーになってしまいますので、次のようなコードをソース冒頭のグローバル領域に追加します。

static HWND editbox1;
TCHAR kakunou[256];


文字を取得しただけでは、まだ画面の描画内容の更新は行われません。

ボタンコントロールを押した場合にかぎらず、一般にwin32APIで画面の更新をするには、さらに下記の InvalidateRect 命令と UpdateWindow 命令が必要です。

				InvalidateRect(hWnd, NULL, TRUE);
				UpdateWindow(hWnd);

は決まり文句ですので、鵜呑みにしてください。


また、 case WM_PAINT: に、描画内容を書く必要があります。

たとえば case WM_PAINT: に、

				TextOut(hdc, 200, 200, kakunou, lstrlen(kakunou));

のコードを追加したりします。


上述のこれらのコードを記述すれば、エディットボックスの入力結果を、ウィンドウに表示できます。