Laden...

Hooks, Dlls, Prozesse

Erstellt von Rahvin vor 16 Jahren Letzter Beitrag vor 15 Jahren 9.390 Views
R
Rahvin Themenstarter:in
156 Beiträge seit 2006
vor 16 Jahren
Hooks, Dlls, Prozesse

Guten Morgen,

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;
}


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();
        }
    }


915 Beiträge seit 2006
vor 16 Jahren

Hallo Rahvin,
leider sieht man im C++ Code den Abschnitt mit LoadLibrary nicht


	hDLL = LoadLibrary ("DeinDLLName.dll");
	HOOKPROC hProc =  (HOOKPROC)GetProcAddress (hDLL, "MouseProc");
	SetWindowsHookEx (WH_MOUSE, hProc , hDLL, 0);

Wo du den Funktionsaufruf für folgende Methode hast:


BOOL APIENTRY DllMain( HINSTANCE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
    g_hInst = hModule;
 
    return TRUE;
}

Oder hast du die funktion ebenso über DLLEXPORT aufgerufen und füllst sie vom C# Code aus, wenn ja wie - Holst sie dir dann wie folgt?


Marshal.GetHINSTANCE(Assembly.Load("DeineDLL.DLL").GetModules()[0])

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

R
Rahvin Themenstarter:in
156 Beiträge seit 2006
vor 16 Jahren

Ich verstehe Deine Frage glaub ich nicht ganz.

Ich binde die hooks.dll wie folgt in C# ein:


        [DllImport("hooks.dll")]
        private static extern bool InstallHook(int hookType, CallbackType function);

        [DllImport("hooks.dll")]
        private static extern bool UninstallHook(int hookType);

Meine C# Callback-Funktion sieht dann ja so aus:


        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;
        }

und diese übergebe ich dann wie folgt an die hooks.dll:


            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;

LoadLibrary verwende ich in der C++ Dll gar nicht, da ich keine weitere Dll dynamisch binde.

Die Funktion:


BOOL APIENTRY DllMain( HINSTANCE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
    g_hInst = hModule;
 
    return TRUE;
}

ist doch der Einstiegspunkt der DLL und wird automatisch aufgerufen oder bin ich jetzt im falschen Film 🙂?

915 Beiträge seit 2006
vor 16 Jahren

Hrm war zu lange nicht mehr in C++ unterwegs "APIENTRY " überlesen kannte das mit DllMain noch gar nicht sondern nur als APIENTRY WinMain. Auch wieder was gelernt 🙂

Hrm, dann wirds schwerer, versuch mal folgendes:


        BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
        {

            switch (dwReason)
            {
                case DLL_PROCESS_ATTACH:
                    DisableThreadLibraryCalls(hModule);
                    g_hInst = hModule;
                    break;
                case DLL_THREAD_ATTACH:
                case DLL_THREAD_DETACH:
                    break;
                case DLL_PROCESS_DETACH:
                    if (g_hProc != NULL)
                        UninstallHook(g_hProc);
                    break;
            }
            return TRUE;
        }

Ansonsten, scheint alles korekt zu sein bzw, sehe nur das eben CallNextHookEx im C# code bei deinen hookCallback fehlt.


[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
   IntPtr lParam);

Ansonsten, kann mir nur schwer vorstellen das dieser Vorgang da er dann "Systemweit" gildet wegen den Sicherheitsbeschränkungen nicht funktioniert - das hebelst ja mit dem C++ Code ausser kraft da sollte der hookCallback auch in C# ausgeführt werden dürfen. Ansonsten, belasse ihn in C++ und mit PostMessage gibst dann einfach an das Handle an eines deiner Fenster/Controls das dann zur Auswertung nutzt in deinen C# Code die WindowsNachrichten weiter.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

R
Rahvin Themenstarter:in
156 Beiträge seit 2006
vor 16 Jahren

Ups, hab gerade gesehen, dass ich nur die Hälfte des dll-Quellcodes kopiert habe 😉. Hier also noch der Rest 😉:


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);
  
}

