[英]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.