【问题标题】:Call to TMouse.GetCursorPos sometimes fails with "A call to an OS function failed"调用 TMouse.GetCursorPos 有时会失败,并显示“调用 OS 函数失败”
【发布时间】:2010-11-02 07:04:32
【问题描述】:

有时我的应用程序会出现以下错误。

这通常发生在用户离开办公桌而我的程序打开时。当他们回来时,这个错误就出现了。

TMouse.GetCursorPostion 除了对 GetCursorPosition 进行 Windows API 调用外,不做任何事情。然后它检查返回值,如果失败则调用 GetLastError。

“对 OS 函数的调用失败”对于追查其原因并不是很有帮助。屏幕保护程序或睡眠模式是否会导致此错误?我可以修改组件以捕获并忽略错误,但如果可能的话,我宁愿首先知道它发生的原因/原因。

我的应用程序使用的是 Delphi 2007,并且调用是由 Quasidata 从 Transfer@Once (v 1.7) 组件发出的。

这里是调用栈:

操作系统:Windows XP Service Pack 3 build 2600 异常编号:1 异常类:EOSError 异常消息:对 OS 函数的调用失败。 主线程($d34): 0045e208 UaarSales.exe SysUtils RaiseLastOSError 0045e191 UaarSales.exe SysUtils RaiseLastOSError 0045e237 UaarSales.exe SysUtils Win32Check 004c6de9 UaarSales.exe 控制 TMouse.GetCursorPos 00736d8b UaarSales.exe taoCntrr 3999 TtaoHoverTimer.Timer 004a1d27 UaarSales.exe ExtCtrls TTimer.WndProc 0047a7a0 UaarSales.exe 类 StdWndProc 7e4196c2 USER32.dll DispatchMessageA 004da230 UaarSales.exe 形成 TApplication.ProcessMessage 004da26a UaarSales.exe 表单 TApplication.HandleMessage 004da55f UaarSales.exe 形成 TApplication.Run 00b3ea76 UaarSales.exe UaarSales 117 初始化

这是定时器程序

procedure TtaoHoverTimer.Timer; var lPos: TPoint; begin lPos := Mouse.CursorPos; // this is line 3999 if (lPos.X = FMousePos.X) and (lPos.Y = FMousePos.Y) and not ((lPos.X = FOutdatedPos.X) and (lPos.Y = FOutdatedPos.Y)) then begin inherited Timer; FOutdatedPos := Point(MaxInt, MaxInt); end; Enabled := False; end;

【问题讨论】:

  • 可能是用户退出了,输入的桌面和当前的桌面已经不一样了,所以GetCursorPostion失败了。
  • 您的应用程序是否在虚拟机 (VMWare) 上运行?
  • @jn - 好的,但是如果用户已注销,我的应用程序如何仍在运行?
  • @ErvinS - 我使用 Microsoft Virtual PC 测试我的应用程序。我在那里没有任何问题。
  • 另一个输入桌面可能已激活的原因有多种(快速用户切换或受密码保护的屏幕保护程序,仅举两个例子)。我只需将第 3999 行中的“Mouse.CursorPos”替换为“Windows.GetCursorPos”,并在失败时将 Enabled 设置为 False。

标签: delphi winapi mouse


【解决方案1】:

CursorPos 使用 Windows GetCursorPos 方法。 MSDN上的评论说有两个要求:

  • “调用进程必须具有对窗口站的 WINSTA_READATTRIBUTES 访问权限。”
  • “调用GetCursorPos时,输入桌面必须是当前桌面。调用OpenInputDesktop判断当前桌面是否为输入桌面。如果不是,使用OpenInputDesktop返回的HDESK调用SetThreadDesktop切换到该桌面。”

因此,屏幕保护程序很可能正在另一个桌面上运行。或者,如果您使用的是 Vista,我很确定密码对话框(用于解锁计算机)也可以在另一个桌面上运行。

既然你有这个组件的源代码,你可能想为 CursorPos 编写你自己的包装器,当出现问题时它会返回一个虚拟值。 (编辑:或评论者建议处理无法内联获取位置的问题,而不是编写函数来返回虚拟值。)

最后,您可以调用 GetLastError 来查看最后一个 Windows 错误是什么,在它引发异常之后。这应该可以肯定地告诉您它遇到的实际问题是什么。在评论中(谢谢!)您已经在异常消息中遇到了错误消息。

【讨论】:

  • XP 遇到问题的用户的操作系统。我不明白屏幕保护程序如何更改已经运行的应用程序的桌面? VCL 正在调用 GetLastError - 返回的消息是“对 OS 函数的调用失败”。
  • 再次调用GetLastError没有用,已经调用了格式化异常信息。另外,无需包装Windows.GetCursorPos(),只需适当处理失败返回值即可。
  • @mghie:谢谢,是的,我应该明白这一点 :) @Mark:> 我不明白屏幕保护程序如何为已经运行的应用程序更改桌面?我认为不会(而且我认为无法更改应用程序在哪个桌面上运行) - 我认为光标仅在单个桌面上处于活动状态。您的应用程序仍保留在其桌面上,但当另一个桌面处于活动状态时,它无法再获取鼠标光标位置。查看msdn.microsoft.com/en-us/library/ms682573(VS.85).aspx 以及该页面底部的链接。
  • 该死,看起来 cmets 丢失了格式。对不起。无论如何希望它是可读的:)
  • 我遇到了这个问题,而且对于它的价值,我只在 XP(从来没有 Vista/7)和 SP2 之后见过它。
【解决方案2】:

没有看到代码和哪个版本的 Windows,只能猜测。 我会在单元 taoCntrr 中查看 TtaoHoverTimer.Timer 过程的代码。

【讨论】:

    【解决方案3】:

    尝试在 Windows 单元中调用 GetCursorPos(cursorPos); 方法。

    类似这样的:

    var
       cursorPos       : TPoint;
    
    begin
         GetCursorPos(cursorPos);
         cursorPos := ScreenToClient(cursorPos);
    

    它在我所有的应用程序上都没有问题。

    【讨论】:

    • 如果输入桌面不是当前桌面,这也会失败,但由于您不费心检查 GetCursorPos 的返回值,您不会注意到!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多