次の方法で共有


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

質問

2016年5月3日火曜日 1:24

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

#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

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

宜しく、お願いします。

すべての返信 (9)

2016年5月3日火曜日 2:06 ✅回答済み | 2 票

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

SendInput()

keybd_event()

などを使用します。


2016年5月3日火曜日 7:29 ✅回答済み | 1 票

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


2016年5月3日火曜日 15:21 ✅回答済み | 1 票

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

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

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


2016年5月3日火曜日 2:18

有難う御座います。

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

宜しくお願い、ます。


2016年5月3日火曜日 3:17

ほとんどそのまま使用できそうなサンプルソースがあったので紹介します。

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));

2016年5月3日火曜日 5:11

有難う御座います。

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

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

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

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

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

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

宜しくお願いします。


2016年5月3日火曜日 14:53

有難う御座います。

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

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

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

トグルします。

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

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

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

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


2016年5月3日火曜日 22:34

hide様の下記のコードでwhile(1)を外すと動きません。

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

出来ないのですが。

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


2016年5月3日火曜日 23:28

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