今回は、C++言語によるネイディブアプリケーションでの作成を行います。
ここでいうネイティブアプリケーションとは、VBランタイムとか.NET FrameworkとかJavaVMのような、特別なランタイムを必要としないで実行できるプログラムのことです。Visual C++でコンパイルすることを前提にしています。
このプログラムをC++言語で記述する場合、特別なテクニック等はでてきません。ただ、C/C++言語の基礎的な知識と、Windows APIについて「あーこんなのがあるんだなぁ」という感じで使えるような感覚ですかね。
何はともあれ、ソースコードはこんな感じになります。
●ソースコードと実行プログラムのダウンロードはこちらから (29KB)
// キーボードシミュレータ(C++ ネイディブ版)
// Copyright(C) 2006 T.Yabuki
//
// 引数で指定された文字列に従い、キーボード入力をシミュレートします。
#define WIN32_LEAN_AND_MEAN // Windows ヘッダーから使用されていない部分を除外します。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
// 入力対象のプログラムをアクティブにするまでの待ち時間(ミリ秒単位)
DWORD dwSleepTime = 5000;
// キーの取りこぼしを防ぐための、各入力間の待機時間(ミリ秒単位)
DWORD dwWaitTime = 10;
// ※1ミリ秒=0.001秒のこと
// ********************************************************************
// * checkKey *
// * キーボードシミュレート対象文字列かどうかを調べる。 *
// * 引数cが対象ならばtrueを返し、そうでなければfalseを返す。 *
// ********************************************************************
bool checkKey(unsigned char c)
{
if ( '0' <= c && c <= '9' ) { // 数字ならOK
return true;
}
else if ( 'A' <= c && c <= 'Z' ) { // 英大文字ならOK
// 余談:処理系によっては、A〜Zは連続していないかもしれないが、
// 少なくとも動作対象の日本語Winodwsで用いられているUTF-8
// は連続しているので、この記述を使う。
return true;
}
else if ( c == ' ' ) { // スペースならOK
return true;
}
else if ( c == '\t' ) { // タブならOK
return true;
}
// それら以外の場合はNG
return false;
}
int main(int argc, char* argv[])
{
if (argc == 1) {
// 引数が指定されていない場合、使用方法を表示
puts("キーボード入力をシミュレートしたい文字列を引数に指定してください。");
puts("");
puts("keysimcpp <文字列> [<待機時間> [<文字間ウェイト>]]");
puts("");
puts("文字列に指定可能な文字は、英大文字・数字・スペース・タブです。");
puts("(英小文字は無視されますのでご注意下さい)");
puts("スペース・タブを指定する場合は、文字列全体をダブルコーテーションで囲います。");
puts("待機時間は、入力開始までの待ち時間をミリ秒単位で指定します(1以上)。");
puts("文字間ウェイトは、入力文字間のウェイト時間をミリ秒単位で指定します(1以上)。");
return 1;
}
// 待機時間取得
if ( argc > 2 ) {
dwSleepTime = atoi(argv[2]);
if (dwSleepTime == 0) {
puts("待機時間の指定が不正です。ミリ秒単位で1以上の数を指定してください。");
return 1;
}
}
// 文字間ウェイト取得
if ( argc > 3 ) {
dwWaitTime = atoi(argv[3]);
if (dwWaitTime == 0) {
puts("文字間ウェイトの指定が不正です。ミリ秒単位で1以上の数を指定してください。");
return 1;
}
}
// 入力対象のプログラムをアクティブにするまでの待ち
puts("入力対象のプログラムをアクティブにしてください。");
printf("%uミリ秒間待機します...", dwSleepTime);
Sleep(dwSleepTime);
puts("\n入力中...");
// コマンドラインの文字列を取得する
const unsigned char *p = (const unsigned char *)(argv[1]);
// 取得された文字列を1文字ずつ検査し、
// シミュレート可能文字列であればkeybd_event() APIに
// 渡す。
for(; *p ; ++p) {
// 入力対象の文字列かどうかを調べる
if ( checkKey(*p) ) {
// キーの押し下げをシミュレートする。
keybd_event( *p, 0, 0, 0);
// キーの解放をシミュレートする。
keybd_event( *p, 0, KEYEVENTF_KEYUP, 0);
// dwWaitTimeミリ秒間待機する
// (キーの取りこぼしを防ぐため)
Sleep(dwWaitTime);
}
}
puts("キーボードシミュレートが終了しました。");
return 0;
}
実行画面は次のようになります。
C:\MyProject\keysimcpp\release>keysimcpp キーボード入力をシミュレートしたい文字列を引数に指定してください。 keysimcpp <文字列> [<待機時間> [<文字間ウェイト>]] 文字列に指定可能な文字は、英大文字・数字・スペース・タブです。 (英小文字は無視されますのでご注意下さい) スペース・タブを指定する場合は、文字列全体をダブルコーテーションで囲います。 待機時間は、入力開始までの待ち時間をミリ秒単位で指定します(1以上)。 文字間ウェイトは、入力文字間のウェイト時間をミリ秒単位で指定します(1以上)。 C:\MyProject\keysimcpp\release>
C:\MyProject\keysimcpp\release>keysimcpp ABCDEFG12345 入力対象のプログラムをアクティブにしてください。 5000ミリ秒間待機します... 入力中... キーボードシミュレートが終了しました。 C:\MyProject\keysimcpp\release>
「入力対象のプログラムをアクティブにしてください」という表示が出たところで、貼り付け先のウィンドウをアクティブにしてやる必要があります。引数は、英大文字・数字・スペース・タブを指定してください。英小文字は無視されます。
入力対象のプログラムとして、例えばメモ帳にしたい場合は、メモ帳をアクティブにしておくことで、次のような画面になります(CAPSロックがやカナロック等がかっていない場合)。

checkKey()は、keybk_event()に渡しても問題のない文字かどうかを検証するための関数です。
bool checkKey(unsigned char c)
{
if ( '0' <= c && c <= '9' ) { // 数字ならOK
return true;
}
else if ( 'A' <= c && c <= 'Z' ) { // 英大文字ならOK
return true;
}
else if ( c == ' ' ) { // スペースならOK
return true;
}
else if ( c == '\t' ) { // タブならOK
return true;
}
// それら以外の場合はNG
return false;
}
問題のない文字ならtrue(1, 成功) を返し、問題のある文字ならfalse(0, 成功) を返します。
で、このソースコードの中の一番肝心な部分が、keybd_event()に文字を渡す部分です。
// コマンドラインの文字列を取得する
const unsigned char *p = (const unsigned char *)(argv[1]);
// 取得された文字列を1文字ずつ検査し、
// シミュレート可能文字列であればkeybd_event() APIに
// 渡す。
for(; *p ; ++p) {
// 入力対象の文字列かどうかを調べる
if ( checkKey(*p) ) {
// キーの押し下げをシミュレートする。
keybd_event( *p, 0, 0, 0);
// キーの解放をシミュレートする。
keybd_event( *p, 0, KEYEVENTF_KEYUP, 0);
// dwWaitTimeミリ秒間待機する
// (キーの取りこぼしを防ぐため)
Sleep(dwWaitTime);
}
}
文字列内をループし、1文字ずつkeybd_event()に渡していきます。
動作の遅いPCでは、キーの取りこぼし(キーバッファのあふれ)が起きてしまう可能性があるため、Sleep()を使って1文字ごとにウェイトをとります。
次回は、C#言語を用いて、キーボードシミュレータ(.NET Framework版)のコマンドライン版を作ってみます。