簡體   English   中英

WM_NCHITTEST 是否應該由 Win10 以每秒 100 次的頻率永久生成,即使鼠標處於空閑狀態?

[英]Is WM_NCHITTEST supposed to be perpetually generated by Win10, at a frequency of 100's per second, even if mouse is idle?

我遇到了WM_NCHITTEST消息的奇怪行為。

總而言之,一旦我將鼠標懸停在目標(即:Hooked)控件上並讓鼠標靜止(或空閑),我每秒就會收到WM_NCHITTEST消息 無論我使用WindowProc()將該控件的WndProc子類化,或者如果我在后代 class 中覆蓋WndProc方法(為簡單起見,我在下面的代碼中子類化),都會發生這種情況。

據我從在線 Win32 API 文檔和其他來源中可以找到,我懷疑此消息是否以這種頻率觸發,但我可能是錯的。 或者可能有一個我完全錯過的明顯解釋,或者可能是我不知道的 API 發生了一些變化。 無論如何,我真的很想知道它是什么,或者發生了什么。

我已經在兩個不同的系統上測試了相同的代碼(下面的示例),結果相同,盡管兩個系統都使用相同的 Delphi/OS 版本和配置。 我已經嘗試在 IDE 之外運行應用程序(所以沒有調試鈎子),在調試和發布配置中(后者沒有調試信息),針對 32 位和 64 位,我總是得到相同的結果。

我正在使用 Win10 Pro 64 位版本 20H2 下的 Delphi XE7 Enterprise 進行開發(我相信是最新的 Windows 版本)。

這是一個非常簡單的程序來重現我正在經歷的事情:一個帶有TForm 、一個TPanel和一個TLabel TCheckBox 面板是選中復選框時被鈎住的控件,label 顯示WndProc()方法接收到多少WM_NCHITTEST消息:

unit Unit5;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm5 = class(TForm)
    CheckBox1: TCheckBox;
    Label1: TLabel;
    Panel1: TPanel;
    procedure FormDestroy(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
  private
    FHookedCtrl: TControl;
    FHookedCtrlWndProc: TWndMethod;
    FMessageCount: Integer;
    procedure SetHookedCtrl(const Value: TControl);
  public
    procedure ControlWndProc(var Message: TMessage);
    property HookedCtrl: TControl read FHookedCtrl write SetHookedCtrl;
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

{ TForm5 }

procedure TForm5.CheckBox1Click(Sender: TObject);
begin
  //checkbox activates or deactivates the hook
  if CheckBox1.Checked then
    //hook the panel's WndProc by subclassing
    HookedCtrl := Panel1
    //release the hook on WndProc
  else HookedCtrl := nil;
end;

procedure TForm5.ControlWndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_NCHITTEST:
      begin
        //show how many messages received with the label's caption
        Inc(FMessageCount);
        Label1.Caption := FormatFloat('##,##0 messages', FMessageCount);
      end;
  end;
  //not really handling the messsage, just counting.
  FHookedCtrlWndProc(Message);
end;

procedure TForm5.FormDestroy(Sender: TObject);
begin
  //make sure to clear the hook if assigned
  HookedCtrl := nil;
end;

procedure TForm5.SetHookedCtrl(const Value: TControl);
begin
  if (Value <> FHookedCtrl) then
  begin
    if Assigned(FHookedCtrl) then
    begin
      //release the hook
      FHookedCtrl.WindowProc := FHookedCtrlWndProc;
      FHookedCtrlWndProc := nil;
      FMessageCount := 0;
    end;
    FHookedCtrl := Value;
    if Assigned(FHookedCtrl) then
    begin
      //hook the panel (i.e. Value)
      FHookedCtrlWndProc := FHookedCtrl.WindowProc;
      FHookedCtrl.WindowProc := ControlWndProc;
    end;
  end;
end;

end.

要重現:運行應用程序,選中 CheckBox,hover 將鼠標懸停在面板上並使其空閑(靜止)。 就我而言,我每秒收到 100 條WM_NCHITTEST消息,而且它永遠不會停止。 這應該發生嗎?

有人可以解釋這里發生了什么嗎?

我使用Microsoft Spy++工具查看發生了什么以及何時發生。

它是 WM_NCHITTEST 處理程序中的以下行

Label1.Caption := FormatFloat('##,##0 messages', FMessageCount);

這導致了這個問題。 當您刪除它時,不再有所有這些WM_NCHITTEST消息。 要查看消息數量,請使用間隔為 1 秒的TTimer並在 label 中顯示消息計數。 您會看到每次定時器觸發時都會收到WM_NCHITTEST (如果您有一個空的 OnTimer 處理程序,您仍然會收到一條消息),當然還有當鼠標移動時。

這是我使用的代碼:

unit Unit5;


interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm5 = class(TForm)
    Label1: TLabel;
    CheckBox1: TCheckBox;
    Panel1: TPanel;
    Timer1: TTimer;
    procedure FormDestroy(Sender: TObject);
    procedure CheckBox1Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    FHookedCtrl: TControl;
    FHookedCtrlWndProc: TWndMethod;
    FMessageCount: Integer;
    procedure SetHookedCtrl(const Value: TControl);
  public
    procedure ControlWndProc(var Message: TMessage);
    property HookedCtrl: TControl read FHookedCtrl write SetHookedCtrl;
  end;

var
  Form5: TForm5;

implementation

{$R *.dfm}

{ TForm5 }

procedure TForm5.CheckBox1Click(Sender: TObject);
begin
  //checkbox activates or deactivates the hook
  if CheckBox1.Checked then
    //hook the panel's WndProc by subclassing
    HookedCtrl := Panel1
  else
    //release the hook on WndProc
    HookedCtrl := nil;
end;

procedure TForm5.ControlWndProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_NCHITTEST:
        //Count how many messages received
        Inc(FMessageCount);
  end;
  //not really handling the messsage, just counting.
  FHookedCtrlWndProc(Message);
end;

procedure TForm5.FormDestroy(Sender: TObject);
begin
  //make sure to clear the hook if assigned
  HookedCtrl := nil;
end;

procedure TForm5.SetHookedCtrl(const Value: TControl);
begin
  if (Value <> FHookedCtrl) then begin
    if Assigned(FHookedCtrl) then begin
      //release the hook
      FHookedCtrl.WindowProc := FHookedCtrlWndProc;
      FHookedCtrlWndProc := nil;
      FMessageCount := 0;
    end;
    FHookedCtrl := Value;
    if Assigned(FHookedCtrl) then begin
      //hook the panel (i.e. Value)
      FHookedCtrlWndProc := FHookedCtrl.WindowProc;
      FHookedCtrl.WindowProc := ControlWndProc;
    end;
  end;
end;

procedure TForm5.Timer1Timer(Sender: TObject);
begin
   // Show how many message received
   Label1.Caption := FormatFloat('##,##0 messages', FMessageCount);
end;

end.

暫無
暫無

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

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