问题摘要: 中的某些代码UartComm.OnGetIdRES()
引发了ERangeError
,这使我的程序崩溃。这个错误不是问题,重要的是为什么我的应用程序全局异常挂钩捕获了异常,但我的程序仍然崩溃。我希望钩子能够捕获所有未处理的异常并抑制它们;该程序应继续运行。
这是负责全局异常挂钩的单元:
unit LogExceptions;
interface
uses
Windows, SysUtils, Classes, JclDebug, JclHookExcept;
procedure AppendToLog(Msg: String; const LogFileLevel: TLogFileLevel);
implementation
uses Main;
procedure HookGlobalException(ExceptObj: TObject; ExceptAddr: Pointer;
OSException: Boolean);
var
Trace: TStringList;
DlgErrMsg: String;
begin
{ Write stack trace to `error.log`. }
Trace := TStringList.Create;
try
Trace.Add(
Format('{ Original Exception - %s }', [Exception(ExceptObj).Message]));
JclLastExceptStackListToStrings(Trace, False, True, True, False);
Trace.Add('{ _______End of the exception stact trace block_______ }');
Trace.Add(' ');
Trace.LineBreak := sLineBreak;
LogExceptions.AppendToLog(Trace.Text, lflError);
{ Show an dialog to the user to let them know an error occured. }
DlgErrMsg := Trace[0] + sLineBreak +
Trace[1] + sLineBreak +
sLineBreak +
'An error has occured. Please check "error.log" for the full stack trace.';
frmMain.ShowErrDlg(DlgErrMsg);
finally
Trace.Free;
end;
end;
procedure AppendToLog(Msg: String; const LogFileLevel: TLogFileLevel);
{ .... irrelevant code ....}
initialization
Include(JclStackTrackingOptions, stTraceAllExceptions);
Include(JclStackTrackingOptions, stRawMode);
// Initialize Exception tracking
JclStartExceptionTracking;
JclAddExceptNotifier(HookGlobalException, npFirstChain);
JclHookExceptions;
finalization
JclUnhookExceptions;
JclStopExceptionTracking;
end.
(如果有帮助,这里有一个JclDebug.pas
和的链接JclHookExcept.pas
)
我为激活钩子所做的就是添加LogExceptions
到.interface uses
Main.pas
现在这里是崩溃的一步一步:
- 执行进入
UartComm.OnGetIdRES()
ERangeError
当我尝试将Length
动态数组设置为-7
:SetLength(InfoBytes, InfoLength);
我们进入
LogExceptions.HookGlobalException()
。此时IDE中显示的调用堆栈是这样的(我省略了内存地址):-> LogExceptions.HookGlobalException :TNotifierItem.DoNotify :DoExceptNotify :HookedRaiseException :DynArraySetLength :DynArraySetLength :@DynArraySetLength UartComm.TfrmUartComm.OnSpecificRES // This method runs `OnGetIdRES()` UartComm.TfrmUartComm.OnSpecificPktRX UartComm.TfrmUartComm.DisplayUartFrame UartComm.TfrmUartComm.UartVaComm1RxChar VaComm.TVaCustommComm.HandleDataEvent VaComm.TVaCommEventThread.DoEvent { ... } { ... Some low-level calls here .... }
一旦我们从
HookGlobalException
调试器中出来,就会抛出一个对话框:引发异常类 ERangeError 并带有消息“范围检查错误”
如果我按“继续”程序仍然冻结工作。如果没有调试器,程序也会在此时冻结。
如果我单击“Break”并继续使用调试器,执行会一直通过堆栈进入
VaComm.TVaCommEventThread.DoEvent
并执行该行:Application.HandleException(Self);
之后它什么也不做(我用调试器进入这个例程,程序永远“运行”)。
即使我不使用 JCL 库作为钩子,而是指向Application.OnException
一些空例程,也会发生完全相同的事情。
为什么异常被钩子捕获,然后在钩子返回时重新引发?如何抑制异常以使程序不会崩溃但继续运行?
更新:我有 3 个伟大的发现:
- JCL 钩子实际上捕获了所有异常,无论是已处理的还是未处理的。这就是为什么
GlobalExceptHook()
在异常通过调用堆栈之前。 Application.OnException
被分配在代码中的其他地方重新分配。Application.HandleException
执行OnException
(但是当我尝试进入时调试器没有告诉我)并且那里有一条线试图关闭 COM 端口。这是让我的程序的 GUI 冻结的那一行。
当我弄清楚一切时,我会写一个答案。