Global Mouse Event Handler

Global mouse event handler



return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);

This code will fail when you run it on .NET 4 on a Windows version earlier than Windows 8. The CLR no longer simulates unmanaged module handles for managed assemblies. You can't detect this failure in your code because it is missing the required error checking. Both on GetModuleHandle and SetWindowsHookEx. Never skip error checking when you pinvoke, the winapi doesn't throw exceptions. Check if they return IntPtr.Zero and simply throw a Win32Exception when they do.

The fix is simple, SetWindowsHookEx() requires a valid module handle but doesn't actually use it when you set a low-level mouse hook. So any handle will do, you can pass the handle for user32.dll, always loaded in a .NET application. Fix:

IntPtr hook = SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle("user32"), 0);
if (hook == IntPtr.Zero)
{
throw new System.ComponentModel.Win32Exception();
}
return hook;

Is it possible to catch global mouse events

The code you posted looks like it should work. If there's a problem, it might be in the code you omitted. Anyway, here's a small example application showing the behavior you want. The underlying logic of this example is the same as the code you posted, except this example uses dynamic binding instead of event tables.

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);

protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnLeftDown(wxMouseEvent&);

void CleanUp();

private:
wxTextCtrl* m_textCtrl;
};

class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};

MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);

wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();

button->Bind(wxEVT_BUTTON,&MyFrame::OnButtonClick,this);
}

void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");

Bind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}
}

void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
Unbind(wxEVT_LEFT_DOWN, &MyFrame::OnLeftDown, this);
Unbind(wxEVT_MOUSE_CAPTURE_LOST, &MyFrame::OnMouseCapLost, this);
}

void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}

void MyFrame::OnLeftDown(wxMouseEvent& event)
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}

bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}

wxIMPLEMENT_APP(MyApp);

I hope this helps.

edit: Here's a version using event tables as well:

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
#pragma hdrstop
#endif

// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#define BUTTON_ID 101

class MyFrame : public wxFrame
{
public:
MyFrame(wxWindow* parent);

protected:
void OnButtonClick(wxCommandEvent& event);
void OnMouseCapLost(wxMouseCaptureLostEvent& event);
void OnMouseEvent(wxMouseEvent&);

void CleanUp();

private:
wxTextCtrl* m_textCtrl;

wxDECLARE_EVENT_TABLE();
};

class MyApp : public wxApp
{
public:
virtual bool OnInit() wxOVERRIDE;
};

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(BUTTON_ID, MyFrame::OnButtonClick)
EVT_MOUSE_EVENTS(MyFrame::OnMouseEvent)
EVT_MOUSE_CAPTURE_LOST(MyFrame::OnMouseCapLost)
wxEND_EVENT_TABLE()

MyFrame::MyFrame(wxWindow* parent)
:wxFrame(parent, wxID_ANY, "Demo", wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
wxPanel* panel = new wxPanel(this, wxID_ANY );
wxButton* button = new wxButton(panel, BUTTON_ID, "Click Me");
m_textCtrl = new wxTextCtrl(panel, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_DONTWRAP|wxTE_MULTILINE|wxTE_READONLY);

wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(button, 0, wxALL, 5);
bSizer->Add(m_textCtrl, 1, wxALL|wxEXPAND, 5);
panel->SetSizer(bSizer);
Layout();
}

void MyFrame::OnButtonClick(wxCommandEvent& event)
{
if ( !HasCapture() )
{
CaptureMouse();
m_textCtrl->AppendText("Mouse captured.\n");
}
}

void MyFrame::CleanUp()
{
if ( HasCapture() )
ReleaseMouse();
}

void MyFrame::OnMouseCapLost(wxMouseCaptureLostEvent& event)
{
m_textCtrl->AppendText("Mouse cap lost.\n");
CleanUp();
}

void MyFrame::OnMouseEvent(wxMouseEvent& event)
{
if( HasCapture() && event.LeftIsDown() )
{
m_textCtrl->AppendText("Click recorded.\n");
CleanUp();
}

}

bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}

wxIMPLEMENT_APP(MyApp);

How to register to global mouse events (move, click) with VB.NET?

You can use my library called InputHelper. It was built to help .NET developers both capture and simulate mouse and keyboard input in a simple, and correct, manner.