Hier sieht man das die CallNextHookEx-Funktion aus der DLL aufgerufen wird, sobald die Callback-Funktion der C#-Anwendung aufgerufen worden ist. Damit gab es auch noch keine 'spürbaren' Probleme 😉.

Dein Codebeispiel werde ich nachher mal ausprobieren. Ich bin gerade auf der Arbeit und kann das daher leider nicht testen 😉.

Edit:

Ansonsten, kann mir nur schwer vorstellen das dieser Vorgang da er dann "Systemweit" gildet wegen den Sicherheitsbeschränkungen nicht funktioniert - das hebelst ja mit dem C++ Code ausser kraft da sollte der hookCallback auch in C# ausgeführt werden dürfen. Ansonsten, belasse ihn in C++ und mit PostMessage gibst dann einfach an das Handle an eines deiner Fenster/Controls das dann zur Auswertung nutzt in deinen C# Code die WindowsNachrichten weiter.

Ich bin mir nicht sicher ob ein fremder Prozess mit einem Zeiger auf eine Callback-Funktion in einem anderen Prozess was anfangen kann, bzw. ob dieser dort gültig ist.

PostMessage habe ich vorher verwendet. Ich finde diese Lösung nicht wirklich elegant und möchte sie möglichst vermeiden. Auch weil die Performance dadurch noch mehr in den Keller geht 😉.

915 Beiträge seit 2006
vor 16 Jahren

Phu machst es mir echt schwer sehe ansonsten den Fehler nicht. Habe etliche Hooks schon geschrieben auf den selben Weg wie du, allerdings eben über LoadLibrary auf deine "leichtere" Idee bin ich gar nicht gekommen. Allerdings nutzte ich lowlevel hooks also WH_KEYBOARD_LL und WH_MOUSE_LL aber das sollte keine Rolle spielen.-)

Ansonsten sollte es eigentlich so funktionieren wie du es geschrieben hast, direkt den Fehler sehe ich nicht. Machs einfach mal über LoadLibrary und eben über nen LowLevel hook und halt FreeLibrary. In google gibts da soviel Code dazu da lohnt es sich nicht das reinzuposten.

Aber falls es doch "so" hinkriegst wüsste ich gerne das "warum oder wie" 😮)

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

915 Beiträge seit 2006
vor 16 Jahren

Hallo sorry das ich mich nicht mehr so lange gemeldet habe.

Ich habe den Hook mal etwas mehr unseren lieben C# standard angepasst 😉

