解释GetKeyState / GetCursorPos中的错误

时间:2013-11-22 10:08:48

标签: delphi delphi-2006

有时我会收到客户的错误报告,我无法解释。在Delphi中的Application.Run()之后,我得到以下错误:

 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
-------------------------------------------------------------------

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
-------------------------------------------------------------------

在这两种情况下,从Eurekalog提交的屏幕截图都是黑色的。

任何人都可以解释一下,这会导致GetCursorPos或GetKeyState失败的原因是什么?

3 个答案:

答案 0 :(得分:7)

GetCursorPos的文档说:

  

调用GetCursorPos时,输入桌面必须是当前桌面。调用OpenInputDesktop以确定当前桌面是否为输入桌面。如果不是,请使用OpenInputDesktop返回的HDESK调用SetThreadDesktop切换到该桌面。

你可能会犯这种错误,最常见的是在解锁工作站时。在我的代码中,我将GetCursorPos替换为此变体:

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;

您可以使用自己喜欢的代码挂钩机制来替换GetCursorPos。我是这样做的:

RedirectProcedure(@Windows.GetCursorPos, @CodePatcher.GetCursorPos);
如下所述

RedirectProcedurePatch routine call in delphi

原来,我的特定情况是,GetCursorPos会失败,但GetCursorInfo不会失败。但正如评论中指出的那样,有些情况GetCursorInfo也会失败。在这种情况下,您可能会发现安排补丁函数不返回False是合适的。

至于GetKeyState,我不确定那个。它可能很相似,但GetKeyState是一个我个人不熟悉的API。

答案 1 :(得分:1)

供参考: 感谢答案和评论,我发现以下情况可能会触发这些错误:

  • 用户在Windows中切换后登录
  • 从受密码保护的屏幕保护程序登录
  • VNC /远程桌面服务

我将忽略全局excpetion处理程序中的错误,如下所示:

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;

答案 2 :(得分:0)

奇怪的是,在我的测试中,我发现GetCursorPos表现出与HookAPI完全相同的问题,至少在密码锁定屏幕被解锁的情况下。

(使用Delphi 7和Windows 7测试,以及来自madshi.net的//== BOF BUG REPORT ================================================= operating system : Windows 7 x64 Service Pack 1 build 7601 executable : Project1.exe compiled with : Delphi 7 madExcept version : 3.0o exception class : EOSError exception message : System Error. Code: 5. Access is denied. main thread ($1b48): 0045c513 +73 Project1.exe SysUtils RaiseLastOSError 0045c543 +07 Project1.exe SysUtils Win32Check 00487dc1 +09 Project1.exe Controls 10578 +1 TMouse.GetCursorPos 0049dc7f +27 Project1.exe Unit1 32 +1 TForm1.Timer1Timer //== EOF BUG REPORT ================================================= 进行修补)。

测试程序:

  1. 创建一个简单的应用程序,每秒只调用一次Mouse.CursorPos(仅在主窗体上放置一个TTimer和一个TEdit,下面显示定时器代码)。
  2. 将CodePatcher单元添加到项目中。
  3. 运行该应用程序。
  4. 锁定屏幕(Flag-L)。
  5. 输入密码以解锁屏幕。
  6. David Heffernan原始代码的结果:

    //==BOF PROJECT1.DPR========================================================================
    program Project1;
    
    uses
      madExcept,
      madLinkDisAsm,
      madListHardware,
      madListProcesses,
      madListModules,
      Forms,
      Unit1 in 'Unit1.pas' {Form1},
      CodePatcher in 'CodePatcher.pas';
    
    {$R *.res}
    
    begin
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.
    //==EOF PROJECT1.DPR========================================================================
    
    //==BOF UNIT1.PAS========================================================================
    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls;
    
    type
      TForm1 = class(TForm)
        Timer1: TTimer;
        Edit1: TEdit;
        procedure Timer1Timer(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
      Edit1.Text := IntToStr(Mouse.CursorPos.X);
    end;
    
    end.
    //==EOF UNIT1.PAS========================================================================
    
    
    //==BOF CODEPATCHER.PAS========================================================================
    unit CodePatcher;
    
    interface
    
    implementation
    
    uses
      Windows, Types, madCodeHook;
    
    var GetCursorPosNextHook : function (var lpPoint: TPoint): BOOL; stdcall;
    
    // David Heffernan's solution
    function GetCursorPosHookProc(var lpPoint: TPoint): BOOL; stdcall;
    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);
        // Uncomment next line to avoid exception caused by TMouse.GetCursorPos
        //Result := True;
      end;
    end;
    
    initialization
      HookAPI('user32.dll', 'GetCursorPos', @GetCursorPosHookProc, @GetCursorPosNextHook);
    
    finalization
      UnhookAPI(@GetCursorPosNextHook);
    
    end.
    //==EOF CODEPATCHER.PAS========================================================================
    

    “结果:=真;”时的结果line 取消注释

    没有例外。

    源文件

    http://api.example.com/users/useridhere/