错误Delphi XE2 - 异常类$ C00000005

时间:2014-01-24 20:45:31

标签: delphi exception delphi-xe2 access-violation

我收到此错误将调试一个项目,以前在Delphi 7中我已经升级到Delphi XE2,同样的错误发生在几种方法中。

First chance exception at $006DC660. Exception class $C0000005 with message 'access violation at 0x006dc660 read of address 0xffffffff' 

这是其中一种方法:

PFI = ^TFI;           
TFI = record
Id         : TToken;
Name       : TName;
Parameters : string;
end;

function TListFI.IsIn(S: PChar): PFI;

  function SearchName2(Item: PFI):Boolean;
  var N1, N2: PChar;
  begin
    N1:= StrNew(Item^.Name);
    N2:= StrNew(S); //Here is the issue
    SearchName2:= (StrComp(StrUpper(N1), StrUpper(N2)) = 0);
    StrDispose(N1);
    StrDispose(N2);
  end;

begin
  IsIn:= PFI(FirstThat(@SearchName2));
end;

我搜索了一下,发现有人描述了类似的问题,并且他确认当增量链接器被禁用时它可以工作,有人可以告诉我它是什么,在哪里或提供一些建议来解决这种情况。

[编辑]

删除@ now会在IsIn中出现以下错误:= PFI(FirstThat(SearchName2));

E2010 Incompatible types: 'TObject' and 'PFI'

我正在添加FirstThat程序以查看它是否有帮助。

TFuncionColeccion = function (Elemento: TObject): Boolean;

function TColeccion.FirstThat (Rutina: TFuncionColeccion): TObject;
var
  i: Integer;
begin
  For i:=0 to Count-1 do
    if Rutina(Items[i]) then
    begin
      FirstThat:=Items[i];
      exit;
    end;
  FirstThat:=nil;
end;

2 个答案:

答案 0 :(得分:6)

通过指针调用本地(嵌套)过程是(并且一直是)错误,这显然是FirstThat函数的作用。编译器必须对堆栈执行特殊操作以调用本地函数并允许它们访问父作用域的变量(代码中的S),但编译器只能知道当本地函数是直接打电话。编译器无法知道FirstThat的参数将是本地函数,因此当FirstThat调用指向函数时它不包含特殊代码。

底线是函数内部的堆栈没有按照预期的方式设置,这意味着可能出现任何数量的奇怪症状。你将不得不使用其他方式。也许make SearchName2是一个双参数函数,然后写FirstThat接受S作为参数,它可以转发给函数参数。

构造函数指针时,不需要使用@运算符。当你这样做时,编译器倾向于跳过类型检查,这允许你首先将本地函数指针传递给FirstThat。当你传递的函数真正匹配所需的原型时,编译器将允许你在没有@运算符的情况下传递它。

答案 1 :(得分:4)

您正在

中报告访问冲突
StrNew(S)

其中S的类型为PChar。对概率非常接近1的解释是,S实际上并不是指向WideChar的空终止数组的指针。

在Delphi 7中,PCharPAnsiChar的别名。这是一个指向空终止数组AnsiChar的指针,即8位字符。在Delphi XE2中,PCharPWideChar的别名,是指向空终止数组WideChar的指针,即16位字符。

有助于了解StrNew的作用。它遍历数组,直到找到空字符。对于8位文本,这是一个零字节。对于16位文本,空值为零16位字。然后它分配一个与输入字符串长度相同的新内存块,并复制到该新内存中。源代码是:

function StrNew(const Str: PWideChar): PWideChar;
var
  Size: Cardinal;
begin
  if Str = nil then Result := nil else
  begin
    Size := StrLen(Str) + 1;
    Result := StrMove(WideStrAlloc(Size), Str, Size);
  end;
end;

唯一合理的失败模式是当StrLen遍历数组时,它会尝试读取无效的内存。只有输入参数无效时才会发生这种情况。换句话说,这一定是您的编程错误。

一种可能的解释是,尽管您承诺传递16位文本,但事实上您正在将8位文本传递给此函数。这是一个容易犯的错误,特别是如果您还不完全熟悉Unicode更改。 8位文本具有零终止符,但后面的字节不会为零。或者零字节从开始处落在奇数编号的偏移处。然后StrNew继续走缓冲区,但现在它已经结束了,它发生了,它在超载到尚未分配的地址之前没有找到零字。这是违规访问。

如果是这样,那么解决方案将是:

  • 将函数的参数更改为PAnsiChar类型,并在呼叫站点修复可疑的投射。
  • 根据需要传递16位文本功能。

在您的更新中,您包含了无法读取的地址0xffffffff。这是十六进制的-1。这似乎是最平凡的错误。你的指针完全是假的!您可以使用以下代码重现您的确切错误消息:StrNew(PChar(-1))

我这里没有足够的信息告诉你为什么你的指针是假的。希望您已经学习了一些调试和诊断技术,使您能够解决问题。至少你现在知道错误在你的代码中。

假设BuscaName2和SearchName2是同一个东西,那么你不需要再看了。只能从包含函数调用本地过程。正如@Rob正确地说的那样,使用带有过程的@几乎总是不正确的,并且是代码严重问题的警告信号。