Hier also global low level hook für C# fürs keyboard:


 public class CHook
    {
        #region Declarations
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hModule"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeLibrary(IntPtr hModule);
        /// <summary>
        /// defines the callback type for the hook
        /// </summary>
        private delegate int keyboardHookProc(int code, int wParam, ref KBDLLHOOKSTRUCT lParam);
        /// <summary>
        /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
        /// </summary>
        /// <param name="idHook">The id of the event you want to hook</param>
        /// <param name="callback">The callback.</param>
        /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
        /// <param name="threadId">The thread you want to attach the event to, can be null</param>
        /// <returns>a handle to the desired hook</returns>
        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
        /// <summary>
        /// Unhooks the windows hook.
        /// </summary>
        /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
        /// <returns>True if successful, false otherwise</returns>
        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(IntPtr hInstance);
        /// <summary>
        /// Calls the next hook.
        /// </summary>
        /// <param name="idHook">The hook id</param>
        /// <param name="nCode">The hook code</param>
        /// <param name="wParam">The wparam.</param>
        /// <param name="lParam">The lparam.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
        /// <summary>
        /// Loads the library.
        /// </summary>
        /// <param name="lpFileName">Name of the library</param>
        /// <returns>A handle to the library</returns>
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpFileName);
        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct KBDLLHOOKSTRUCT
        {
            public int vkCode;
            public int scanCode;
            public int flags;
            public int time;
            public int dwExtraInfo;
        }

        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x100;
        private const int WM_KEYUP = 0x101;
        private const int WM_SYSKEYDOWN = 0x104;
        private const int WM_SYSKEYUP = 0x105;
        #endregion

        #region Instance Variables
        /// <summary>
        /// Handle to the hook, need this to unhook and call the next hook
        /// </summary>
        private IntPtr m_hHook = IntPtr.Zero;
        /// <summary>
        /// Handle to the module handle instance, need this to unload the library
        /// </summary>
        private IntPtr m_hInstance = IntPtr.Zero;
        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the hooked keys is pressed
        /// </summary>
        public event KeyEventHandler KeyDown;
        /// <summary>
        /// Occurs when one of the hooked keys is released
        /// </summary>
        public event KeyEventHandler KeyUp;
        #endregion

        #region Constructors and Destructors
        /// <summary>
        /// Initializes a new instance
        /// </summary>
        public CHook() { }
        /// <summary>
        /// Release unmanaged code
        /// </summary>
        ~CHook()
        {
            this.UnHook();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Installs the global hook 'keyboard'
        /// </summary>
        public void Hook()
        {
            this.m_hInstance = LoadLibrary("User32");
            this.m_hHook = SetWindowsHookEx(WH_KEYBOARD_LL, this.HookProc, this.m_hInstance, 0);
        }
        /// <summary>
        /// Uninstalls the global hook
        /// </summary>
        public void UnHook()
        {
            FreeLibrary(this.m_hInstance);
            UnhookWindowsHookEx(m_hHook);
        }
        /// <summary>
        /// The callback for the keyboard hook
        /// </summary>
        /// <param name="code"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int HookProc(int code, int wParam, ref KBDLLHOOKSTRUCT lParam)
        {
            if (code >= 0)
            {
                Keys key = (Keys)lParam.vkCode;

                KeyEventArgs kea = new KeyEventArgs(key);
                if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (this.KeyDown != null))
                    this.KeyDown(this, kea);
                else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (this.KeyUp != null))
                    this.KeyUp(this, kea);
                if (kea.Handled)
                    return 1;

            }
            return CallNextHookEx(m_hHook, code, wParam, ref lParam);
        }
        #endregion
    }

Falls doch die WindowsNachrichten abfangen möchtest, einfach in deinem C++ hook die BOOL APIENTRY DllMain rausschmeißen und stattdessen über LoadLibrary das ganze implementieren. Siehe hierzu die oberen Threads.

Aber für deine Zwecke reichen die globalen low level hooks für die Maus und die Tastatur aus.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

915 Beiträge seit 2006
vor 16 Jahren

