views:

631

answers:

3

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;

A: 

Without seeing the code and which version of Windows, one can only guess. I would look into the code of the TtaoHoverTimer.Timer procedure in unit taoCntrr.

François
+8  A: 

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.

David M
The OS for the user having the problem if XP. What I don't understand is how the screen saver could change the desktop for an already running application?The VCL is calling GetLastError - the message returned is "A call to an OS function failed".
Mark Elder
No use in calling GetLastError again, it has been called already to format the exception message. Also, no need to wrap Windows.GetCursorPos(), just handle the failure return value appropriately.
mghie
@mghie: Thanks, yes, I should have caught that :)@Mark:> What I don't understand is how the screen saver could change the desktop for an already running application?I don't think it does (and I don't think it's possible to change which desktop an app is running on) - I think the cursor is only active on a single desktop. Your app remains on its desktop but it can no longer get the mouse cursor position while another desktop is active. Have a look at http://msdn.microsoft.com/en-us/library/ms682573(VS.85).aspx and the links at the bottom of that page.
David M
Damn, looks like comments lose formatting. Sorry. Hope it's readable anyway :)
David M
A: 

Try calling the GetCursorPos(cursorPos); method in the Windows unit.

Something like this:

var
   cursorPos       : TPoint;

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

It works with no problem on all my applications.

Drejc