Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Call to TMouse.GetCursorPos sometimes fails with "A call to an OS function failed"

On occasion my application gets the error below.

Normally this happens when the user steps away from their desk leaving my program open. When they come back this error has appeared.

TMouse.GetCursorPostion does not do anything except make the Windows API call to GetCursorPosition. Then it checks the return value and calls GetLastError if it failed.

"A call to an OS function failed" is not very helpful in tracking down the cause of this. Could a screen saver or sleep mode be kicking in causing this error? I could modify the component to just catch and ignore the error, but if possible I would rather know what/why it is happening in the first place.

My application is using Delphi 2007 and the call is being made from Transfer@Once (v 1.7) component by Quasidata.

Here is the call stack:

operating system  : Windows XP Service Pack 3 build 2600
exception number  : 1
exception class   : EOSError
exception message : A call to an OS function failed.

main thread ($d34):
0045e208 UaarSales.exe SysUtils       RaiseLastOSError
0045e191 UaarSales.exe SysUtils       RaiseLastOSError
0045e237 UaarSales.exe SysUtils       Win32Check
004c6de9 UaarSales.exe Controls       TMouse.GetCursorPos
00736d8b UaarSales.exe taoCntrr  3999 TtaoHoverTimer.Timer
004a1d27 UaarSales.exe ExtCtrls       TTimer.WndProc
0047a7a0 UaarSales.exe Classes        StdWndProc
7e4196c2 USER32.dll                   DispatchMessageA
004da230 UaarSales.exe Forms          TApplication.ProcessMessage
004da26a UaarSales.exe Forms          TApplication.HandleMessage
004da55f UaarSales.exe Forms          TApplication.Run
00b3ea76 UaarSales.exe UaarSales  117 initialization

Here is the Timer procedure


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;

like image 986
Mark Elder Avatar asked Jun 11 '09 21:06

Mark Elder


1 Answers

CursorPos uses the Windows GetCursorPos method. The remarks on MSDN says it has two requirements:

  • "The calling process must have WINSTA_READATTRIBUTES access to the window station."
  • "The input desktop must be the current desktop when you call GetCursorPos. Call OpenInputDesktop to determine whether the current desktop is the input desktop. If it is not, call SetThreadDesktop with the HDESK returned by OpenInputDesktop to switch to that desktop."

So, chances are that the screensaver is running on another desktop. Alternately, if you're using Vista I'm pretty sure the password dialog (for unlocking a computer) runs on another desktop as well.

Since you have the source for this component, you may want to write your own wrapper for CursorPos that returns a dummy value when there's a problem. (Edit: or a commenter suggested handling the failure to get a position inline instead of writing a function to return a dummy value.)

Finally, you can call GetLastError to see what the last Windows error was, after it's thrown the exception. That should tell you for sure what the actual problem it's encountering is. As in a comment (thanks!) you have already encountered the error message in the exception message.

like image 131
David Avatar answered Oct 26 '22 23:10

David