Und hier für die Maus:


    public class CHook
    {
        #region Declarations
        /// <summary>
        /// 
        /// </summary>
        /// <param name="hModule"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool FreeLibrary(IntPtr hModule);
        /// <summary>
        /// defines the callback type for the hook
        /// </summary>
        private delegate int mouseHookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam);
        /// <summary>
        /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
        /// </summary>
        /// <param name="idHook">The id of the event you want to hook</param>
        /// <param name="callback">The callback.</param>
        /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
        /// <param name="threadId">The thread you want to attach the event to, can be null</param>
        /// <returns>a handle to the desired hook</returns>
        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowsHookEx(int idHook, mouseHookProc callback, IntPtr hInstance, uint threadId);
        /// <summary>
        /// Unhooks the windows hook.
        /// </summary>
        /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
        /// <returns>True if successful, false otherwise</returns>
        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(IntPtr hInstance);
        /// <summary>
        /// Calls the next hook.
        /// </summary>
        /// <param name="idHook">The hook id</param>
        /// <param name="nCode">The hook code</param>
        /// <param name="wParam">The wparam.</param>
        /// <param name="lParam">The lparam.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref MSLLHOOKSTRUCT lParam);
        /// <summary>
        /// Loads the library.
        /// </summary>
        /// <param name="lpFileName">Name of the library</param>
        /// <returns>A handle to the library</returns>
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpFileName);
        /// <summary>
        ///
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public int mouseData;
            public int flags;
            public int time;
            public IntPtr dwExtraInfo;
        }
        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }

            public static implicit operator System.Drawing.Point(POINT p)
            {
                return new System.Drawing.Point(p.X, p.Y);
            }

            public static implicit operator POINT(System.Drawing.Point p)
            {
                return new POINT(p.X, p.Y);
            }
        }
        private const int WH_MOUSE_LL = 14;
        #endregion

        #region Instance Variables
        /// <summary>
        /// Handle to the hook, need this to unhook and call the next hook
        /// </summary>
        private IntPtr m_hHook = IntPtr.Zero;
        /// <summary>
        /// Handle to the module handle instance, need this to unload the library
        /// </summary>
        private IntPtr m_hInstance = IntPtr.Zero;
        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the hooked keys is pressed
        /// </summary>
        public event MouseEventHandler MouseMove;
        public event MouseEventHandler MouseDown;
        public event MouseEventHandler MouseUp;
        public event MouseEventHandler MouseWheel;
        #endregion

        #region Constructors and Destructors
        /// <summary>
        /// Initializes a new instance
        /// </summary>
        public CHook() { }
        /// <summary>
        /// Release unmanaged code
        /// </summary>
        ~CHook()
        {
            this.UnHook();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Installs the global hook 'mouse'
        /// </summary>
        public void Hook()
        {
            this.m_hInstance = LoadLibrary("User32");
            this.m_hHook = SetWindowsHookEx(WH_MOUSE_LL, this.HookProc, this.m_hInstance, 0);
        }
        /// <summary>
        /// Uninstalls the global hook
        /// </summary>
        public void UnHook()
        {
            FreeLibrary(this.m_hInstance);
            UnhookWindowsHookEx(m_hHook);
        }
        /// <summary>
        /// 
        /// </summary>
        private enum MouseMessages
        {
            WM_LBUTTONDBLCLK = 0x203,
            WM_LBUTTONDOWN = 0x201,
            WM_LBUTTONUP = 0x202,
            WM_MBUTTONDOWN = 0x207,
            WM_MBUTTONUP = 0x208,
            WM_RBUTTONDOWN = 0x204,
            WM_RBUTTONUP = 0x205,
            WM_MOUSEMOVE = 0x200,
            WM_MOUSEWHEEL = 0x20A,
            WM_MOUSEHWHEEL = 0x20E,
        }
        /// <summary>
        /// The callback for the mouse hook
        /// </summary>
        /// <param name="code"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int HookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam)
        {
            if (code >= 0)
            {
                if(this.MouseDown !=null 
                    && (wParam == (int)MouseMessages.WM_LBUTTONDOWN
                    || wParam == (int)MouseMessages.WM_MBUTTONDOWN
                    || wParam == (int)MouseMessages.WM_RBUTTONDOWN))
                {
                    MouseButtons eMouseButtons = MouseButtons.None;
                    switch ((MouseMessages)wParam)
                    {
                        case MouseMessages.WM_LBUTTONDOWN:
                            eMouseButtons = MouseButtons.Left;
                            break;

                        case MouseMessages.WM_MBUTTONDOWN:
                            eMouseButtons = MouseButtons.Middle;
                            break;

                        case MouseMessages.WM_RBUTTONDOWN:
                            eMouseButtons = MouseButtons.Right;
                            break;
                    }

                    this.MouseDown(this, new MouseEventArgs(eMouseButtons, 0, lParam.pt.X, lParam.pt.Y, 0));
                }
                else if (this.MouseUp != null
                    && (wParam == (int)MouseMessages.WM_LBUTTONUP
                    || wParam == (int)MouseMessages.WM_MBUTTONUP
                    || wParam == (int)MouseMessages.WM_RBUTTONUP))
                {
                    MouseButtons eMouseButtons = MouseButtons.None;
                    switch ((MouseMessages)wParam)
                    {
                        case MouseMessages.WM_LBUTTONDOWN:
                            eMouseButtons = MouseButtons.Left;
                            break;

                        case MouseMessages.WM_MBUTTONDOWN:
                            eMouseButtons = MouseButtons.Middle;
                            break;

                        case MouseMessages.WM_RBUTTONDOWN:
                            eMouseButtons = MouseButtons.Right;
                            break;
                    }

                    this.MouseUp(this, new MouseEventArgs(eMouseButtons, 0, lParam.pt.X, lParam.pt.Y, 0));
                }
                else if (this.MouseWheel != null
                    && (wParam == (int)MouseMessages.WM_MOUSEWHEEL || wParam == (int)MouseMessages.WM_MOUSEHWHEEL))
                    this.MouseWheel(this, new MouseEventArgs(MouseButtons.None, 0, lParam.pt.X, lParam.pt.Y, 0));
                else if (this.MouseMove != null
                    && wParam == (int)MouseMessages.WM_MOUSEMOVE)
                    this.MouseMove(this, new MouseEventArgs(MouseButtons.None, 0, lParam.pt.X, lParam.pt.Y, 0));
            }
            return CallNextHookEx(m_hHook, code, wParam, ref lParam);
        }
        #endregion
    }

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

