也许是一个愚蠢的问题,但是...
我正在编写一个class
应该注意将Window(FGuestHWnd
从现在开始)保持在视觉上锚定到“宿主窗口”(FHostHWnd
)的方法。
FGuestHWnd
并且HostHWnd
没有父母/所有者/孩子的关系。FGuestHWnd
属于另一个进程-不在乎。FHostHWnd
是VCL的Window句柄TWinControl
,因此它是我的进程内的子窗口。它可以位于“父级/子级”树中的任何级别。例如,假设它是一个TPanel
。现在,我必须“ hook”FHostHWnd
移动/调整大小并SetWindowPos(FGuestHWnd...
在自定义计算后调用。
调整大小很简单:我可以SetWindowLong(FHostHWnd, GWL_WNDPROC, ...)
用来将FHostHWnd
WndProc重定向到我的自定义WindowPorcedure和trap WM_WINDOWPOSCHANGING
。当此消息FHostHWnd
的祖先之一被调整大小时,该消息将自动发送到该消息,因为它FHostHWnd
是与客户对齐的。
移动,如果我没有丢失任何东西,会有些棘手,因为如果我移动主窗体,FHostHWnd
则不会真正移动它。相对于其父代,它保持相同的位置。因此,不会以任何方式通知祖先的移动。
我的解决方案是将“任何祖先”的WndProc“重定向”到自定义的“窗口过程”,并仅将WM_WINDOWPOSCHANGING捕获为“移动”消息。在这种情况下,我可以FHostHWnd
通过自定义消息进行通知。我班上的一些字段将跟踪Win Handles,原始WndProc地址和新WndProc地址的链。
这是一些代码来解释我的结构:
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;
这是处理程序:
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;
我的问题是:
最终调用时,如何从WindowProcedure内部获取窗口句柄CallWindowProc
?
(如果有Window Handle,也可以在中找到它FOldHostAncestorWndProcList
,然后在中找到正确的Old-WndProc指针FHostAncestorHWndList
),或者,如何获取CURRENT方法指针,以便可以在其中找到它并在中FNewHostAncestorWndProcList
查找HWND FHostAncestorHWndList
。
还是您建议其他解决方案?
请注意,我想保留所有面向HWND的功能,而不是VCL / TWinControl意识的。
换句话说,我的应用程序仅应实例化TMyWindowHandler并将两个值HWND
(主机和来宾)传递给它。
我个人不会MakeObjectInstance
在这里使用。MakeObjectInstance
如果您希望将实例绑定到单个窗口句柄,则很有用。的神奇之处MakeObjectInstance
在于生成了一个thunk,可以将窗口过程调用转发给实例方法。并且这样做时,窗口句柄不会传递给实例方法,因为假设是实例已经知道其关联的窗口句柄。当然是TWinControl
的主要用例MakeObjectInstance
。
现在,您将其绑定到多个窗口句柄。当实例方法执行时,您无法知道与该方法执行相关联的是多个窗口句柄中的哪个。那就是您问题的症结所在。
我的建议是放弃,MakeObjectInstance
因为它不能满足您的需求。而是定义这种形式的普通窗口过程:
function WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;
当您像这样实现窗口过程时,您确实会收到一个窗口句柄。
您可能需要保留TMyWindowHandler
实例的全局列表,以便可以查找TMyWindowHandler
与传递给窗口过程的窗口关联的实例。或者,您可以用来SetProp
将某些数据与窗口关联。
请注意,对窗口进行子分类的方式存在各种问题。SetWindowSubclass
提供该功能是为了避免这些问题。此处有更多详细信息:控件的子类化。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句