如何在托管代码中正确翻译WH_MOUSE lparam

时间:2015-11-05 13:50:49

标签: c# c++ winapi hook marshalling

我已经设置了WH_MOUSE挂钩,一切正常,但我无法将lparam(指向MOUSEHOOKSTRUCT结构的指针)传递给我的HOOKPROC函数在C#中正确翻译。

我的项目由两部分组成,一部分是C ++中的非托管部分,用于挂钩和过滤并通知我的托管代码。

问题是我得到的数据不正确,例如翻译lparam后的奇怪X和Y坐标。 X大部分时间都是0,而Y在大多数情况下都是正确的,然后每隔一次点击我得到一个像198437245的X和-1的值等等。

请注意,我已经确认以下内容:

  • lparam的值正确传递给我的C#代码(通过托管和非托管部件上的断点进行验证),例如:当鼠标事件发生时,它们都是2420528
  • 非托管代码与托管代码在同一上下文中,即相同的地址空间。
  • lparam的值是正确的,因为我可以使用以下方法将其成功转换为非托管部分中的有效坐标:

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

    但是,使用以下翻译后,X和Y会出现乱码。

这是我的C ++ HOOKPROC函数:

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

    // send lparam to C# code
}

以下是我在C#中翻译lparam的方法:

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

以下是我将POINTMOUSEHOOKSTRUCT结构映射到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;
}

我做错了什么?

更新

C ++中的{p> sizeof(MOUSEHOOKSTRUCT)和C#中的Marshal.SizeOf(typeof(MouseHookStruct))都打印20

我在Windows 7 64位上,但C#和C ++代码都编译并运行为32位。

2 个答案:

答案 0 :(得分:2)

public class POINT

POINT是原生winapi中的结构。你倾向于将它声明为C#中的一个类。但不是在以下时间:

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

pt字段现在是引用,而不是值。编组器将尝试取消引用MOUSEHOOKSTRUCT.pt,就像它是一个指针一样。非常值得注意的是,这不会产生更频繁的响亮btw,预计会出现AccessViolationException。也许你只是在辅助显示器上进行测试,这是不吉利的。

您必须将其声明为struct

答案 1 :(得分:0)

我发现,当执行流程到达我的C#代码时,多次调用Marshal.ReadInt32(lparam)Marshal.ReadInt32(lparam, 4)来读取XY值并没有返回相同的值值。如果我正在快速阅读 ,我会得到正确的结果,但不会在下一次迭代中。

这让我相信,当lparam到达我的C#代码并且我想处理它时,底层结构被释放,lparam指向垃圾。

我不确定为什么我不幸的是大部分时间都得到正确的Y值,这真的让我失望并让我怀疑结构映射是不正确的,可能是因为它的内存后来被填充,而X的记忆立即被其他东西填满。

我通过读取C ++中的实际MOUSEHOOKSTRUCT字节并将它们发送到我的C#代码而不是发送lparam指针来解决我的问题。