我在工作中切换到其他项目,我注意到Delphi XE2调试器没有显示引发异常的行。当我回到家时,我开始调查。然后我发现它可以在工具 - >中禁用选项 - >调试器选项并检查集成调试。此外,我取消选中要忽略的例外类型列表中的语言例外下的所有内容。 语言例外通知已选中。 项目 - >选项 - >正在编译,我有默认设置,启用了溢出和范围检查。我正在运行调试构建。我清理它。
我之前没有注意到,但是现在Delphi调试器在调用此代码时没有给我这一行:
procedure TForm1.BitBtn1Click(Sender: TObject);
var
_List: TStringList;
begin
_List := TStringList.Create;
try
Caption := _List[0]; // 'List index out of bounds (0)' here
finally
FreeAndNil(_List);
end;
end;
但这有效(仅用于显示调试器确实显示某些内容的行):
{$R+} // Range check is ON
procedure TForm1.BitBtn2Click(Sender: TObject);
var
_myArray: array [1 .. 5] of string;
i: integer;
begin
for i := 0 to 5 do
begin
_myArray[i] := 'Element ' + IntToStr(i); // Range check error here
ShowMessage('myArray[' + IntToStr(i) + '] = ' + _myArray[i]);
end;
end;
这里发生了什么?如何让调试器尽可能多地显示?
谢谢。
答案 0 :(得分:2)
让我先回答这个问题。
如何使编译器尽可能显示
编译器显示错误发生在对btnclick的调用中
诀窍是使用 F5 在btnclick proc的第一行放置一个断点。
然后重建(!)应用程序并再次运行
执行将停止断点
使用 F8 逐步执行代码,直到出现错误
在产生错误的行上放置一个断点 F5
中止并重新运行申请
当你到达第二个断点而不是按 F8 时,按 F7 进入导致错误的例程,继续按 F7 / F8 直到你看到究竟是什么导致了这个问题。
为什么会这样?
编译器通过跟踪堆栈跟踪来追溯异常的来源
因为在您的情况下,生成异常的代码没有堆栈跟踪(因为它不是调试代码),编译器无法执行此操作,而是遵循具有的堆栈跟踪;它在你的代码中向上移动一级并在那里标记异常。
详细了解
你比较苹果和橘子。
此代码(图表A):
Caption := _List[0]; // 'List index out of bounds (0)' here
与此代码完全没有任何共同之处(图表B):
_myArray: array [1 .. 5] of string;
....
_myArray[0]:= 'Hallo';
图表A使用TStringList类的items
属性,该属性或多或少地定义如下(我已经简化了一点,但基本原理是正确的):
type
TStringList = class(TStrings)
strict private
FList: array of string;
....
private
procedure Put(index: integer; const value: string);
function Get(index: integer): string;
published
property Items[index: integer]: string read Get write Put; default;
// ------------------------------------------------------^^^^^^^
....
end;
请注意default
属性末尾的Items
关键字。
这一切意味着当您致电_List[0]
时,您实际上正在调用_List.Items[0]
,因为该属性上的读取修饰符而被转换为Caption:= _List.Getitems(0)
。
default
关键字允许您省略.Items
。
Get
代码如下所示:
function TStringList.Get(Index: Integer): string;
begin
if Cardinal(Index) >= Cardinal(FCount) then
Error(@SListIndexError, Index); <<-Here is the line that generates the error*
Result := FList[Index].FString;
end;
* 实际上错误是在Error
例程
除非您拥有RTL / VCL源代码和,否则您将使用调试DCU运行,您将无法确定异常的确切触发器(在内部system.classes)单位 请注意,此错误不依赖于范围检查,它将始终触发 因为Delphi没有生成错误的确切行的调试信息,所以它会做出下一个最好的事情并尝试猜测。
简短版
stringlist是一个假装成数组的复杂抽象
调用大量代码,使编译器难以精确定位错误。
在图表B中:
_myArray: array [1 .. 5] of string;
....
i:= 0;
_myArray[i]:= 'Hallo';
生成范围检查错误或访问冲突 这两个错误都发生在确切的行上,允许编译器停在正确的位置。
简短版
plain 数组是一个基本的构建块,在其他地方没有对代码的隐藏调用,这使得编译器的精确定位错误非常容易。
了解属性
类和记录属性(现在类操作符)看起来像对变量的简单赋值/操作,但实际上是对(可能是复杂的)子例程的调用。