GetCursorPos 返回 (0, 0) 主因是未检查返回值或传入未初始化 POINT;必须用 out 参数并判断返回 bool,且在 Session 0 或服务中必然失败。

GetCursorPos 为什么总返回 (0, 0)?
调用 GetCursorPos 却拿到 (0, 0),大概率是传入了未初始化的 POINT 结构体,或函数调用失败但没检查返回值。Windows API 要求必须检查返回值:返回 false 表示失败(比如权限不足、远程桌面会话中受限),此时坐标值不可信。
- 必须用
out POINT pt或先初始化POINT pt = default; - 调用后立刻判断返回值:
if (!GetCursorPos(out pt)) { /* 处理失败 */ } - 在 Windows 服务、无交互桌面会话(如 Session 0)中,
GetCursorPos永远失败——这不是 bug,是系统安全限制
C# 里怎么安全调用 GetCursorPos?
别手写 P/Invoke 签名,直接用 .NET 6+ 内置的 PointToClient 不行——那是窗体相对坐标。全局坐标必须走 API,但可以简化封装:
[DllImport("user32.dll")]
private static extern bool GetCursorPos(out POINT lpPoint);
<p>public static Point? GetGlobalMousePosition()
{
if (GetCursorPos(out POINT pt))
return new Point(pt.X, pt.Y);
return null;
}
-
POINT是 struct,字段是int X, Y,不是long,别用IntPtr强转 - 返回
Point?比抛异常更合理:位置获取失败是常态,不是异常场景 - 不要在 UI 线程高频轮询(比如 Timer.Interval = 1ms),CPU 会飙高;50ms 以上较稳妥
监听鼠标移动要不要用钩子(SetWindowsHookEx)?
除非你真需要「每毫秒都捕获」或「跨进程精确拦截点击」,否则不用。全局钩子开销大、易被杀软拦截、.NET Core/.NET 5+ 在非管理员权限下注册 WH_MOUSE_LL 钩子经常静默失败。
- 普通应用只需“知道当前在哪”,用定时调用
GetCursorPos完全够用 - 若真要监听移动事件,优先考虑
WH_MOUSE_LL(低级钩子),它不注入 DLL,兼容性比WH_MOUSE好 - 钩子回调函数必须标记
[UnmanagedFunctionPointer(CallingConvention.StdCall)],否则崩溃 - 钩子句柄必须保存为类字段,不能局部变量——GC 会回收委托导致访问违规
WPF/WinForms 应用里误用 Mouse.GetPosition 的坑
Mouse.GetPosition 返回的是相对于某元素的坐标,不是屏幕坐标。新手常写 Mouse.GetPosition(null) 以为能得全局坐标,实际抛 ArgumentNullException;写 Mouse.GetPosition(Application.Current.MainWindow) 得到的也只是窗口客户区坐标,不含标题栏和边框。
- 要屏幕坐标,必须组合使用:
PointToScreen(Mouse.GetPosition(element)) - WinForms 中对应的是
Control.PointToScreen(Cursor.Position),注意Cursor.Position本身就是屏幕坐标,无需转换 - 多显示器环境下,
Cursor.Position和GetCursorPos返回的仍是绝对屏幕坐标(以主屏左上为原点),系统自动处理跨屏偏移
真正难的不是调哪个函数,而是想清楚你要的“全局坐标”到底服务于什么场景:是做悬浮窗定位?还是录屏标点?或是辅助工具抓取控件?不同目标对精度、延迟、权限的要求差很远。别一上来就堆钩子。










