2

I've set up a WH_MOUSE hook, everything is working fine except that I can't get the lparam (pointer to a MOUSEHOOKSTRUCT structure) passed to my HOOKPROC function correctly translated in C#.

My project consists of two parts, an unmanaged part in C++ which does the hooking and filtering and notifying my managed code.

The problem is I get incorrect data e.g. weird X and Y coordinates after translating lparam. X is 0 most of the time, while Y is correct most of the time, then every other click I get a value like 198437245 for X and -1 for Y etc.

Please note that I have already confirmed the following:

  • lparam's value is correctly being passed to my C# code (verified via breakpoints on the managed and unmanaged parts), e.g. they are both 2420528 when a mouse event happens.
  • The unmanaged code is in the same context as the managed code i.e. same address space.
  • lparam's value is correct, because I can successfully translate it to valid coordinates in the unmanaged part using:

    POINT pt = reinterpret_cast<MOUSEHOOKSTRUCT*>(lparam)->pt;
    int x = pt.x; // correct, e.g. 250
    int y = pt.y; // correct, e.g. 400
    

    However, after using the below translation, X and Y become garbled.

Here's my C++ HOOKPROC function:

static LRESULT CALLBACK InternalMouseHookCallback(int code, WPARAM wparam, LPARAM lparam)
{
    // filter messages
    // ...

    // send lparam to C# code
}

Here's how I'm translating lparam in C#:

IntPtr lparam = ...; // passed from unmanaged code and confirmed to be the same value
MouseHookStruct mouseData =
    (MouseHookStruct)Marshal.PtrToStructure(lparam, typeof(MouseHookStruct));

Here's how I've mapped POINT and MOUSEHOOKSTRUCT structs to C#:

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

[StructLayout(LayoutKind.Sequential)]
public class MouseHookStruct
{
    public POINT pt;
    public IntPtr hwnd;
    public uint wHitTestCode;
    public IntPtr dwExtraInfo;
}

What am I doing glaringly wrong?

UPDATE

sizeof(MOUSEHOOKSTRUCT) in C++ and Marshal.SizeOf(typeof(MouseHookStruct)) in C# both print 20.

I'm on Windows 7 64-bit but the C# and the C++ code are both compiled and running as 32-bit.

Saeb Amini
  • 23,054
  • 9
  • 78
  • 76
  • Are you handling the case, where *nCode* is less than zero in your *InternalMouseHookCallback*? – IInspectable Nov 05 '15 at 14:35
  • @IInspectable, yep, in that case I just `return CallNextHookEx(...)`, – Saeb Amini Nov 05 '15 at 14:39
  • That should really go into the question. Have you compared the sizes of the unmanaged `MOUSEHOOKSTRUCT` and your `MouseHookStruct`? They should be the same. Likewise, the offsets to all members should be identical. – IInspectable Nov 05 '15 at 14:44
  • @IInspectable, why would `nCode` be relevant though? if the C++ code is able to translate it correctly in the same call, the C# code should be able to do it too, regardless of `nCode`'s value. – Saeb Amini Nov 05 '15 at 14:50
  • @IInspectable, that's the part I'm not sure about and probably the cause of incorrect translation, I'm probably not mapping the structs correctly. I'll check the runtime sizes and update in a bit. – Saeb Amini Nov 05 '15 at 14:51
  • @IInspectable `sizeof(MOUSEHOOKSTRUCT)` in C++ and `Marshal.SizeOf(typeof(MouseHookStruct))` both print `20`. – Saeb Amini Nov 05 '15 at 15:01

2 Answers2

2
public class POINT

POINT is a structure in the native winapi. You tend to get away with declaring it as a class in C#. But not when:

public class MouseHookStruct
{
    public POINT pt;
    // etc...
}

The pt field is now a reference, not a value. The marshaller will try to dereference the MOUSEHOOKSTRUCT.pt as though it was a pointer. Quite remarkable that this doesn't produce a loud bang more often btw, an AccessViolationException is expected. Maybe you got unlucky by only ever testing this on a secondary monitor.

You must declare it as a struct instead.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

I found out that multiple calls to Marshal.ReadInt32(lparam) and Marshal.ReadInt32(lparam, 4) to read the X and Y values while the execution flow had reached my C# code did not return the same values. If I was reading it just fast enough, I would get correct results, but not on the next iterations.

This led me to believe that by the time lparam reached my C# code and I wanted to process it, the underlying struct was freed and lparam was pointing to garbage.

I'm not sure why I was unluckily getting correct Y values most of the time, which was really throwing me off and made me suspect that the struct mapping is incorrect, maybe because its memory was being filled in later, while X's memory was being filled instantly with something else.

I've resolved my problem by reading the actual MOUSEHOOKSTRUCT bytes in C++ and sending them to my C# code rather than sending the lparam pointer.

Saeb Amini
  • 23,054
  • 9
  • 78
  • 76