【发布时间】:2015-06-17 12:35:47
【问题描述】:
问题总结: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 添加到Main.pas 中的interface uses 列表中。
现在这里是崩溃的一步一步:
- 执行进入
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 冻结的那一行。
等我弄明白了再写答案。
【问题讨论】:
-
你为什么一开始就试图将动态数组的长度设置为负值? NewLength 应为 0 或正数。
-
@SilverWarior 是的,我发现了这个错误并修复了它。但问题是那里有更多这样的错误,我需要全局挂钩来防止这些错误使我的程序崩溃。
-
我知道这不是你问题的答案,但让它崩溃可能比让你的应用程序做意想不到的事情更好。但是,如果您使用 Application.OnException ,您是否有相同的行为?
-
据我所见,该函数仅将函数添加到异常链中,这意味着在每个异常上都会调用它,但随后会再次调用正常的程序线程-这意味着在全局挂钩完成后执行正常的异常处理。查看 JclHookExcept.pas 中的代码 - 它只是调用通知器然后继续。
-
@mrabat 那么如何解释如果我使用
Application.OnException而不是JCL 钩子会发生完全相同的事情呢?我在帖子中添加了“第 5 步”以提供更多信息。
标签: delphi exception-handling delphi-xe2 jedi-code-library