リモートPC:キーボード操作のSendInput()の改善
/ (スラッシュ、除算記号)が入力できない
リモートPCを動作させるため、対象PC上では、Cプログラムでキーボードの操作をしています。
そこで、使っているのがSendInputというコマンド。
どうやって使っているかというと、プログラムは、ネットからいただきましたが、以下のようなルーチンにしています。こうすることで、codeで示された仮想キーが押されたことになります。
inp.type = INPUT_KEYBOARD;
inp.ki.wVk = code; //仮想キーコード
inp.ki.wScan = (short) MapVirtualKey(code, 0);
inp.ki.dwFlags = KEYEVENTF_EXTENDEDKEY;
inp.ki.dwExtraInfo = 0;
inp.ki.time = 0;
// キーボード操作実行
SendInput(1, &inp, sizeof (INPUT));
しかしながら、問題が発生しました。/ (スラッシュ)が入力できないのです。
いつものように、色々と調べました。
そうしたら、キーには、いわゆるオリジナルのものと、その後拡張されたキーがあるということでした。拡張されたキーを示すため、dwFlagsにKEYEVENTF_EXTENDEDKEYを設定するということです。 でも、その拡張キーがどれにあたるのか、適切な情報がほとんど見当たりません。
ネットで見つけたおそらく適切と思われる情報として見つけたのは、以下です。
About Keyboard Input
Microsoftの情報なので、正しいでしょう。その中で、Extended-Key Flagという項目の所に、記述がありました。
拡張キーは、キーボードの右側にあるALTとCTRL、テンキーの左側のかたまりにあるINS、DEL、HOME、END、PAGE UP/DOWN、矢印キー、NUM LOCKキー、BREAK (CTRL+PAUSE)キー、PRINT SCRNキー、テンキーにある/とENTERキーということのようです。
ということは、参考にしたプログラムで、dwFlagsをKEYEVENTF_EXTENDEDKEY固定していたのは、適切でなかったと考えられます。よく動いていたものです。冗長性があるのでしょう。
問題があった/(スラッシュ)キーは、仮想キーコードが0xBFですが、ほかの一般のキーと何か違っていて、EXTENDEDの指定にしていると動作しなかったのでしょう。たぶん、それが、本来の動作なのでしょうけど。
そこで、codeをチェックして、拡張キーの時だけEXTENDEDの指定をするように変更しました。
そうすると、/のキーも適切に動作するようになりました。
拡張キーは、上述したものですが、少し情報が古いようにも思えました。今のキーボードですと、Windowsキーなども増えているので、他に対象のキーがないかをKeymillというソフトを使って調べました。素晴らしいツールのご提供感謝します。
そうしたら、上述に加えて、右Shiftキー、Sleepキー、WakeUpキー、Powerキー、NumLockキー、左右Windowsキー、アプリケーションキーが、私のキーボードでは拡張キーと判明しました。
ただ、プログラム上は、少し簡略化して、0x2C(PrintScr), 0x5B(左Win),0x5C(右Win),0x5D(アプリキー),0x5E(Sleep),0x6F(/),0x90(NumLock)だけを拡張キーの対象としました。
というのも、それ以外のキーは、拡張キーでないところでも定義されていたからです。
例えば、右Altや右Ctrlは、拡張設定しなければ、左にも同じ機能のキーがあるので、一般的には区別しなくても、問題ないからです。PageUpもテンキーのNumLockしていない9キーがそれに当たるなど、拡張キー設定をしなくても機能するわけです。左右の区別をつけないといけないような使い方をするものを持っていないので、それで良しとしました。
キーボードのコードについては、Wikipediaにも情報がありました。
キーボードを離す時も、dwFlagsに適切にKEYEVENTF_EXTENDEDKEYを設定すると共に、KEYEVENTF_KEYUPをorで設定する必要があります。当初、それを忘れて、押しっぱなしになってしまったりしました。
クライアントとリモートのキーボードの状態の同期
もう1つの問題がありました。クライアントとリモート操作される側のキーボードの状態を合わせる必要がありました。
Num Lock、Caps Lock、Scroll Lockがそれです。
なぜかというと、今のプログラムでは、リモートへ単にキーを押した/離したという情報を伝えているだけなので、Num Lockなどのように、1回押すと状態がトグルする(OnまたはOffとなる)ものは、初期状態がわからないと、クライアント側とリモート側で、常に逆の状態になってしまって、適切な動作ができなくなるからです。
クライアント側の状態をリモート側に伝えて、LockキーをOnまたはOffの状態にしないといけません。これもなかなか適切な情報を見つけるのに苦労しました。
クライアント側では、ブラウザ上で動作させているJavascriptで、keydownのイベントが発生した時、e.getModifierState("NumLock")という感じで、各キーのロック状態を把握できることがわかりました。返り値がtrueだと、ロック状態、falseだとロック解除状態です。ただし、これは、keydownイベントが発生した時に行えるので、初期化の時には対応できないということを考える必要がありました。
ロック状態を把握できたら、その状態をリモート側に送信します。
リモート側では、Cプログラムが動作していますが、GetKeyState(VK_NUMLOCK)という感じで、状態をチェックしできるということがわかりました。この時取得できるデータの最下位ビットが1だとロック状態だということになります。
プログラムでは、直接ロック状態にするとか、解除するという関数はないようなので、まず、状態を把握して、受け取ったクライアントのロック状態と比較して、違っていたら状態を反転させるという形で、プログラムする必要があるようなので、そうしました。状態反転は、SendInputを使って、疑似的にキーを押させるわけです。
これらにより、また、リモート制御プログラムは使い勝手が上がりました。
今回はここまで。では、また。
この記事へのコメント