After seeing and answering way too many Stack Overflow questions where the user either used a deprecated API or where their P/Invoke signatures where way off, I decided to create this as a more .NET-friendly way of managing input as well as an attempt to fight the only partially correct code that users gathered from many different places on the internet.

InputHelper is built around the native Windows API (WinAPI) and utilizes functions such as SendInput(), SendMessage()/PostMessage() and SetWindowsHookEx() in order to get its job done.

Its Wiki is sadly still far from complete as I've only documented the mouse and keyboard hooks, however the library includes XML documentation files containing comments explaining every function in it, which are automatically displayed by IntelliSense as you type your code. So for a mini-documentation and overview of the library you can always open Visual Studio's Object Browser (press F12 on any type or namespace, or go to View > Object Browser) and locate it there.

InputHelper currently consists of four caregories:

  • InputHelper.Hooks: Classes for capturing system-wide mouse and keyboard input. This can be used to make hot keys, for instance.

  • InputHelper.Keyboard: Methods for simulating physical ("real") keyboard input/key strokes.

  • InputHelper.Mouse: Methods for simulating physical mouse input.

  • InputHelper.WindowMessages: Methods for simulating mouse and keyboard input at a more virtual level. This utilizes window messages (Send-/PostMessage) and can be used to target specific windows.

The one you're looking for is InputHelper.Hooks, specifically, InputHelper.Hooks.MouseHook.

To start with, download the compiled version of the library on the Releases page and add it as a reference to your project. For convenience import the InputHelperLib namespace in the file(s) that you'll be using it:

Imports InputHelperLib

Now, the easiest way to create a mouse hook is to just instantiate an instance of the MouseHook class at class-level:

Dim MouseHook As New InputHelper.Hooks.MouseHook

This will listen for mouse events until the variable goes out of scope or until you call Dispose() on it.

If you don't want to start the hook right away, just declare the variable and initialize it whenever you want.

Dim MouseHook As InputHelper.Hooks.MouseHook

'Start the hook at the click of a button.
Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
If MouseHook Is Nothing Then
MouseHook = New InputHelper.Hooks.MouseHook
End If
End Sub

The hook includes four events:

  • MouseDown - Occurs when a mouse button is pressed or held down.

  • MouseMove - Occurs when the mouse moves.

  • MouseUp - Occurs when a mouse button is released.

  • MouseWheel - Occurs when the mouse wheel is scrolled.

You can subscribe to the events using the AddHandler statement.

Dim MouseHook As InputHelper.Hooks.MouseHook

'Start the hook at the click of a button.
Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
If MouseHook Is Nothing Then
MouseHook = New InputHelper.Hooks.MouseHook

AddHandler MouseHook.MouseDown, AddressOf MouseHook_MouseDown
AddHandler MouseHook.MouseMove, AddressOf MouseHook_MouseMove
AddHandler MouseHook.MouseWheel, AddressOf MouseHook_MouseWheel
End If
End Sub

Example MouseDown event handler:

Private Sub MouseHook_MouseDown(sender As Object, e As InputHelperLib.InputHelper.Hooks.MouseHookEventArgs)
If e.Button = System.Windows.Forms.MouseButtons.Left AndAlso e.DoubleClick = True Then
MessageBox.Show("Left mouse button was double-clicked.")
End If
End Sub

Example MouseMove event handler:

Private Sub MouseHook_MouseMove(sender As Object, e As InputHelperLib.InputHelper.Hooks.MouseHookEventArgs)
LogTextBox.AppendText("Mouse moved to (X: " & e.Location.X & ", Y: " & e.Location.Y & ")")
End Sub

Example MouseWheel event handler:

Private Sub MouseHook_MouseWheel(sender As Object, e As InputHelperLib.InputHelper.Hooks.MouseHookEventArgs)
If e.Delta > 0 AndAlso e.ScrollDirection = InputHelper.Hooks.ScrollDirection.Vertical Then
MessageBox.Show("Mouse scrolled up")
End If
End Sub

And now you have a functioning, global mouse hook!

For more details about the hook and its event args, please have a look at the project's wiki.

