3 Stimmen

Fenster empfängt unendlich viele Nachrichten, wenn die Maus angehakt ist

Ich schreibe eine Anwendung, die einen Kreis an der Stelle zeichnen soll, an der der Benutzer mit der Maus klickt. Um das zu erreichen, hänge ich die Maus global mit SetWindowHookEx(WH_MOUSE,...)

Das Hooking und die Prozedur, die die Mausaktion verarbeitet, befinden sich in der DLL. Die Prozedur sendet eine registrierte Nachricht, wenn sie feststellt, dass die Maustaste mit PostMessage(FindWindow('TMyWindow',nil), MyMessage, 0,0);

Meine Anwendung mit TMyWindow-Formular verarbeitet die Nachrichten in WndProc Verfahren. Ich überprüfe, ob die Nachricht, die ich erhalten habe, mit der von mir registrierten übereinstimmt, und erst dann zeichne ich den Kreis. Nach dem Zeichnen des Kreises erstelle ich einen Timer, der das Bild nach 500ms freigeben soll.

Es scheint also alles gut zu funktionieren, bis ich auf einen Teil meines Antragsformulars klicke (z. B. auf einen noch vorhandenen Kreis, der vor nicht allzu langer Zeit gezeichnet wurde). Wenn ich das tue, fängt das Formular an, meine registrierten Nachrichten endlos zu empfangen und natürlich wird die Kreiszeichnungsprozedur jedes Mal aufgerufen.

Ich verstehe nicht, warum das so ist. Warum funktioniert es gut, wenn ich irgendwo außerhalb meines Antragsformulars klicke, aber es bleibt hängen, wenn ich innerhalb meines Formulars klicke?

Lassen Sie es mich wissen, wenn Sie weitere Einzelheiten benötigen.

感謝

EDIT 1 :

Hauptgerät. Die $202-Meldung lautet WM_LBUTTONUP.

unit main;

interface

uses
    HookCommon,
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Menus, AppEvnts;

type
    TTimer2 = class(TTimer)
    private
        FShape: TShape;
    public
        destructor Destroy; override;
        property Shape: TShape read FShape write FShape;
    end;

type
  TShowMouseClick = class(TForm)
    timerCountTimer: TTimer;
    tray: TTrayIcon;
    popMenu: TPopupMenu;
    mnuExit: TMenuItem;
    mnuActive: TMenuItem;
    N1: TMenuItem;
    mnuSettings: TMenuItem;
    timersStx: TStaticText;
    procedure timerCountTimerTimer(Sender: TObject);
    procedure mnuExitClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    timerList: TList;
    procedure shape();
    procedure freeInactive(var Msg: TMessage); message WM_USER + 1545;
  public
    shapeColor: Tcolor;                    
    procedure TimerExecute(Sender: TObject);
  protected
    procedure WndProc(var Message: TMessage); override;
    { Public declarations }
  end;

var
  ShowMouseClick: TShowMouseClick;

implementation
{$R *.dfm}

uses settings;

{$REGION 'Hide from TaskBar'}
procedure TShowMouseClick.FormActivate(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);  
end;
procedure TShowMouseClick.FormShow(Sender: TObject);
begin
  ShowWindow(Application.Handle, SW_HIDE);
end;
{$ENDREGION}

procedure TShowMouseClick.WndProc(var Message: TMessage);
begin
    inherited WndProc(Message);
    if (Message.Msg = HookCommon.MouseHookMessage) and
        (Message.WParam = $202) then
        shape;
end;

procedure TShowMouseClick.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  FormStyle := fsStayOnTop;
  WindowState := wsMaximized;

  mnuActive.Checked := true;
  HookCommon.HookMouse;
  timerList := TList.Create;
  timerList.Clear;
  shapeColor := clGreen;
end;

procedure TShowMouseClick.FormDestroy(Sender: TObject);
begin
    HookCommon.UnHookMouse;
end;

procedure TShowMouseClick.mnuExitClick(Sender: TObject);
begin
  Close;
end;

procedure TShowMouseClick.timerCountTimerTimer(Sender: TObject);
begin
  timersStx.Caption := 'Active timers: ' + IntToStr(timerList.Count);
end;

procedure TShowMouseClick.shape;  
var
  tm: TTimer2;
begin
  tm := TTimer2.Create(nil);

  tm.Tag := 0 ;
  tm.Interval := 1;
  tm.OnTimer := TimerExecute;
  tm.Shape := nil;
  timerList.Add(tm);
  timersStx.Caption := 'Active timers: ' + IntToStr(timerList.Count);
  tm.Enabled := true;
end;

procedure TShowMouseClick.TimerExecute(Sender: TObject);
var
    img: TShape;
    snd: TTimer2;
