SendMessageでキーコードを送るには?

解決


ユウキ  2004-03-30 07:42:57  No: 8040

メモ帳に「Ctrl」+「A」をおくるコードを作成したんですが、どうもうまくいきません。

//Form1にButton1をおいてください。
procedure TForm1.Button1Click(Sender: TObject);
var NOTEEDIT :HWND;
begin
  NOTEEDIT:=FindWindow('Notepad',nil);
  NOTEEDIT:=Findwindowex(NOTEEDIT,0,'Edit',nil);

  SendMessage(PPCtrl, WM_KEYDOWN, VK_CONTROL, 0);
  SendMessage(PPCtrl, WM_KEYDOWN, Ord('A'), 0);
  SendMessage(PPCtrl, WM_KEYUP, Ord('A'), 0);
  SendMessage(PPCtrl, WM_KEYUP, VK_CONTROL, 0);
end;

解決策がありましたら、ご教授お願いします。

(1日に2つの質問をしてすみません。でも本当にわかんないんです。。。トホホ・・)


たかみちえ  URL  2004-03-30 08:07:32  No: 8041

まずうまくいくかどうかはおいといて、
PPCtrlとは何型変数でしょうか?
みたところそんな変数は宣言されていないし、
このコードのとおりだと、メモ帳のエディットコントロールのハンドルは、NOTEEDIT変数に入っていると思うのですが。


ユウキ  2004-03-30 21:18:13  No: 8042

すいません!訂正してませんでした!PPCtrlは正しくは、NOTEEDITです。
というわけでコードは
procedure TForm1.Button1Click(Sender: TObject);
var NOTEEDIT :HWND;
begin
  NOTEEDIT:=FindWindow('Notepad',nil);
  NOTEEDIT:=Findwindowex(NOTEEDIT,0,'Edit',nil);

  SendMessage(NOTEEDIT, WM_KEYDOWN, VK_CONTROL, 0);
  SendMessage(NOTEEDIT, WM_KEYDOWN, Ord('A'), 0);
  SendMessage(NOTEEDIT, WM_KEYUP, Ord('A'), 0);
  SendMessage(NOTEEDIT, WM_KEYUP, VK_CONTROL, 0);
end;
になります。すいませんでした。


にしの  2004-03-30 22:11:31  No: 8043

アクセラレータは別のところで処理されているような気がします。
メニューの「すべて選択」を選ぶのであれば、
  NOTEEDIT:=FindWindow('Notepad',nil);
  PostMessage(NOTEEDIT, WM_COMMAND, 25, 0);
でできるはず。

25が、「すべて選択」のIDです。環境によって違うかもしれません。
# こちらの環境は、WindowsXP Proです

例えば、
  NOTEEDIT:=FindWindow('Notepad',nil);
  PostMessage(NOTEEDIT, WM_COMMAND, 2, 0);
とやれば、「開く」が実行されるはずです。


ユウキ  2004-03-30 22:27:05  No: 8044

にしのさん、お返事ありがとうございました。
var NOTEEDIT :HWND;
begin
  NOTEEDIT:=FindWindow('Notepad',nil);
  SendMessage(NOTEEDIT, WM_COMMAND, 25, 0);
end;
で動作確認できました。

>25が、「すべて選択」のIDです。環境によって違うかもしれません。
とありましたが、このIDはどうやって調べるんですか?


にしの  2004-03-30 22:34:34  No: 8045

もう1つ。
捜査対象のウィンドウにフォーカスを充てれば、keybd_eventが使えます。

var
  NOTEEDIT :HWND;
begin
  NOTEEDIT:=FindWindow('Notepad',nil);
  BringWindowToTop(NOTEEDIT);
  keybd_event(VK_CONTROL, 0, 0, 0);
  keybd_event(Ord('A'), 0, 0, 0);
  keybd_event(Ord('A'), 0, KEYEVENTF_KEYUP, 0);
  keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
end;

> >25が、「すべて選択」のIDです。環境によって違うかもしれません。
> とありましたが、このIDはどうやって調べるんですか?
メモ帳であれば、リソースエディタなどで確認できます。リソースとして持っていない場合(コーディングでアクセラレータキーを設定している場合)や、リソースが暗号化(圧縮など)されている場合には使えません。

