Sometimes I get bug reports from customers, that I can't explain. After Application.Run() in Delphi I get the following errors:
EOSError: System error: Code:_5 Access denied
Call Stack Information:
-------------------------------------------------------------------
|Address |Module |Unit |Class |Procedure |Line |
-------------------------------------------------------------------
|Running Thread: ID=4352; Priorität=0; Klasse=; [Main Thread] |
|-----------------------------------------------------------------|
|772B291F|USER32.dll | | |GetKeyState | |
|772B7B96|USER32.dll | | |GetPropA | |
|772B7B5A|USER32.dll | | |GetPropA | |
|772A7BC5|USER32.dll | | |DispatchMessageA| |
|772A7BBB|USER32.dll | | |DispatchMessageA| |
|00A6D804|Program.exe|Program.dpr| | |803[369]| // Application.Run
-------------------------------------------------------------------
and
EOsError: A call to an OS function failed
Call Stack Information:
-------------------------------------------------------------------
|Address |Module |Unit |Class |Procedure |Line |
-------------------------------------------------------------------
|Running Thread: ID=2712; Priorität=0; Klasse=; [Main Thread] |
|-----------------------------------------------------------------|
|7E379758|USER32.dll | | |GetCursorPos | |
|7E379ED9|USER32.dll | | |GetKeyState | |
|7E37B3FC|USER32.dll | | |CallNextHookEx | |
|7E380078|USER32.dll | | |GetPropA | |
|7E380042|USER32.dll | | |GetPropA | |
|7E3696C2|USER32.dll | | |DispatchMessageA| |
|7E3696B8|USER32.dll | | |DispatchMessageA| |
|00A6E823|Program.exe|Program.dpr| | |803[369]| //Application.Run
-------------------------------------------------------------------
In both cases the screenshot submitted from Eurekalog is completely black.
Can anybody explain, what can cause GetCursorPos or GetKeyState to fail this way?
The documentation for GetCursorPos
says:
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.
You can fall foul of this, most commonly when unlocking a workstation. In my code I replace GetCursorPos
with this variant:
function GetCursorPos(var lpPoint: TPoint): BOOL; stdcall;
(* The GetCursorPos API in user32 fails if it is passed a memory address >2GB
which breaks LARGEADDRESSAWARE apps. We counter this by calling GetCursorInfo
instead which does not suffer from the same problem.
In addition we have had problems with GetCursorPos failing when called
immediately after a password protected screensaver or a locked workstation
re-authenticates. This problem initially appeared with XP SP1 and is brought
about because TMouse.GetCursorPos checks the return value of the GetCursorPos
API call and raises an OS exception if the API has failed.
*)
var
CursorInfo: TCursorInfo;
begin
CursorInfo.cbSize := SizeOf(CursorInfo);
Result := GetCursorInfo(CursorInfo);
if Result then begin
lpPoint := CursorInfo.ptScreenPos;
end else begin
lpPoint := Point(0, 0);
end;
end;
You can use your favourite code hooking mechanism to replace GetCursorPos
. I do it like so:
RedirectProcedure(@Windows.GetCursorPos, @CodePatcher.GetCursorPos);
with RedirectProcedure
as described here: Patch routine call in delphi
It turned out for my particular scenario, that GetCursorPos
would fail, but GetCursorInfo
would not fail. But as has been pointed out in the comments, there are scenarios where GetCursorInfo
will also fail. In that case you might find it expedient to arrange that the patched function does not return False
.
As for GetKeyState
, I'm not sure about that one. It's quite possibly similar but GetKeyState
is an API that I am not personally familiar with.
For reference: Thanks to the answer and comments I found the following situations which might likely trigger these errors:
I will ignore the errors in a global excpetion handler as follows:
procedure MyGlobalExceptionHandler(Sender: TObject; E: Exception);
var
TopCallStackFunction: string;
begin
if E is EOSError then
begin
TopCallStackFunction := GetEurekalogTopCallStackFunction();
//EOSError: System error: Code: 5 Access denied,
//caused by GetKeyState or EndPaint or GetCursorPos
if ((E as EOSError).ErrorCode = Windows.ERROR_ACCESS_DENIED)
and ((TopCallStackFunction = 'GetKeyState')
or (TopCallStackFunction = 'EndPaint')
or (TopCallStackFunction = 'GetCursorPos')) then
Exit;
//EOsError: A call to an OS function failed, caused by GetCursorPos
if ((E as EOSError).ErrorCode = 0)
and (TopCallStackFunction = 'GetCursorPos') then
Exit;
end;
... //other error handling
end;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With