Bonus question: Does your solution work when the mouse move/click "event" has been triggered by another software and not by the human who is actually operating the mouse.

Yes and no. It depends on how the other software triggered the event. If it uses SendInput or alike to simulate actual, physical input then yes. If it uses window messages to target specific windows then no.

Swift on OS X. How to handle global mouse events?

You are correct about using addGlobalMonitorForEventsMatchingMask:handler:

A simple example might look something like this:

AppDelegate.swift

class AppDelegate: NSObject, NSApplicationDelegate {

@IBOutlet weak var window: NSWindow!
@IBOutlet var textLabel : NSTextField!
var eventHandler : GlobalEventMonitor?
var gecount : Int = 0

func applicationDidFinishLaunching(aNotification: NSNotification) {

eventHandler = GlobalEventMonitor(mask: .LeftMouseDownMask, handler: { (mouseEvent: NSEvent?) in
self.gecount += 1
self.textLabel.stringValue = "global event monitor: \(self.gecount)"
})
eventHandler?.start()
}
}

GlobalEventMonitor.swift

public class GlobalEventMonitor {

private var monitor: AnyObject?
private let mask: NSEventMask
private let handler: NSEvent? -> ()

public init(mask: NSEventMask, handler: NSEvent? -> ()) {
self.mask = mask
self.handler = handler
}

deinit {
stop()
}

public func start() {
monitor = NSEvent.addGlobalMonitorForEventsMatchingMask(mask, handler: handler)
}

public func stop() {
if monitor != nil {
NSEvent.removeMonitor(monitor!)
monitor = nil
}
}
}

Events are delivered asynchronously to your app and you can only observe the event; you cannot modify or otherwise prevent the event from being delivered to its original target application. Key-related events may only be monitored if accessibility is enabled or if your application is trusted for accessibility access (see AXIsProcessTrusted).

Note that your handler will not be called for events that are sent to
your own application.

In order to capture events within your app your can either use the addLocalMonitorForEventsMatchingMask:handler: or the NSClickGestureRecognizer object.

If you wanted to combine the global event monitor with the gesture recognizer object it's simply a matter of implementing both objects into your class.

QT global mouse listener

It's actually very simple. I did not find ANY examples or anything.

I then found a video on YouTube which shows exactly what I'm searching for (For the keyboard but the mouse is basically the same).

So if ever someone needs this here you go:

#include <Windows.h>
#pragma comment(lib, "user32.lib")
HHOOK hHook = NULL;
using namespace std;

LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
switch( wParam )
{
case WM_LBUTTONDOWN: qDebug() << "Left click"; // Left click
}
return CallNextHookEx(hHook, nCode, wParam, lParam);
}

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
hHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, 0);
if (hHook == NULL) {
qDebug() << "Hook failed";
}
ui->setupUi(this);
}

The following codes can be used inside the switch to detect which event was received:

  • WM_MOUSEMOVE = 0x200
  • WM_LBUTTONDOWN = 0x201
  • WM_LBUTTONUP = 0x202
  • WM_LBUTTONDBLCLK = 0x203
  • WM_RBUTTONDOWN = 0x204
  • WM_RBUTTONUP = 0x205
  • WM_RBUTTONDBLCLK = 0x206
  • WM_MBUTTONDOWN = 0x207
  • WM_MBUTTONUP = 0x208
  • WM_MBUTTONDBLCLK = 0x209
  • WM_MOUSEWHEEL = 0x20A
  • WM_XBUTTONDOWN = 0x20B
  • WM_XBUTTONUP = 0x20C
  • WM_XBUTTONDBLCLK = 0x20D
  • WM_MOUSEHWHEEL = 0x20E

Is there any way to global hook the mouse actions like i'm hooking the keyboard keys?

Yes, i used the following code in a project of mine, it allows direct access to most windows mouse events:

using System;
using System.Runtime.InteropServices;