googleで、eXeScopeや、Resource Hackerを検索してみてください。


ユウキ  2004-03-30 22:43:41  No: 8046

>リソースとして持っていない場合(コーディングでアクセラレータキーを設定している場合)や、リソースが暗号化(圧縮など)されている場合には使えません。

目的のプログラムはこの枠に当てはまるようです。。。
なのでkeybd_eventを利用したんですが、タイミングが合わず
これより後のコードが無視されてしまうことがあります。。(困ったもんだ・・・)

スレ違いですが、、クラスもキャプションも同じウィンドウに、命令を送るにはどうしたらいいでしょうか?


にしの  2004-03-30 23:58:06  No: 8047

BringWindowToTopの後に、キーフォーカスを設定する必要があるのかもしれません。
目的のプログラムというのがどういうものか解りませんが、たいていはkeybd_eventで出来ますよ。

> クラスもキャプションも同じウィンドウに、命令を送るにはどうしたらいいでしょうか?
クラスというのが、'EDIT'などのウィンドウクラス名であれば、FindWindowでOKです。
もし、トップウィンドウでなく子ウィンドウが必要であれば、FindWindowExで親ウィンドウを指定して検索できます。
クラス名が同じウィンドウが複数あって特定できないというのであれば、EnumWindowなどでウィンドウを列挙し、GetWindowTextなどでキャプションを得て判断できます。
# キャプションの他にも、ウィンドウの大きさなどでも判別できるかと思います

命令=メッセージのことでしょうか。
言い忘れていましたが、SendMessageとPostMessageは別物です。WM_GETTEXTなどは戻り値を返すのでSendMessageを使用しますが、キーを送信するときなどはPostMessageを使用します。


ユウキ  2004-03-31 00:26:42  No: 8048

FinderSystemにて目的のウィンドウの情報を検索した結果。
===========================
ハンドル:1639424  Hex:0x00190400
クラス:TFlatBitColorBtnEx
キャプション:btnHomeAll
===========================

この情報のうちのクラスとキャプションの部分が
重複したコンポーネントが存在します。

>クラスもキャプションも同じウィンドウに、命令を送るにはどうしたらいいでしょうか?
言い方を変えてみますと、ハンドルからウィンドウを特定できるか?
そして特定したウィンドウにメッセージを送れるか?
ということになると思います、多分。。。
お返事お待ちしております・・・


んぽ  2004-03-31 00:39:18  No: 8049

>なのでkeybd_eventを利用したんですが、タイミングが合わず
Sleep()を挟んで時間を調節してみては?


にしの  2004-03-31 01:18:02  No: 8050

クラス名から察すると、ボタンのようですが・・・。
キーを送りたいんですよね?
どういうキーを送ろうとしています?


jok  2004-03-31 01:34:21  No: 8051

> この情報のうちのクラスとキャプションの部分が
> 重複したコンポーネントが存在します。

コントロールIDは違うはずですが、これで区別できませんか?


ユウキ  2004-03-31 05:08:02  No: 8052

んぽさんへ、
まずSleepを間に挟むというのは、今、その場しのぎでやっています。
Sleepを追加することによりとりあえず動作はするんですが、
ユーザーが連続で処理を実行すると、コードの一部が無視されます。
さらに処理後のSendMessage(Handle,WM_CLOSE,0,0);が動作しなくなります。

にしのさんへ
察しのとおりこのコントロールは、ボタンです。しかし、
>ハンドルからウィンドウを特定
が実行できないと、ボタンをクリックするメッセージを送ることはできません。
なので、目的のソフトに設定されているこのボタンと同じ動作をする
キーボードショートカットを利用しようと考えていましたが、
Keybd_eventも不安定な動作のためこれは断念しなければならない
状態になってしまったんです。
ちなみに送るはずのショートカットは、Ctrl+Iです。

jokさんへ
コントロールIDとは、ハンドルのことですか?


jok  2004-03-31 05:20:14  No: 8053

> コントロールIDとは、ハンドルのことですか?

ちがいます。

GetWindowLong(hWindow,GWL_ID)

で得られる数値です。


ユウキ  2004-03-31 05:51:34  No: 8054

