这个BUG是老早就存在的,在我刚来公司没多久的时候,JACK就跟我说起过这个问题,这个问题的症状很奇怪,正常使用时,它永远不会出现,而当用户把屏幕锁定以后再过若干时间,就会有一长串的弹出窗口,更怪的是,这些窗口有时有,有时没有.
同事最近老是遇上这个问题,要我一定得想想办法,凑了点时间仔细查看了这个问题,发现如下的细节:
- 这个错误出现的前提是: 1. 屏幕锁定, 2. 屏保出现
- 程序的窗口需要是当前窗口,如果仅在后台运行,这个错误不会出现
- 即使是在当前窗口,这个错误也不是每次都会出现,似乎与当前子窗口有关
- 即使是相同子窗口,这个错误也是有时出现,有时不出现
- 根据经验判断应该是定时器中出现的问题
- 这个错误其实包括2个, 1. "A call to an OS function failed.", 2. "Access denied" 错误
由于这个错误只在屏幕锁定且有屏保时出现,我先把计算机的屏保设定为1分钟,以减少浪费的时间,然后用DELPHI打开工程进入DEBUG模式启动程序,再锁上屏幕等屏保出现.如果有效,那么出错时DELPHI应该截获这个出错信息并停留在那里.
1分钟以后,输入密码重新进入桌面,果然,DELPHI报错了,可是,错误的地方不是我的程序,而是进入了Forms.pas这个系统单元,在function TApplication.ProcessMessage这个函数中,停留在这句 TranslateMessage(Msg); 上.
按F8进行DEBUG,进入了ExtCtrls这个系统单元,原来是这里出的错,上面的函数是在 Application.HandleException(Self); 这里调用到的.继续F8,我的程序跳出了错误框,然后就结束了.没有找到哪里出错.不过,这也验证了的确是定时器的问题,但是,是哪个定时器呢?
我把主界面及子窗口中的所有定时器全部关闭,可是这个错误还是照常出现.奇怪了.
procedure TTimer.WndProc的定义如下:
- procedure TTimer.WndProc(var Msg: TMessage);
- begin
- with Msg do
- if Msg = WM_TIMER then
- try
- Timer;
- except
- Application.HandleException(Self);
- end
- else
- Result := DefWindowProc(FWindowHandle, Msg, wParam, lParam);
- end;
我把断点设在第6行 Timer上,用F7不断进行跟踪,跳转了几次后,发现进入了一个自定义的控件单元,有戏!
进入的函数是OnHintTimer. 看,这就是一个隐藏的TIMER不是.我在这个函数的开始加个EXIT使之禁用,再运行程序,锁屏,等屏保,再进入.果然没有错误了!看来问题就是在这儿.
程序函数中出错的地方居然是取鼠标位置的几句,很奇怪会出这样的错误.我又试了一下,恢复原来的代码,使当前窗口是此程序,如果把鼠标移到此程序窗口之外,再锁屏,等屏保,解锁再进入,果然就是没有错误的.
再次尝试,把出错的一段句子加上try...except....语句.在except中加上Mouse.CursorPos.x,结果...居然就没有错,晕死.
总结一下这个错误:当打开此程序,并激活某个使用特定控件的窗口,使此控件的OnHintTimer可用,并且鼠标指针在此控件上,能引发OnHintTimer事件.在这种情况下,锁定屏幕,使计算机进入屏保(如果不锁屏,只进入屏保也不会引发这个错误),然后再解除屏幕锁定,就会在此控件的OnHintTimer事件中发生错误,而发生的错误是在获得鼠标位置的语句.错误为"A call to an OS function failed.",由系统抛出.
老实说,这个错误非常奇怪,因为在OnHintTimer中只是获得鼠标位置,我甚至已经把此事件中的语句删得仅剩一句
x :=Mouse.CursorPos.x;
了.也还是一样的错误.到底为什么,还是一个谜,我会继续追踪下去.
无论如何,这个错误是找到了,要解决的话,只要在OnHintTimer事件中套上try...except...end语句,那么就不会出现那些烦人的错误框了.



