6

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;

Mark Elder
  • 3,987
  • 1
  • 31
  • 47
  • 1
    Maybe the user is logged out and the input desktop isn't the same as the current desktop anymore, so GetCursorPostion fails. – newgre Jun 11 '09 at 23:00
  • Does your application run on a virtual machine (VMWare) ? – ErvinS Jun 12 '09 at 07:47
  • @jn - OK, but if the user is logged out how is my application still running? – Mark Elder Jun 12 '09 at 15:38
  • @ErvinS - I test my application using Microsoft Virtual PC. I have not had any problems there. – Mark Elder Jun 12 '09 at 15:38
  • There are different reasons why another input desktop could have become active (fast user switching or password-protected screen saver, to name only two). I'd simply replace "Mouse.CursorPos" with "Windows.GetCursorPos" in line 3999, and on failure set Enabled to False. – mghie Jun 13 '09 at 23:12

3 Answers3

13

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
  • 13,360
  • 7
  • 66
  • 130
  • 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 Jun 12 '09 at 15:37
  • 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 Jun 13 '09 at 23:16
  • 1
    @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 Jun 16 '09 at 06:59
  • Damn, looks like comments lose formatting. Sorry. Hope it's readable anyway :) – David Jun 16 '09 at 07:00
  • I experience this problem and for what it's worth, I have only ever seen it on XP (never Vista/7) and only after SP2. – David Heffernan Dec 03 '10 at 09:46
  • I get this on windows 8.1 64, when my app is using it in a timer and I get a large amount of these error dialogs with beepings when I lock the screen or if I run a program that requires uac confirmation dialog or sometimes just for no obvious reason – ycomp Nov 25 '14 at 12:58
-1

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.

Francesca
  • 21,452
  • 4
  • 49
  • 90
-1

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
  • 14,196
  • 16
  • 71
  • 106
  • This fails too if the input desktop is not the current desktop but since you don't bother to check the return value from GetCursorPos you don't notice! – David Heffernan Dec 03 '10 at 09:45