H
5 Beiträge seit 2008
vor 16 Jahren

Hallo zusammen,

ich wollte mich mal ein wenig mit dem Thema Hooks befassen und habe den oben aufgeführten Code des Maus Hooks ausprobiert.

Bekomme leider einen CallbackOnCollectedDelegate Fehler.

Kann mir da jemand helfen?
Habe schon im I-Net gesucht, aber noch nichts brauchbares gefunden, komme hier einfach nicht weiter.

Achja, ich habe nur den Inhalt der HookProc Methode angepasst, sonst ist der Code identisch.

schonmal Danke

915 Beiträge seit 2006
vor 16 Jahren

Hallo heikowin

kannst du deine "HookProc" Methode reinposten evtl. ists nur was kleines?

Der Code geht ansonsten eigentlich, hab nur nen modifer geändert nachträglich und versehentlich das Ding public statt private gemacht, habe das mal geändert sollte nun via Copy and Paste gehen .-)

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

H
5 Beiträge seit 2008
vor 16 Jahren

Hallo,

ich poste einfach mal den ganzen code, habe da bestimmt durch rumprobieren noch was anderes geändert, was ich nicht sehe.


using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace ScreenShotApplication.Hooks
{
    public class CMouseHook
    {
        #region Declarations
        /// <summary>
        /// defines the callback type for the hook
        /// </summary>
        private delegate int mouseHookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam);
        /// <summary>
        /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
        /// </summary>
        /// <param name="idHook">The id of the event you want to hook</param>
        /// <param name="callback">The callback.</param>
        /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
        /// <param name="threadId">The thread you want to attach the event to, can be null</param>
        /// <returns>a handle to the desired hook</returns>
        [DllImport("user32.dll")]
        private static extern IntPtr SetWindowsHookEx(int idHook, mouseHookProc callback, IntPtr hInstance, uint threadId);
        /// <summary>
        /// Unhooks the windows hook.
        /// </summary>
        /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
        /// <returns>True if successful, false otherwise</returns>
        [DllImport("user32.dll")]
        private static extern bool UnhookWindowsHookEx(IntPtr hInstance);
        /// <summary>
        /// Calls the next hook.
        /// </summary>
        /// <param name="idHook">The hook id</param>
        /// <param name="nCode">The hook code</param>
        /// <param name="wParam">The wparam.</param>
        /// <param name="lParam">The lparam.</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref MSLLHOOKSTRUCT lParam);
        /// <summary>
        /// Loads the library.
        /// </summary>
        /// <param name="lpFileName">Name of the library</param>
        /// <returns>A handle to the library</returns>
        [DllImport("kernel32.dll")]
        private static extern IntPtr LoadLibrary(string lpFileName);
        /// <summary>
        ///
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public int mouseData;
            public int flags;
            public int time;
            public IntPtr dwExtraInfo;
        }
        /// <summary>
        /// 
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            public int X;
            public int Y;

            public POINT(int x, int y)
            {
                this.X = x;
                this.Y = y;
            }

            public static implicit operator System.Drawing.Point(POINT p)
            {
                return new System.Drawing.Point(p.X, p.Y);
            }

            public static implicit operator POINT(System.Drawing.Point p)
            {
                return new POINT(p.X, p.Y);
            }
        }
        private const int WH_MOUSE_LL = 14;

        #endregion

        #region Instance Variables

        /// <summary>
        /// Handle to the hook, need this to unhook and call the next hook
        /// </summary>
        private IntPtr m_hHook = IntPtr.Zero;

        #endregion

        #region Events
        /// <summary>
        /// Occurs when one of the hooked keys is pressed
        /// </summary>
        public event MouseEventHandler MouseMove;
        //public event MouseEventHandler MouseDown;
        //public event MouseEventHandler MouseClick;
        //public event MouseEventHandler MouseUp;
        //public event MouseEventHandler MouseWheel;

        #endregion

        #region Constructors and Destructors
        /// <summary>
        /// Initializes a new instance
        /// </summary>
        public CMouseHook()
        {
        }
        /// <summary>
        /// Release unmanaged code
        /// </summary>
        ~CMouseHook()
        {
            this.UnHook();
        }
        #endregion

        #region Methods
        /// <summary>
        /// Installs the global hook 'mouse'
        /// </summary>
        public void Hook()
        {
            IntPtr hInstance = LoadLibrary("User32");
            this.m_hHook = SetWindowsHookEx(WH_MOUSE_LL, this.HookProc, hInstance, 0);
        }
        /// <summary>
        /// Uninstalls the global hook
        /// </summary>
        public void UnHook()
        {
            UnhookWindowsHookEx(m_hHook);
        }
        /// <summary>
        /// 
        /// </summary>
        private enum MouseMessages
        {
            WM_LBUTTONDBLCLK = 0x203,
            WM_LBUTTONDOWN = 0x201,
            WM_LBUTTONUP = 0x202,
            WM_MBUTTONDOWN = 0x207,
            WM_MBUTTONUP = 0x208,
            WM_RBUTTONDOWN = 0x204,
            WM_RBUTTONUP = 0x205,
            WM_MOUSEMOVE = 0x200,
            WM_MOUSEWHEEL = 0x20A,
            WM_MOUSEHWHEEL = 0x20E,
        }
        /// <summary>
        /// The callback for the mouse hook
        /// </summary>
        /// <param name="code"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int HookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam)
        {
            if (wParam == (int)MouseMessages.WM_MOUSEMOVE)
            {
                MouseButtons eMouseButtons = MouseButtons.None;

                this.MouseMove(this, new MouseEventArgs(eMouseButtons, 0, lParam.pt.X, lParam.pt.Y, 0));
            }
            return CallNextHookEx(m_hHook, code, wParam, ref lParam);
        }
        #endregion
    }
}