jokさんへ、
クラス名もキャプションも同じウィンドウが複数あって特定できないので、
プログラムからコントロールIDを調べるのは不可能です。

GetWindowLongを調べると、GWL_IDで戻ってくるものはウィンドウIDだそうです。
http://yokohama.cool.ne.jp/chokuto/urawaza/api/GetWindowLong.html
FinderSystemによるとハンドルの16進数したものがウィンドウIDだそうです。
しかし、ハンドルは起動するたびに変動していますので、ウィンドウIDも変動してしまいますね。。


にしの  2004-03-31 06:11:09  No: 8055

コントロールIDは、Delphi製(C++Builderも?)の場合、Handle=IDの場合が多いです。
クラス名「TFlatBitColorBtnEx」の命名規則から推測するに、Delphi製かC++Builder製でしょうから、使えないと思います。
念のため、IDが振られているか確認してください。固定のIDが振られていれば結構簡単になるのでは?

ウィンドウの生成順序はかわらないはずですので、何番目にそのボタンが現れるか調べて、FindWindow, FindWindowEx(もしくはEnumWindowなど)で探すのがよいかと。
もし生成順序が変わるのであれば(例えばオプションでToolbarを非表示にしたり等のため)、ボタンの位置(必要であれば親ウィンドウに対する相対位置を計算)などの情報で特定すればよろしいかと思います。


にしの  2004-03-31 06:17:15  No: 8056

> FinderSystemによるとハンドルの16進数したものがウィンドウIDだそうです。
この認識は間違いですよ。
「Delphiでは」という限定と思った方がよろしいかと。

VC++では、リソースとしてそれぞれのコントロールにIDを振っていたと思います。
# VCはあまりやらないので曖昧ですが・・・


jok  2004-03-31 06:19:07  No: 8057

うーむ、コントロールIDは任意の番号を付けられます。開発環境に依存します。
古いスタイルのVCのダイアログアプリだと、定数で定義されますのでいつも
同じIDを持っています。Win2000 でメモ帳を三つ立ち上げて、その Edit 部分の
IDは三つとも同じです。ペイントのツールパレットの虫眼鏡ボタンについても
いつも同じIDを示します。しかし、Delphi ではIDEが勝手に動的につけてくれる
ようで、起動するたびに変動するようですね。このような場合は確かにつかえません。

例えばペイントのツールボックスの虫眼鏡ボタンのハンドルをどうやって見つける
かをコントロールIDを使わずに出来ればいいのですよね?
Win2kのペイントの場合、このボタンは AfxWnd42u というクラスのウィンドウの
子ウィンドウで16個の ToolChid というボタンの一つです。すべてのウィンドウ
テキストは空白です。しかし、この16個のボタンのうちZオーダーの高いほうから
6番目が目指す虫眼鏡ボタンです。子ウィンドウをZオーダーの順で列挙するには
最もたかいものを

hWindow := GetWindow(hParent,GW_CHILD);

で見つけ、そのつぎは

hWindow := GetWindow(hWindow,GW_HWNDNEXT);

で見つかります。ですから、Zオーダは起動するたびには変化しないので、これを
使えば任意番目のボタンのハンドルが取得できます。


ユウキ  2004-03-31 07:02:46  No: 8058

みなさん!ありがとうございました!問題はめでたく解決しました!

たかみちえさんへ、
  コードの間違いの指摘ありがとうございました!
  今後気をつけたいと思います。

んぽさんへ、
  コードの提案、ありがとうございました!
  今度何かあったときはお願いしますね!

にしのさんへ、
  長くにわたり、お返事ありがとうございました!
  >> FinderSystemによるとハンドルの16進数したものがウィンドウIDだそうです。
  >この認識は間違いですよ。
  >「Delphiでは」という限定と思った方がよろしいかと。
  これについては、勉強になったと思います!

jokさんへ
  親切な回答、ありがとうございました!
  また助けてもらいましたね!いい加減なお願いかもしれませんが、
  何かあったそのときは、よろしくお願いします。

余談:
  作成しているツールも完成まで後少し!がんばりたいと思います!
  皆さん本当にありがとうございました!


ユウキ  2004-03-31 07:03:33  No: 8059

おっと、解決をチェックするの忘れてました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加