globale Windows Botschaft eines Mausklicks abfangen



  • Hallo,
    ich würde gerne einen rechten Mausklick abfangen. Allerdings einen der nicht an meine Applikation gerichtet ist (wie z.B. bei Babylon Translator), meine Applikation ist in der Taskleisten bzw. ein Trayicon. Botschaften direct an meine Applikation sind nicht das Problem z.B. Applikation->OnMessage. Habe auch keine API Funktion gefunden. Ich denke da an so was wie die HotKeys.
    Bin für jeden Hinweis dankbar.

    Thomas Fuchs



  • Stich- un Suchworte: Maus-Hook oder auch MouseHook oder Mouse-Hook. Such mal. 😉



  • Siehe FAQ... da hats ein Beispiel mit Tasten, das müsste sich entsprechen "vergewaltigen" lassen... "Tasten systemweit abfangen".

    -junix



  • Ich hab da mal ein Projekt gemacht, in dem ich systemweit die Mauskoordinaten abgefangen habe:

    // Die DLL
    // Projektname: MOUSEDLL
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    #pragma argsused
    
    // Wie in der cpp-Datei der Form-Klasse eine neue Message-ID erstellen
    #define WM_MOUSEHOOK   WM_USER+100
    //---------------------------------------------------------------------------
    
    // Funktionen deklarieren
    extern "C" __declspec(dllexport) __stdcall void SetHook(HWND);
    extern "C" __declspec(dllexport) __stdcall void RemoveHook(void);
    LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    
    // Globale Variablen
    HHOOK      ghHook = NULL; // Hook-Handle
    HINSTANCE  ghInst;        // Instanz-Handle
    HWND       ghWnd;         // Fenster-Handle
    //---------------------------------------------------------------------------
    
    int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
    {
        // Das Instanz-Handle der DLL speichern
        // Wird unten in SetHook() benötigt
        ghInst = hinst;
        return 1;
    }
    //---------------------------------------------------------------------------
    
    void __stdcall SetHook(HWND hWnd)
    {
       // Das funktioniert leider nicht !
       // Ich wollte hier das Handle speichern, aber es wird nicht gespeichert,
       // denn wenn ich es unten in der HookProc verwenden will (SendMessage()),
       // dann kommt in der Form keine Message an. :(
       ghWnd = hWnd;
    
       if(!ghHook)
       {
          // Starten des Mouse-Hooks
          ghHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseHookProc, ghInst, 0);
          if(!ghHook)
             MessageBox(NULL, "Hook kann nicht erstellt werden", "ERROR", MB_OK|MB_ICONERROR);
       }
       else
          MessageBox(NULL, "Hook ist bereits erstellt", "MouseHook", MB_OK);
    }
    //---------------------------------------------------------------------------
    
    void __stdcall RemoveHook(void)
    {
       // Beenden des Hooks
       UnhookWindowsHookEx(ghHook);
    }
    //---------------------------------------------------------------------------
    
    LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
       static BOOL bFirstTime = TRUE;
       // Das Fenster mit der WindowClass "TMouseHookForm" finden
       static HWND hForm = FindWindow("TMouseHookForm", NULL);
    
       if(bFirstTime)
       {
          if(!hForm || !IsWindow(hForm))
             MessageBox(NULL, "MouseHookForm could not be found", "ERROR", MB_OK|MB_ICONERROR);
          bFirstTime = FALSE;
       }
    
       if(nCode >= 0)  // means: nCode==HC_ACTION or nCode==HC_NOREMOVE
          // Die Message an die Form senden
          SendMessage(hForm, WM_MOUSEHOOK, wParam, lParam);
    
       // Die nächste Hook-Prozedur aufrufen (optional)
       return CallNextHookEx(ghHook, nCode, wParam, lParam);
    }
    //---------------------------------------------------------------------------
    

    Dies kompilierst du als DLL, was dir eine LIB auswirft. Dann startest du ein neues Projekt. Die Form darin nennst du TMouseHookForm. Die Unit dazu sieht bei mir folgendermaßen aus:

    //  Die Header-Datei
    //---------------------------------------------------------------------------
    #ifndef HookUnitH
    #define HookUnitH
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    
    // Userdefined Message
    // Wird beim Eingehen von Mouse-Messages von der DLL gesendet
    // und von der MainForm empfangen (s.u.: MESSAGE_MAP)
    #define WM_MOUSEHOOK   WM_USER+100
    //---------------------------------------------------------------------------
    
    class TMouseHookForm : public TForm
    {
    __published:    // Komponenten, die von der IDE verwaltet werden
        TLabel *Label1;  // Caption: "Label1"
        TLabel *Label2;  // Caption: "Label2"
        TLabel *Label3;  // Caption: "X"
        TLabel *Label4;  // Caption: "Y"
        TLabel *Label5;  // Caption: "Msg-ID:"
        TLabel *Label6;  // Caption: "Label6"
        void __fastcall FormCreate(TObject *Sender);
        void __fastcall FormDestroy(TObject *Sender);
    
    private:    // Benutzerdeklarationen
        void __fastcall wmMessage(TMessage& msg);
    
    protected:
      // Ist dafür zuständig, dass die Message WM_MOUSEHOOK
      // an die Funktion wmMessage weitergeleitet wird
      BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_MOUSEHOOK, TMessage, wmMessage);
      END_MESSAGE_MAP(TForm);
    
    public:     // Benutzerdeklarationen
        __fastcall TMouseHookForm(TComponent* Owner);
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TMouseHookForm *MouseHookForm;
    //---------------------------------------------------------------------------
    #endif
    
    //  Die cpp-Datei
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    
    #include "HookUnit.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    
    //---------------------------------------------------------------------------
    // Die Funktionen aus der dem Projekt hinzugefügten LIB deklarieren
    extern "C" __declspec(dllexport) __stdcall void SetHook(HWND);
    extern "C" __declspec(dllexport) __stdcall void RemoveHook(void);
    //---------------------------------------------------------------------------
    TMouseHookForm *MouseHookForm;
    //---------------------------------------------------------------------------
    __fastcall TMouseHookForm::TMouseHookForm(TComponent* Owner)
        : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TMouseHookForm::FormCreate(TObject *Sender)
    {
      // Den Hook starten
      SetHook(Handle);
      // Die Form vor alle anderen Fenster setzen, damit die Maus-Koordianten
      // immer präsent sind
      SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TMouseHookForm::FormDestroy(TObject *Sender)
    {
      // Naja, weg mit dem Hook ;)
      RemoveHook();
    }
    //---------------------------------------------------------------------------
    
    // Hier geht die Message WM_MOUSEHOOK ein
    void __fastcall TMouseHookForm::wmMessage(TMessage& msg)
    {
      // Die Position des Mouse-Cursors bestimmen
      POINT p;
      GetCursorPos(&p);
    
      // Die Labels mit den Werten beschriften
      Label1->Caption = p.x;
      Label2->Caption = p.y;
    
      // Wenn der rechte Maus-Button gedrückt und losgelassen wurde
      if(msg.WParam == WM_RBUTTONUP)
         // Gib eine MessageBox aus
         MessageBox(Handle, "YOU CLICKED RIGHT", "HUHU",
                    MB_OK|MB_ICONINFORMATION|MB_SYSTEMMODAL);
    
      // Die ID der eingehenden Maus-Message ins Label schreiben
      Label6->Caption = (int)msg.WParam;
    }
    //---------------------------------------------------------------------------
    

    Dann füge noch die oben genannte LIB dazu, und starte. Viel Spaß. 😉

    [ Dieser Beitrag wurde am 12.01.2003 um 00:25 Uhr von WebFritzi editiert. ]



  • [ Dieser Beitrag wurde am 12.01.2003 um 00:26 Uhr von WebFritzi editiert. ]



  • Danke für die Info. Klappt alles hervorragend.

    Thomas



  • Fritzi: Wenn du schon fertigen Source postest, dann doch bitte etwas kommentiert...

    -junix



  • Was hättest du denn gerne kommentiert? Hilfe zu den API-Calls kann jeder in der BCB-Hilfe anfordern. Und wer den Code mal durchgeht, der wird merken, dass die Funktionsnamen sich von selber erklären. Alles Übrige steht in der FAQ über Keyboard-Hooks.



  • Ich glaub der junix hat einfach ein persönliches Problem mit dir und macht dich deshalb immer im Forum fertig. 😉 :p



  • Hmm, da sag ich jetzt besser nix zu... 🙄



  • Aber mal im Ernst, der Beitrag würde sich doch hervorragend in der FAQ machen, und da wäre ein etwas ausführlicherer Kommentar schon ganz nett. 😉



  • OK, hab das jetzt auskommentiert und noch ein wenig daran rumgebastelt. Ich hab da noch ein Problem: Ich würde der LIB gerne mein WindowHandle mitgeben, damit FindWindow() in der HookProcedure nicht verwendet werden muss. Das gefällt mir nicht. Dazu hatte ich eine globale Variable ghWnd deklariert und sie bei SetHook() gesetzt mit dem Parameter von SetHook(HWND). Leider klappt das nicht, d.h. die Message wird nicht an mein Fenster gesendet, außer, wenn ich über einem Fenster meines eigenen Prozesses mit der Mouse bin. Ich denke, das liegt daran, dass beim Start des Hooks jeder Prozess die DLL in seinen Adressraum aufnimmt und der Wert von hWnd dann verschwunden ist, weil hWnd ja nur in MEINEM PROZESS initialisiert wird. Irgendwie so muss das laufen. Ich würde der DLL/LIB trotzdem gerne das Handle mitgeben. Weiß jemand, wie man das realisieren könnte?





  • Hmm, das Problem ist, dass das mit dem Pragma Comment bei mir nicht funktioniert. "linker" gibt's beim BCB nicht als erstem Parameter. 😕



  • Original erstellt von WebFritzi:
    Was hättest du denn gerne kommentiert?

    Also wenn ich mir die geänderte Version so ansehe sieht das doch viel besser und erklärender aus als vorher meinst du nicht? (-;
    Aber vielleicht noch ein klärendes Wort: Man soll auch nicht jede einzelne Funktion kommentieren. Was ich viel eher meinte - und as hast du ja jetzt auch gemacht - ist, dass du einzelne Blöcke noch kommentieren solltest was du beabsichtigst das sie tun sollen.
    Zum Beispiel sowas hier:

    // Das Instanz-Handle der DLL speichern
        // Wird unten in SetHook() benötigt
    

    Ist doch wunderbar erklärend, so muss keiner nachsehen was denn gInst oder wie die Variable hiess denn für eine Verwendung hat.

    Gute Arbeit, Fritzi.

    -junix



  • Danke junix. 🙂

    So, ich hab das mit dem Window-Handle jetzt hinbekommen. Das hat mich vieeel Arbeit gekostet. Aber ich war froh, als es dann lief. Das Problem im BCB ist, dass dieses #pragma comment(linker, ...) nicht geht. Ist wohl ein Manko im Linker des BCB. Nachdem ich die google Groups von oben bis unten durchgewühlt hatte, bin ich zu dem Schluss gekommen, dass es wohl nur mit WinAPI geht. Leider gab es auch keinen fertigen Code, und ich musste mir alles selber zusammenfriemeln. Hier die fertige DLL:

    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    #pragma argsused
    
    // Wie in der cpp-Datei der Form-Klasse eine neue Message-ID erstellen
    #define WM_MOUSEHOOK   WM_USER+100
    
    #define SHMEMSIZE 4096
    //---------------------------------------------------------------------------
    
    // Funktionen deklarieren
    extern "C" __declspec(dllexport) __stdcall void SetHook(HWND);
    extern "C" __declspec(dllexport) __stdcall void RemoveHook(void);
    LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
    //---------------------------------------------------------------------------
    
    // Globale Variablen
    HHOOK      ghHook = NULL; // Hook-Handle
    HINSTANCE  ghInst;        // Instanz-Handle
    HWND       ghWnd = NULL;  // Fenster-Handle (shared memory)
    LPVOID     lpvMem = NULL; // pointer to shared memory
    //---------------------------------------------------------------------------
    
    BOOL WINAPI DllEntryPoint(HINSTANCE hinst, DWORD reason, LPVOID lpvReserved)
    {
        HANDLE hMapFile = NULL;  // handle to file mapping
        BOOL   bFirstProcess;
    
        // Das Instanz-Handle der DLL speichern
        // Wird unten in SetHook() benötigt
        ghInst = hinst;
    
        switch(reason)
        {
              // Die DLL wird in den Adressraum eines Prozesses geladen
              case DLL_PROCESS_ATTACH:
              {
                // Ein File-Mapping-Objekt erstellen (mit Name)
                hMapFile = CreateFileMapping(
                    (HANDLE)0xFFFFFFFF,  // use paging file
                    NULL,                // no security attributes
                    PAGE_READWRITE,      // read/write access
                    0,                   // size: high 32-bits
                    sizeof(DWORD),       // size: low 32-bits
                    "DLLMemFileMap");    // name of map object
                if(hMapFile == NULL)
                   return FALSE;
    
                // bFirstProcess == true nur im ersten ladenden Prozess
                bFirstProcess = (GetLastError() != ERROR_ALREADY_EXISTS);
    
                if(!bFirstProcess)
                {
                   hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, // Read/write permission.
                                              FALSE,               // Do not inherit the name
                                              "DLLMemFileMap");    // of the mapping object.
                   if(hMapFile == NULL)
                      return FALSE;
                }
    
                // Einen Zeiger auf das file-mapped shared memory bekommen
                lpvMem = MapViewOfFile(
                    hMapFile,            // object to map view of
                    FILE_MAP_ALL_ACCESS, // read/write access
                    0,                   // high offset:  map from
                    0,                   // low offset:   beginning
                    0);                  // default: map entire file
                if(lpvMem == NULL)
                   return FALSE;
    
                if(bFirstProcess)
                   // Den Speicher initialisieren
                   memset(lpvMem, '\0', SHMEMSIZE);
                else
                   // ghWnd setzen auf das erste Byte im shared Memory
                   // Dort ist das Hande gespeichert (siehe SetHook())
                   ghWnd = *(HWND*)lpvMem;
              }
              break;
    
              // Neuer Thread im Prozess
              case DLL_THREAD_ATTACH:
                 break;
    
              // Beenden eines Threads im Prozess
              case DLL_THREAD_DETACH:
                 break;
    
              // Die DLL wird aus dem Adressraum eines Prozesses freigegeben
              case DLL_PROCESS_DETACH:
              {
                  // Den Speicher aus dem Adressraum des Prozesses "rausmappen"
                  UnmapViewOfFile(lpvMem);
    
                  // Das Handle des Prozesses auf das File-Mapping-Objekt schließen
                  CloseHandle(hMapFile);
              }
              break;
        }
    
        return TRUE;
    }
    //---------------------------------------------------------------------------
    
    void __stdcall SetHook(HWND hWnd)
    {
       // Hier wird hWnd in das shared Memory geschrieben
       // und ghWnd (im aufrufenden Prozess) gesetzt
       HWND* hTempWnd = (HWND*)lpvMem;
       *hTempWnd = hWnd;
       ghWnd = hWnd;
    
       if(!ghHook)
       {
          // Starten des Mouse-Hooks
          ghHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)MouseHookProc, ghInst, 0);
          if(!ghHook)
             MessageBox(NULL, "Hook kann nicht erstellt werden", "ERROR", MB_OK|MB_ICONERROR);
       }
       else
          MessageBox(NULL, "Hook ist bereits erstellt", "MouseHook", MB_OK);
    }
    //---------------------------------------------------------------------------
    
    void __stdcall RemoveHook(void)
    {
       // Beenden des Hooks
       UnhookWindowsHookEx(ghHook);
    }
    //---------------------------------------------------------------------------
    
    LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
    {
       if(nCode >= 0)  // means: nCode==HC_ACTION or nCode==HC_NOREMOVE
          // Die Message an die Form senden
          SendMessage(ghWnd, WM_MOUSEHOOK, wParam, lParam);
    
       // Die nächste Hook-Prozedur aufrufen (optional)
       return CallNextHookEx(ghHook, nCode, wParam, lParam);
    }
    //---------------------------------------------------------------------------
    

    Da ich das Prinzip der DLL noch nicht so GANZ verstanden habe, musste ich am Ende viel ausprobieren. Daher habe ich jetzt nochmal eine Frage: Wie läuft das eigentlich mit ner DLL? Wenn ich sie per LIB meinem Projekt hinzufüge und dieses dann starte, dann geht er doch irgendwann (Wann?) in den DllEntryPoint und führt den Code dort aus, nicht wahr? Dort werden dann ein paar globale Variablen gesetzt. Was passiert aber jetzt danach?
    Ich hatte die Variable bFirstProcess (im EntryPoint) zuerst als global definiert gehabt. Dann fragte ich ganz am Anfang von SetHook() ab, ob bFirstProcess == true ist. Das war aber nicht der Fall. Wieso?

    ÜBRIGENS: Man kann diese DLL (auch die erste in diesem Thread) auch in WinAPI schreiben. Dann wird sie kleiner. Man includiert dann aber natürlich nicht vcl.h sondern windows.h.

    [ Dieser Beitrag wurde am 13.01.2003 um 00:28 Uhr von WebFritzi editiert. ]



  • Selbst wenn das mit diesem #pragma ... nicht geht. Beim BCB gibt es auf jeden Fall einen anderen Weg eine Shared Section in der DLL einzurichten. 🙂



  • Original erstellt von <hmm>:
    Selbst wenn das mit diesem #pragma ... nicht geht. Beim BCB gibt es auf jeden Fall einen anderen Weg eine Shared Section in der DLL einzurichten. 🙂

    Wie schön. Wie wär's dann mal damit, dass du sagst, wie?!



  • Ja, das weiß ich auch nicht, sonst hätte ich es auch geschrieben. 😃

    Sorry. 🙂



  • Guck dir mal das an: http://world.std.com/~lword/work/dllhint.html (.DEF Datei).
    Ich hoffe das "pragma data_seg" funktioniert wenigstens. 😃


Anmelden zum Antworten