193 lines
6.0 KiB
C#

#define NO_UNSAFE
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace DevComponents.DotNetBar
{
/// <summary>
/// Summary description for Hook.
/// </summary>
internal class Hook : IDisposable
{
private const int WH_MOUSE = 7;
private const int HC_ACTION = 0;
private const int WM_MOUSEMOVE = 0x0200;
private const int WH_KEYBOARD = 2;
private const int KF_EXTENDED=0x0100;
private const int KF_DLGMODE=0x0800;
private const int KF_MENUMODE=0x1000;
private const int KF_ALTDOWN=0x2000;
private const int KF_REPEAT=0x4000;
private const int KF_UP=0x8000;
private IntPtr m_hMouseHook=IntPtr.Zero;
private IntPtr m_hKeyboardHook=IntPtr.Zero;
private MouseProc m_mouseHook;
private KeyboardProc m_keyboardHook;
private IMessageHandlerClient m_Client=null;
public Hook(IMessageHandlerClient client)
{
m_Client=client;
m_mouseHook = new MouseProc(OnMouseHook);
m_keyboardHook=new KeyboardProc(OnKeyboardHook);
m_hMouseHook = SetWindowsHookEx(WH_MOUSE, m_mouseHook, IntPtr.Zero, GetCurrentThreadId());
m_hKeyboardHook=SetWindowsHookExKeyboard(WH_KEYBOARD,m_keyboardHook,IntPtr.Zero,GetCurrentThreadId());
if (m_hMouseHook == IntPtr.Zero || m_hKeyboardHook==IntPtr.Zero)
{
throw new Win32Exception();
}
}
public void Dispose()
{
if (m_hMouseHook != IntPtr.Zero)
{
IntPtr h = m_hMouseHook;
m_hMouseHook = IntPtr.Zero;
UnhookWindowsHookEx(h);
}
if (m_hKeyboardHook != IntPtr.Zero)
{
IntPtr h = m_hKeyboardHook;
m_hKeyboardHook = IntPtr.Zero;
UnhookWindowsHookEx(h);
}
}
// A note on using unsafe code:
//
// When taking a native pointer and converting it into a structure pointer,
// if you only need to examine the contents of the structure then it is very
// wasteful to use Marshal.PtrToStructure. Structures are very efficient
// because they are stack allocated, but by converting them to an object
// to be returned, Marshal causes a boxing operation to happen which
// allocates an object on the heap. This normally isn't a big deal
// but for window messages that occur with a high frequency you can
// see a spike of many hundreds of objects being allocated. Using
// unsafe blocks are great when you are writing an EXE that runs on a
// user's local machine. If you are writing a library that can be downloaded
// over the internet you should not use them, because they require elevated
// security permissions. That's not a big deal for this sample because
// we already need unmanaged code permission to create the hook.
#if NO_UNSAFE
private IntPtr OnMouseHook(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode == HC_ACTION)
{
int msg=WinApi.ToInt(wParam);
if(msg == NativeFunctions.WM_LBUTTONDOWN || msg==NativeFunctions.WM_NCLBUTTONDOWN ||
msg==NativeFunctions.WM_RBUTTONDOWN || msg==NativeFunctions.WM_MBUTTONDOWN ||
msg==NativeFunctions.WM_NCMBUTTONDOWN || msg==NativeFunctions.WM_NCRBUTTONDOWN)
{
MOUSEHOOKSTRUCT mhs = (MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MOUSEHOOKSTRUCT));
m_Client.OnMouseDown(mhs.hwnd,IntPtr.Zero,lParam);
}
}
return CallNextHookEx(m_hMouseHook, nCode, wParam, lParam);
}
private IntPtr OnKeyboardHook(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode == HC_ACTION)
{
int ilParam=WinApi.ToInt(lParam);
int iwParam=WinApi.ToInt(wParam);
int iCode=ilParam>>16;
if((iCode & KF_ALTDOWN)!=0)
{
if((iCode & KF_UP)!=0)
{
if(m_Client.OnSysKeyUp(IntPtr.Zero,wParam,lParam))
return (IntPtr)1;
}
else //if((ilParam & KF_REPEAT)==0)
{
if(m_Client.OnSysKeyDown(IntPtr.Zero,wParam,lParam))
return (IntPtr)1;
}
}
else
{
if((iCode & KF_UP)==0)
{
if(m_Client.OnKeyDown(IntPtr.Zero,wParam,lParam))
return (IntPtr)1;
}
else if(iwParam==18 || iwParam==121)
{
if(m_Client.OnSysKeyUp(IntPtr.Zero,wParam,lParam))
return (IntPtr)1;
}
}
}
return CallNextHookEx(m_hKeyboardHook, nCode, wParam, lParam);
}
#else
private unsafe IntPtr OnMouseHook(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode == HC_ACTION && MouseEvent != null)
{
if (wParam == (IntPtr)WM_MOUSEMOVE)
{
MOUSEHOOKSTRUCT* pmhs = (MOUSEHOOKSTRUCT*)lParam;
MouseEventArgs e = new MouseEventArgs(MouseButtons.None, 0, pmhs->pt.x, pmhs->pt.y, 0);
MouseEvent(this, e);
}
}
return CallNextHookEx(_hHook, nCode, wParam, lParam);
}
#endif
private delegate IntPtr MouseProc(int nCode, IntPtr wParam, IntPtr lParam);
private delegate IntPtr KeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr SetWindowsHookEx(int hookid, MouseProc pfnhook, IntPtr hinst, int threadid);
[DllImport("user32.dll", CharSet=CharSet.Auto, SetLastError=true, EntryPoint="SetWindowsHookEx")]
private static extern IntPtr SetWindowsHookExKeyboard(int hookid, KeyboardProc pfnhook, IntPtr hinst, int threadid);
[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
private static extern bool UnhookWindowsHookEx(IntPtr hhook);
[DllImport("user32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
private static extern IntPtr CallNextHookEx(IntPtr hhook, int code, IntPtr wparam, IntPtr lparam);
[DllImport("kernel32.dll", ExactSpelling=true, CharSet=CharSet.Auto)]
private static extern int GetCurrentThreadId();
private struct POINT
{
public POINT(int x, int y)
{
this.x=x;
this.y=y;
}
public int x;
public int y;
}
private struct MOUSEHOOKSTRUCT
{
public MOUSEHOOKSTRUCT(POINT pt, IntPtr hwnd, ushort wHitTestCode, IntPtr dwExtraInfo)
{
this.pt=pt;
this.hwnd=hwnd;
this.wHitTestCode=wHitTestCode;
this.dwExtraInfo=dwExtraInfo;
}
public POINT pt;
public IntPtr hwnd;
public ushort wHitTestCode;
public IntPtr dwExtraInfo;
}
}
}