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.
精彩评论