avo
avo

Reputation: 10701

How is a .NET method callback from native code implemented?

I use Windows CBT hook in my C# code, like below. This code works great, but it just occurred to me that I don't have clear understanding of how it actually works.

So, how does the callback from unmanaged Windows code to a regular function pointer (CBTProc) turn into non-static managed member method call, so I can access this in my CbtHookProc?

Generally, I understand how C# delegates capture the scope in managed code. My question is about how it still works for unmanaged callbacks. In native C++, that would not be possible without using a global variable or some tricks like ATL thunks.

class CbtHook
{
    delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
    HookProc cbtHookProc;
    IntPtr cbtHookId;

    const int WH_CBT = 5;

    [DllImport("user32.dll")]
    static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, uint threadId);

    [DllImport("kernel32.dll")]
    static extern uint GetCurrentThreadId();

    [DllImport("user32.dll")]
    static extern IntPtr CallNextHookEx(IntPtr hhook, int nCode, IntPtr wParam, IntPtr lParam);

    IntPtr CbtHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        // can access 'this' here
        return CallNextHookEx(this.cbtHookId, nCode, wParam, lParam);
    }

    public CbtHook()
    {
        this.cbtHookProc = new HookProc(CbtHookProc);
        this.cbtHookId = SetWindowsHookEx(WH_CBT, this.cbtHookProc, IntPtr.Zero, GetCurrentThreadId());
    }
}

Upvotes: 1

Views: 704

Answers (1)

dtb
dtb

Reputation: 217293

From MSDN:

When delegates are marshaled as function pointers, the runtime allocates a thunk that does the transition from unmanaged to managed. This thunk is what the unmanaged code actually calls before the managed delegate is finally invoked.

Upvotes: 2

Related Questions