我已将 Delphi 2009 桌面应用程序转换为控制台应用程序(添加
{$APPTYPE CONSOLE}
并避免创建 MainForm,而是创建 TDataModule 后代并使用它们),现在我收到 MadExcept 调用堆栈:
exception class : EAccessViolation
exception message : Access violation at address 0085840B in module 'HRSuite.exe'. Read of address 0C0B7260.
main thread ($4cc0):
0085840b +03f HRSuite.exe DBClient 1212 +3 TCustomClientDataSet.CloseCursor
00567fef +0a7 HRSuite.exe DB 10601 +21 TDataSet.SetActive
00567df0 +004 HRSuite.exe DB 10553 +0 TDataSet.Close
00559c2e +012 HRSuite.exe DB 4066 +3 TField.Destroy
0049ed23 +047 HRSuite.exe Classes 11584 +9 TComponent.DestroyComponents
0049eb17 +04f HRSuite.exe Classes 11486 +8 TComponent.Destroy
0049fd42 +066 HRSuite.exe Classes 12519 +6 TDataModule.Destroy
005c9384 +0b8 HRSuite.exe HRBaseDMU 349 +22 THRBaseDM.Destroy
0049ed23 +047 HRSuite.exe Classes 11584 +9 TComponent.DestroyComponents
005353e9 +035 HRSuite.exe Forms 1755 +9 DoneApplication
0054325a +022 HRSuite.exe Forms 11116 +1 Finalization
00405ac9 +039 HRSuite.exe System 12865 +20 FinalizeUnits
00468428 +054 HRSuite.exe madExcept InterceptFinalizeUnits
00468430 +000 HRSuite.exe madExcept InterceptHalt0FinalizeUnits
00405ec5 +09d HRSuite.exe System 13632 +61 @Halt0
010cabb9 +151 HRSuite.exe HRSuite 545 +37 initialization
75a0fcc7 +017 KERNEL32.DLL BaseThreadInitThunk
据我所知,我至少尝试确定哪个 DataModule 在 Destroy 中存在异常:
procedure THRBaseDM.DataModuleDestroy(Sender: TObject);
var tmp: Integer;
begin
Writeln(Self.Name);
Writeln('BASE before destroy: '+Self.Name);
inherited;
Writeln('BASE after destroy: '+Self.Name);
end;
但最终输出行是:
FreeDaysDM
BASE before destroy: FreeDaysDM
BASE after destroy: FreeDaysDM
An error occurred in the application.
我预计在没有关闭通知的情况下会有“销毁前的基础:...”,这可能是识别有问题的 DataModule 的方法,但是在没有关闭通知的情况下没有打开通知,所以 - 我应该尝试获取有关的信息以某种不同的方式冒犯 DataModule。但如何呢?
OnDestroy
不是捕获异常的地方,相反,必须在数据模块中重写析构函数本身:
public
destructor Destroy; override;
然后
destructor THRBaseDM.Destroy;
var i: Integer;
begin
Writeln('REAL BASE before destroy: '+Self.Name);
try
//... some large business or technical code
inherited;
except
on E: Exception do begin
Writeln('REAL '+Self.name);
Writeln('REAL '+E.Message);
end;
end;
Writeln('REAL BASE after destroy: '+Self.Name);
end;
这样的覆盖给我们带来了预期的错误报告。