I Love Stackoverflow
I Love Stackoverflow

Reputation: 6868

Access violation while using "record" type in delphi7

What I need to do is read table details from excel file and want to create .pas file from it.

For the read-write purpose, I've used record type in delphi 7.

Here is the code what I've tried till now:

unit fImportFile;
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    tSourceFile: TEdit;
    dlgSourceFile: TOpenDialog;
    btnImport: TButton;
    procedure tSourceFileClick(Sender: TObject);
    procedure btnImportClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TTableDetails = record
    fTableName: String;
  end;

  TFieldDetails = record
    fFieldName: String;
    fType: String;
    fShortAlias: String;
    fLongAlias: String;
    fDomainName: String;
    fFieldAttributes:TStringList;
    fComments: String;
  end;

var
  Form1: TForm1;

implementation

uses ComObj, uFileGeneration;

{$R *.dfm}

procedure TForm1.tSourceFileClick(Sender: TObject);
begin
  with dlgSourceFile do
  begin
    FileName := tSourceFile.Text;
    if ExtractFilePath(FileName) <> '' then
      InitialDir := ExtractFilePath(FileName);
    if Execute then
      if FileName <> '' then
        tSourceFile.Text := FileName;
  end;

end;

procedure TForm1.btnImportClick(Sender: TObject);
const
  cEndOfTables = '';
  cTable = 'Table';
  cTableCell = 2;
  cTableNameCell = 3; // TableNameField
  cFieldName = 'Field Name';
var
  Excel: OleVariant;
  iRow: Integer;
  aTableDetails: TTableDetails;
  aFieldDetails: Array of TFieldDetails;
  fieldCount: Integer;
  iTableName, FldWithChar, FldWithoutChar: String;
  FldList, WordList: TStringList;
  FieldName: String;
begin
  FldList := nil;
  WordList := nil;

  try
    Excel := CreateOleObject('Excel.Application');
    Excel.Visible := False;
    Excel.Workbooks.Open(tSourceFile.Text);
    iRow := 1;

    while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') do //To exit loop when the excel record will read blank TableName
    begin
      if (Excel.ActiveSheet.Cells[iRow,cTableCell].Value = cTable) then
      begin
        iTableName := Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value;
        if (iTableName = '') then
        begin
          ShowMessage('Table Name cannot be blank.');
          exit;
        end;
        aTableDetails.fTableName := iTableName;
        Inc(iRow);

        fieldCount := 0;
        FldList := TStringList.Create;
        WordList := TStringList.Create;
        ShowMessage('1 -- iRow --> ' + IntToStr(iRow));

        while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') AND
          (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> cTable) do //Will create record until another table will found
        begin
          ShowMessage('2 -- Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value = ' + Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value);
          FieldName := Excel.ActiveSheet.Cells[iRow,cTableNameCell].Value;
          aFieldDetails[fieldCount].fFieldName := FieldName; //ERROR LINE

          ShowMessage('3 -- iRow --> ' + IntToStr(iRow));
          FldWithChar := aFieldDetails[fieldCount].fFieldName;
          FldWithoutChar := NameWithoutAnyChar(FldWithChar, WordList);
          FldList.Add(FldWithChar + '=' + FldWithoutChar);
          WordList.Clear;
          ShowMessage('4 -- iRow --> ' + IntToStr(iRow));

          if (aFieldDetails[fieldCount].fFieldName = '') then
          begin
            ShowMessage('Field Name cannot be blank. TableName: '+iTableName);
            exit;
          end;
          aFieldDetails[fieldCount].fType := Excel.ActiveSheet.Cells[iRow,3].Value;
          aFieldDetails[fieldCount].fShortAlias := Excel.ActiveSheet.Cells[iRow,4].Value;
          aFieldDetails[fieldCount].fLongAlias := Excel.ActiveSheet.Cells[iRow,5].Value;
          aFieldDetails[fieldCount].fDomainName := Excel.ActiveSheet.Cells[iRow,6].Value;
          aFieldDetails[fieldCount].fFieldAttributes.CommaText := Excel.ActiveSheet.Cells[iRow,7].Value;
          aFieldDetails[fieldCount].fComments := Excel.ActiveSheet.Cells[iRow,8].Value;
          Inc(fieldCount);
          Inc(iRow);
        end;
        //Once new table row will be fouund it will call below method to create a dataview file for current table
        ShowMessage('5 -- iRow --> ' + IntToStr(iRow));
        GenerateDataviewFiles(aTableDetails, aFieldDetails, FldList, fieldCount-1);
        ShowMessage('6 -- iRow --> ' + IntToStr(iRow));
      end; //End of If condition
    end; //End of outer most while loop
  finally
    FreeAndNil(WordList);
    FreeAndNil(FldList);
    Excel.Workbooks.Close;
    Excel.Quit;
    Excel := Unassigned;
  end;
end;
end.

I've commented //ERROR LINE where I'm getting error for access violation.

What I'm doing here is, creating array of TFieldDetails and wanted to loop through it in another file.

Please help me through this as I am a new to delphi.

Upvotes: 1

Views: 501

Answers (2)

Ren&#233; Hoffmann
Ren&#233; Hoffmann

Reputation: 2815

You forgot to initialize dynamic array aFieldDetails. Thus, its length is still zero when you try to write to it with an index fieldCount.

If you know the total count of fields, you should preallocate the full array by calling

SetLength(aFieldDetails, TheTotalFieldCount);

If you don't know the total count beforehand, you may use a list or another dynamic data structure, because reallocating every time you add an item is very expensive in terms of performance.

aFieldDetailList := TList.Create;
try
  while (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> '') AND
        (Excel.ActiveSheet.Cells[iRow,cTableCell].Value <> cTable) do //Will create record until another table will found
  begin
    New(aFieldDetail); // create a new instance of aFieldDetail; aFieldDetail is of pointer type "^TFieldDetails"

    // do your loop work
  end;
  // do your after-loop work
finally
  // free all allocated memory
  for aFieldDetail in aFieldDetailList do
    Dispose(aFieldDetail);
  FreeAndNil(aFieldDetailList);
end;

Upvotes: 3

Javid
Javid

Reputation: 2935

The problem is that you're trying to access an index of aFieldDetails which is out of bounds. You should set length of a dynamic array before that. Like this:

...
SetLength(aFieldDetails, fieldCount+1);
aFieldDetails[fieldCount].fFieldName := FieldName; //ERROR LINE
...

However, this has a poor performance unless you know how many total fields you have since the beginning. It's because everytime SetLength gets called, another block of memory is allocated and the whole array is copied to it.

I suggest you to use TList which tries to keep performance in a good shape even if you don't know how many items are gonna be added to your list.

Upvotes: 4

Related Questions