Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   lokaler Hook wird immer doppelt durchlaufen? (https://www.delphipraxis.net/189073-lokaler-hook-wird-immer-doppelt-durchlaufen.html)

r29d43 30. Apr 2016 13:10

lokaler Hook wird immer doppelt durchlaufen?
 
Hallo, weil ich seit langem mal wieder einen lokalen Hook brauche, konstruierte ich mir erstmal einen solchen in Form eines quasi simpelst möglichen getMsg-Hooks. Allerdings habe ich bei dem jetzt das kuriose Prob, dass alle Messages, die an das Panel1 gehen und auf dem ich mit der Maus jetzt gerade herumklicke, diesen Hook irgendwie doppelt durchlaufen, also dann auch in der Case-Anweisung im Code doppelt gezählt werden.

Delphi-Quellcode:
function GetMsgProc (nCode: Integer; wParam, lParam: Longint): Longint; stdcall;
type MSG = record
       hwnd : HWND;
       message : UINT;
       wParam : DWord;
       lParam : DWord;
       time : DWORD;
       pt : TPOINT;
     end;
     PMSG = ^MSG;
begin
{ }
  if nCode = HC_ACTION
//  if not nCode < 0
  then begin

    with form4 do
    if PMSG(lParam)^.hwnd = Form4.panel1.handle then
    case PMSG(lParam)^.message of
      WM_LBUTTONDOWN :
                       begin
                         inc(LButtonDownCounter);
                         label1.caption := IntToStr(LButtonDownCounter)
                       end;
      WM_LBUTTONUP :
                       begin
                         inc(LButtonUpCounter);
                         label2.caption := IntToStr(LButtonUpCounter)
                       end;
      WM_LBUTTONDBLCLK :
                       begin
                         inc(DoppelklickCounter);
                         label3.caption := IntToStr(DoppelklickCounter)
                       end;
    end; { of case }
  end;

  Result := CallNextHookEx(Form4.HookID, nCode, wParam, lParam);
end;


procedure TForm4.FormCreate(Sender: TObject);
begin
  HookID := SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc , 0, GetCurrentThreadId());
  LButtonDownCounter := 0;
  LButtonUpCounter := 0;
  DoppelklickCounter := 0;
end;


procedure TForm4.FormDestroy(Sender: TObject);
begin
  if HookID <> 0 then UnHookWindowsHookEx(HookID);
end;

Irgendwo muss hier doch noch ein winzig kleiner Fehler stecken, nur wo?
(leider habe ich gerade 2 dicke Tomaten auf den Augen).

Zacherl 30. Apr 2016 15:35

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Kann auf den ersten Blick auch keinen Fehler sehen. Aber warum verwendest du überhaupt MSDN-Library durchsuchenSetWindowsHookEx? Das ist doch für einen lokalen Hook etwas mit Kanonen auf Spatzen geschossen.

Für die gezeigten Aktionen könntest du doch einfach die entsprechenden Events des Panels (OnDblClick, OnMouseDown, OnMouseUp, etc) nutzen. Wenn du mehr Messages brauchst, wäre die saubere Lösung eine eigene Panel Klasse vom TCustomPanel abzuleiten. Alternativ könntest du auch Subclassing verwenden, um die WndProc lokal umzubiegen.

r29d43 30. Apr 2016 16:11

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Thx für die Anregung, aber der von mir angezeigte Code war ja nur ein einfachst mögliches Testprog, um den lokalen Hook für mich intern jetzt erstmal wieder kurz reaktiviert zu haben. Nachher möchte ich so einen Hook für ein Programm nutzen, das aus vielen Formularen besteht, und wo ich mittels des Hooks dann alle Positionsänderungen dieser einzelnen Formulare mittels wm_move o. wm_moving kontrollieren (protokollieren, speziell verändern o. ganz verhindern) kann.

Außerdem geht's mir hier auch noch ein kleines bisschen ums Prinzip. So ein Hook hatte bei mir bisher nämlich eigentlich immer ganz gut geklappt, und jetzt auf einmal nicht mehr? Warum???

Uwe Raabe 30. Apr 2016 16:22

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Ist sicher gestellt, daß
Delphi-Quellcode:
TForm4.FormCreate
nicht zweimal aufgerufen wird?