H
5 Beiträge seit 2008
vor 16 Jahren

Hallo,

ich habe es nochmal mit dem oben aufgeführten MausHook versucht aber ich komme immer auf den selben Fehler: CallbackOnCollectedDelegate .

Hat keine eine Idee wie ich das Problem lösen kann?

49.485 Beiträge seit 2005
vor 16 Jahren

Hallo heikowin,

ich habe nicht alles gelesen, nur deinen letzten Post. Und da enthält schon der erste Treffer bei Googlesuche nach CallbackOnCollectedDelegate die Lösung: CallbackOnCollectedDelegate

herbivore

915 Beiträge seit 2006
vor 16 Jahren

Sorry hab lange nicht mehr hier reingeschaut 🙁

Also, herbivore hat die richtige Spur angegeben dennoch fürchte ich ist der Fehler nicht leicht zu finden, daher schreibe ich mal die Lösung rein 🙂

Hatte ja einen destructor angegeben


 ~CMouseHook()
        {
            this.UnHook();
        }

Leider fehlt in der UnHook() Methode das auch der pointer auf das zwischenliegende Modul wieder freigegeben wird sobald die instanz der Klasse geschlossen wird.

Also folgendes bereinigt das ganze, hab den Quellcode oben bei mir auch daraufhin angepasst. (siehe daher m_hInstance und DLLImport FreeLibrary).


