I'm working on a DLL for VBA to comsume using C#. I am trying to use Key and Mouse Hooks on Excel, but it's not working as expected. In my application, I need to track interactions with the Shift key and mouse wheel while the user is inside Excel. However, the keyboard hook process doesn't seem to occur when Excel is active. It works seamlessly when another application or the desktop is active.
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Linq;
namespace MouseKeyEvents
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)] // Important
[ComSourceInterfaces(typeof(IMouseKeyHookEvents))] // Specify the event interface
[Guid("2AC28F2E-1674-4614-907B-222F6CA2E970")]
public class MouseKeyHookHelper : IMouseKeyHookHelper
{
// Define a log file path
private const string logFilePath = "C:\\Users\\bc\\Desktop\\logfile.txt";
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProc hookCallback;
private static HookProc hookCallbackKey;
private static IntPtr hookHandle = IntPtr.Zero;
private static IntPtr hookHandleKey = IntPtr.Zero;
// Event handlers for mouse events
public delegate void MouseKeyMoveDelegate();
public event MouseKeyMoveDelegate MouseWheelShift;
// Add a field to track the Shift key state
private static bool shiftKeyPressed = false;
// Set up the hook
public void SetHook()
{
try
{
hookCallback = HookCallback;
hookCallbackKey = HookCallbackKey;
File.AppendAllText(logFilePath, "Hooke hooked-1" + Environment.NewLine);
if (excelProcess != null)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
hookHandleKey = SetWindowsHookEx(WH_KEYBOARD_LL, hookCallbackKey, GetModuleHandle(curModule.ModuleName), 0);
hookHandle = SetWindowsHookEx(WH_MOUSE_LL, hookCallback, GetModuleHandle(null), 0);
}
if (hookHandle == IntPtr.Zero || hookHandleKey == IntPtr.Zero)
{
int errorCode = Marshal.GetLastWin32Error();
// Log or display the error code and message.
File.AppendAllText(logFilePath, $"Failed to set mouse hook. Error code: {errorCode}" + Environment.NewLine);
}
else
{
File.AppendAllText(logFilePath, "Hooke hooked-2" + Environment.NewLine);
}
}
else
{
// Handle the case when Excel is not running or not found
// You can show an error message or take appropriate action here
}
File.AppendAllText(logFilePath, "Hooke hooked-3" + Environment.NewLine);
}
catch
{
File.AppendAllText(logFilePath, "hooking error." + Environment.NewLine);
}
}
// Unhook the hook
public void Unhook()
{
UnhookWindowsHookEx(hookHandle);
UnhookWindowsHookEx(hookHandleKey); // Unhook the keyboard hook
}
public void OnShiftAndMouseMove()
{
MouseWheelShift?.Invoke();
}
// Hook callback function for mouse events
private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
// Log messages to a file
//File.AppendAllText(logFilePath, "HookCallback called." + Environment.NewLine);
if (wParam == (IntPtr)WM_MOUSEMOVE)
{
// Handle mouse move event
// ...
}
else if (wParam == (IntPtr)WM_LBUTTONDOWN)
{
// Handle mouse button down event
// ...
}
else if (wParam == (IntPtr)WM_LBUTTONUP)
{
// Handle mouse button up event
// ...
}
else if (wParam == (IntPtr)WM_MOUSEWHEEL && shiftKeyPressed)
{
File.AppendAllText(logFilePath, "HookCallback called." + Environment.NewLine);
// Mouse wheel was scrolled with Shift key pressed
OnShiftAndMouseMove();
}
else if (wParam == (IntPtr)WM_MOUSEWHEEL)
{
File.AppendAllText(logFilePath, "wheel called." + Environment.NewLine);
}
}
return CallNextHookEx(hookHandle, nCode, wParam, lParam);
}
// Hook callback function for keyboard events
private static IntPtr HookCallbackKey(int nCode, IntPtr wParam, IntPtr lParam)
{
File.AppendAllText(logFilePath, "HookCallbackKey called." + Environment.NewLine);
if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_KEYUP))
{
KBDLLHOOKSTRUCT kbdHookStruct = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
int vkCode = kbdHookStruct.vkCode;
if (vkCode == VK_LSHIFT || vkCode == VK_RSHIFT)
{
shiftKeyPressed = (wParam == (IntPtr)WM_KEYDOWN);
if (shiftKeyPressed)
{
// Raise the MouseWheelShift event when Shift key is pressed
File.AppendAllText(logFilePath, "Shift pressed." + Environment.NewLine);
//OnShiftAndMouseMove();
}
}
}
return CallNextHookEx(hookHandleKey, nCode, wParam, lParam);
}
// Constants for key detection
private const int WH_KEYBOARD_LL = 13;
private const int WH_KEYBOARD = 2;
private const int WM_KEYDOWN = 0x0100;
private const int WM_KEYUP = 0x0101;
private const int VK_SHIFT = 0x10;
private const int VK_LSHIFT = 0xA0;
private const int VK_RSHIFT = 0xA1;
// Constants for hook types
private const int WH_MOUSE_LL = 14;
private const int WM_MOUSEMOVE = 0x0200;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc 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);
}
[Guid("11BED025-5F24-44C8-92E5-7DB986C7739A")]
[ComVisible(true)]
public interface IMouseKeyHookHelper
{
void SetHook();
void Unhook();
}
// Define the event interface
[Guid("E3F7BCFA-7DC6-4DAC-8BC5-322FFB66C262")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IMouseKeyHookEvents
{
[DispId(1)]
void MouseWheelShift();
}
[StructLayout(LayoutKind.Sequential)]
struct KBDLLHOOKSTRUCT
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public IntPtr dwExtraInfo;
}
}```