別アプリのツールバーのボタンを押下するには

解決


Atchoum  2012-05-03 00:35:02  No: 42082

別アプリのツールバーのツールボタンを押下させるにはどうしたらよいでしょうか?

FindWindowExで対象のツールバーのハンドルまでは取得できています。
ハンドルが取得できたのでWM_LBUTTONDOWN、WM_LBUTTONUPでクリックしたつもりにできればいいなと考えました。

 r:TRect;
 hToolBar:HWND;

  hToolBar := FindWindowExで取得

  SendMessage(hToolBar, TB_GETITEMRECT, 1, longint(@r));
  SendMessage(hToolBar,WM_LBUTTONDOWN,0,MakeLParam(r.Left + 2,r.Top +2));
  SendMessage(hToolBar,WM_LBUTTONUP,0,MakeLParam(r.Left + 2,r.Top + 2));

上記のようにしてもrには正しい値が入りません。

共有メモリを使って・・・なんていう記事を見かけましたがやはり共有メモリで値を渡さないといけないのでしょうか。
http://questionbox.jp.msn.com/qa4980573.html

よろしくお願いいたします。


Mr.XRAY  URL  2012-05-03 02:28:19  No: 42083

こんにちは.Mr.XRAYです.

>やはり共有メモリで値を渡さないといけないのでしょうか。

ですね.残念ながら.他のプロセスの情報取得ということになりますから.
ボタンのクリックのコードは,そのままで動作すると思います.

では,頑張ってください.


  2012-05-03 03:00:04  No: 42084

PostMessageでは?
 PostMessage(hToolBar,WM_LBUTTONDOWN,0,MakeLParam(15,9));
 PostMessage(hToolBar,WM_LBUTTONUP,0,MakeLParam(15,9));

15,10は適当です。一番左のボタンのあたりかなぁと


Mr.XRAY  URL  2012-05-03 05:54:22  No: 42085

ハンドル名を見たら,Atchoum さんじゃぁないですか.それじゃぁ,というわけで,

もし,他の方の作成した共有メモリのコードを使ってもいいのであれば,
この間の,RichEditのサンプルに含まれているユニットが使えます.

サンプルは,こんなんではどうでしよう.
えっと,Delphi 2007でしたっけ.多分動作するでしよう(確信なし)
動作確認は,Windows 7 U64 + Delphi XEです.

この間のRichEditは,PostMessageをSendMessageにしたのがありましたが,今回は
逆に,マウスボタンのDownとUPは,PostMessageの方がいいです.

implementation

uses CommCtrl, CommonMemoryUnit;

{$R *.dfm}

//=============================================================================
//  他のアプリのツールバーのボタンをクリックするサンプル
//  Halbow資料館の共有メモリユニット,CommonMemoryUnitを利用
//
//  TB_GETITEMRECTの使用には,usesにCommCtrlが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  r          : TRect;
  hToolBar   : HWND;
  hTargetWnd : HWND;
  ALeft      : WORD;
  ATop       : WORD;
  ComMem     : TCommMemNT;
  ASize      : Integer;
begin
  //他のアプリのTToolbarのハンドルを取得
  hTargetWnd := FindWindow('TTargetForm', nil);
  if hTargetWnd = 0 then exit;

  hToolBar := FindWindowEx(hTargetWnd, 0, 'TToolbar', nil);
  if hToolBar = 0 then exit;

  //TToolbarのボタンのTRect構造体の値を,共有メモリを介して取得
  ASize  := SizeOf(r);
  ComMem := TCommMemNT.Create(hToolBar, ASize);
  try
    SendMessage(hToolBar, TB_GETITEMRECT, 1, LPARAM(ComMem.MemPtr));
    ComMem.Read(0, @r, ASize);
  finally
    ComMem.Free;
  end;

  //TToolbarのボタンをクリック
  ALeft := r.Left + 2;
  ATop  := r.Top  + 2;
  PostMessage(hToolBar,WM_LBUTTONDOWN, 0, MakeLParam(ALeft, ATop));
  PostMessage(hToolBar,WM_LBUTTONUP,   0, MakeLParam(ALeft, ATop));
end;


Atchoum  2012-05-07 19:37:59  No: 42086

こんにちわ。
早速のレスありがとうございます。

てさん
ご指摘ありがとうございます。
自分の中でSendMessageとPostMessageあいまいでした。
SendMessage→同期
PostMessage→非同期
ですね。

