2

Winspector Spy に似たプログラムを作成しようとしています。私の問題は、仮想ツリービューを常に更新したいということです。つまり、ウィンドウが作成されたとき、ウィンドウが破棄されたときなどに更新します。もちろん、すべての外部 HWND です。

そのために、すべてのハンドル + 情報を含むデータ コンテナーを作成し、EnumWindows と EnumChildWindows を別のスレッドで実行して、データ コンテナーに上記の情報を入力することを考えていました。

そのようにすることをお勧めしますか、それとも別の解決策がありますか? このようにすると、プログラムのライフタイム全体でスレッドを実行しExecute、データコンテナをクリアして、毎秒、または何かを埋める無限ループを作成する必要がありますか?

ここに私のデータコンテナがあります:

unit WindowList;

interface

Uses
  Windows, SysUtils, Classes, VirtualTrees, WinHandles, Messages,
  Generics.Collections;


type
  TWindow = class;
  TWindowList = class(TObjectList<TWindow>)
  public
    constructor Create;
    function AddWindow(Wnd : HWND):TWindow;
  end;

  ///////////////////////////////////////

  TWindow = class
  public
    Node         : PVirtualNode;
    Children     : TObjectList<TWindow>;
    Handle       : HWND;
    Icon         : HICON;
    ClassName    : string;
    Text         : string;
    constructor Create(Wnd : HWND);
    destructor Destroy;
    function AddWindow(Wnd : HWND):TWindow;
  end;


implementation

{ TWindowList }

function TWindowList.AddWindow(Wnd: HWND): TWindow;
var
  Window : TWindow;
begin
  Window := TWindow.Create(Wnd);
  Add(Window);
  Result := Window;
end;

constructor TWindowList.Create;
begin
  inherited Create(True);
end;

{ TWindow }

function TWindow.AddWindow(Wnd: HWND): TWindow;
var
  Window : TWindow;
begin
  Window := TWindow.Create(Wnd);
  Children.Add(Window);
  Result := Window;
end;

constructor TWindow.Create(Wnd: HWND);
begin
  Handle := Wnd;
  if Handle = 0 then Exit;
  ClassName := GetClassName(Handle);
  Text := GetHandleText(Handle);
  Node := Nil;
  Children := TObjectList<TWindow>.Create(True);
end;

destructor TWindow.Destroy;
begin
  ClassName := '';
  Text := '';
  Children.Free;
end;

end.
4

2 に答える 2

3

/をフックWH_CBTて調べることができますHCBT_CREATEWNDHCBT_DESTROYWND

システムは、ウィンドウをアクティブ化、作成、破棄、最小化、最大化、移動、またはサイズ変更する前に、WH_CBT フック プロシージャを呼び出します ...

また、WH_SHELL または WH_CBT フック プロシージャが他のプロセスからイベントを受け取るにはどうすればよいですか? も参照してください。

于 2011-05-14T13:46:35.327 に答える
1

これは実際にはコメントである必要がありますが、コードはコメントでは適切に見えません。

コードにはいくつかの奇妙な点があります。

destructor TWindow.Destroy;
begin
  ClassName := '';
  Text := '';
  Children.Free;
end;

デストラクタで文字列を空にする必要はありませんが、継承された Destroy を呼び出す必要があります。
次のように変更します。

destructor TWindow.Destroy;
begin
  Children.Free;
  inherited Destroy;
end;

TWindow は TObject を継承しているので、このコードでは問題ありませんが、継承を変更するとコードが壊れinheritedますdestructor

コンストラクターでは、継承されたコンストラクターを呼び出す必要があります。

これを変える:

constructor TWindow.Create(Wnd: HWND);
begin
  Handle := Wnd;
  if Handle = 0 then Exit;
  ClassName := GetClassName(Handle);
  Text := GetHandleText(Handle);
  Node := Nil;
  Children := TObjectList<TWindow>.Create(True);
end;

これに:

constructor TWindow.Create(Wnd: HWND);
begin
  inherited Create;
  Handle := Wnd;
  if Handle = 0 then Exit;
  ClassName := GetClassName(Handle);
  Text := GetHandleText(Handle);
  Children := TObjectList<TWindow>.Create(True);
end;

TWindow は TObject から継承するため、inherited Createここで省略してもかまいませんが、コードを変更して別のものから継承することにした場合は、コンストラクターが壊れます。

0に何かを設定する必要はありませんnil''コンストラクターでは、create が呼び出される前に、すべてのクラス メンバーが自動的に 0 に設定されます。

最後にスタイルのメモ

あなたの大文字のスタイルは難しすぎて、問題から気をそらします。
また、あなたのインデントは一定ではなく、異常です。
インデントは、プログラムのロジックに従うために重要です。これを使用してプログラム構造をスキャンする人は、通常とは異なるインデントに気が散ってしまいます。

キーワードと同じです。私のコードでは、予約語は常に小文字で始まり、変数とルーチンに付けた名前は常に大文字で始まることがわかっています。
これにより、プログラムの構造をすばやくスキャンできます。
予約語での大文字の使用は、(前の文を読むときのように) スキャンの流れを壊し、その理由から推奨されません。

特に、ずさんなインデントや予約語での大文字の使用にアレルギーのある人にとっては。

参照: http://en.wikipedia.org/wiki/Indent_style

VCL ソースで使用されているのと同じスタイルを使用することをお勧めします。個人的な好みではありませんが、多くの人が使用するクリーンなスタイルです。

Steve McConnel の優れたcode completeは、399 ページから 452 ページまでのレイアウトとスタイルを示しており、本の中で最大の章になっています。

于 2011-05-14T13:46:40.257 に答える