[DllImport("kernel32.dll", SetLastError=true)]
private static extern bool FreeLibrary(IntPtr hModule);

  public void UnHook()
        {
            FreeLibrary(m_hInstance);
            UnhookWindowsHookEx(m_hHook);
        }

Ansonsten hast sind noch zwei kleinere Fehler drinnen. Bei deiner Methode HookProc fehlt die Abfrage auf Code ≥ 0. Denn sonst gibts beim CallNextHookEx Fehler, grade wenn der GC drüberläuft den dann sollen keine Nachrichten mehr verarbeitet werden. Das passiert daher weil das Ding nicht synchron "aufgeräumt" wird. Dann denk dran das Events immer darauf prüfen solltest ob diese überhaupt gebunden sind, also einfach ein if (this.MouseMove != null) bevor das Event invokst.

Nach dem ganzen lief dein Quellcode bei mir ohne Probleme durch, falls es weiterhin Probleme geben sollte, gib einfach bescheid.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

H
5 Beiträge seit 2008
vor 16 Jahren

Hallo,

leider funktioniert es nicht, bekomme immer noch die selbe FM.
Auch mit dem aktualisierten "original" MausHook.

Kann es vielleicht an irgendwelchen Einstellungen in meinem VS2005 liegen?

915 Beiträge seit 2006
vor 16 Jahren

Hrm, normal sollte es gehen...

1.) Arbeitest den etwas für Windows CE aus?

2.) Stößst du irgendwo den GC.Collect manuell an?

3.) Schließt du die instanz von deiner MouseHook Klasse oder nutzt halt die Methode <MouseHook>UnHook() ? Den nur auf den Garbage Collector zu warten bringt nichts, das würde Fehelr verursachen, daher enwteder beim schließen der Instanz den GC.Collect manuell anstoßen oder <MouseHook>UnHook() aufrufen.

4.) Du startet dein testprogramm und der Fehler kommt direkt ?

Probiere es grade irgendwie nachzuvollziehen 🙂

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

H
5 Beiträge seit 2008
vor 16 Jahren

Hallo,

es ist eine Windows-Anwendung, mit dem Hook will ich die Koordinaten der Maus auch außerhalb meiner Anwendung auslesen.

zu 1. nein
zu 2. nein
zu 3. Hook() wird über einen Button ausgelöst und nach einigen Anweisungen wird UnHook() aufgerufen

zu 4. ab und zu kommt der Fehler sofort nach Betätigung des Button oder erst nach mehreren Minuten
Es gibt keine Regelmäßigkeit wann der Fehler auftritt.

Leider ist das bei den Hooks ja nicht richtig möglich den Code mit Haltepunkten zu debuggen, sonst könnte ich mehr sagen.

915 Beiträge seit 2006
vor 16 Jahren

Hrm, habe nun etwas rumporbiert aber den Fehler nicht wirklich reproduzierne können, ausser wenn man mehrfach den hook auf einer Instanz startet und aufruft, dann kommt es natürlich zu fehlern. Aber ein einmaliger Aufruf auf einer Instanz mit anschließenden unhook und wieder den hook starten verursachte keine Probleme bei mir...

Evtl. fällt mir später noch etwas dazu ein, derzeit komme ich irgendwie nicht drauf was das sein könnte.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

915 Beiträge seit 2006
vor 15 Jahren

Okay, hat zwar gedauert aber herbivore hatte den richtigen richer 😉
Einfach den Eventhandler neu initalisieren und diesen als Member in der Hook Klasse halten und der Fehler sollte nicht mehr auftauchen.

