2

我正在尝试制作一个类似于 Winspector Spy 的程序。我的问题是我希望我的虚拟树视图随时更新——也就是说,在创建窗口、销毁窗口等时更新它。当然,所有外部 HWND。

为此,我正在考虑编写一个包含所有 Handles + 信息的数据容器,并在单独的线程中执行 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_CREATEWND/HCBT_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,因此在此代码中无关紧要,但如果您更改继承,您的代码将中断,因此切勿inheriteddestructor.

在构造函数中,您需要调用继承的构造函数:

改变这个:

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这里省略并不重要,但是如果您决定更改代码并从其他东西继承,那么您的构造函数将会中断。

无需设置任何内容0nil''在构造函数中,所有类成员在调用 create 之前自动设置为 0。

最后是关于风格的说明

你的大写风格太难读了,分散了问题的注意力。
此外,您的缩进是不稳定和不寻常的。
缩进对于遵循程序的逻辑很重要。经常使用它来扫描程序结构的人会因不寻常的缩进而分心。

与关键字相同。在我的代码中,我知道保留字总是以小写字母开头,而我给变量和例程的名称总是以大写字母开头。
这使我能够快速扫描程序的结构。
在保留字中使用大写字母会破坏扫描的流程(就像阅读上一句时所做的那样),因此不建议使用。

特别是对于那些对保留字中的草率缩进和使用大写字母过敏的人。

见:http ://en.wikipedia.org/wiki/Indent_style

我建议使用与 VCL 源代码相同的样式。这不是我个人最喜欢的,但它是很多人使用的干净风格。

史蒂夫·麦康奈尔(Steve McConnel)的出色著作code complete从 399 页到 452 页的布局和风格,使其成为本书最大的章节。

于 2011-05-14T13:46:40.257 回答