Mr.XRayさん
いつもお世話になっています。
サンプルありがとうございました。
1回めのレス拝見してから、XRrayさんに甘えてちゃいかんなと思いがんばってみましたよ!
もちろん、以前教えていただいた TCommMemNTを使いました。
そこで質問なんですが、共有メモリを使ってメッセージを送らなければいけないかどうか、どのように判断したらよいでしょうか?
別アプリに対してのメッセージを送るときはほぼ共有メモリがからむとおもっていいのでしょうか。
自アプリで取得したTB_GETITEMRECTで取得した値を別アプリに送信したところで、別アプリにとっては「寝耳に水」ってことはわかるんですけど、別アプリ、たとえば計算機のハンドルを取得してキーを押すときには共有メモリは必要ないですよね。

------------------------------------------
ツールバーのあるテスト用フォームのソース
TCOOLBarの上にTToolBarを乗せています。
ToolBarの上にToolButton3個設置。
-----------------------------------------
unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, ToolWin, ImgList;

type
  TForm2 = class(TForm)
    CoolBar1: TCoolBar;
    ToolBar2: TToolBar;
    ToolButton1: TToolButton;
    ToolButton2: TToolButton;
    ToolButton3: TToolButton;
    ImageList1: TImageList;
    procedure ToolButton1Click(Sender: TObject);
    procedure ToolButton2Click(Sender: TObject);
    procedure ToolButton3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.ToolButton1Click(Sender: TObject);
begin
  showmessage('3番目のボタンクリック!');
end;

procedure TForm2.ToolButton2Click(Sender: TObject);
begin
  showmessage('1番目のボタンクリック!');
end;

procedure TForm2.ToolButton3Click(Sender: TObject);
begin
  showmessage('2番目のボタンクリック!');
end;

------------------------------------------
クリック送信するアプリケーションのソース
------------------------------------------
procedure TForm1.Button14Click(Sender: TObject);
var
 hCalc,hToolBar,hCoolBar:HWND; {ハンドル}
 CM: TCommMemNT;

 r:TRect;
 intSize:integer;
begin
  //対象ハンドルを取得
  hCalc:=FindWindow(nil,'Form2');
  hCoolBar:=FindWindowEx(hCalc,0,'TCoolBar',0);
  hToolBar:=FindWindowEx(hCoolBar,0,'TToolBar',0);

  if hToolBar <> 0 then
  begin
    //共有メモリクラスを生成
    intSize := Sizeof(r);
    CM := TCommMemNT.Create(hToolBar, intSize);
    try
      //初期化
      r.Left := 0;
      r.Top := 0;
      r.Right := 0;
      r.Bottom := 0;
      CM.Write(0, @r, intSize);

      //共有メモリに転送データを格納
      //ツールボタンは0番目から始まる。2番目のボタンを押下したいので1とする。
      SendMessage(hToolBar, TB_GETITEMRECT, 1, Lparam(CM.MemPtr));

      //共有メモリから読み出し
      CM.Read(0,@r,Sizeof(r));

      //読み出した値でクリックイベントを送信
      PostMessage(hToolBar,WM_LBUTTONDOWN,0,MakeLParam(r.Left + 2,r.Top +2));
      PostMessage(hToolBar,WM_LBUTTONUP,0,MakeLParam(r.Left + 2,r.Top + 2));
      
      //「2番目のボタンクリック!」が表示される。
    finally
      CM.Free;
    end;
  end;

end;

結構検索して似たようなトピックがあっても、質問された方が「できました!ありがとうございます」で終わっていることが
多く、どうやってできたのかとか、サンプルとかが表示されていなくて困ったりしたので、
長くなりますが、なるべく自分のソースも乗せようと思います。


Mr.XRAY  2012-05-07 20:35:30  No: 42087

こんにちは.
サンプルコードの動作確認しました(実際にやってみるのが一番!).

>自分の中でSendMessageとPostMessageあいまいでした。

私も結構あいまいですよ(笑)
今回のWM_LBUTTONDOWN,WM_LBUTTONUPなどは,SendMessageでも動作すると思います.
テストコードの程度でしたら,たいした問題にはならないかと.

ただ,実際のアプリでは,これらのコードはアプリのコードの一部となります.
その時,「あれ?何かタイミングがおかしい」
というこになりそうです.

また,自アプリで,WM_LBUTTONDOWNを自アプリにSendMessageで送る場合,
実際に確認してみるといいかも知れません.

>SendMessage(hToolBar, TB_GETITEMRECT, 1, Lparam(CM.MemPtr));

はSendMessageですね.
というのは,rという値が戻ってこないと,次の処理に行けませんから.
Windows SDK(MSDNの記事と同じ)には,メッセージの説明にPostを使え,
とかの説明がある場合は,それに従うとか,ですね.