/// <summary>
/// The CallWndProc hook procedure is an application-defined or library-defined
/// callback function used with the SetWindowsHookEx function. The HOOKPROC type
/// defines a pointer to this callback function. CallWndProc is a placeholder for
/// the application-defined or library-defined function name.
/// </summary>
/// <param name="nCode">
/// Specifies whether the hook procedure must process the message.
/// </param>
/// <param name="wParam">
/// Specifies whether the message was sent by the current thread.
/// </param>
/// <param name="lParam">
/// Pointer to a CWPSTRUCT structure that contains details about the message.
/// </param>
/// <returns>
/// If nCode is less than zero, the hook procedure must return the value returned
/// by CallNextHookEx. If nCode is greater than or equal to zero, it is highly
/// recommended that you call CallNextHookEx and return the value it returns;
/// otherwise, other applications that have installed WH_CALLWNDPROC hooks will
/// not receive hook notifications and may behave incorrectly as a result. If the
/// hook procedure does not call CallNextHookEx, the return value should be zero.
/// </returns>
internal delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);

internal class NativeMethods
{
/// <summary>
/// The SetWindowsHookEx function installs an application-defined hook
/// procedure into a hook chain. You would install a hook procedure to monitor
/// the system for certain types of events. These events are associated either
/// with a specific thread or with all threads in the same desktop as the
/// calling thread.
/// </summary>
/// <param name="hookType">
/// Specifies the type of hook procedure to be installed
/// </param>
/// <param name="callback">Pointer to the hook procedure.</param>
/// <param name="hMod">
/// Handle to the DLL containing the hook procedure pointed to by the lpfn
/// parameter. The hMod parameter must be set to NULL if the dwThreadId
/// parameter specifies a thread created by the current process and if the
/// hook procedure is within the code associated with the current process.
/// </param>
/// <param name="dwThreadId">
/// Specifies the identifier of the thread with which the hook procedure is
/// to be associated.
/// </param>
/// <returns>
/// If the function succeeds, the return value is the handle to the hook
/// procedure. If the function fails, the return value is 0.
/// </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowsHookEx(HookType hookType,
HookProc callback, IntPtr hMod, uint dwThreadId);

/// <summary>
/// The UnhookWindowsHookEx function removes a hook procedure installed in
/// a hook chain by the SetWindowsHookEx function.
/// </summary>
/// <param name="hhk">Handle to the hook to be removed.</param>
/// <returns>
/// If the function succeeds, the return value is true.
/// If the function fails, the return value is false.
/// </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);

/// <summary>
/// The CallNextHookEx function passes the hook information to the next hook
/// procedure in the current hook chain. A hook procedure can call this
/// function either before or after processing the hook information.
/// </summary>
/// <param name="idHook">Handle to the current hook.</param>
/// <param name="nCode">
/// Specifies the hook code passed to the current hook procedure.
/// </param>
/// <param name="wParam">
/// Specifies the wParam value passed to the current hook procedure.
/// </param>
/// <param name="lParam">
/// Specifies the lParam value passed to the current hook procedure.
/// </param>
/// <returns>
/// This value is returned by the next hook procedure in the chain.
/// </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
}

internal static class HookCodes
{
public const int HC_ACTION = 0;
public const int HC_GETNEXT = 1;
public const int HC_SKIP = 2;
public const int HC_NOREMOVE = 3;
public const int HC_NOREM = HC_NOREMOVE;
public const int HC_SYSMODALON = 4;
public const int HC_SYSMODALOFF = 5;
}

internal enum HookType
{
WH_KEYBOARD = 2,
WH_MOUSE = 7,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}

[StructLayout(LayoutKind.Sequential)]
internal class POINT
{
public int x;
public int y;
}

/// <summary>
/// The MSLLHOOKSTRUCT structure contains information about a low-level keyboard
/// input event.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct MOUSEHOOKSTRUCT
{
public POINT pt; // The x and y coordinates in screen coordinates
public int hwnd; // Handle to the window that'll receive the mouse message
public int wHitTestCode;
public int dwExtraInfo;
}

/// <summary>
/// The MOUSEHOOKSTRUCT structure contains information about a mouse event passed
/// to a WH_MOUSE hook procedure, MouseProc.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct MSLLHOOKSTRUCT
{
public POINT pt; // The x and y coordinates in screen coordinates.
public int mouseData; // The mouse wheel and button info.
public int flags;
public int time; // Specifies the time stamp for this message.
public IntPtr dwExtraInfo;
}

