Home > OS >  Delphi THandleStream seek bug
Delphi THandleStream seek bug

Time:08-29

I spent hours on this, and when I discovered the solution, I was about to fall from my chair...

I'm reading a 6Gb AVI file uncompressed.

Using this variable to handle the file, in Delphi 10.4:

VideoFile:THandleStream; 
...
VideoFile := THandleStream.Create(FileHandle);

I realized when going from seeks below 0x7FFFFFFF was OK, but above was not OK.

When I looked to the declarations, Offset is an Int64, OK:

function THandleStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
begin
  Result := FileSeek(FHandle, Offset, Ord(Origin));
end;

Then FileSeek() goes here with ... a 32-bit signed integer... That I did not go into at first glance. What a mistake!

function FileSeek(Handle: THandle; Offset, Origin: Integer): Integer;
begin
{$IFDEF MSWINDOWS}
  Result := SetFilePointer(Handle, Offset, nil, Origin);
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
  Result := lseek(Handle, Offset, Origin);
{$ENDIF POSIX}
end;

This is really nasty and very very bad... Why did Embarcadero choose this misleading implementation?

I have used VideoFile.Position := XXXX to overcome the issue. But why leave Int64 in THandleStream.Seek()?

CodePudding user response:

Your diagnosis is incorrect.

There are two versions of FileSeek() - a 32bit version, and a 64bit version:

function FileSeek(Handle: THandle; Offset, Origin: Integer): Integer;
begin
{$IFDEF MSWINDOWS}
  Result := SetFilePointer(Handle, Offset, nil, Origin);
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
  Result := lseek(Handle, Offset, Origin);
{$ENDIF POSIX}
end;

function FileSeek(Handle: THandle; const Offset: Int64; Origin: Integer): Int64;
{$IFDEF MSWINDOWS}
begin
  Result := Offset;
  Int64Rec(Result).Lo := SetFilePointer(Handle, Int64Rec(Result).Lo,
    @Int64Rec(Result).Hi, Origin);
  if (Int64Rec(Result).Lo = $FFFFFFFF) and (GetLastError <> 0) then
    Int64Rec(Result).Hi := $FFFFFFFF;
end;
{$ENDIF MSWINDOWS}
{$IFDEF POSIX}
begin
  Result := lseek(Handle, Offset, Origin);
end;
{$ENDIF POSIX}

You are looking only at the 32bit version of FileSeek(), but THandleStream overrides the 64bit version of TStream.Seek() to call the 64bit version of FileSeek() instead.

THandleStream.Seek() is passing a THandle, an Int64, and a Byte to FileSeek(), so which version do you think is going to be called? The 64bit version. The compiler can upsize a Byte to an Integer in both cases, but it is not going to downsize an Int64 to an Integer when it can pass the Int64 as-is instead. So the 64bit version of FileSeek() is a better match for what THandleStream.Seek() is passing.

If that were not the case, then your claim that:

I have used VideoFile.Position := XXXX to overcome the issue.

would not be true, since the TStream.Position setter method simply calls the same 64bit Seek() method that you are claiming to be having trouble with:

procedure TStream.SetPosition(const Pos: Int64);
begin
  Seek(Pos, soBeginning); // <-- soBeginning is from TSeekOrigin
end;

So, whatever trouble you are actually having, it is not due to the 64bit THandleStream.Seek() calling the 32bit FileSeek(). That is simply not how THandleStream works.

  • Related