begin
    snd := nil;
    if Sender is TTimer2 then
        snd := TTimer2(Sender);

    if snd = nil then Exit;

    if snd.Tag = 0 then
    begin
        snd.Interval := 500;
        img := TShape.Create(nil);
        img.Parent := ShowMouseClick;
        img.Brush.Color := clGreen;
        img.Shape := stCircle;
        img.Width := 9;
        img.Height := 9;
        img.Left := Mouse.CursorPos.X-4;
        img.Top := Mouse.CursorPos.Y-3;
        snd.Tag := 1;
        snd.Shape := img;
    end else begin
        snd.Enabled := false;
        PostMessage(ShowMouseClick.Handle,WM_USER + 1545 , 0,0);
        Application.ProcessMessages;
    end;

end;

procedure TShowMouseClick.freeInactive(var Msg: TMessage);
var
    i: integer;
begin
    for i := timerList.Count - 1 downto 0 do
        if TTimer2(timerList[i]).Enabled = false then
        begin
            TTimer2(timerList[i]).Free;
            timerList.Delete(i);
        end;
end;

destructor TTimer2.Destroy;
begin
    FreeAndNil(FShape);
    inherited;
end;

end.

Gemeinsame Einheit.

unit HookCommon;

interface

uses Windows;

var
  MouseHookMessage: Cardinal;

procedure HookMouse;
procedure UnHookMouse;

implementation

procedure HookMouse; external 'MouseHook.DLL';
procedure UnHookMouse; external 'MouseHook.DLL';

initialization
  MouseHookMessage := RegisterWindowMessage('MouseHookMessage');
end.

DLL-Code.

library MouseHook;

uses
  Forms,
  Windows,
  Messages,
  HookCommon in 'HookCommon.pas';

{$J+}
const
  Hook: HHook = 0;
{$J-}

{$R *.res}

function HookProc(nCode: Integer; MsgID: WParam; Data: LParam): LResult; stdcall;
var
  notifyTestForm : boolean;
begin

  notifyTestForm := false;

  if msgID = $202 then
    notifyTestForm := true;
  if notifyTestForm then
  begin
       PostMessage(FindWindow('TShowMouseClick', nil), MouseHookMessage, MsgID, 0);
  end;

  Result := CallNextHookEx(Hook,nCode,MsgID,Data);
end;

procedure HookMouse; stdcall;
begin
  if Hook = 0 then Hook:=SetWindowsHookEx(WH_MOUSE,@HookProc,HInstance,0);
end;

procedure UnHookMouse; stdcall;
begin
  UnhookWindowsHookEx(Hook);
  Hook:=0;
end;

exports
  HookMouse, UnHookMouse;

begin
end.

Die Quelle für den Maushaken ist este

2voto

Francesca Punkte 21286

Entweder werden Ihre Mausklicks oder Ihre MyMessage-Nachrichten nicht aus der Nachrichtenwarteschlange entfernt (unwahrscheinlich) oder sie werden irgendwie zurückgesendet, oder Ihr Code befindet sich in einer Rekursion.

Ich würde versuchen, jeden Code aus Ihrem TMyWindow.WndProc zu entfernen und ersetzen Sie es mit einigen harmlosen Code (wie ein OutputDebugString, um zu sehen, es im Nachrichtenbereich der IDE aufgerufen), um zu sehen, wenn es noch Schleife oder nicht ist.
Etwa so:

  with Message do
    case Msg of
      WM_MyMessage: OutputDebugString('MyMessage received. Drawing a circle');
    else 
      inherited WndProc(Message);

Wenn es nur einmal pro Klick schreibt, dann ist die Rekursion in Ihrer Behandlung der Nachricht (oder in der Timer-Handler) zu zeichnen / löschen den Kreis.

Wenn es sich um eine Schleife handelt, dann erzeugt Ihr Klick mehrere Nachrichten oder eine, die sich ewig dreht...

更新しました。
Nachdem ich mir Ihren Code angeschaut habe, würde ich die Art und Weise, wie Sie mit den Zeitgebern umgehen, ändern.
- Erstellen Sie den Timer nicht mit einem Intervall von 1, um die Form zu erstellen. Sie werden Ihre App mit Timer-Ereignissen überschwemmen.
- Sobald Sie den Befehl Ausführen eingeben, deaktivieren Sie den Timer
- Vermeiden Sie den Aufruf von Application.ProcessMessages.
- Sie können einige Gründe haben, aber ich finde dies sehr verworren, wenn es mir scheint, dass ein einfaches OnMouse-Ereignis auf Ihrem Formular dies leicht erreichen könnte.

0voto

Dies geschieht, weil FindWindow von sich aus Nachrichten sendet, die auch in Ihrem Hook landen. Insbesondere sendet es eine WM_GETTEXT, um den Titel des Fensters zu erhalten.

Um dies zu vermeiden, führen Sie das FindWindow im Voraus aus (außerhalb des Hook-Callbacks).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X