お知らせ

電子会議

ライブラリ

パレット

Delphi FAQ検索

Delphi FAQ一覧

サンプル蔵





FDelphi FAQ
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル

"列挙(コールバック)時の情報を列挙後へ渡す"





こんにちは

列挙するためのWindowsAPIを使った場合に、列挙時の情報を
列挙から戻った後で利用するために、情報を取得する方法です。

以下のやり方に気づくまでは、ユニット内でグローバルな変数を
作ったりしていましたが、それではスレッドにも対応できないし、
列挙のためのコールバック関数を必要毎に作らなくてはならない
などしたため、今では全て以下の方法でやっています。

その方法とは、

ほとんどの列挙では「コールバック関数へ引き渡す引数」があります。
この引数は、APIではint型数値になっています。
つまり、
  EnumWindows(@EnumWindowsProc, 1234);
と実行すると、コールバック関数の第2引数 lParam には、1234の値が
入っています。もちろん、この値はコールバック関数側で変えられません。

しかし、この引数は厳密にいうと、今のWindowsで4バイト整数値と
なっています。つまり、4バイトのデータであれば、キャスト可能と
いうことです。
従って、この引数に「あるメモリのアドレス」を指定しておけば、
そのアドレスが列挙されるコールバック関数へ引き渡すことができます。

つまり、オブジェクトのアドレスや構造体のポインタを渡すことが
できるのです。これを利用して、TListのオブジェクトポインタを渡して
列挙されたウィンドウをTListに追加したり、構造体のポインタを渡して
構造体のメンバの値を見たり、設定することができるようになります。

そうするためのポイントは、「コールバックの関数を作る際に、第2引数
を都合のよいポインタにする」ことです。コールバック関数自体は、
自分のプログラム内に記述するもので、引数や戻り値の構造とデータサイズ、
呼出規約が一致していれば、API通りにする必要はないのです。
つまり、EnumWindowsの場合なら
function EnumWindowsProc([ハンドル], [4バイトデータ]): Boolean; stdcall;
の構成が一致していれば、いいわけです。

列挙のやり方としては、この方法が一番よいと判断しています。


■列挙したウィンドウをリストに追加する例...

ウィンドウ列挙の情報をTListに取得する方法です。
ポイントは、コールバック関数の第2引数をTListにしてしまうところです。

// コールバック関数(関数名はなんでもよい)
function EnumWindowsProc(ehWnd: HWND; lParam: TList): Boolean; stdcall;
begin
  lParam.Add(Pointer(ehWnd));
  Result := True;
end;

// 実行するための関数(なんでもよいがボタンクリックにした)
procedure TForm1.Button1Click(Sender: TObject);
var
  AllWindow: TList;
begin
  AllWindow := TList.Create;  // TListオブジェクトの構築
  // 以下の関数の第2引数は、オブジェクトをキャストして渡す
  EnumWindows(@EnumWindowsProc, Cardinal(AllWindow));
  //
  // この間、AllWindow.Items[??]には、ウィンドウのリストが入っている
  // 取得時は、HWND(AllWindow.Items[0]) のようにキャストすべし
  //
  AllWindow.Free;  // TListオブジェクトの解放
end;


■列挙の中で、構造体に値を代入する例...

ウィンドウ列挙の情報を構造体へ取得する方法です。
ポイントは、コールバック関数の第2引数を構造体のポインタにしてしまうというこ
と。

// 構造体の定義(なんでもよい)
type
  PEnumWindowsRec = ^TEnumWindowsRec;
  TEnumWindowsRec = record
    ChkA: HWND;  // メンバ(ほしい情報などにする)
    ChkB: HWND;  // この辺りは必要に合わせて作ってください。
  end;

// コールバック関数(関数名はなんでもよい)
function EnumWindowsProc(ehWnd: HWND; lParam: PEnumWindowsRec): Boolean; 
stdcall;
begin
  Result := True;
  if lParam^.ChkA = 0 then
    lParam^.ChkA := ehWnd   // 1番始めに取得したハンドルを代入
  else if lParam^.ChkB = 0 then
    lParam^.ChkB := ehWnd   // 2番目に取得したハンドルを代入
  else
    Result := False;        // それ以外は列挙を止める
end;

// 実行するための関数(なんでもよいがボタンクリックにした)
procedure TForm1.Button1Click(Sender: TObject);
var
  myRec: TEnumWindowsRec;
begin
  myRec.ChkA := 0;  // 初期化しておく
  myRec.ChkB := 0;
  // 以下の関数の第2引数は、構造体のポインタをキャストして渡す
  EnumWindows(@EnumWindowsProc, Cardinal(@myRec));
  //
  // この後に、myRec.ChkAやmyRec.ChkBに列挙から取得したハンドルが入る
end;

なお、このやり方では、EXEならEXE内で、DLLなら同じDLL内で
コールバック関数を作ってください。メモリポインタを引き渡しているので、
メンバの値を変更する場合など、その構造体のメモリに書き込みを行うので
コールバック側がそのメモリアドレスに書き込みできるシステム権利を持っ
ていないとエラーになります。
(できないわけではありません。もう少し複雑にすれば、別モジュール間
でも使えます)

ρρρ==============================
      ごんた  gonta@artlex.net
  http://www.artlex.net/gonta/jpn/
==============================σσσ

Original document by ごんた          氏 ID:(LED04354)


ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。

Copyright 1996-2002 Delphi Users' Forum