[BlueLeaf1336]> PROBLEMS> ClickThereItIs!>

ClickThereItIs! - Whose Window this is ?

historyTOP

2003/11/22:作成
2004/10/27:更新

最新版。20041027ModuleView.zip(191,113bytes)

memoTOP

とりあえずテストプログラム第1弾です。

あるタイミングにおいて(ってこのタイミングが非常に難しいんですが)、マウスの直下にあるウィンドウを捜します。これは大丈夫です。WindowFromPoint(うろ覚え)を使います。

でとりあえずテストなので、ボタンのMouseUpイベントをそのタイミングとします。ボタンの上でマウスの左ボタンをを押してそのままドラッグして別の場所で左ボタンを離すと、デスクトップの任意の場所にマウスがある状態で自作のアプリケーションがイベントを拾えます。で、このときのマウスの位置をそれなりにスクリーン座標に変換しその直下のウィンドウを見つけるというわけです。

それだけではなくて、今度はそのウィンドウを所有しているプログラム(アプリケーション)を捜します。たとえば自分の作ったアプリケーションなら"Project1.exe"とかデスクトップなら"Explorer.exe"とかフォルダを開いているのならやっぱり"Explorer.exe"とか。もちろんフルパスで。

本当の目的は開いているフォルダのフルパスをとりたいのですが、とりあえずそのウィンドウが何らかのアプリケーションなのかエクスプローラなのかを判断する必要があるかと。そしてもちろんエクスプローラなら、開いているフォルダのパスを取得したいと。ただこっちの方はさらに先の話になるかと思います。

とか書いてますが、実際には、マウスの下のウィンドウの持ち主は取得できています。以下にコードを示します。誰かの役にも自分の役にも立ちますように。

問題は、使用しているAPIが「psapi.dll」なんていう見たこともないDLLを使用していてこれがまたNT系でしか使用できないときた。嫌な感じだ。

screen shotTOP

Button1を押したまま(左ボタンを離さずに)で、エクスプローラで開いたフォルダの上でマウスを離した状態です。1行目にその時のスクリーン座標、2行目にその場所にあったウィンドウを実行しているプロセス、で関連するモジュールが表示されています。モジュールの最初には大本のアプリケーションが表示されるようです。

codeTOP

unit Main;

interface

uses
  Windows, SysUtils, Classes, Controls, Forms, StdCtrls, Buttons, ExtCtrls;

type
  TMainForm = class(TForm)
    Panel1: TPanel;
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1MouseUp(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
  private

  public

  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
    psapi;

(*
    ===========================================================================
    プロセスIDに関係するモジュールフルパス
    ---------------------------------------------------------------------------
    XP, 2003, 2000(Pro/Svr), NT(WS/Svr) 限定
    ---------------------------------------------------------------------------
    ms-help://MS.PSDK.1033/perfmon/base/enumerating_all_modules_for_a_process.htm
    ===========================================================================
*)
procedure   PrintModules(AProcID: Cardinal; AResults: TStrings);
const
    MAX_HMODULE = 1024;
var
    DesiredAccess: Cardinal;                        //  フラグ
    ModHandle : array[0..MAX_HMODULE-1] of HMODULE; //  モジュールハンドル
    ProcHandle: THandle;                            //  プロセスハンドル
    cbNeeded  : DWORD;                              //  配列の大きさ
    i         : integer;
    p         : PChar;
    ModNameLen: integer;                            //  モジュール名長
    ModName   : string;                             //  モジュール名
begin
    //  とりあえずプロセスIDを出力しておく
    AResults.Add(Format('Process ID: %d', [AProcID]));
    //  プロセスのハンドルを取り出す
    DesiredAccess := PROCESS_QUERY_INFORMATION or PROCESS_VM_READ;
    ProcHandle := OpenProcess(DesiredAccess, false, AProcID);
    //  失敗?
    if (ProcHandle = INVALID_HANDLE_VALUE) then
    begin
        Exit;
    end
    //  成功!
    else
    begin
        try
            //  このプロセスの全てのモジュールのリストを取得する(pspi.dll)
            if (EnumProcessModules(ProcHandle, @ModHandle, MAX_HMODULE, cbNeeded)) then
            begin
                //  モジュールの個数だけ繰り返しましょう
                for i := 0 to (cbNeeded div sizeof(HMODULE)) - 1 do
                begin
                    //  メモリ確保
                    GetMem(p, MAX_PATH);
                    //  モジュールファイルのフルパス取得
                    ModNameLen := GetModuleFileNameEx(ProcHandle, ModHandle[i], p, MAX_PATH);
                    //  長さ調節
                    SetString(ModName, p, ModNameLen);
                    //  メモリ解放
                    FreeMem(p);
                    //  リストに追加
                    AResults.Add(Format('%s (%8d)', [ModName, ModHandle[i]]));
                end;
            end;
        finally
            //  開けたら閉める
            CloseHandle(ProcHandle);
        end;
    end;
end;

(*
    ===========================================================================
    マウスの下にあるウィンドウのプロセスIDに関連するモジュール名称
    ===========================================================================
*)
procedure TMainForm.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
    Pnt : TPoint;
    WH  : HWND;
    ProcessID{, ThreadID}: integer;
begin
    //  うっとおしいのでクリア
    Memo1.Lines.Clear;
    //  マウスを放したときの座標
    Pnt := Point(X, Y);
    //  その座標をスクリーン座標に変換する
    Pnt := TControl(Sender).ClientToScreen(Pnt);
    //  変換した座標を表示する
    Memo1.Lines.Add(Format('Mouse X:%3.3d Y:%3.3d', [Pnt.X, Pnt.Y]));
    //  その位置にあるウィンドウのハンドルを取る
    WH := WindowFromPoint(Pnt);
    //  有効ですか?
    if (WH <> 0) then
    begin
        //  そのウィンドウのプロセスIDは?
        {ThreadID := }GetWindowThreadProcessId(WH, @ProcessID);
        //  でそのモジュール名は?
        PrintModules(ProcessID, Memo1.Lines);
    end;
end;

end.

Delphi 6 Personal dpr/dfm/pasTOP

20031122ClickHereItIs_001.zip(2,918bytes)

2004/10/27TOP

ほんの少し変更しました。本当にどうでもよい変更ですが。

白いパネルでマウスを押し、そのままマウスを移動させると調べようとするウィンドウを点滅させます。ただ、点滅はウィンドウなんですが、実際に調べるのはそのウィンドウの親をてっぺんまでさかのぼったウィンドウになります。出力するのは、そのウィンドウが使用しているDLL全て(のはず)です。

図は、自分自身を調べた状態です。ところで、変更箇所というのは、ウィンドウの点滅機能です。

実行ファイルとソースコード。20041027ModuleView.zip(191,113bytes)

EOFTOP