キーボード操作の自動実行

投稿者: | 2012/11/15

前回の続きとして、フックしたキーボード制御の情報をもとに同じキーボード操作を実行したい。これはもはやフックとは関係ないから今までのフックについての知識は必要ない。

処理の流れは以下の通り

  1. フックで得たキーリストの解析
    これはWindowsとか関係なくただの文字列解析だ。
  2. キーリストを実行
    いろいろはまりポイントがある

コードは以下の通り。

#define MAX_KEY 512
#define MAX_KEY_LEN 32

// -------------------------------------------------
void AnalyzeStream(const char* file_buffer, int& key_count, int key_type_array[], int key_num_array[]){

     char key_str_array[MAX_KEY_LEN] = {""};
     const char* str = file_buffer;
     // 一行ずつチェックしてコマンドを取得する
     // ex) 256 65 A のような形で入っているので空白区切りで取り込む
     while(sscanf_s(str, "%d %d %s\n", &key_type_array[key_count], &key_num_array[key_count], key_str_array, MAX_KEY_LEN)==3){
          key_count++;
          str = strchr(str, '\n');
          if(str==NULL) break;
          str++;// \nはいらないから先に進める
     }
}

// ----------------------------------------------------
int ExecKeyList(int key_exec_count, const char* file_buffer){

     // コマンドを解析
     int key_count = 0;
     int key_type_array[MAX_KEY] = { 0 };
     int key_num_array[MAX_KEY] = { 0 };
     {
          AnalyzeStream(file_buffer, key_count, key_type_array, key_num_array);
     }
     // コマンドを発行
     INPUT input[MAX_KEY]; // 構造体がいつ処理されるかわからないので念のため配列で持っておく。

     for(int k=0; k<key_exec_count ; ++k){
          for(int i=0; i<key_count ; ++i){

               memset(&input[i], 0x00, sizeof(input));
               input[i].type = INPUT_KEYBOARD;
               input[i].ki.wVk = key_num_array[i];
               if(key_type_array[i]==WM_KEYUP || key_type_array[i]==WM_SYSKEYUP)
                    input[i].ki.dwFlags = KEYEVENTF_KEYUP;
               if(key_num_array[i]==VK_LEFT || key_num_array[i]==VK_RIGHT ||
                  key_num_array[i]==VK_UP || key_num_array[i]==VK_DOWN)
                    input[i].ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;

               SendInput(1, &input[i], sizeof(INPUT));

               if((key_num_array[i]==VK_MENU)||(key_num_array[i]==VK_LMENU)||
                  (key_num_array[i]==VK_RMENU)||(key_num_array[i]==VK_TAB)){
                    Sleep(50);     // ALT+TABなどが実行できない場合があるので待つ
               }
               else{
                    Sleep(30);
               }
          }
     }

     MessageBoxA(NULL, "Commands is executed!", "Information", MB_OK);

     return 1;
}

ExecKeyList()に実行回数とファイルのバッファを与えると読み込んだファイルを解析しキーボード操作を行う。

それにしてもキーボードフック同様いろいろはまったのでまた列挙してみる。

  • PostMessage()でも一応実行できるけど・・
    最初にPostMessage()を使って実装しようとしてみたけど、どうやら最前面にあるプロセスに対してPostしているらしくGetForegroundWindow()などを使うことで実行は出来た。しかしそもそもキーボードの同時押しが認識できない!ので却下。
  • SendInputを使うことで同時押しを克服
    これにより大文字のAなどが使えるようになった。しかし今度はALT+TABが使えない。これでは他のプロセスへ移れない。
  • Sleep()をすることでALT+TABが利く
    どうやらALT+TABのような負荷の高い処理は一定の間が必要なようだ。おそらくすごく大きな範囲のコピーペーストなども同様に一定の間が必要になるだろう。これで終わりかと思いきや文字列の選択状態が作れないからコピーペーストができない。
  • KEYEVENTF_EXTENDEDKEYフラグを追加することで選択状態が取れる
    矢印キーによる選択状態はKEYBDINPUT::dwFlagsにKEYEVENTF_EXTENDEDKEYを指定すると可能になる。
  • KEY_UPの処理はKEYBDINPUT::dwFlagsにKEYEVENTF_KEYUPを設定する
    これがないと一時的にキーボード操作がおかしくなるので注意。

なかなかすんなりいかないキーボード操作だったけど、以上でキーボードフックからその情報を実際に実行するまでを行うことができた。

参考
SendInput
http://msdn.microsoft.com/ja-jp/library/cc411004.aspx
INPUT構造体
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646270(v=vs.85).aspx
KEYBDINPUT構造体
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646271(v=vs.85).aspx


コメントを残す

メールアドレスが公開されることはありません。