Pergunta

I am getting this error will debugging a project, which used to be in Delphi 7 and I have been upgrading to Delphi XE2, the same error happens in several methods.

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

This is one of the methods:

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;

I have googled and I found someone describing a similar problem, and he affirms that when the incremental linker is disabled it works, can someone tell me what and where is it or give some advice to solve this situation.

[EDIT]

Removing the @ now gives me the following error in IsIn:= PFI(FirstThat(SearchName2));

E2010 Incompatible types: 'TObject' and 'PFI'

I am adding the FirstThat procedure to see if it may help.

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;
Foi útil?

Solução

It is (and always has been) an error to call local (nested) procedures by pointer, which is clearly what your FirstThat function does. The compiler has to do special things with the stack to call local functions and give them access to the parent scope's variables (S in your code), but the compiler can only know to do those special things when the local function is called directly. The compiler cannot know that the argument to FirstThat will be a local function, so it doesn't include the special code when FirstThat invokes the pointed-to function.

The bottom line is that the stack inside the function doesn't get set up the way it's supposed to, and that means any number of strange symptoms may appear. You'll have to use some other way. Maybe make SearchName2 be a two-argument function, and then write FirstThat to accept S as a parameter that it can forward to the function argument.

You shouldn't need to use the @ operator when constructing a function pointer. When you do, the compiler tends to skip type checking, which is what allowed you to pass a local function pointer to FirstThat in the first place. When the function you're passing really matches the required prototype, the compiler will allow you to pass it without the @ operator.

Outras dicas

You are reporting an access violation in

StrNew(S)

where S is of type PChar. The explanation for that, with probability very close to 1, is that S is not in fact a pointer to null terminated array of WideChar.

In Delphi 7, PChar is an alias for PAnsiChar. That is a pointer to null terminated array of AnsiChar, i.e. 8 bit characters. In Delphi XE2, PChar is an alias for PWideChar, a pointer to null terminated array of WideChar, i.e. 16 bit characters.

It helps to understand what StrNew does. It walks the array until it finds a null character. For 8 bit text that is a single zero byte. For 16 bit text, the null is a zero 16 bit word. Then it allocates a new block of memory of the same length as the input string, and makes a copy into that new memory. The source code is:

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;

The only plausible failure mode is that when StrLen walks the array, it attempts an invalid memory read. And that can only happen if your input parameter is invalid. In other words, this must be a programming error on your part.

One possible explanation is that you are in fact passing 8 bit text to this function despite promising to pass 16 bit text. An easy mistake to make, especially if you are not yet fully familiar with the Unicode change. The 8 bit text has a zero terminator, but the byte that follows happens not to be zero. Or the zero byte falls at an odd numbered offset from the start. And then StrNew continues walking the buffer, but now it is off the end and it so happens that it doesn't find a zero word before overrunning into an address that has not been allocated. And that is an access violation.

If that is so then solution will be either:

  • Change the function's parameter to be of type PAnsiChar, and fix the dubious casting at the call site.
  • Pass the function 16 bit text as it requires.

In your update you include the address which cannot be read, 0xffffffff. This is -1 in hex. And that would seem to be the most prosaic of errors. Your pointer is completely bogus! Your exact error message can be reproduced with this code: StrNew(PChar(-1)).

I don't have enough information here to tell you why your pointer is bogus. Hopefully you've learnt some debugging and diagnostic techniques that will enable you to solve the problem. At least you now know that the error is in your code.

Assuming that BuscaName2 and SearchName2 are one and the same thing, then you need look no further. Local procedures can only be called from a containing function. As @Rob correctly says, the use of @ with procedures is almost always incorrect and is a warning sign of serious problems with your code.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top