加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > Windows > 正文

windows – 如何从WndProc内部获取窗口句柄?

发布时间:2020-12-14 02:10:04 所属栏目:Windows 来源:网络整理
导读:也许是一个愚蠢的问题,但…… 我正在编写一个类,应该注意保持一个Window(FGuestHWnd,从现在开始)视觉锚定到“主机窗口”(FHostHWnd). FGuestHWnd和HostHWnd没有父/所有者/子关系. FGuestHWnd属于另一个进程 – 不在乎. FHostHWnd是VCL TWinControl的Window句
也许是一个愚蠢的问题,但……

我正在编写一个类,应该注意保持一个Window(FGuestHWnd,从现在开始)视觉锚定到“主机窗口”(FHostHWnd).

> FGuestHWnd和HostHWnd没有父/所有者/子关系.
> FGuestHWnd属于另一个进程 – 不在乎.
> FHostHWnd是VCL TWinControl的Window句柄,因此它是我进程中的子窗口.它可以位于父/子树内的任何级别.例如,假设它是TPanel.

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

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

移动,如果我没有遗漏某些东西,有点棘手,因为如果我移动主要形式FHostHWnd并没有真正感动.它保持相对于其父级的相同位置.因此,它不会以任何方式通知祖先的运动.

我的解决方案是将任何ANCESTOR的WndProc“重定向”到自定义窗口过程,并仅为“移动”消息捕获WM_WINDOWPOSCHANGING.
在这种情况下,我可以通过自定义消息通知FHostHWnd.
我班上的一些领域将跟踪Win Handles链,原始WndProc addesses和新的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,LongInt(FNewHostAncestorWndProcList[0])) = 0) then
      RaiseLastOSError;
    ParentHWnd := GetParent(FHostHWnd);
  end;
end;

这是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;

我的问题是:

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

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

请注意,我希望保持一切以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函数是为了避免这些问题.更多细节:Subclassing Controls.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读