internal enum MouseMessage
{
WM_MOUSEMOVE = 0x0200,
WM_LBUTTONDOWN = 0x0201,
WM_LBUTTONUP = 0x0202,
WM_LBUTTONDBLCLK = 0x0203,
WM_RBUTTONDOWN = 0x0204,
WM_RBUTTONUP = 0x0205,
WM_RBUTTONDBLCLK = 0x0206,
WM_MBUTTONDOWN = 0x0207,
WM_MBUTTONUP = 0x0208,
WM_MBUTTONDBLCLK = 0x0209,

WM_MOUSEWHEEL = 0x020A,
WM_MOUSEHWHEEL = 0x020E,

WM_NCMOUSEMOVE = 0x00A0,
WM_NCLBUTTONDOWN = 0x00A1,
WM_NCLBUTTONUP = 0x00A2,
WM_NCLBUTTONDBLCLK = 0x00A3,
WM_NCRBUTTONDOWN = 0x00A4,
WM_NCRBUTTONUP = 0x00A5,
WM_NCRBUTTONDBLCLK = 0x00A6,
WM_NCMBUTTONDOWN = 0x00A7,
WM_NCMBUTTONUP = 0x00A8,
WM_NCMBUTTONDBLCLK = 0x00A9
}

/// <summary>
/// The structure contains information about a low-level keyboard input event.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct KBDLLHOOKSTRUCT
{
public int vkCode; // Specifies a virtual-key code
public int scanCode; // Specifies a hardware scan code for the key
public int flags;
public int time; // Specifies the time stamp for this message
public int dwExtraInfo;
}

internal enum KeyboardMessage
{
WM_KEYDOWN = 0x0100,
WM_KEYUP = 0x0101,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105
}

To use it you have to register the mouse hook. LowLevelMouseProc is the callback. This method is executed every time a new mouse event occures.

private void SetUpHook()
{
Logger.Debug("Setting up global mouse hook");

// Create an instance of HookProc.
_globalLlMouseHookCallback = LowLevelMouseProc;

_hGlobalLlMouseHook = NativeMethods.SetWindowsHookEx(
HookType.WH_MOUSE_LL,
_globalLlMouseHookCallback,
Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]),
0);

if (_hGlobalLlMouseHook == IntPtr.Zero)
{
Logger.Fatal("Unable to set global mouse hook");
throw new Win32Exception("Unable to set MouseHook");
}
}

To clear mouse hook:

private void ClearHook()
{
Logger.Debug("Deleting global mouse hook");

if (_hGlobalLlMouseHook != IntPtr.Zero)
{
// Unhook the low-level mouse hook
if (!NativeMethods.UnhookWindowsHookEx(_hGlobalLlMouseHook))
throw new Win32Exception("Unable to clear MouseHoo;");

_hGlobalLlMouseHook = IntPtr.Zero;
}
}

And last but not least an example of the LowLevelMouseProc, the callback you can use to intercept mouse events:

public int LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
// Get the mouse WM from the wParam parameter
var wmMouse = (MouseMessage) wParam;
if (wmMouse == MouseMessage.WM_LBUTTONDOWN && LeftButtonState == ButtonState.Released)
{
Logger.Debug("Left Mouse down");
}
if (wmMouse == MouseMessage.WM_LBUTTONUP && LeftButtonState == ButtonState.Down)
{
Logger.Debug("Left Mouse up");
}

if (wmMouse == MouseMessage.WM_RBUTTONDOWN && RightButtonState == ButtonState.Released)
{
Logger.Debug("Right Mouse down");
}
if (wmMouse == MouseMessage.WM_RBUTTONUP && RightButtonState == ButtonState.Down)
{
Logger.Debug("Right Mouse up");
}
}

// Pass the hook information to the next hook procedure in chain
return NativeMethods.CallNextHookEx(_hGlobalLlMouseHook, nCode, wParam, lParam);
}

As with all direct windows calls, the code gets unnecessarily long. But the only thing you have to do, is to call SetUpHook and provide your own version of LowLevelMouseProc.

EDIT: There are shorter versions to do this. But this method allows you to catch global mouse events. Not just events issued to your window. All mouse events, system wide will be piped into LowLevelMouseProc



Related Topics



Leave a reply



Submit