开发者

EnumWindows and EnumChildWindows to keep my Treeview updated

开发者 https://www.devze.com 2023-03-06 09:23 出处:网络
I am trying to make a program similar to Winspector Spy. My problem is that I would like my Virtual Treeview to be updated at all times - that is, update it when a window is created, when a window is

I am trying to make a program similar to Winspector Spy. My problem is that I would like my Virtual Treeview to be updated at all times - that is, update it when a window is created, when a window is destroyed, etc. Of course, all external HWND's.

For that, I was thinking of writing a data container that contained all the Handles + information, and do the EnumWindows and EnumChildWindows in a seperate Thread, where I would fill my data container with said information.

Would you recommend I do it that way, or do you have another solution? If I do it this way, then should I make my Thread run during the whole Program Lifetime, and then just have an infinite loop within the Execute that would clear my datacontainer, and fill it again, every second, or something?

Here is my Data Container:

unit WindowList;

interface

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


type
  TWi开发者_开发问答ndow = 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.


You could hook for WH_CBT and examine HCBT_CREATEWND/HCBT_DESTROYWND

The system calls a WH_CBT hook procedure before activating, creating, destroying, minimizing, maximizing, moving, or sizing a window ...

Also take a look at What do I have to do to make my WH_SHELL or WH_CBT hook procedure receive events from other processes?


This should really be a comment, but the code doesn't look OK in comments.

There are a few oddities in your code:

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

There's no need to empty strings in a destructor, but you need to call inherited Destroy.
Change it to:

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

TWindow inherits from TObject, so it doesn't matter in this code, but if you change the inheritance your code will break, so never omit inherited in a destructor.

In a constructor you need to call the inherited constructor:

Change this:

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;

To this:

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;

Because TWindow inherits from TObject, it does not matter that you ommited inherited Create here, but if you decide to change the code and inherit from something else, than your constructor will break.

There's no need to set anything to 0, nil or '' in a constructor, all class member are automatically set to 0 before create is called.

Finally a note on style

YoUr Capitalization style Is Hard too Read and distracts from the issue.
Also your indentation is inconstant and unusual.
Indentation is important to follow the logic of a program. People who use it to scan program structure a lot get very distracted by unusual indentation.

Same with the keywords. In my code I know that reserved words always start with a lower case and names I gave to vars and routines always start with a capital.
This enables me to scan the structure of a program quickly.
Use of Capitals In Reserved words Breaks the FloW of scanning (like it does when reading the previous sentence) and is not recommended for that reason.

Especially for people who are allergic to sloppy indentation and Use Of Capitals In reserved words.

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

I'd recommend using the same style that is used in the VCL source. It's not my personal favourite, but it is a clean style used by a lot of people.

Steve McConnel his excellent book code complete denotes pages 399 to 452 to layout and style, making it the largest chapter of the book.

0

精彩评论

暂无评论...
验证码 换一张
取 消