3

I'm currently working on a personal project where I'm trying to create an APM calculator like. In order to realize it I need to be able to detect any key pressed and any mouse click. I'm using hooks to catch keyboard events and mouse events but I can't find any way to capture special mouse button click...

When I'm talking about special mouse button, I mean additional buttons like on gaming mice. Example : Logitech G600.

I would like to know if there is any way to detect mouse click from any mouse button or special mouse button ?

I already found how to do it for conventional buttons and even for the navigation buttons, but I can't seem to find anything about additional buttons.

Here is my code where I removed all the keyboard part :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Windows.Input;

namespace MouseHook
{
    static class Program
    {
        private const int WH_MOUSE_LL = 14;
        private static LowLevelMouseProc _procMouse = HookCallbackMouse;
        private static IntPtr _hookIDMouse = IntPtr.Zero;

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205,
            WM_XBUTTONDOWN = 0x020B

            // MISSING ADDITIONAL BUTTONS
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            _hookIDMouse = SetHookMouse(_procMouse);

            Application.Run(new Form1());

            UnhookWindowsHookEx(_hookIDMouse);
        }

        private static IntPtr SetHookMouse(LowLevelMouseProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        private static IntPtr HookCallbackMouse(int nCode, IntPtr wParam, IntPtr lParam)
        {
            // DEAL WITH ANY BUTTON OR ADDITIONAL BUTTON MOUSE

            return CallNextHookEx(_hookIDMouse, nCode, wParam, lParam);
        }
    }
}

Thanks.

  • 1
    Don't most Gaming mice map the "special buttons" to keyboard-shortcuts in their device drivers? – Fildor Mar 12 '18 at 14:46
  • Maybe, I will try to find the keyboard-shorcuts in debugging mode, I will keep you updated. Thanks for the tips. – Billard Philippe Mar 12 '18 at 14:57
  • Why debug mode? Just go to the device driver's settings ... you can verify your findings through debug, but just looking it up should be faster. – Fildor Mar 12 '18 at 15:00
  • If you haven't, see MSDN on [About Mouse Input - XBUTTONS](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645601(v=vs.85).aspx#_win32_XBUTTONs) and [WM_APPCOMMAND](https://msdn.microsoft.com/en-us/library/windows/desktop/ms646275(v=vs.85).aspx). – Jimi Mar 12 '18 at 22:06

1 Answers1

0

If I understood you correct you want to find the WindowsMessages that are being send once you click your additional mouse buttons. In order to detect those I suggest you to create a custom MessageFilter, which implements the interface IMessageFilter.

class MessageFilter : IMessageFilter
{
    int[] msgToIgnore = new int[] {512, 799, 49632, 49446, 1847, 15, 96, 275, 160, 1848, 674 , 513, 675, 514, 280, 161, 274, 673, 515, 516, 517, 518, 519, 520, 521, 522, 163, 164, 167, 168, 169, 165, 166};

    public bool PreFilterMessage(ref Message m)
    {
        bool match = false;

        foreach (var msg in msgToIgnore)
        {
            if (m.Msg == msg)
                match = true;
        }

        if (!match)
        {
            MessageBox.Show(m.Msg.ToString());
            return true;
        }
        else
            return false;
    }
}

A MessageFilter filters every single WindowsMessage that is being send to your application. So I basically added the msgToIgnore array to ignore all the other basic WindowsMessages, like any normal mouse click, move, ... or anything that happens on the application start. (I tested it with an empty plain Form and clicked, doubleClicked, dragged, ... on every part of the Form) This ignores all those WindowsMessages, but once you send another WindowsMessage, so e.g. your click on the additional mouse button, the WindowsMessage will be shown in a MessageBox.

All you have to do to activate this MessageFilter is to add this one to your application, I suggest to do it in the constructor after InitializeComponent().

MessageFilter msgFilter = new MessageFilter();

Application.AddMessageFilter(msgFilter);
L. Guthardt
  • 1,990
  • 6
  • 22
  • 44
  • Tell me if I'm wrong, but I need to catch any click (even normal ones), and I need to catch those even if I'm not focused on the window (i.e. the APM calculator). So your solution wouldn't work in that case right ? That's my bad actually I should have be more precise about that. But thanks for the sample it can be helpfull later ! – Billard Philippe Mar 13 '18 at 15:57