在 delphi7 中使用 "record" 类型时发生访问冲突

Access violation while using "record" type in delphi7

我需要做的是从 excel 文件中读取 table 详细信息并创建 .pas 文件。

出于读写目的,我使用了 record 输入 delphi 7.

这是我到目前为止尝试过的代码:

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.

我在 //ERROR LINE 中评论了访问冲突错误。

我在这里做的是,创建 TFieldDetails 数组,并希望在另一个文件中循环它。

请帮助我解决这个问题,因为我是 delphi 的新手。

问题是您正在尝试访问超出范围的 aFieldDetails 的索引。您应该在此之前设置动态数组的长度。像这样:

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

但是,除非您从一开始就知道总共有多少个字段,否则性能会很差。这是因为每次 SetLength 被调用时,都会分配另一块内存并将整个数组复制到它。

我建议您使用 TList,即使您不知道要将多少项目添加到列表中,它也会尝试保持良好的性能。

您忘记初始化动态数组aFieldDetails。因此,当您尝试使用索引 fieldCount.

写入它时,它的长度仍然为零

如果您知道字段的总数,则应通过调用

预分配整个数组
SetLength(aFieldDetails, TheTotalFieldCount);

如果您事先不知道总数,您可以使用列表或其他动态数据结构,因为每次添加项目时重新分配在性能方面非常昂贵。

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;