Zacherl 30. Apr 2016 16:28

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Zitat:

Zitat von r29d43 (Beitrag 1337199)
So ein Hook hatte bei mir bisher nämlich eigentlich immer ganz gut geklappt, und jetzt auf einmal nicht mehr? Warum???

Bei mir tritt das selbe Problem auf. Wenn ich testweise aber mal direkt das Formular überwache, dann kommen die Messages bei Click auf die Client-Area ebenfalls doppelt an. Clicks in die Titelleiste allerdings nur einmal.

Ich vermute mal, dass die VCL da intern irgendwie mit den Messages hantiert und dadurch verursacht, dass diese manchmal doppelt verschickt werden.

r29d43 30. Apr 2016 17:00

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Zitat:

Ist sicher gestellt, daß TForm4.FormCreate nicht zweimal aufgerufen wird?
Das sollte eigentlich nicht passiert sein. Und da Zacherl den Code auchmal kurz gestest hat, hätte er ja quasi einen solchen gleichen Fehler auch gemacht, was wohl ziemlich unwahrscheinlich sein dürfte.

Zitat:

Bei mir tritt das selbe Problem auf. Wenn ich testweise aber mal direkt das Formular überwache, dann kommen die Messages bei Click auf die Client-Area ebenfalls doppelt an. Clicks in die Titelleiste allerdings nur einmal.

Ich vermute mal, dass die VCL da intern irgendwie mit den Messages hantiert und dadurch verursacht, dass diese manchmal doppelt verschickt werden.
Ersteres kann ich bestätigen. Die LButtonUp-Message wird beim Klick auf die Titelleiste auch nur 1 mal gezählt (die LButtonDown-Message 0 mal). Bezüglich der Verursachung dieses Effektes glaube ich allerdings eher an eine irgendwie eigene Verursachung. Ansonsten wäre so eine imo dicke Fehlersache doch sicher schon längst bekannt und behoben worden.

Delphi-Laie 30. Apr 2016 21:52

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Zitat:

Zitat von r29d43 (Beitrag 1337203)
Zitat:

Ist sicher gestellt, daß TForm4.FormCreate nicht zweimal aufgerufen wird?
Das sollte eigentlich nicht passiert sein. Und da Zacherl den Code auchmal kurz gestest hat, hätte er ja quasi einen solchen gleichen Fehler auch gemacht, was wohl ziemlich unwahrscheinlich sein dürfte.

Sollte, hätte und dürfte ist Konjunktiv II. Entscheidend ist hier aber nur der Indikativ.

Mit z.B. einem simplen

Delphi-Quellcode:
showmessage('TForm4.FormCreate')


oder einem Piepton mit Zufallsfrequenz

Delphi-Quellcode:
windows.beep(200+random(1000),100)


irgendwo in der FormCreate-Prozedur ließe (huch, Konjunktiv II) sich das einfach verifizieren oder falsifizieren. Dann könnte (huch, schon wieder Konjunktiv II) man auch auf den Konjunktiv II in den Antworten verzichten.

r29d43 30. Apr 2016 23:42

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Damit auch für die Grammatikprofis :shock: alle Zweifel restlos ausgeräumt sind: Es blieb bei nur einem Meldungsfenster bzw. auch bei nur einem Ton.

Delphi-Laie 30. Apr 2016 23:49

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Zitat:

Zitat von r29d43 (Beitrag 1337215)
Damit auch für die Grammatikprofis :shock: alle Zweifel restlos ausgeräumt sind: Es blieb bei nur einem Meldungsfenster bzw. auch bei nur einem Ton.

Na, das ist doch eine klare - indikative! - Ansage, warum nicht gleich so?

Das waren nur zwei Beispiele, wie man schnell - ohne Debugger - das Durchlaufen von Code feststellen kann, ich benutze derlei jedenfalls ziemlich oft. Vielleicht gibt es noch mehr und geeignetere Methoden?!

Uwe Raabe 1. Mai 2016 08:50

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Hast du schon mal einen Haltepunkt gesetzt und den Aufruf-Stack angeschaut?

r29d43 1. Mai 2016 15:07

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1337224)
Hast du schon mal einen Haltepunkt gesetzt und den Aufruf-Stack angeschaut?

