Search code examples
delphidelphi-7

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.


Solution

  • 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.