gpt4 book ai didi

windows - 如何从 WndProc 中获取窗口句柄?

转载 作者:可可西里 更新时间:2023-11-01 13:51:08 31 4
gpt4 key购买 nike

也许是个愚蠢的问题,但是......

我正在编写一个 ,它应该负责将一个窗口(FGuestHWnd,从现在开始)视觉锚定到一个“宿主窗口”( FHostHWnd).

  • FGuestHWndHostHWnd 没有父/所有者/子关系。
  • FGuestHWnd 属于另一个进程 - 无关紧要。
  • FHostHWnd 是 VCL TWinControl 的窗口句柄,因此它是我进程中的子窗口。它可以位于父/子树内的任何级别。例如,假设它是一个 TPanel

现在我必须“ Hook ”FHostHWnd 的移动/调整大小并在自定义计算后调用 SetWindowPos(FGuestHWnd...

调整大小很简单:我可以使用 SetWindowLong(FHostHWnd, GWL_WNDPROC, ...)FHostHWnd 的 WndProc“重定向”到我的自定义 WindowPorcedure 并捕获 WM_WINDOWPOSCHANGING。当调整其祖先之一的大小时,此消息会自动发送到 FHostHWnd,因为 FHostHWnd 是客户端对齐的。

如果我没有遗漏任何东西,移动会有点棘手,因为如果我移动主窗体 FHostHWnd 并没有真正移动。它相对于其父项保持相同的位置。因此它不会以任何方式收到祖先移动的通知。

我的解决方案是将任何 ANCESTOR 的 WndProc“重定向”到自定义窗口过程,并为“移动”消息捕获 WM_WINDOWPOSCHANGING。在那种情况下,我可以用自定义消息通知 FHostHWnd。我的类中的某些字段将跟踪 Win 句柄链、原始 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;

我的问题是:

当我最终调用 CallWindowProc 时,如何从我的 WindowProcedure 中获取窗口句柄?
(如果我有窗口句柄,我也可以在 FOldHostAncestorWndProcList 中找到它,然后在 FHostAncestorHWndList 中查找正确的 Old-WndProc 指针)或者,作为替代方法,如何获取 CURRENT 方法指针以便我可以在 FNewHostAncestorWndProcList 中找到它并在 FHostAncestorHWndList 中查找 HWND。

或者您建议其他解决方案?

请注意,我希望所有内容都面向 HWND,而不是 VCL/TWinControl-aware。
换句话说,我的应用程序应该只实例化 TMyWindowHandler 并将两个 HWND(主机和 guest )传递给它。

最佳答案

可以将用户定义的数据传递给 MakeObjectInstance()。它需要一个闭包作为输入,并且可以使用 TMethod 记录来操作闭包,因此您可以将其 Data 字段设置为指向您想要的任何内容,并且可以访问它通过方法体内的 Self 指针。例如:

type
PMyWindowHook = ^TMyWindowHook;
TMyWindowHook = record
Wnd: HWND;
OldWndProc: TFarProc;
NewWndProc: Pointer;
Handler: TMyWindowHandler;
end;

TMyWindowHandler = class
private
FHostAncestorHWndList: TList;
FHostAncestorWndProcList: TList;
FHostHWnd: HWND;
FGuestHWnd: HWND;
//...
procedure HookHostAncestorWindows;
procedure UnhookHostAncestorWindows;
procedure HostAncestorWndProc(var Msg: TMessage);
end;

procedure TMyWindowHandler.HookHostAncestorWindows;
var
ParentHWnd: HWND;
Hook: PMyWindowHook;
NewWndProc: Pointer;
M: TWndMethod;
begin
ParentHWnd := GetParent(FHostHWnd);
while ParentHWnd <> 0 do
begin
M := HostAncestorWndProc;
New(Hook);
try
TMethod(M).Data := Hook;
Hook.Hwnd := ParentHWnd;
Hook.OldWndProc := TFarProc(GetWindowLong(ParentHWnd, GWL_WNDPROC));
Hook.NewWndProc := MakeObjectInstance(M);
Hook.Handler := Self;
FHostAncestorWndProcList.Insert(0, Hook);
try
SetLastError(0);
if SetWindowLongPtr(ParentHWnd, GWL_WNDPROC, LONG_PTR(Hook.NewWndProc)) = 0 then
begin
if GetLastError() <> 0 then
begin
FreeObjectInstance(Hook.NewWndProc);
RaiseLastOSError;
end;
end;
except
FHostAncestorWndProcList.Delete(0);
raise;
end;
except
Dispose(Hook);
raise;
end;
ParentHWnd := GetParent(ParentHWnd);
end;
end;

procedure TMyWindowHandler.UnhookHostAncestorWindows;
var
Hook: PMyWindowHook;
begin
while FHostAncestorWndProcList.Count > 0
begin
Hook := PMyWindowHook(FHostAncestorWndProcList.Items[0]);
FHostAncestorWndProcList.Delete(0);
SetWindowLongPtr(Hook.Hwnd, GWL_WNDPROC, LONG_PTR(Hook.OldWndProc));
FreeObjectInstance(Hook.NewWndProc);
Dispose(Hook);
end;
end;

procedure TMyWindowHandler.HostAncestorWndProc(var Msg: TMessage);
var
Hook: PMyWindowHook;
pNew: PWindowPos;
begin
Hook := PMyWindowHook(Self);
case Msg.Msg of
WM_DESTROY: begin
Msg.Result := CallWindowProc(Hook.Wnd, Hook.OldWndProc, Msg.Msg, Msg.WParam, Msg.LParam);
Hook.Handler.FHostAncestorWndProcList.Remove(Hook);
SetWindowLongPtr(Hook.Hwnd, GWL_WNDPROC, LONG_PTR(Hook.OldWndProc));
FreeObjectInstance(Hook.NewWndProc);
Dispose(Hook);
Exit;
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(Hook.Wnd, Hook.OldWndProc, Msg.Msg, Msg.WParam, Msg.LParam);
end;

诚然,这不是一个理想的设置。 SetWindowSubClass() 是比 SetWindowLong(GWL_WNDPROC) 更好的选择。 Hook 过程为您提供HWND,您可以指定用户定义的数据。无需破解。例如:

type
TMyWindowHandler = class
private
FHostAncestorHWndList: TList;
FHostAncestorWndProcList: TList;
FHostHWnd: HWND;
FGuestHWnd: HWND;
//...
procedure HookHostAncestorWindows;
procedure UnhookHostAncestorWindows;
class function HostAncestorWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData): LRESULT; stdcall; static;
end;

