Maybe a stupid question, but...
I'm writing a class
that should take care of keeping a Window (FGuestHWnd
, from now on) visually anchored to a "Host Window" (FHostHWnd
).
FGuestHWnd
andHostHWnd
have NO parent/owner/child relationship.FGuestHWnd
belongs to another process - don't care.FHostHWnd
is the Window handle of a VCLTWinControl
, so it's a child window inside my process. It can sit at any level inside the Parent/Child tree. For example, let's say it's aTPanel
.
Now I have to "hook" FHostHWnd
's moving/resizing and call SetWindowPos(FGuestHWnd...
after my custom calculation.
Resizing is straightforward: I can use SetWindowLong(FHostHWnd, GWL_WNDPROC, ...)
to "redirect" FHostHWnd
's WndProc to my custom WindowPorcedure and trap WM_WINDOWPOSCHANGING
. This message is automatically sent to FHostHWnd
when one of its ancestors get resized, because FHostHWnd
is client-aligned.
MOVING, if I'm not missing something, is a little trickier because if i move the main form FHostHWnd
is not really moved. It keeps the same position relative to its parent. So it is NOT notified in any way of an ancestor's movement.
My solution is to "redirect" ANY ANCESTOR's WndProc to a custom Window Procedure and trap WM_WINDOWPOSCHANGING for "move" messages only.
In that case I could notify FHostHWnd
with a custom message.
Some fields inside my class will keep track of the chain of Win Handles, Original WndProc addesses and new WndProc addresses.
Here is some code to explain my structure:
TMyWindowHandler = class(TObject)
private
FHostAncestorHWndList: TList;
FHostHWnd: HWND;
FGuestHWnd: HWND;
FOldHostAncestorWndProcList: TList;
FNewHostAncestorWndProcList: TList;
//...
procedure HookHostAncestorWindows;
procedure UnhookHostAncestorWindows;
procedure HostAncestorWndProc(var Msg: TMessage);
end;
procedure TMyWindowHandler.HookHostAncestorWindows;
var
ParentHWnd: HWND;
begin
ParentHWnd := GetParent(FHostHWnd);
while (ParentHWnd > 0) do
begin
FHostAncestorHWndList.Insert(0, Pointer(ParentHWnd));
FOldHostAncestorWndProcList.Insert(0, TFarProc(GetWindowLong(ParentHWnd, GWL_WNDPROC)));
FNewHostAncestorWndProcList.Insert(0, MakeObjectInstance(HostAncestorWndProc));
Assert(FOldHostAncestorWndProcList.Count = FHostAncestorHWndList.Count);
Assert(FNewHostAncestorWndProcList.Count = FHostAncestorHWndList.Count);
if (SetWindowLong(ParentHWnd, GWL_WNDPROC, LongInt(FNewHostAncestorWndProcList[0])) = 0) then
RaiseLastOSError;
ParentHWnd := GetParent(FHostHWnd);
end;
end;
and here is The Handler:
procedure TMyWindowHandler.HostAncestorWndProc(var Msg: TMessage);
var
pNew: PWindowPos;
begin
case Msg.Msg of
WM_DESTROY: begin
UnHookHostAncestorWindows;
end;
WM_WINDOWPOSCHANGING: begin
pNew := PWindowPos(Msg.LParam);
// Only if the window moved!
if ((pNew.flags and SWP_NOMOVE) = 0) then
begin
//
// Do whatever
//
end;
end;
end;
Msg.Result := CallWindowProc(???, ???, Msg.Msg, Msg.WParam, Msg.LParam );
end;
My question is:
How can I get the Window Handle from inside my WindowProcedure when I finally invoke CallWindowProc
?
(If I had the Window Handle I could also find it in FOldHostAncestorWndProcList
, then lookup the right Old-WndProc-pointer in FHostAncestorHWndList
)
Or, as an alternative, how to get the CURRENT method pointer so that I can find it in FNewHostAncestorWndProcList
and lookup the HWND in FHostAncestorHWndList
.
Or do you suggest other solutions?
Notice that I'd like to keep everything HWND-oriented, not VCL/TWinControl-aware.
In other words, my application should only instantiate TMyWindowHandler passing to it the two HWND
s (host and guest).