簡體   English   中英

如何從WndProc內部獲取Window Handle?

[英]How to get the Window Handle from inside WndProc?

也許是一個愚蠢的問題,但......

我正在編寫一個class ,應該注意保持一個Window( FGuestHWnd ,從現在開始)視覺錨定到“主機窗口”( FHostHWnd )。

  • FGuestHWndHostHWnd沒有父/所有者/子關系。
  • 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, GWL_WNDPROC, 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 ,然后查找合適的老的WndProc指針FHostAncestorHWndList )或者,作為替代,如何讓當前的方法指針,這樣我可以找到它FNewHostAncestorWndProcList和查找FHostAncestorHWndList的HWND。

或者您是否建議其他解決方案?

請注意,我希望保持一切以HWND為導向,而不是VCL / TWinControl。
換句話說,我的應用程序應該只實例化TMyWindowHandler傳遞給它的兩個HWND (主機和客戶機)。

可以將用戶定義的數據傳遞給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)更好的選擇。 鈎子過程為您提供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;

我個人不會在這里使用MakeObjectInstance 如果您希望將實例綁定到單個窗口句柄,則MakeObjectInstance非常有用。 MakeObjectInstance在於生成一個thunk,它將窗口過程調用轉發給實例方法。 在這樣做時,窗口句柄不會傳遞給實例方法,因為假設實例已經知道其關聯的窗口句柄。 對於TWinControl ,情況確實如此,這是MakeObjectInstance的主要用例。

現在,您將它綁定到多個窗口句柄。 當實例方法執行時,您無法知道許多窗口句柄中的哪一個與此方法執行相關聯。 這是你問題的關鍵所在。

我的建議是放棄MakeObjectInstance因為它無法滿足您的需求。 而是,定義此窗體的純窗口過程:

function WindowProc(hwnd: HWND; uMsg: UINT; wParam: WPARAM; 
  lParam: LPARAM): LRESULT; stdcall;

當你實現這樣的窗口過程時,你會收到一個窗口句柄,如你所願。

您可能需要保留TMyWindowHandler實例的全局列表,以便您可以查找與傳遞給窗口過程的窗口關聯的TMyWindowHandler實例。 或者,您可以使用SetProp將某些數據與窗口相關聯。

請注意,您對窗口進行子類化的方式存在各種問題。 提供SetWindowSubclass函數是為了避免這些問題。 更多細節: 子類控件

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM