none
C言語でwin32apiを使ってnotepadにpostmessageでcontrol aを送りたいが方法が分からない。

    質問

  • 一つの文字だけを送る場合は下記で上手く動きます。

    #include <windows.h>

    int main(void){
         HWND hWnd;
         hWnd = FindWindow("Notepad", NULL);
         hWnd = FindWindowEx(hWnd, NULL, "Edit", NULL);
         PostMessage(hWnd, WM_CHAR, 'a', 0);
         return 0;
    }

    然し、ここでcontrol aを送るには、PostMessageのWM_CHAR

    の次の引数をどの様に記述をすれば良いのでしょうか。

    宜しく、お願いします。


    • 編集済み 臼井博義 2016年5月3日 1:25 入力ミスの為
    2016年5月3日 1:24

回答

  • WM_CHARは「キャラクタが入力された」ことを通知しますが、「キーボードが押された」状態が変わるわけではありません。Ctrlキーの押下状態をエミュレートするには、

    SendInput()

    keybd_event()

    などを使用します。

    • 回答としてマーク 星 睦美 2016年5月20日 8:08
    2016年5月3日 2:06
  • SendInput は キーボード入力のシミュレーションを行うAPIのようで、フォアグラウンドになっているGUIに対してキーボード入力をシミュレーションするようです。
    先に紹介したURLに記載のプログラムは Notepad 専用というわけではなく、プログラムを実行時に Notepad をフォアグラウンドにしておくことで期待の動作になりました。
    違う言い方をすると、対象のアプリをフォアグラウンドに、対象のエディット先にフォーカスがあるように、それぞれ事前に設定する必要があるようです。

    前処理として指定のウィンドウをフォアグラウンドに、エディット先にフォーカスがあるように、それぞれ処理を追加して以下に記載します。
    ・Notepadは事前に起動しておいてください。
    ・下記サンプルは Ctrl+v の動作なので、事前に何かしらの文字を Ctrl+C しておいてください。

    #include <stdio.h>
    #include <tchar.h>
    #include <Windows.h>

    int main() { HWND hWnd1, hWnd2; hWnd1 = FindWindow(_T("Notepad"), NULL); hWnd2 = FindWindowEx(hWnd1, NULL, _T("Edit"), NULL); Sleep(5000); // 適当 SetForegroundWindow(hWnd1); SetFocus(hWnd2); // Create a generic keyboard event structure INPUT ip; ip.type = INPUT_KEYBOARD; ip.ki.wScan = 0; ip.ki.time = 0; ip.ki.dwExtraInfo = 0; while (1) { // Press the "Ctrl" key ip.ki.wVk = VK_CONTROL; ip.ki.dwFlags = 0; // 0 for key press SendInput(1, &ip, sizeof(INPUT)); // Press the "V" key ip.ki.wVk = 'V'; ip.ki.dwFlags = 0; // 0 for key press SendInput(1, &ip, sizeof(INPUT)); // Release the "V" key ip.ki.wVk = 'V'; ip.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &ip, sizeof(INPUT)); // Release the "Ctrl" key ip.ki.wVk = VK_CONTROL; ip.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &ip, sizeof(INPUT)); Sleep(1000); // pause for 1 second } return 0; }

    当方ではこれで意図通りに動作しています。
    APIの動作という意味ではこれで理解できると思いますが、残念ながら臼井さんの期待する動作とは違うであろうと思います。
    実際、上記APIを実行中にマウス操作などで別アプリを選択すると、Ctrl+v の操作はそちらへ移動しました。
    期待する動作とは異なると推測します。
    私なりに調べてみましたが、PostMessage、SendMessage による方法で CTRL、ALT、SHIFT、などのキー押下イベントを送付する方法を見つけることはできませんでした。

    追記:

    過去に類似記事がありました。こちらも仲澤さんのコメントでしたね。
    https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/5c4c7069-ba4a-4be4-8ca7-13bc79bd5ca3?forum=vcgeneralja



    • 編集済み HIDE0707 2016年5月3日 7:49
    • 回答としてマーク 星 睦美 2016年5月20日 8:08
    2016年5月3日 7:29
  • SendInput 自体は、エディットボックスや特定のアプリケーションといった縛りはありません。
    それを呼んだ時点で、アクティブなウィンドウ(アプリケーション)に対するキーボード操作となりますので、エディットボックスがあろうとなかろうと、機能します。

    問題は、SendInput を呼ぶ時点でアクティブなウィンドウが何かです。
    たとえば、自分のプログラムのウィンドウに置いたボタンを押されたときに処理する場合は、アクティブなウィンドウは自分のプログラムになっているので、そのまま実行すると目的のアプリケーションにキー入力を送ることができません。
    このため、今までのソースコードの事例では、FindWindow や SetForegroundWindow を使って対象のウィンドウをアクティブにしているのです。
    ご自身の想定しているシナリオでも必要なのであれば応用してください。不要であれば、FindWindow/SetForegroundWindow は無視すればよいでしょう。

    -----
    ところで
    何をされたときに「control space」を送りたいのか?なぜ、キーボードで自ら「control space」を押したくないのか?このあたりが疑問です。
    もしかしたら、今の話の流れ自体がやりたいこととマッチしてないかもしれないので。

    2016年5月3日 15:21
    モデレータ

すべての返信

  • WM_CHARは「キャラクタが入力された」ことを通知しますが、「キーボードが押された」状態が変わるわけではありません。Ctrlキーの押下状態をエミュレートするには、

    SendInput()

    keybd_event()

    などを使用します。

    • 回答としてマーク 星 睦美 2016年5月20日 8:08
    2016年5月3日 2:06
  • 有難う御座います。

    済みませんが、そのコードをここで例示願います。

    宜しくお願い、ます。

    2016年5月3日 2:18
  • ほとんどそのまま使用できそうなサンプルソースがあったので紹介します。

    https://batchloaf.wordpress.com/2012/10/18/simulating-a-ctrl-v-keystroke-in-win32-c-or-c-using-sendinput/

    下記の所が CTRL キーを送信する部分ですね。

    // Press the "Ctrl" key
    ip.ki.wVk = VK_CONTROL;
    ip.ki.dwFlags = 0; // 0 for key press
    SendInput(1, &ip, sizeof(INPUT));



    • 編集済み HIDE0707 2016年5月3日 3:26
    2016年5月3日 3:17
  • 有難う御座います。

    この方法では、キーを送る対象のアプリケーションは指定をしていない様ですが。

    これは、もしかしたらnotepad専用と言う事でしょうか。

    私としては、例示としてはnotepadを指定していますが。

    実際は、汎用性が合ってそれ以外も念頭に入れていますので。

    若しも、これがnotepad専用と言う事で有れば、違うと思います。

    汎用と言う事では、他の方法と言うのは無いのでしょうか。

    宜しくお願いします。

    2016年5月3日 5:11
  • SendInput は キーボード入力のシミュレーションを行うAPIのようで、フォアグラウンドになっているGUIに対してキーボード入力をシミュレーションするようです。
    先に紹介したURLに記載のプログラムは Notepad 専用というわけではなく、プログラムを実行時に Notepad をフォアグラウンドにしておくことで期待の動作になりました。
    違う言い方をすると、対象のアプリをフォアグラウンドに、対象のエディット先にフォーカスがあるように、それぞれ事前に設定する必要があるようです。

    前処理として指定のウィンドウをフォアグラウンドに、エディット先にフォーカスがあるように、それぞれ処理を追加して以下に記載します。
    ・Notepadは事前に起動しておいてください。
    ・下記サンプルは Ctrl+v の動作なので、事前に何かしらの文字を Ctrl+C しておいてください。

    #include <stdio.h>
    #include <tchar.h>
    #include <Windows.h>

    int main() { HWND hWnd1, hWnd2; hWnd1 = FindWindow(_T("Notepad"), NULL); hWnd2 = FindWindowEx(hWnd1, NULL, _T("Edit"), NULL); Sleep(5000); // 適当 SetForegroundWindow(hWnd1); SetFocus(hWnd2); // Create a generic keyboard event structure INPUT ip; ip.type = INPUT_KEYBOARD; ip.ki.wScan = 0; ip.ki.time = 0; ip.ki.dwExtraInfo = 0; while (1) { // Press the "Ctrl" key ip.ki.wVk = VK_CONTROL; ip.ki.dwFlags = 0; // 0 for key press SendInput(1, &ip, sizeof(INPUT)); // Press the "V" key ip.ki.wVk = 'V'; ip.ki.dwFlags = 0; // 0 for key press SendInput(1, &ip, sizeof(INPUT)); // Release the "V" key ip.ki.wVk = 'V'; ip.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &ip, sizeof(INPUT)); // Release the "Ctrl" key ip.ki.wVk = VK_CONTROL; ip.ki.dwFlags = KEYEVENTF_KEYUP; SendInput(1, &ip, sizeof(INPUT)); Sleep(1000); // pause for 1 second } return 0; }

    当方ではこれで意図通りに動作しています。
    APIの動作という意味ではこれで理解できると思いますが、残念ながら臼井さんの期待する動作とは違うであろうと思います。
    実際、上記APIを実行中にマウス操作などで別アプリを選択すると、Ctrl+v の操作はそちらへ移動しました。
    期待する動作とは異なると推測します。
    私なりに調べてみましたが、PostMessage、SendMessage による方法で CTRL、ALT、SHIFT、などのキー押下イベントを送付する方法を見つけることはできませんでした。

    追記:

    過去に類似記事がありました。こちらも仲澤さんのコメントでしたね。
    https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/5c4c7069-ba4a-4be4-8ca7-13bc79bd5ca3?forum=vcgeneralja



    • 編集済み HIDE0707 2016年5月3日 7:49
    • 回答としてマーク 星 睦美 2016年5月20日 8:08
    2016年5月3日 7:29
  • 有難う御座います。

    実は対象としているアプリケーションと言うのは、

    エディットの部分は有りません。そのアプリケーションと言うのは

    漢直winでcontrol spaceを押すたびにオンとオフを交互に繰り返して

    トグルします。

    だから、その意味では少し意味が違うのかも知れません。回答されたこの場合の

    キーボード入力と言うのはエディットコントロールを指していると思います。

    私が実行したいのは、エディットコントロールを持っていないアプリケーション

    に対してcontrol spaceを送りたいのですが。無理なんでしょうか。

    2016年5月3日 14:53
  • SendInput 自体は、エディットボックスや特定のアプリケーションといった縛りはありません。
    それを呼んだ時点で、アクティブなウィンドウ(アプリケーション)に対するキーボード操作となりますので、エディットボックスがあろうとなかろうと、機能します。

    問題は、SendInput を呼ぶ時点でアクティブなウィンドウが何かです。
    たとえば、自分のプログラムのウィンドウに置いたボタンを押されたときに処理する場合は、アクティブなウィンドウは自分のプログラムになっているので、そのまま実行すると目的のアプリケーションにキー入力を送ることができません。
    このため、今までのソースコードの事例では、FindWindow や SetForegroundWindow を使って対象のウィンドウをアクティブにしているのです。
    ご自身の想定しているシナリオでも必要なのであれば応用してください。不要であれば、FindWindow/SetForegroundWindow は無視すればよいでしょう。

    -----
    ところで
    何をされたときに「control space」を送りたいのか?なぜ、キーボードで自ら「control space」を押したくないのか?このあたりが疑問です。
    もしかしたら、今の話の流れ自体がやりたいこととマッチしてないかもしれないので。

    2016年5月3日 15:21
    モデレータ
  • hide様の下記のコードでwhile(1)を外すと動きません。

    何が悪いのでしょうか。かと言ってwhile(1)では、ループが解除

    出来ないのですが。

    これはwhile(1)とINPUTと言うのは関係が有るのでしょうか。

    2016年5月3日 22:34
  • なるほど。
    元のサンプルの Sleep(5000); を SetFocus の下に移動すれば動くと思いますよ。
    (残念ながら、今の位置にある意味はないので…)

    2016年5月3日 23:28
    モデレータ