>たとえば計算機のハンドルを取得してキーを押すときには共有メモリは必要ないですよね。

です.ハイ.

>多く、どうやってできたのかとか、サンプルとかが表示されていなくて困ったりしたので、

そうなんですよ.
「ありがとうございます」とお礼もいいのですが,実際にどうなったのか.
レスした側としても,知りたいですね.環境によっては,動作しない場合もあります.
こういう掲示板は,本来,相互扶助の場なんですよね.
Delphi使いの仲間として,お互いに助け合うという気持ちが大切だと思います.

私は「過去ログで調べろ!」
というのは好きではありません.新たにプログラムを始めた人にとって,新鮮で,
以前からやっている人にとっては当たり前でも,同じような初歩的な疑問があると
思うんです.
同じ結果を出すにしても,OSやDelphiのバージョンよっては,また別の方法が出てくる
可能性だってあるんですから.

もっとも,今回のToolbarのボタンクリックは,かなり高度な技だとは思いますが(笑)
というわけで,私もまとめてみました.

http://mrxray.on.coocan.jp/Delphi/plSamples/677_ToobarButtonClick.htm


Atchoum  2012-05-07 22:44:32  No: 42088

XRayさん
確認ありがとうございます。
早速まとめているのを発見しましたよ!仕事早い!

「過去ログで調べろ」といわれているスレッドに
XRayさんがフォローしているのをよく見かけていました^^

そうそう、上のDelphiでのサンプルできたことにはできたんですが、
対象となるツールボタンが乗っているプログラムがC#で作ったらしくて
Winspector SPYというツールでツールバーを調べるとクラスが"WindowsForms10.Window.8.app.0.33c0d9d"。
相手がこのクラスだと、rの値が0のままでうまくクリックできませんでした。
せっかくできたのに、ここへ来てちょっとお手上げ。


Nov  2012-05-07 23:33:01  No: 42089

>せっかくできたのに、ここへ来てちょっとお手上げ。
Winspector SPYというツールは使ったことがありませんが、もし、メッセージを調べられるなら、ツールボタンをクリックしたときにWM_NOTIFYメッセージ(またはWM_COMMANDかも)を親ウィンドウに送っていると思います(たぶん)。
その(WM_NOTIFY)内容を直接送れば、クリック位置を考えなくてもすむかもしれません。WM_NOTIFYも呼び出し側プロセスのメモリを参照しているようなので、共有メモリを使う必要があるかもですね。
徒労に終わるかもしれないので、強くはお勧めしません...


Atchoum  2012-05-07 23:52:03  No: 42090

Novさん
情報ありがとうございます。

いろいろこのツールバーのこと調べてみるとToolStripというコントロールらしく、
フォーカスが別ウィンドウにいるときに1回クリックしただけではフォーカスが移らないようです。
こちらのページでメッセージ調べられています。
http://d.hatena.ne.jp/Kazzz/20061106/p1

ウィンドウを前に出してActiveにしたりしてみましたがだめでした。
他のプログラムの動作なので仕方がありません。あきらめます。

Delphiのツールボタンならうまくいきますので、このトピックとしてはOKかな。


Nov  2012-05-08 01:31:55  No: 42091

>Delphiのツールボタンならうまくいきますので、このトピックとしてはOKかな。
了解です。
ただ、ここからは戯言ですが、少し気になったのが、
>こちらのページでメッセージ調べられています。
は、PostMessageしたものしか見ていないような...(ツールの設定?)
WH_CALLWNDPROCフックで調べればはっきりするのですが、たぶん、WM_NOTIFYはSendMessageです。


Nov  2012-05-08 04:06:10  No: 42092

失礼しました。
>PostMessageしたものしか見ていないような...(ツールの設定?)
これは、リンク先をよく読んだら、
>マウスメッセージ以外はキャプチャ対象から外し、更に不要なメッセージ(WM_SETCURSOR等)のログは削除しているため
となってますので、やはり、調べてもWM_NOTIFYが送られていない可能性大ですね。残念。


Atchoum  2012-05-08 18:40:56  No: 42093

Novさん
アドバイスありがとうございます。
あのあとメッセージを確認してみましたがWM_NOTIFYは送られていませんでした。

う〜ん。


Atchoum  2012-05-08 21:02:26  No: 42094

TB_GETITEMRECTをつかうのをあきらめ、
クリックしたいツールボタンの座標をしらべてWM_LBUTTONDOWN、UP
することにします。
幸いなことにツールボタンの位置は変わらないので。


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

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






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