This is a 3DS max loader in Delphi6. The program always end with an access violation in memory, somwhere in the T3DObject.Create section, when it is called from an another class. (first occur in TChunk.Load3Data when AddObject is called) Why? How can it be solved?

unit Unit3DS_11;

interface

uses Windows, Classes;

type point = record
        k:integer;
        x,y,z,nx,ny,nz:Single;
     end;

type face = record
        a,b,c:integer;
     end;

type T3DObject = class(TObject)
  private
        // Nothing here at moment
  public
     PointCount, FaceCount: Integer;
     ObjectName:String;
     p:array of point;
     f:array of face;
     constructor Create;
     destructor Destroy; override;
     procedure Draw;
end;

type TChunk = class(TObject)
  private
     function ReadObjectString:String;
  public
     Id: word;
     iBytesReaded, ChunkLength : Integer;
     iFileHandle, iFileLength, ObjCount:Integer;
     Objects:array of T3DObject;
     function ReadChunk: Integer;
     function ReadBuffer(var Buffer; Count:Integer):Integer;
     procedure AddObject(ObjNr: Integer);
     procedure Load3Data;
     constructor Create;
     destructor Destroy; override;
end;

type T3DModel = class(TObject)
  private
     ObjCount:Integer;
     RootChunk:TChunk;
     Objects:array of T3DObject;
  public
     constructor Create;
     destructor Destroy; override;
     procedure LoadFromFile(const FileName:string);
     procedure Clone3Data;
     procedure Draw;
end;

implementation

uses SysUtils, OpenGL;

constructor T3DObject.Create;
begin
     inherited;
     ObjectName:='';
     PointCount:=0; FaceCount:=0;
     p:=nil;
     f:=nil;
end;

destructor T3DObject.Destroy;
begin
        Finalize(p);
        Finalize(f);
        inherited;
end;

procedure T3Dobject.Draw;
var i:integer;
begin
        for i:=0 to FaceCount-1 do begin
                   glBegin(GL_POLYGON);
      glVertex3f(p[f[i].a].x,p[f[i].a].y,p[f[i].a].z);
      glVertex3f(p[f[i].b].x,p[f[i].b].y,p[f[i].b].z);
      glVertex3f(p[f[i].c].x,p[f[i].c].y,p[f[i].c].z);
                   glEnd();
end;  end;

constructor TChunk.Create;
begin
        inherited;
  iFileHandle:=0; iFileLength:=0; iBytesReaded:=0; ObjCount:=0; ChunkLength:=0;
  Id:=0;
        Objects:=nil;
end;

destructor TChunk.Destroy;
begin
        Finalize(Objects);
        inherited;
end;

function TChunk.ReadChunk: Integer;
begin
   iBytesReaded:=FileRead(iFileHandle, Id, 2);
   iBytesReaded:=iBytesReaded + FileRead(iFileHandle, ChunkLength, 4);
   Result:=iBytesReaded;
end;

function TChunk.ReadBuffer(var Buffer; Count: Integer): Integer;
begin
  Result:=FileRead(iFileHandle, Buffer, Count);
  iBytesReaded:=iBytesReaded + Result;
end;

function TChunk.ReadObjectString:String;
var //S:String;
    C:Char;
    i:integer;
    SStr:ShortString;
