【问题标题】:How to Correctly Translate WH_MOUSE lparam in Managed Code如何在托管代码中正确翻译 WH_MOUSE lparam
【发布时间】:2015-11-05 13:50:48
【问题描述】:

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

我的项目由两部分组成,C++ 中的非托管部分执行挂钩、过滤和通知我的托管代码。

问题是我得到不正确的数据,例如翻译 lparam 后奇怪的 X 和 Y 坐标。 X 大部分时间是 0,而 Y 大部分时间是正确的,然后每隔一次点击我会得到一个值,比如 X 为 198437245,Y 为 -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++ 中的sizeof(MOUSEHOOKSTRUCT) 和C# 中的Marshal.SizeOf(typeof(MouseHookStruct)) 都打印20

我使用的是 64 位的 Windows 7,但 C# 和 C++ 代码均已编译并以 32 位运行。

【问题讨论】:

  • 您是否正在处理 InternalMouseHookCallbacknCode 小于零的情况?
  • @IInspectable,是的,在这种情况下,我只是 return CallNextHookEx(...)
  • 这应该是真正的问题。您是否比较了非托管 MOUSEHOOKSTRUCT 和您的 MouseHookStruct 的大小?他们应该是一样的。同样,所有成员的偏移量应该相同。
  • @IInspectable,为什么 nCode 会相关?如果 C++ 代码能够在同一个调用中正确翻译它,那么 C# 代码也应该能够做到这一点,而不管 nCode 的值如何。
  • @IInspectable,这是我不确定的部分,可能是翻译不正确的原因,我可能没有正确映射结构。我会检查运行时大小并稍后更新。

标签: c# c++ winapi hook marshalling


【解决方案1】:
public class POINT

POINT 是原生 winapi 中的一个结构体。您倾向于将其声明为 C# 中的类。但不是什么时候:

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

pt 字段现在是一个引用,而不是一个值。编组器将尝试取消对 MOUSEHOOKSTRUCT.pt 的引用,就好像它是一个指针一样。顺便说一句,这不会更频繁地产生响亮的爆炸声,这是非常了不起的,预计会出现 AccessViolationException。也许你只在辅助显示器上测试过这个就很不走运。

您必须改为将其声明为 struct

【讨论】:

    【解决方案2】:

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

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

    我不知道为什么我大部分时间都不幸地得到了正确的 Y 值,这真的让我大吃一惊,让我怀疑结构映射不正确,可能是因为它的内存稍后被填充,而 X 的记忆瞬间被别的东西填满了。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 2010-10-15
      • 2010-12-10
      • 1970-01-01
      • 2012-03-04
      相关资源
      最近更新 更多