如何在托管代码中正确翻译 WH_MOUSE lparam
How to Correctly Translate WH_MOUSE lparam in Managed Code
我已经设置了一个 WH_MOUSE
挂钩,一切正常,除了我无法将 lparam
(指向 MOUSEHOOKSTRUCT 结构的指针)传递给我的 HOOKPROC
函数在 C# 中正确翻译。
我的项目由两部分组成,C++ 中的非托管部分执行挂钩和过滤并通知我的托管代码。
问题是我得到的数据不正确,例如翻译后奇怪的 X 和 Y 坐标 lparam
。 X 大多数时候是 0,而 Y 大多数时候是正确的,然后每隔一次点击我得到一个值,比如 X 的 198437245 和 Y 的 -1 等
请注意,我已经确认了以下内容:
lparam
的值已正确传递到我的 C# 代码(通过托管和非托管部分上的断点进行验证),例如当鼠标事件发生时,它们都是 2420528
。
- 非托管代码与托管代码处于相同的上下文中,即相同的地址 space。
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));
以下是我将 POINT
和 MOUSEHOOKSTRUCT
结构映射到 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
.
我使用的是 Windows 7 64 位,但 C# 和 C++ 代码都已编译并且 运行 为 32 位。
我发现在执行流程到达我的 C# 代码时多次调用 Marshal.ReadInt32(lparam)
和 Marshal.ReadInt32(lparam, 4)
来读取 X
和 Y
值并没有 return相同的值。如果我阅读它 只是 足够快,我会得到正确的结果,但不会在下一次迭代中得到。
这让我相信,当 lparam
到达我的 C# 代码并且我想处理它时,底层结构已被释放并且 lparam
指向垃圾。
我不确定为什么我大多数时候都不幸地得到了正确的 Y 值,这真的让我失望并让我怀疑结构映射不正确,可能是因为它的内存后来被填充了,而X的记忆瞬间被别的东西填满了。
我通过读取 C++ 中的实际 MOUSEHOOKSTRUCT
字节并将它们发送到我的 C# 代码而不是发送 lparam
指针解决了我的问题。
public class POINT
POINT是原生winapi中的一个结构体。您倾向于在 C# 中将其声明为 class。但不是什么时候:
public class MouseHookStruct
{
public POINT pt;
// etc...
}
pt
字段现在是一个引用,而不是一个值。编组器将尝试取消对 MOUSEHOOKSTRUCT.pt 的引用,就好像它是一个指针一样。非常值得注意的是,这不会更频繁地产生响亮的爆炸声,顺便说一句,预计会出现 AccessViolationException。可能你只在辅助显示器上测试过它而感到不走运。
您必须将其声明为 struct
。
我已经设置了一个 WH_MOUSE
挂钩,一切正常,除了我无法将 lparam
(指向 MOUSEHOOKSTRUCT 结构的指针)传递给我的 HOOKPROC
函数在 C# 中正确翻译。
我的项目由两部分组成,C++ 中的非托管部分执行挂钩和过滤并通知我的托管代码。
问题是我得到的数据不正确,例如翻译后奇怪的 X 和 Y 坐标 lparam
。 X 大多数时候是 0,而 Y 大多数时候是正确的,然后每隔一次点击我得到一个值,比如 X 的 198437245 和 Y 的 -1 等
请注意,我已经确认了以下内容:
lparam
的值已正确传递到我的 C# 代码(通过托管和非托管部分上的断点进行验证),例如当鼠标事件发生时,它们都是2420528
。- 非托管代码与托管代码处于相同的上下文中,即相同的地址 space。
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));
以下是我将 POINT
和 MOUSEHOOKSTRUCT
结构映射到 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
.
我使用的是 Windows 7 64 位,但 C# 和 C++ 代码都已编译并且 运行 为 32 位。
我发现在执行流程到达我的 C# 代码时多次调用 Marshal.ReadInt32(lparam)
和 Marshal.ReadInt32(lparam, 4)
来读取 X
和 Y
值并没有 return相同的值。如果我阅读它 只是 足够快,我会得到正确的结果,但不会在下一次迭代中得到。
这让我相信,当 lparam
到达我的 C# 代码并且我想处理它时,底层结构已被释放并且 lparam
指向垃圾。
我不确定为什么我大多数时候都不幸地得到了正确的 Y 值,这真的让我失望并让我怀疑结构映射不正确,可能是因为它的内存后来被填充了,而X的记忆瞬间被别的东西填满了。
我通过读取 C++ 中的实际 MOUSEHOOKSTRUCT
字节并将它们发送到我的 C# 代码而不是发送 lparam
指针解决了我的问题。
public class POINT
POINT是原生winapi中的一个结构体。您倾向于在 C# 中将其声明为 class。但不是什么时候:
public class MouseHookStruct
{
public POINT pt;
// etc...
}
pt
字段现在是一个引用,而不是一个值。编组器将尝试取消对 MOUSEHOOKSTRUCT.pt 的引用,就好像它是一个指针一样。非常值得注意的是,这不会更频繁地产生响亮的爆炸声,顺便说一句,预计会出现 AccessViolationException。可能你只在辅助显示器上测试过它而感到不走运。
您必须将其声明为 struct
。