begin
        C:=#255; i:=0;
   while (C<>#0) {and (i<=20)} do begin
     iBytesReaded:=iBytesReaded+FileRead(iFileHandle, C, 1);
     SStr[i+1]:=C;
     Inc(i);
   end;
  SetLength(Result, I-1);
  Move(SStr[1], Result[1], I-1);
end;

procedure TChunk.AddObject(ObjNr:Integer);
begin
        SetLength(Objects,ObjNr);
        Objects[ObjNr].Create;
end;

procedure TChunk.Load3Data;
var i,x: Integer;
begin
        while (iBytesReaded < iFileLength) do begin
        ReadChunk;
case Id of
      $4D4D: begin
      // MAIN CHUNK
      //  ShowMessage('Found $4D4D at: ' + IntToStr(iBytesReaded) + '  length: ' + IntToStr(ChunkLength));
             end;

      $3D3D: begin
      // 3D EDITOR CHUNK
      //  ShowMessage('Found $3D3D at: ' + IntToStr(iBytesReaded) + '  length: ' + IntToStr(ChunkLength));
        end;

      $4000: begin
      // OBJECT BLOCK
      inc(ObjCount);
      AddObject(ObjCount);
      Objects[ObjCount].ObjectName:=ReadObjectString; // PROPERTY USE ???!!!
      //  ShowMessage(ObjectName);
        end;

      $4100: begin
      // 3D Data Chunk (kb)
             end;

      $4110: begin  // VERTICES LIST
         with Objects[ObjCount] do begin
      ReadBuffer(PointCount, 2);
      SetLength(p,PointCount);
            for i:=0 to PointCount-1 do begin
              ReadBuffer(p[i].x,4);
              ReadBuffer(p[i].y,4);
              ReadBuffer(p[i].z,4);
    end;    end;     end;

          $4120: begin //  FACES DESCRIPTION
     with Objects[ObjCount] do begin
        ReadBuffer(FaceCount, 2);
        SetLength(f,FaceCount);
                for i:=0 to FaceCount-1 do begin
            ReadBuffer(f[i].a, 2);
            ReadBuffer(f[i].b, 2);
            ReadBuffer(f[i].c, 2);
            ReadBuffer(x, 2);
           end;     end;    end;
else
iBytesReaded:=FileSeek(iFileHandle,ChunkLength-6,1);  end;
     end; // while end;
end;

constructor T3DModel.Create;
begin
        inherited;
     ObjCount:=0;
     //RootChunk:=nil;
     Objects:=nil;
end;

destructor T3DModel.Destroy;
begin
        Finalize(Objects);
        inherited;
end;

procedure T3DModel.LoadFromFile(const FileName:string);
var iFileHandle, iFileLength: Integer;
begin
iFileHandle:=0; //iFileLength:=0;
    try
      iFileHandle := FileOpen(FileName, fmOpenRead);  
    finally
      iFileLength := FileSeek(iFileHandle,0,2);
      FileSeek(iFileHandle,0,0);
      RootChunk:=TChunk.Create;
      RootChunk.iFileHandle:=iFileHandle;
      RootChunk.iFileLength:=iFileLength;
      RootChunk.Load3Data;
      Clone3Data;
      RootChunk.Free;   
      RootChunk.Destroy;
      //ShowMessage('RootChunk Destroyed Successfully');
      FileClose(iFileHandle);
    end;
end;

procedure T3DModel.Clone3Data;
var i,j:integer;
begin
   ObjCount:=RootChunk.ObjCount;
   SetLength(Objects,ObjCount);
   for i:=1 to ObjCount do begin
    Objects[i].Create;
        with Objects[i] do begin
        PointCount:=RootChunk.Objects[i].PointCount;
        FaceCount:=RootChunk.Objects[i].FaceCount;
        SetLength(p,Objects[i].PointCount);
        SetLength(f,Objects[i].FaceCount);
        ObjectName:=RootChunk.Objects[i].ObjectName;
    for j:=0 to PointCount-1 do p[j]:= RootChunk.Objects[i].p[j];
    for j:=0 to FaceCount-1 do f[j]:= RootChunk.Objects[i].f[j];
  end;
end;
end;

procedure T3DModel.Draw;
var i:integer;
begin
        for i:=1 to ObjCount do Objects[i].Draw;
end;

end.

===========================================================
===========================================================

To be honest, I didn't want to look too hard at this (and I haven't) just because you didn't put your code in the proper tags.

[[I][/I]code=Delphi[I][/I]]
program hello;
begin
writeln( 'Hello, world!' )
end.
[[I][/I]/code[I][/I]]

becomes all nice and pretty, easy to read, and easy to select, copy, and paste for testing:

program hello;
begin
  writeln( 'Hello, world!' )
end.

BTW, you don't actually have to explicitly assign '' or 0 or nil to anything in the constructor. The inherited; does that for you.

At just a quick glance, it looks like you are trying to keep track of the objects with a counter. So when you say AddObject the first time it goes something like this:

  1. AddObject( 1 )
  2. setLength( Objects, 1 )
  3. Objects[ 1 ].create

You are getting a memory fault because Objects[1] doesn't exist. Objects[0] does though...

The other problem is in part 3 there. You cannot just say foo.create You must say foo := TFoo.create (that, or you must first use the new procedure...).

In general, the way you should handle this kind of thing (playing with dynamic arrays) is always to refer directly to the dynamic array for information about itself. You can do it thus:

procedure TChunk.AddObject;
  begin
  setLength( Objects, length( Objects ) +1 );
  Objects[ high( Objects ) ] := T3DObject.create
  end;

This code also has the property that it will generate a proper exception on error.

If ever you want to know how many objects there are, use the length function.

Also, in the destructor, don't just finalize( Objects ) , as that doesn't bother to call each object's destructor, which means you've got a memory leak. Instead, make sure to free each Object:

for n := 0 to high( Objects ) do Objects[ n ].free;

There is no actual need to call finalize or setlength( Objects, 0 ) or Objects := nil or whatever --the destructor does that for you since Objects is of the proper storage class: destroying the TChunk object will also destroy the member objects.

Hope this helps.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.