Wie vernichtet stand Andreas unter den flammenden Augen seiner Kunden.
Ihm war's, als stünde des Schicksals dunkle Wetterwolke über seinem Haupte X(

R
344 Beiträge seit 2006
vor 15 Jahren

Hallo alle,

bei der Suche nach einem Pondon für die Maus von:

        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)

(mit dem ich alle Tastendrucke im Programm bekomme)
habe ich nichts gefunden.

Jetzt bin ich hierauf gestoßen. Umgeändert habe ich es wie folgt:

 
       public delegate int mouseHookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam); // public
        [StructLayout(LayoutKind.Sequential)]
        public struct MSLLHOOKSTRUCT // public
        {
            public POINT pt;
            public int mouseData;
            public int flags;
            public int time;
            public IntPtr dwExtraInfo;
        }
        private int HookProc(int code, int wParam, ref MSLLHOOKSTRUCT lParam)
        {
            if (this.MouseHookAction != null)
            {
                GC.Collect(); // verhindert CallbackOnCollectedDelegate
                GC.WaitForPendingFinalizers(); // verhindert CallbackOnCollectedDelegate
                this.MouseHookAction(code, wParam, ref lParam);
                
            }
            return CallNextHookEx(m_hHook, code, wParam, ref lParam);
        }

Weil ich nicht das von außerhalb brauche habe ich das auf das Fenster begrenzt.

        int b_MouseHookAction(int code, int wParam, ref ScreenShotApplication.Hooks.CMouseHook.MSLLHOOKSTRUCT lParam)
        {
            Rectangle t = new Rectangle(this.Location, this.Size);
            if (t.Contains(lParam.pt))
            {
                switch (wParam)
                {
                    case Const.WM_LBUTTONDOWN:
                        label1.Text = "WM_LBUTTONDOWN";
                        break;
                    case Const.WM_RBUTTONDOWN:
                        label1.Text = "WM_RBUTTONDOWN";
                        break;
                    case Const.WM_MBUTTONDOWN:
                        label1.Text = "WM_MBUTTONDOWN";
                        break;
                    case Const.WM_LBUTTONUP:
                        label1.Text = "WM_LBUTTONUP";
                        break;
                    case Const.WM_RBUTTONUP:
                        label1.Text = "WM_RBUTTONUP";
                        break;
                    case Const.WM_MBUTTONUP:
                        label1.Text = "WM_MBUTTONUP";
                        break;
                    case Const.WM_MOUSEWHEEL:
                        label1.Text = "WM_MOUSEWHEEL";
                        break;
                    case Const.WM_MOUSEMOVE:
                        label1.Text = "WM_MOUSEMOVE";
                        break;
                }
            }
            return 0;
        }

Das klappt auch und ich könnte mir alles bauen, was ich brauche. Die gelegentlichen **CallbackOnCollectedDelegate **sind Dank des Links von Herbivore auch nicht mehr aufgetreten.

Nun endlich die Frage:

Gibt es für meinen Zweck nichts einfacheres?
Möchte einfach nur alle Mausaktionen auf Form-Ebene haben.

Gruß Robert

H
523 Beiträge seit 2008
vor 15 Jahren

Hallo Andreas,

auf der Suche nach Beispielcode wie ich systemweit Tastatureingaben (mir geht es um eine spezielle Tastenkombination) abfrage, bin ich auf deine Hook-Klasse gestoßen. Diese habe ich in mein Projekt eingebunden und das von mir definierte Event wird auch wunderbar aufgerufen.

Jetzt meine Frage als jemand der noch nicht so erfahren in C# ist: Wie frage ich mit Hilfe Deiner Klasse bestimmte Tastenkombinationen (z. B. SHIFT+ALT-N) ab?

Wäre klasse, wenn Du mir helfen könntest!

Viele Grüße

Markus

H
523 Beiträge seit 2008
vor 15 Jahren