Problem mit SetMouseHook



  • Hallo!

    LRESULT CALLBACK MouseHook(int code, WPARAM wParam, LPARAM lParam)
    {
    if(code == HC_ACTION)
    {
    if(wParam == WM_RBUTTONDOWN)
    {
    print("WM_RBUTTON PRESSED");

    		wParam=WM_LBUTTONDOWN;
    		return CallNextHookEx(v_MouseHook, code, wParam, lParam);
    	}
    

    }

    Ich habe diesen Hook erfolgreich in eingespeist mit

    v_MouseHook = SetWindowsHookEx(WH_MOUSE, MouseMove, NULL, MainThread);

    Jetzt zeigt es mir immer über diese print Funktion an, wenn ich die rechte Maus klicke, aber es sendet trotzdem noch die rechte Maus an den Prozess obwohl ich das eigentlich manipulieren will.

    Ich kann nur return 1 dann wird gar nichts weitergeleitet aber dann steht alles still wenn ich auf der Taste bleibe.

    Was muss ich machen um damit wirklich die Maus zu manipulieren?

    Liebe Grüße



  • Rufst du denn bei negativem code auch CallNextHookEx wieder auf, s.a. MouseProc (unter "Returns")?

    PS: Formatiere deinen Code hier mal (per Code-Button)!



  • LRESULT CALLBACK MouseHook(int code, WPARAM wParam, LPARAM lParam)
    {
    	if(code == HC_ACTION)
    	{
    		if(wParam == WM_RBUTTONDOWN) 
    		{
    
    		        print("Pressed Right Button");
    
    			return 1;
    		}
          }
    
          return CallNextHookEx(v_MouseHook, code, wParam, lParam);
    }
    

    Ja natuerlich! Es fliesst die ganze Anwendung fluessig außer wenn ich auf die rechte Maus druecke, dann schreibt es erst den Text und die Anwendung laeuft erst wieder weiter wenn ich von der Maus runtergehe oder sie bewege. Es leitet bei return 1; aber zumindest nicht den Mausklick an die Anwendung weiter. Aber ich will, dass ich rechts klicke es schreibt den Text und laeuft einfach weiter ohne zu warten bis ich die Maus loslasse. Und es soll moeglich sein, statt dessen eine andere Taste "weiterzuleiten".

    Ich stelle mir das so vor, dass die Windows Message zuerst an die Hook Funktion da gesendet wird, und dann an die Anwendung die gehookt wurde. Aber scheinbar lässt sich damit nicht statt einem rechten Klick ein linker Klick machen oder so.

    Und wenn ich auf der Maus drauf bleibe dann steht alles still als ob es das Programm anhaelt bis die Message in der Callback Funktion fertig bearbeitet wurde.

    EDIT: Es scheint jetzt alles einwandfrei zu funktionieren, weil anscheinend wird beim WM_RBUTTONDOWN eine HC_NOREMOVE Message gesendet und keine HC_ACTION. Ich hab es zwar nicht kapiert, aber alles funktioniert jetzt wie ich es will.

    Weiß vielleicht jemand einen Link wo dass sinnvoll in Deutsch erklaert wird alles wo Windows Messages gespeichert werden und was das alles bedeutet? Ist der Messagequeue im RAM des Prozesses wo der Thread ist oder irgendwo auf in den Windowsprozessen eingelagert? Und wenn da soviele Messages gesendet werden, wird der nicht irgendwann "voll"?


  • Mod

    @CUser1 sagte in Problem mit SetMouseHook:

    CallNextHookEx

    @CUser1 Lerne englisch!
    Siehe https://docs.microsoft.com/en-us/windows/win32/winmsg/messages-and-message-queues

    Die MessageQueue ist nur so voll wie Nachrichten "nicht" abgeholt werden.
    Wenn eine Nachricht abgeholt wird, wird Sie aus der Queue entfernt. Eine Messagequeue ist vergleichsweise klein.

    Die MessageQueue liegt im aktuellen Prozess. Jeder Thread kann eine eigene haben. Wird aber GetMessafe/PeekMessage nicht verwendet und kein Fenster erzeugt gibt es auch keine MessaageQueue.

    Die meisten Nachrichten werden auch nicht in der MessageQueue abgelegt. Die mneisten Nachrichten werden direkt ausgeführt/gesendet per SendMessage.

    In der MessageQueue landen typischerweise rohe Tastatur- und Mausnachrichten.
    Nachrichten aus anderen Threads. etc. Und natürlich Nachrichten die mit PostMessage abgesendet werden. Bestimmte Nachrichten sind nie in der Messagequeue (WM_TIMER, WM_PAINT) werden aber durch Nutzung der Queue erzeugt.



  • @Martin-Richter sagte in Problem mit SetMouseHook:

    @CUser1 sagte in Problem mit SetMouseHook:

    CallNextHookEx

    @CUser1 Lerne englisch!

    Ruf die nächste Ex von cpt. Hook an 🤡



  • @Martin-Richter

    Also schreibt Windows die Messages der Reihe nach in den VRAM des Prozesses wo der Thread ist der Messages verwendet, an eine Stelle wo grade Platz ist?

    Das ist ja dann so wie der Stack und Windows muss wissen wo genug Luft nach oben ist?

    Die Antwort auf meine Frage bzgl. ab wie vielen Messages der Queue "voll" ist sehe ich leider nicht? Was wenn schon 100.000 Messages warten und die schon komplett fragmentiert vorliegen im VRAM oder schon auf die Festplatte ausgelagert werden (theoretisch)?

    Was ist dann bei den nicht Queued Nachrichten? Da wird dann der Prozess bzw. alle Threads angehalten und sofort die WndProc gecalled?



  • @CUser1 sagte in Problem mit SetMouseHook:

    @Martin-Richter

    Also schreibt Windows die Messages der Reihe nach in den VRAM des Prozesses wo der Thread ist der Messages verwendet, an eine Stelle wo grade Platz ist?

    Nein. Wie genau es implementiert ist, ist aber auch gar nicht wichtig. Wichtig ist dass jeder Thread eine Message-Queue hat, und Nachrichten die an Fenster gehen, werden in die Queue des Threads eingefügt der für dieses Fenster zusändig ist.

    Das ist ja dann so wie der Stack und Windows muss wissen wo genug Luft nach oben ist?

    Nein, das funktioniert deutlich anders als ein Stack.

    Die Antwort auf meine Frage bzgl. ab wie vielen Messages der Queue "voll" ist sehe ich leider nicht?

    Siehe https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea :

    There is a limit of 10,000 posted messages per message queue. This limit should be sufficiently large. If your application exceeds the limit, it should be redesigned to avoid consuming so many system resources. To adjust this limit, modify the following registry key.

    HKEY_LOCAL_MACHINE
       SOFTWARE
          Microsoft
             Windows NT
                CurrentVersion
                   Windows
                      USERPostMessageLimit
    

    The minimum acceptable value is 4000.


    Was wenn schon 100.000 Messages warten und die schon komplett fragmentiert vorliegen im VRAM oder schon auf die Festplatte ausgelagert werden (theoretisch)?

    Wenn das Limit erreicht ist werden neue Messages einfach verworfen.

    Was ist dann bei den nicht Queued Nachrichten? Da wird dann der Prozess bzw. alle Threads angehalten und sofort die WndProc gecalled?

    Nein, es werden gar keine Threads angehalten.

    Für "SendMessage": Wenn die Nachricht für den aktuellen Thread bestimmt ist, dann wird sie direkt bearbeitet ohne sie in die Message-Queue einzufügen. Und wenn die Nachricht für einen anderen Thread ist, dann wird sie zur "sofortigen" Ausführung vorgemerkt und die SendMessage funktion wartet dann so lange bis der andere Thread die Nachricht auch wirklich ausgeführt hat.

    Weiters gibt es dann noch Nachrichten die nie in der Queue gespeichert werden wie z.B. WM_PAINT. Statt diese in die Queue einzufügen wird bei GetMessage/PeekMessage geprüft ob die Nachricht gerade "nötig" ist, und wenn ja, wird sie einfach zurückgegeben.


    ps: Die Abkürzung VRAM wird üblicherweise für Video-RAM verwendet. Wenn du virtuellen Speicher meinst, schreib einfach "virtueller Speicher".



  • @hustbaer Was heißt dann direkt bearbeiten? Das wie du sagst bei WM_PAINT Windows das Window einfach redrawed, ohne das der Thread bzw. dessen WndProc des Fensters überhaupt etwas mitbekommt?



  • Nein, es wird direkt deine WndProc aufgerufen, ohne den Umweg über die Message Queue.



  • @Mechanics sagte in Problem mit SetMouseHook:

    Nein, es wird direkt deine WndProc aufgerufen, ohne den Umweg über die Message Queue.

    Nein 🙂
    EDIT: Bzw. "ja", da du es denke ich auf den SendMessage Teil bezogen hast. War etwas verwirrt weil @CUser1 schon falsch verstanden hat wie WM_PAINT funktioniert.

    @CUser1 sagte in Problem mit SetMouseHook:

    @hustbaer Was heißt dann direkt bearbeiten? Das wie du sagst bei WM_PAINT Windows das Window einfach redrawed, ohne das der Thread bzw. dessen WndProc des Fensters überhaupt etwas mitbekommt?

    Ebenfalls nein 🙂

    WM_PAINT wird niemals in die Queue gestellt aber es wird auch nicht direkt die WndProc aufgerufen. GetMessage tut nur einfach so als wäre ganz hinten in der Queue ein WM_PAINT gewesen wenn die Paint-Region nicht leer ist.

    Also das läuft (grob skizziert, sinngemäss) eher so:

    BOOL GetMessage(LPMSG lpMsg, HWND  hWnd, UINT  wMsgFilterMin, UINT  wMsgFilterMax) {
    start:
        if (checkMessageQueue(hWnd, wMsgFilterMin, wMsgFilterMax)) {
            // ... "Echte" Message aus der Queue holen, lpMsg befüllen etc. ...
            if (lpMsg->msg == WM_QUIT)
                return FALSE;
            else
                return TRUE;
        }
    
        if (WM_PAINT >= wMsgFilterMin && WM_PAINT <= wMsgFilterMax) {
            if (!isPaintRegionEmpty(hWnd)) {
                // Wir tun einfach so als wäre in der Queue noch eine WM_PAINT Nachricht gewesen
                lpMsg->msg = WM_PAINT;
                // ...
                return TRUE;
            }
        }
    
        // ...
    
        // Keine Message da => warten
        waitForNewMessageOrTimerOrPaintRegionChange(hWnd);
        goto start;
    }
    


  • ps: Das erklärt auch warum WM_PAINT Messages immer und immer wieder reported werden, bis man endlich BeginPaint/EndPaint aufruft.

    Die MS Doku sagt dazu

    GetMessage does not remove WM_PAINT messages from the queue. The messages remain in the queue until processed.

    Stimmt bloss nicht. Es sieht bloss so aus als ob sie nicht entfernt würden, weil sie in Wirklichkeit gar nie reinkommen sondern bei Bedarf einfach aus dem Nichts erzeugt werden.



  • @CUser1 Und zu dem Teil:

    Für "SendMessage": Wenn die Nachricht für den aktuellen Thread bestimmt ist, dann wird sie direkt bearbeitet ohne sie in die Message-Queue einzufügen.

    Damit meine ich das was @Mechanics beschrieben hat: in dem Fall wird die WndProc direkt aufgerufen.



  • @hustbaer Wenn ich es richtig verstehe dann "erzeugt" GetMessage die WM_PAINT oder WM_TIMER nicht aus dem Messagequeue sondern durch die Abfrage anderer Bedingungen, was MartinRichter mit direkt gemeint hat, weil dass nicht über den Queue läuft.

    Aber trotzdem muss man PeekMessage oder GetMessage callen weil sonst bleibt das WM_PAINT trotzdem aus.



  • @hustbaer Ja dann meint direkt aber bloß, dass sie anderen Messages "vorgezogen" wird? Und nicht wie ein Treiber der die CPU schlagartig unterbricht?



  • @CUser1 sagte in Problem mit SetMouseHook:

    @hustbaer Wenn ich es richtig verstehe dann "erzeugt" GetMessage die WM_PAINT oder WM_TIMER nicht aus dem Messagequeue sondern durch die Abfrage anderer Bedingungen, was MartinRichter mit direkt gemeint hat, weil dass nicht über den Queue läuft.

    Aber trotzdem muss man PeekMessage oder GetMessage callen weil sonst bleibt das WM_PAINT trotzdem aus.

    Ja, das hast du richtig verstanden.

    Bei SendMessage an ein Fenster des eigenen Threads läuft das aber anders. Weil warten bis der eigene Thread (der ja gerade in SendMessage hängt) irgendwann mal PeekMessage/GetMessage aufruft -- da könnte man lange warten. In dem Fall muss Windows also die WndProc direkt aufrufen.

    Und nicht wie ein Treiber der die CPU schlagartig unterbricht?

    Schlagartig unterbrochen wird dabei aber auch nichts. Dein Thread hat ja SendMessage(eigenesFenster) aufgerufen. Und in dem Fall ruft SendMessage dann einfach direkt die WndProc auf.



  • @hustbaer Wenn man SendMessage an einen anderen Thread sendet und Windows dann die WndProc aufruft dann bräuchte man ja Critical Section damit nicht Windows und der eigene Thread ggf. zugleich Werte verändern?



  • @CUser1 sagte in Problem mit SetMouseHook:

    @hustbaer Wenn man SendMessage an einen anderen Thread sendet und Windows dann die WndProc aufruft dann bräuchte man ja Critical Section damit nicht Windows und der eigene Thread ggf. zugleich Werte verändern?

    Wenn du SendMessage an ein Fenster eines anderen Threads machst, dann geht das ganz normal über die Queue. Also "fast ganz normal". Solche Messages werden bevorzugt behandelt, und ich bin mir nicht sicher ob in dem Fall nicht auch GetMessage/PeekMessage im "Ziel-Thread" direkt die WndProc aufruft. Aber der "Ziel-Thread" muss auf jeden Fall erstmal eine "Message-Processing" Funktion aufrufen (also GetMessage, PeekMessage und noch ein paar andere).



  • @hustbaer Ja es ergibt eh alles Sinn wenn man mit den Begriffen "schlagartig unterbrechen" oder "direkt" konform umgeht.


  • Mod

    @CUser1 sagte in Problem mit SetMouseHook:

    @hustbaer Wenn man SendMessage an einen anderen Thread sendet und Windows dann die WndProc aufruft dann bräuchte man ja Critical Section damit nicht Windows und der eigene Thread ggf. zugleich Werte verändern?

    Nein. Da es der eigene Thread ist, würde das sowieso nichts nutzen.
    Also:

    Wenn Du SendMessage aufrufst und ein anderer Thread ist involviert, dann blockiert Dein Thread so lange bis SendMessage zurück kommt. Punkt.

    Ein Thread wird eine Nachricht von einem anderen Thread nur entgegen nehmen, wenn Peek-,GetMessage aufgerufen wird. Und nur wenn der Thead mit return aus der WindowProc zurückkehr, läuft der sendende Thread weiter.

    Bei COM wird es spaßiger. Da kann ein externer Aufruf von einem STA Aufruf tatsächlich reentrant sein und ein eingehender COM Aufruf oder eine Nachricht kann wären einem simplen Aufruf in der eigenen Anwendung etwas verändern...



  • @Martin-Richter Das mit COM habe ich nicht verstanden?

    Bei COM erstellt man ja einen Pointer und die COM Funktion erzeugt ein Objekt und der Pointer zeigt dann dorthin oder?

    Ist das nicht wie bei FILE * f = fopen(...); da wird diese struct auch wo anders erstellt durch die mrsvc80.dll oder so?

    Verwenden die da malloc um solche aobjekte zu erzeugen?

    Oder wo werden die COM Objekte erzeugt?

    Und wo ist der Messagequeue? Hustbär sagte der ist nicht im virtuellen Speicher des Prozesses wo der Thread ist?


Anmelden zum Antworten