Thx für den Tipp, der brachte was. Denn da sah ich auf einmal, wie bei den einzelnen Hookbesuchen ständig zwischen 0 und 1 wechselnde Werte im wParameter-Parameter auftauchten. Gemäß der MSDK beinhaltet dieser wParameter die Information darüber, ob die Message schon von der Message-Schlange entfernt wurde oder ob nicht. (Das hatte ich bisher einfach als eher unwichtig ignoriert).

Demgemäß scheint es also doch normal zu sein, dass zumindest eine GetMsgProc bei jeder Message zweimal aufgerufen wird. Nämlich einmal mit der Message wo diese noch nicht von der Message-Schlange heruntergenommen wurde, und ein zweites Mal wo das schon geschehen ist und wo diese Message dann anschließend (nach der Hook-Kette) direkt zur WndProc des Control weitergeschickt werden wird.

Soll heißen (zumindest für mich): Wenn man auf eine Message in so einer GetMsgProc reagieren möchte, dann muss man also auch diesen wParameter noch mit berücksichtigen und kann so auch nochmal ein bisschen genauer auf die Situation reagieren ...schon bei einer angänglichen Vorab-Betrachtung der Message, bevor die überhaupt schon abgeschickt wurde, oder erst danach wenn fest steht, dass sie tatsächlich zum Control abgeschickt wurde etc. etc..

Zacherl lag also doch schon irgendwo richtig mit seiner Vermutung
Zitat:

Ich vermute mal, dass die VCL da intern irgendwie mit den Messages hantiert und dadurch verursacht, dass diese manchmal doppelt verschickt werden.
nur anstatt "manchmal" eben wahrscheinlich immer (im oben erwähnten Kontext).

r29d43 1. Mai 2016 23:05

AW: lokaler Hook wird immer doppelt durchlaufen?
 
Vielleicht noch eine kleine Ergänzung dazu:

Diese pro einer Message doppelten Hook-Aufrufe gibts auch noch bei einigen anderen lokalen Hooks. Zum Beispiel auch beim lokalen Mouse-Hook (den ich bisher immer nutzte). Bei diesem war mir nur bisher noch nicht aufgefallen, weil der von mir irgendwoher kopierte Code zur Message-Bearbeitung mit <if nCode = HAction then> begann (und im MouseHook das Info, ob die Message schon von der MsgLoop gelöscht wurde, schon im 'nCode'-Parameter sitzt). Das nämlich lässt den Hook-Durchlauf mit noch nicht von der Message-Loop gelöschten Message außen vor. Den würde man bekommen mit <if nCode = HC_NOREMOVE then>. Und wenn man die Message-Bearbeitung im Mouse-Hook mit <if nCode >= 0 then> beginnt, dann bearbeitet man auch beim Mouse-Hook tatsächlich beide Hook-Durchläufe pro Message. Auch beim Keyboard-Hook (WH_KEYBOARD) dürfte das so ganz ähnlich liegen.

Ob es so einen doppelten Hookdurchlauf pro Message überhaupt gibt hängt davon ab, ob beim Auslesen der Message die Api-Funktionen GetMessage oder PeekMessage beteiligt sind (dann wird der Hook vom System aufgerufen) und ob die Application zum Abgreifen dieser Message von der MsgLoop dafür z.B. 2 PeekMessage-Calls benutzt. Einen ohne die Message dabei zu löschen, und einen zweiten mit Message-Löschung und anschließend dann wohl Weiterleitung an die WndProc des jeweiligen Controls (...so +/- gemäß MSDK).

Last but not least: Wer oben event. mitgelesen hat weiß, dass es mir hier letztlich um die wm_move und wm_moving -Messages ging. Messages, die man jetzt aber zufälligerweise garnicht durch einen GetMsg-Hook bekommt, sondern wofür man einen lokalen CallWnd-Hook (WH_CALLWNDPROC) benötigt.

(Das nur der Vollständigkeit halber, weil's bei vielen sicher ähnlich liegt wie bei mir: Einen 'perfekten' schönen Code aus dem Internet kopiert und deswegen an dieser Sache nie vorbeigekommen :shock:)


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:45 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz