0

Reading through George Mamaladze c# "global mouse key hook" source code I am trying to figure how some code works. Here is the "corazon" generally

public delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);

private static Handle HookGlobal(int hookId, Callback callback)
{
    HookProcedure hookProc = (code, param, lParam) => MyProc(code, param, lParam, callback);

    Handle handle = SetWindowsHookEx(
            hookId,
            hookProc,
            Process.GetCurrentProcess().MainModule.BaseAddress,
            0);
    return handle;
}

private static IntPtr MyProc(int nCode, IntPtr wParam, IntPtr lParam, Callback callback)
{            
    var callbackData = new CallbackData(wParam, lParam);
    bool continueProcessing = callback(callbackData);
    if (!continueProcessing) 
    { return new IntPtr(-1); }
    return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}

The message pump set by WinApi function SetWindowsHookEx will call the MyProc method with message data.

HHOOK WINAPI SetWindowsHookEx(
  _In_ int       idHook,
  _In_ HOOKPROC  lpfn,
  _In_ HINSTANCE hMod,
  _In_ DWORD     dwThreadId
);

According to MSDN, the HOOKPROC type defines a pointer to the callback function. (example...) MouseProc is a placeholder for the application-defined or library-defined function name. (There are several placeholder procedure callbacks...)

LRESULT CALLBACK MouseProc(
    _In_ int    code,
         WPARAM wParam,
    _In_ LPARAM lParam
);

Does the hookProc delegate instance keep reference to the lambda and thus to MyProc method in George's code?
Here is a simpler approach from Stephen Taub's MSDN blog

private delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
private static HookProcedure procedure = Callback;
private static IntPtr _hookID = IntPtr.Zero;        

public static void SetHook()
{
    _hookID = SetWindowsHookEx(
        WH_KEYBOARD_LL,
        procedure,
        Process.GetCurrentProcess().MainModule,
        0);        
}

private static IntPtr Callback(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode >= 0)
    {
    }
    return CallNextHookEx(_hookId, nCode, wParam, lParam);
}

They should achieve the same thing. What's George up to with this warped stuff? An explanation may help with my dizziness or shortness of breath.

  • The solution by George Mamaladze does not look reliable, how is he ensuring `hookProc` doesn't get GC'ed? For that reason alone I'd trust Stephen Toub's code more. – Lukazoid Oct 14 '15 at 23:32
  • @Lukazoid now i did say "generally " concerning the code** but a quick glance at mamaladze code doesn't reveal that sort of mechanism, it's this use of lambda i don't know – Jedi Commymullah Oct 14 '15 at 23:42
  • This is a reasonable question with a very poor title, I suggest you edit that – Steve Oct 15 '15 at 00:05
  • I see a memory bleed. Anyone else see it? – code4life Oct 15 '15 at 00:05

1 Answers1

2

Does the hookProc delegate instance keep reference to the lambda and thus to MyProc method in George's code?

No it does not, hookProc may be eligible for garbage collection as soon as the function exits and your hook will no longer function, you need to keep a reference to the delegate like in Stepen's code to stop it from happening.

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • Wouldn't a static callback do the same thing? – code4life Oct 15 '15 at 00:05
  • i am using succinct code like stephen's but i would like to expose an external callback within the hook callback function parameters too like george – Jedi Commymullah Oct 15 '15 at 00:10
  • @code4life no actually, I have run into it myself. If you did `SetWindowsHookEx(hookId, MyProc, ...` that actually gets compiled to `SetWindowsHookEx(hookId, new HookProcedure(MyProc), ...` and that hidden delegate can be garbage collected once SetWindowsHookEx returns. If SetWindowsHookEx then tries to use the callback you will get a `CallbackOnCollectedDelegate` error. See [this old answer of mine](http://stackoverflow.com/questions/12878729/thread-hook-procedure-is-no-longer-called-after-pressing-tab-several-times-why/19626866#19626866) on this same API call for more details. – Scott Chamberlain Oct 15 '15 at 01:05
  • @JediCommymullah What you could do is have a `static Dictionary` that you hold the callbacks in. When a user wants to unsubscribe you can just remove the callback from the dictionary. – Scott Chamberlain Oct 15 '15 at 02:10
  • @ScottChamberlain: ah, thanks for the info, makes sense! – code4life Oct 15 '15 at 02:31
  • that's soooooo interesting scott. a dictionary would really open it up. maybe i could stash hookProc in a field and pass that instead? – Jedi Commymullah Oct 15 '15 at 03:16
  • @JediCommymullah One thing to be careful of, Dictionary is not thread safe, either use `ConcurrentDictionary` or put a lock around it. – Scott Chamberlain Oct 15 '15 at 03:18