views:

177

answers:

3
public delegate void KeyboardHookCaptureHandler(KeyboardHookEventArgs keyboardEvents);

public class KeyboardHookEventArgs : EventArgs {

    private Keys _pressedKey;
    private int _pressedKeyCode;    

    public Keys PressedKey { get { return _pressedKey; } }
    public int PressedKeyCode { get { return _pressedKeyCode; } }

    public KeyboardHookEventArgs(int vkCode) {
        _pressedKey = (Keys)vkCode;
        _pressedKeyCode = vkCode;
    }
}

public class KeyboardHook {

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

    public event KeyboardHookCaptureHandler KeyIntercepted;

    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;

    private LowLevelKeyboardProc _proc;
    private IntPtr _hookID = IntPtr.Zero;

    public KeyboardHook() {
        _proc = HookCallback;
        _hookID = SetHook(_proc);
    }
    public bool UnHookKey() {
        return UnhookWindowsHookEx(_hookID);
    }

    private IntPtr SetHook(LowLevelKeyboardProc proc) {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule) {
            return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }

    private IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam) {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
            int vkCode = Marshal.ReadInt32(lParam);          
            KeyboardHookEventArgs keyHookArgs = new KeyboardHookEventArgs(vkCode);
            KeyIntercepted(keyHookArgs);
        }
        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }


    [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);
}

so I have no idea what this code means even though its the core of my program. It hooks a keyboard press event and sends it to my program. Can anyone take there precious time and explain a few things to me. I understand the args class so you can skip that. I am mostly interested in what a delegate is, what an IntPtr is and the two methods and what they do line by line.

thanks if anyone has the time

+4  A: 

A delegate type basically specifies the signature of a function or method: it's a way of capturing a function or method as an object, so that you can call that method later. A delegate instance is therefore basically a reference to a function or method.

An IntPtr is an operating system native pointer -- an opaque reference to a piece of unmanaged memory.

The SetHook method is installing a hook procedure into Windows, so that the hook procedure will be called for every keyboard event in the system. What is the hook procedure? It is proc, an instance of the LowLevelKeyboardProc delegate type. In this case, proc is always being set to refer to your HookCallback function. So what SetHook ends up doing is telling Windows to call HookCallback every time a keyboard event happens.

HookCallback is unpacking the native operating system information associated with the keyboard event, and raising the KeyIntercepted event with that unpacked data. It's then passing control to the next hook in the chain, in case anybody else is wanting to hook keyboard events.

So the final result of all this is that every time a keyboard event happens, this class raises the KeyIntercepted event. Users of this class can provide KeyIntercepted event handlers to do useful things, for example sending your bank password to the crime syndicate of your choice... *grin*

itowlson
+1  A: 

A delegate wraps a method, allowing to pass it around like a first-class object. Typically, you use it to pass callbacks and register event handlers.

An IntPtr is a representation of a pointer with slightly-reduced functionality - it's basically a pointer you can use without dropping type-safety. Typically, it's used for interoperability with native code.

The two methods basically wrap the native API calls with more "friendly" versions.

Anon.
+1  A: 

There is nothing unmanaged about a delegate. It is in fact the managed and Object Oriented friendly equivalent (on some steroids) of the basic function pointer.

In this context is it declaring the delegate type (stating the function's arguments and return type). You can then instantiate instances of that delegate (in much the same way you instantiate instances of a Type) which refer to specific functions.

basic example:

public delegate int AddSomething(int x);

public class Foo
{
    public static void Main(string[] args)
    {
        // the following are equivalent
        AddSomething add1 = Foo.PlusAnything;
        AddSomething add1alt = new AddSomething(Foo.PlusAnything);
        Console.WriteLine(add1(5)); // prints "6"

        // instance delegates, bound to a method on a particular instance
        AddSomething add3 = new Foo(3).AddAnything;
        AddSomething add5 = new Foo(5).AddAnything;
        Console.WriteLine(add3(4)); // prints "7"
        Console.WriteLine(add5(6)); // prints "11"            
    }

    static int PlusOne(int x)  { return x+1; }

    private int y;
    public Foo(int toAdd) { this.y = toAdd; }

    int PlusAnything(int x)  { return x+this.y; } 
}

IntPtr is a managed way of working with the something roughly like a void* (a pointer to anything) but with a well defined size that is dependent on the platform (so 32bits on a 32 bit platform and 64 bits on a 64 bit platform).

It is commonly used when there is a need to hold a reference to some arbitrary unmanaged resource (like a native file handle,a pointer to some buffer in unmanaged code or a pointer to some object or structure allocated on the unmanaged heap). Often interaction with unmanaged code in this way is called interop, and the common mechanism (and the one you have above) is called P/Invoke.

The delegate in question here is defining, for the benefit of the managed ocde and the interop, the signature of the callback that occurs for the keyboard hook. It describes certain aspects of how things will be converted to their managed equivalents. By doing it this way your managed function (which can move about in memory) can be passed to some unmanaged code because the runtime knows this is happening and it ensures that the right thing happens. There is quite a lot of 'magic' happening here behind the scenes so that this can all work correctly but the developer (i.e. you) is still expected to know what the relevant pointers means and what you are supposed to do with them.

when trying to work out how to use unmanaged fucntions in win32 the P/Invoke wiki is very useful. You example for the UnhookWindowsHookEx which details how to call that function. You would still be expected to know what the actual function does and how it works though.

Not knowing what to do with an IntPtr is not a major issue but if you don't know what a delegate is you have some serious learning about c#/.net to do before you should go anywhere near this code base.

ShuggyCoUk
incidentally don't take this personally or as an insult. I mean it seriously that there a gaps in your knowledge around c# that you should fill by getting a book that teaches c#. Attempting to deal with interop (which is what this is) without first understanding the basics will lead to complicated problems down the line
ShuggyCoUk