procedure TMyWindowHandler.HookHostAncestorWindows;
var
ParentHWnd: HWND;
begin
ParentHWnd := GetParent(FHostHWnd);
while ParentHWnd <> 0 do
begin
FHostAncestorWndProcList.Insert(0, Pointer(ParentWnd));
try
if not SetWindowSubclass(ParentWnd, @HostAncestorWndProc, 1, DWORD_PTR(Self)) then
RaiseLastOSError;
except
FHostAncestorWndProcList.Delete(0);
raise;
end;
ParentHWnd := GetParent(ParentHWnd);
end;
end;

procedure TMyWindowHandler.UnhookHostAncestorWindows;
begin
while FHostAncestorWndProcList.Count > 0 do
begin
RemoveWindowSubclass(HWND(FHostAncestorWndProcList.Items[0]), @HostAncestorWndProc, 1);
FHostAncestorWndProcList.Delete(0);
end;
end;

class function TMyWindowHandler.HostAncestorWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData): LRESULT; stdcall;
var
pNew: PWindowPos;
begin
case uMsg of
WM_NCDESTROY: begin
RemoveWindowSubclass(hWnd, @HostAncestorWndProc, 1);
TMyWindowHandler(dwRefData).FHostAncestorWndProcList.Remove(Pointer(hWnd));
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;
Result := DefSubclassProc(hWnd, uMsg, wParam, lParam);
end;

关于windows - 如何从 WndProc 中获取窗口句柄?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24533123/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com