globale Hooks mit Callback-Funktion aus beliebiger Anwendung
-
Guten Morgen,
ich hoffe, ich bin hier im richtigen Unterforum ;).
Ich hab da ein Problem und hoffe, dass Ihr mir hier helfen könnt ;).
Ich möchte gerne globale Hooks in verschiedenen Anwendungen verwenden können. Dazu hab ich die Hooks (bzw. erstmal einen) in eine DLL (in C++ geschrieben) ausgelagert. Diese möchte ich meine Anwendung einbinden (in C# geschrieben) und mir zunächst mal alle Mausereignisse anzeigen lassen.
Da ich die Hooks wie gesagt in verschiedenen Anwendungen verwenden möchte und diese die Hook-Informationen selber verabeiten sollen, möchte ich der DLL wiederrum eine Callback-Funktion aus der C#-Anwendung übergeben, welche beim Eintreten eines Hookereignisses aufgerufen werden soll.
Das klappt so weit auch alles ganz gut, solange sich die Maus im Fenster meiner C#-Anwendung befindet. Sobald die Maus allerdings das Fenster verlässt, werden keine Mausereignisse mehr registriert, bzw. die Callback-Funktion der C#-Anwendung nicht mehr aufgerufen, auch dann nicht, wenn ich mit der Maus wieder über das Fenster der Anwendung fahre und das verstehe ich leider nicht.
Ich hatte zuerst vermutet das ein fremder Prozess die Callback-Funktion meiner Anwendung nicht aufrufen kann, da sie in einem anderen Speicherbereich liegt aber zumindest das eigene Fenster müsste doch immer in der Lage sein die Funktion aufzurufen. Ich hoffe, dass mir hier jemand das Problem erklären und eine Lösung zeigen kann :).Hier der Code der DLL:
//hooks.h #include <windows.h> #include <winuser.h> #if defined (_MSC_VER) # define DLLEXPORT extern "C" __declspec (dllexport) #else # define DLLEXPORT #endif DLLEXPORT BOOL InstallHook(int hookType, HOOKPROC hkproc); DLLEXPORT BOOL UninstallHook(int hookType); LRESULT CALLBACK MouseProc(int nCode, WPARAM WParam, LPARAM lParam);// hooks.cpp // #include "stdafx.h" #include "hooks.h" #include <iostream> #ifdef _MANAGED #pragma managed(push, off) #endif HINSTANCE g_hInst = NULL; #pragma data_seg (".SHARED") HHOOK g_hMouseHook = NULL; HOOKPROC g_hProc = NULL; #pragma data_seg () #pragma comment(linker, "/SECTION:.SHARED,RWS") BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { g_hInst = hModule; return TRUE; } DLLEXPORT BOOL InstallHook(int hookType, HOOKPROC hkproc) { g_hProc = hkproc; switch(hookType) { case 0: if (g_hMouseHook != NULL) return true; g_hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hInst, 0); if (g_hMouseHook == NULL) return false; break; } return true; } DLLEXPORT BOOL UninstallHook(int hookType) { switch(hookType) { case 0: if (g_hMouseHook != NULL) { UnhookWindowsHookEx(g_hMouseHook); g_hMouseHook = NULL; } return true; } return true; } LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode < 0) return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); if (nCode == HC_ACTION) { g_hProc(nCode, wParam, lParam); } return CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); }Die C#-Anwendung (in einem Textfenster werden bei installiertem Maushook, die Position der Maus angezeigt):
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; namespace cSharpHookTest { public partial class Form1 : Form { public delegate void CallbackType(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("hooks.dll")] private static extern bool InstallHook(int hookType, CallbackType function); [DllImport("hooks.dll")] private static extern bool UninstallHook(int hookType); [StructLayout(LayoutKind.Sequential)] public class MouseHookStruct { public Point pt; public int hwnd; public int wHitTestCode; public int dwExtraInfo; } CallbackType myDelegate; public Form1() { InitializeComponent(); } private void hookCallback(int nCode, IntPtr wParam, IntPtr lParam) { MouseHookStruct st = (MouseHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseHookStruct)); this.txtOutput.Text += "X: " + st.pt.X.ToString() + " - Y: " + st.pt.Y.ToString(); if (((int)wParam) == 0x0201) this.txtOutput.Text += " LEFT MOUSE BUTTON PRESSED"; this.txtOutput.Text += System.Environment.NewLine; } private void cmdHookOn_Click(object sender, EventArgs e) { cmdHookOn.Enabled = false; cmdHookOff.Enabled = true; myDelegate = new CallbackType(hookCallback); if (InstallHook(0, myDelegate)) txtOutput.Text = "Hook erfolgreich gesetzt." + System.Environment.NewLine; else txtOutput.Text = "Hook nicht erfolgreich gesetzt." + System.Environment.NewLine; } private void cmdHookOff_Click(object sender, EventArgs e) { cmdHookOff.Enabled = false; cmdHookOn.Enabled = true; if (UninstallHook(0)) txtOutput.Text = "Hook erfolgreich entfernt." + System.Environment.NewLine; else txtOutput.Text = "Hook nicht erfolgreich entfernt." +System.Environment.NewLine; myDelegate = null; } private void cmdExit_Click(object sender, EventArgs e) { Application.Exit(); } }
-
Du missverstehst wie Hooks arbeiten. Die DLL wird injeziert in den anderen Prozess und nicht Deine EXE.
Du müsstest also einen IPC Mechanismus zwischen allen DLL Instanzen (denn die DLL wird in jedem Prozess injeziert!!!) und Deiner EXE bauen...
-
Du missverstehst wie Hooks arbeiten. Die DLL wird injeziert in den anderen Prozess und nicht Deine EXE.
Nein nein, mir ist schon klar, dass die DLL dirkekt in die Prozesse injeziert wird.
Die DLL erhält ja aber einen Zeiger auf eine Callback-Funktion in meiner Anwendung. Es wäre natürlich eine Erklärung, wenn dieser Zeiger in dem fremden Prozess nicht gültig ist aber wieso klappt es nicht mal im eigenen Prozess, sobald die Maus das Fenster verlassen hat?Du müsstest also einen IPC Mechanismus zwischen allen DLL Instanzen (denn die DLL wird in jedem Prozess injeziert!!!) und Deiner EXE bauen...
Könntest Du mir das bitte näher erklären ;)...
-
Es gibt keine Cross-Prozess Aufrufe!
Die gibt es schon gar nicht in C# und .NET!
Prozesse sind abgeschlossene Bereiche. Man kann aus dem Prozess nicht ausbrechen und Code eines anderen Prozesses ausführen und das im Kontext des alten Threads. (Dieser Satz müsste Dir die Unsinnigkeit eines solchen Vorhabens klar machen).Die Hookadresse ist ungültigund damit ist schluss. Das OS merkt, dass die Adresse des Hooks faul ist und deaktiviert den Hook, damit dieser das OS nicht lahmlegt. Du hast ja immerhin einen systemweiten Hook erzeugt!
IPC = Inter-Process-Communication. Arbeite mit Nachrichten PostMessage, Shared Memory, Events, Pipes... was auch immer! Auf jedenfall etwas was wirklich schnell ist.
-
Ok, vielen Dank erstmal für die klare Antwort ;). Ich habe zwar befürchtet, dass es so nicht funktioniert aber die Hoffnung stirbt ja bekanntlich zuletzt *g*.
Könntest Du mir eine andere Vorgehensweise empfehlen? Ich habe das Ganze mal mit PostMessage probiert, leider ist meine Anwendung dabei regelmäßig abgeschmirt, was entweder an zuvielen Nachrichten oder schlechter Programmierung gelegen hat ;). Wäre MMF eine mögliche Alternative?
-
Warum soll PostMessage abschmieren. Du darfst nur nicht auf die Idee kommen Zeiger zu übertragen. Wenn Dir WPARAM und LPARAM als Datenfelder genügen ist das doch OK!
MMF sind schwierig zu synchronisieren. Aber sicherlich ist das auch OK.
Jede IPC Methode, die schnell genug ist ist auch gut!
Manche allerings eben etwas schwerer im Handling!
-
@Martin Richter:
Moin Moin,
ich hab mich gestern Abend nochmal etwas mit IPC auseinander gesetzt. Das Thema ist ja leider doch etwas komplizierter als ich ohnehin schon befürchtet hatte.
Kannst Du mir vielleicht gute Literatur oder Links zu dem Thema nennen, bzw. mir sagen, wie Du das Problem angehen würdest ;)?
Ich habe folgende Kriterien, bzw. möchte folgendes erreichen:
- möglichst hohe Flexibilität (Die Hook-DLL soll möglichst wenig verarbeiten, dass soll mein Hauptprogramm übernehmen)
- Geschwindigkeit
- Prozessmanipulation (Ich möchte die Hook-Daten vor der Weitergabe ggf. manipulieren können)
-
Ich würde das Problem meiden wie die Pest!
Globalehooks sind für mich das schlimmste Übel. Das auch deswegen weil so viel Schindluder damit betrieben wird.
Was willst Du denn machen?
Denn wenn Du auch noch die ganzen Events verbiegen möchtest wird es kritisch (auch zeitkritisch). Solange es nur um die Maus geht, mag viel gehen.Die ist aber auch kar, dass man nicht alle Prozesse Hooken kann! Das es eine Prozessisolation gibt, die unter Vista z.B. den IE ziemlich hart abschottet!
"Das soll Dein Hauptprogramm machen", sehe ich fast als unmöglich an. Dein Hook würde das System evtl. blockieren nur weil der IPC zu Deinem Programm gestört ist. Und in .NET sehe ich das ganz und gar als kritisch. Dein .Net Programm fährt mal ein bischen Garbage Collection und Dein gesamtes System steht...
In Deinen Hook gehört sehr viel Intelligenz...
-
Jetzt mach mir bitte keine Angst ;).
Ich brauch das Ganze für meine Bachelor-Thesis. Es geht um Vor- und Nachteile, Einsatzmöglichkeiten und Risiken von Hooks. Daher ist es auch nicht schlimm, wenn etwas kritisch ist, da ich das Problem dann eben als solches darstellen kann. Es wird damit später kein wichtiges Produktivsystem betrieben, auch wenn es schön wäre, wenn sich solch ein Ergebnis herauskristallisieren würde ;).
Ich sammle gerade halt noch Informationen und Literatur und mache 'erste' Versuche. Es ist schon interessant auf wieviele Probleme man stößt und wie wenig Leute Ahnung von der Materie haben ;). Daher freut es mich, dass Du wirklich Ahnung zu haben scheinst :).
Falls Du noch irgendwelche Tips, Links oder Literaturempfehlungen, Beispiele oder Quellcode hast, wäre ich sehr dankbar!
-
Grundsätzlich zum Thema Hooks und Injektion von DLLs in andere Prozesse würde ich Dir Jeffrey Richter (Name bürgt für Qaulität
) "Advanced Windows" Kapitel 18 "Breaking through process boundary walls" anempfehlen.Ansonsten diskutiere ich das Thema Hooks nicht gerne, es wird einfach viel zuviel Schindluder damit getrieben.
-
Erstmal vielen Dank für deine vielen Antworten. Ich hab mal nach dem Buch geschaut und gesehen, dass es eine aktuellere Auflage namens 'Windows via C/C++ (PRO-Developer)' von 2007 gibt ;). (http://www.amazon.de/Windows-via-C-C%2B%2B-PRO-Developer/dp/0735624240/ref=pd_bbs_sr_2?ie=UTF8&s=books-intl-de&qid=1204714927&sr=8-2)
Ich möchte wie gesagt kein Schindluder mit Hooks betreiben, sondern meine Abschlussarbeit darüber schreiben. Wäre sehr nett von Dir, wenn ich mich ggf. noch mit der einen oder anderen Frage an Dich wenden dürfte. Wenn Du nicht in einem öffentlichen Forum über Hooks reden möchtest, würde ich Dir natürlich auch eine PN oder eMail schreiben ;).
Nochmal vielen Dank...