Tuesday, July 10, 2012

Hooking Mouse and Keyboard, Marshaling and few user32 library methods call in C#



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;

namespace WindowsFormsApplication1
{

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL = 0x0,
        SMTO_BLOCK = 0x1,
        SMTO_ABORTIFHUNG = 0x2,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x8
    }
    public partial class Form1 : Form
    {

        public delegate int HookProc(int code, IntPtr wparam, IntPtr lparam);
        static int hHook = 0;
        static int mHook = 0;
        IntPtr windowHandle;
        uint processHandle;

        int WH_MOUSE = 7;
        int WH_KEYBOARD = 2;
        static int HC_ACTION = 0;
        static int VK_CONTROL = 11;

        //This is the Import for the SetWindowsHookEx function.
        //Use this function to install a thread-specific hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        //This is the Import for the UnhookWindowsHookEx function.
        //Call this function to uninstall the hook.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetForegroundWindow();


        [DllImport("user32.dll")]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);

        //This is the Import for the CallNextHookEx function.
        //Use this function to pass the hook information to the next hook procedure in chain.
        [DllImport("user32.dll", CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern short GetAsyncKeyState(int vkey);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int GetLastError();


        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr FindWindow(string classname, string windowName);

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr SendMessage(IntPtr hWnd,uint Msg,IntPtr wParam,IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr SendMessageTimeout(
            IntPtr hWnd,
            uint Msg,
            UIntPtr wParam,
            IntPtr lParam,
            SendMessageTimeoutFlags fuFlags,
            uint uTimeout,
            out UIntPtr lpdwResult);

        HookProc MouseHookProcDel;
        HookProc KeyBoardHookProcDel;

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

        //Declare the wrapper managed MouseHookStruct class.
        [StructLayout(LayoutKind.Sequential)]
        public class MouseHookStruct
        {
            public POINT pt;
            public int hwnd;
            public int wHitTestCode;
            public int dwExtraInfo;
        }

        //to marshal to lparam of keyboard hook into a manage type
        [StructLayout(LayoutKind.Sequential)]
        public class KBDLLHOOKSTRUCT
        {
            public uint vkCode;
            public uint scanCode;
            public uint flags;
            public uint time;
            public uint dwExtraInfo;
        }

        public Form1()
        {
            InitializeComponent();
        }


        public static int MouseHookProcedure(int code, IntPtr wparam, IntPtr lparam)
        {
            MouseHookStruct mouse = (MouseHookStruct)Marshal.PtrToStructure(lparam, typeof(MouseHookStruct));
       
            if(code == 0)
            {
                //Create a string variable that shows the current mouse coordinates.
                String strCaption = "x = " +
                        mouse.pt.x.ToString("d") +
                            "  y = " +
                mouse.pt.y.ToString("d");
                //You must get the active form because it is a static function.
                Form tempForm = Form.ActiveForm;

                //Set the caption of the form.
                tempForm.Text = strCaption;
                return CallNextHookEx(hHook, code, wparam, lparam);
            }
            else
                //calls the next hook with same message and parameters
                return CallNextHookEx(hHook, code, wparam, lparam);
        }

        public static int KeyBoardProcedure(int code, IntPtr wparam, IntPtr lparam)
        {
           //KBDLLHOOKSTRUCT's structure is copied as it is from msdn to support marshaling
            KBDLLHOOKSTRUCT pkbhs = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lparam, typeof(KBDLLHOOKSTRUCT));
            int bControlKeyDown = 0 ;
            switch (code)
            {
                //0 refers to HC_ACTION
                case 0:
                    {
                        // Check to see if the CTRL key is pressed
                        bControlKeyDown = GetAsyncKeyState(VK_CONTROL) >> ((sizeof(short) * 8) - 1);

                       //write to logic to handle ctrl+esc or alt+tab or any other key here
                       //then return 1               
                        break;
                    }

                default:
                    return CallNextHookEx(mHook, code, wparam, lparam);
                   
            }
            return CallNextHookEx(mHook, code, wparam, lparam);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (hHook == 0 && mHook==0)
            {
                // Create an instance of HookProc.
                MouseHookProcDel = new HookProc(Form1.MouseHookProcedure);
                KeyBoardHookProcDel = new HookProc(Form1.KeyBoardProcedure);

                hHook = SetWindowsHookEx(WH_MOUSE,
                            MouseHookProcDel,
                            (IntPtr)0,
                            AppDomain.GetCurrentThreadId());

                mHook = SetWindowsHookEx(WH_KEYBOARD,
                            KeyBoardHookProcDel,
                            (IntPtr)0,
                            AppDomain.GetCurrentThreadId());
                //If the SetWindowsHookEx function fails.
                if (hHook == 0)
                {

                    MessageBox.Show("SetWindowsHookEx Failed" + ":" + GetLastError().ToString());
                    return;
                }
                button1.Text = "UnHook Windows Hook";
            }
            else
            {
                bool ret = UnhookWindowsHookEx(hHook);
                ret = UnhookWindowsHookEx(mHook);
                //If the UnhookWindowsHookEx function fails.
                if (ret == false)
                {
                    MessageBox.Show("UnhookWindowsHookEx Failed");
                    return;
                }
                hHook = 0;
                mHook = 0;
                button1.Text = "Hook Mouse and Keyboard";
                this.Text = "Mouse Hook";
            }
        }

        private void GetActiveWindowTitle()
        {          
            const int nChars = 256;
            IntPtr handle = IntPtr.Zero;
            StringBuilder Buff = new StringBuilder(nChars);
            handle = GetForegroundWindow();

            if (GetWindowText(handle, Buff, nChars) > 0)
            {
                MessageBox.Show(Buff.ToString());

            }
            return;
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            //on timer elapsed, we get the active window's title
            // uncomment to use it
            //GetActiveWindowTitle();
        }

        private void CheckWhetherProcessIsHung()
        {
           //easy way of doing this would be to check process.Responding attribute
           //C# makes it totally easy with Process class

            //edit is the class name here for Notepad
            IntPtr hwnd = FindWindow("Edit", null);
            UIntPtr let;

            //Aborts if the process to which we are sending message fails
            IntPtr result =  SendMessageTimeout(hwnd, 0, (UIntPtr)0, (IntPtr)0, SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 15000, out let);
           
            //there will be error if result = 0, get last error and show it then
            if (result == (IntPtr)0)
                MessageBox.Show(GetLastError().ToString());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            CheckWhetherProcessIsHung();
        }
    }
}


References:
Pinvoke.net , msdn dev center -desktop and microsoft support pages

No comments:

Post a Comment