【问题标题】:Access violation at address 00822135 in module 'GUI.exe'.Read of address 00000040 [closed]模块'GUI.exe'中地址00822135的访问冲突。读取地址00000040 [关闭]
【发布时间】:2012-12-25 12:54:12
【问题描述】:

sqlite 有一个数据库——一个描述符,包含表列表、域列表、字段列表、限制列表(主键和外键)、索引列表。我在内置组件的帮助下从 Delphi XE3 连接到这个基础。有一个单独的模块,其中描述了 TTableSpec、TFieldSpec、TConstraintSpec、TConstraintDetSpec、TDomainSpec 类。这些类对应于上述基础sqlite的记录。在 TTableSpec 类中有这样的 FFields 字段: TComponent 成为对象的所有者,如 TFieldSpec 也已从基础卸载(例如,DBSchema 也包含 FTables 字段,作为所有表的所有者),并且在类 TFieldSpec 中有一个 FDomainSpec 字段:TDomainSpec。实际上我给出了一个连接到错误的类代码

type
TDataTypeId = (DataTypeId_String, DataTypeId_SmallInt, DataTypeId_Integer, DataTypeId_Word,
               DataTypeId_Boolean, DataTypeId_Float, DataTypeId_Currency,
               DataTypeId_BCD, DataTypeId_FmtBCD, DataTypeId_Date,
               DataTypeId_Time, DataTypeId_DateTime, DataTypeId_TimeStamp,
               DataTypeId_Bytes, DataTypeId_VarBytes, DataTypeId_Blob,
               DataTypeId_Memo, DataTypeId_Graphic, DataTypeId_fmtMemo,
               DataTypeId_FixedChar, DataTypeId_WideChar, DataTypeId_LargeInt,
               DataTypeId_Array, DataTypeId_FixedWideChar, DataTypeId_WideMemo, DataTypeId_Default);
TDBSchemaSpec=class(Tcomponent)
  private
    FDomains: TComponent;
    FTables : TComponent;
  public
    procedure Setup();
    destructor Destroy; override;
    property Domains: TComponent read FDomains;
    property Tables : TComponent read FTables;
end;
TDomainSpec = class(TComponent)
  private
    FName: string;
    FDescription: String;
    FDataTypeId: TDataTypeId;
    FLength: Cardinal;
    FCharLength: Cardinal;
    FPrecision: Cardinal;
    FScale: Cardinal;
    FWidth: Word;
    FAlign: TAlignSpec;
    FShowNull: Boolean;
    FShowLeadNulls: Boolean;
    FThousandsSeparator: Boolean;
  public
    procedure Setup(FName: string; FDescription: String; FDataTypeId: TDataTypeId;
    FLength: Cardinal;FCharLength: Cardinal;FPrecision: Cardinal;FScale: Cardinal;
    FWidth: Word;FAlign: TAlignSpec;FShowNull: Boolean;FShowLeadNulls: Boolean;
    FThousandsSeparator: Boolean);
    destructor Destroy; override;
    property Name: String read FName;
    property Description: String read FDescription;
    property DataTypeId: TDataTypeId read FDataTypeId;
    property Length: Cardinal read FLength;
    property CharLength: Cardinal read FCharLength;
    property Precision: Cardinal read FPrecision;
    property Scale: Cardinal read FScale;
    property Width: Word read FWidth;
    property Align: TAlignSpec read FAlign;
    property ShowNull: Boolean read FShowNull;
    property ShowLeadNulls: Boolean read FShowLeadNulls;
    property ThousandsSeparator: Boolean read FThousandsSeparator;
  end;

TTableSpec= class(TComponent)
  private
    FFields : TComponent;
    FIndices: TComponent;
    FConstraints : TComponent;
    FName : string;
    FDescription: string;
    FCanAdd:  boolean;
    FCanEdit: boolean;
    FCanDelete: boolean;
  public
    procedure Setup(FName : string; FDescription:string;
    FCanAdd:  boolean; FCanEdit: boolean; FCanDelete: boolean);
    destructor Destroy; override;
    property Description : string read FDescription;
    property Name : string read FName;
    property CanAdd:  boolean read FCanAdd;
    property CanEdit: boolean read FCanEdit;
    property CanDelete: boolean read FCanDelete;
    property Fields : TComponent read FFields;
    property Indices: TComponent read FIndices;
    property Constraints : TComponent read FConstraints;
  end;

TFieldSpec = class(TComponent)
  private
    FDomainSpec: TDomainSpec;
    FPosition: integer;
    FFieldName: string;
    FDescription: string;
    FCanInput: boolean;
    FCanEdit: boolean;
    FShowInGrid: boolean;
    FShowInDetails: boolean;
    FIsMean: boolean;
    FAutoCalculated: boolean;
    FRequired: boolean;
  public
    procedure Setup(FDomainSpec: TDomainSpec; FPosition: integer; FFieldName: string; FDescription: string; FCanInput: boolean; FCanEdit: boolean;
    FShowInGrid: boolean; FShowInDetails: boolean; FIsMean: boolean;FAutoCalculated: boolean;
    FRequired: boolean);
    destructor Destroy; override;
    property DomainSpec: TDomainSpec read FDomainSpec;
    property Position: integer read FPosition;
    property FieldName: string read FFieldName;
    property Description: string read FDescription;
    property CanInput: boolean read FCanInput;
    property CanEdit: boolean read FCanEdit;
    property ShowInGrid: boolean read FShowInGrid;
    property ShowInDetails: boolean read FShowInDetails;
    property IsMean: boolean read FIsMean;
    property AutoCalculated: boolean read FAutoCalculated;
    property Required: boolean read FRequired;
 end;

在应用程序中有一个表格 fmListOfTables,在 CheckListBox 中选择表格名称,该表格的数据将在其他窗口的 DBGrid 中显示。错误

Access violation at address 00822135 in module 'GUI.exe'.Read of address 00000040.(When debugging Project GUI.exe raised exception class $C0000005 with message 'access violation at 0x00822135:  read of address 0x00000040'.  )  

出现在行

if ((TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i] is TFieldSpec) and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec).DataTypeId<>DataTypeId_Blob) and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec).DataTypeId<>DataTypeId_WideMemo)) then

在表的选择过程中

procedure TfmListOfTables.ListBox1DblClick(Sender: TObject);
var  fmShowData : TfmTableData;
      i : integer;
      querystr : string;
begin
  MainWindow.IBDatabase1.DatabaseName:=MainWindow.dbname;
  MainWindow.IBDatabase1.Connected:=true;
  fmShowData:=TfmTableData.Create(MainWindow);
  fmShowData.Caption:=ListBox1.Items.Strings[ListBox1.ItemIndex];
  fmShowData.tname:=ListBox1.Items.Strings[ListBox1.ItemIndex];
  DisplayTable:=ListBox1.Items.Strings[ListBox1.ItemIndex];
  querystr:='select ';
  for i:= 0 to TTableSpec(DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.ComponentCount-1 do
     begin
     if ((TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i] is TFieldSpec) and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec).DataTypeId<>DataTypeId_Blob) and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec).DataTypeId<>DataTypeId_WideMemo)) then
        querystr:=querystr+TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).FieldName+', ';
     end;
  Delete(querystr, Length(querystr)-1, 1);
  querystr:=querystr+'from '+fmShowData.tname;
  fmShowData.IBQuery1.SQL.Clear;
  fmShowData.IBQuery1.SQL.Add(querystr);
  fmShowData.IBQuery1.Open;
  fmShowData.DBGrid1.DataSource:=fmShowData.DataSource1;
  for I := 0 to fmShowData.DBGrid1.Columns.Count-1 do
    fmShowData.DBGrid1.Columns[i].Width:=90;
  fmShowData.DragKind:=dkDock;
  fmShowData.DragMode:=dmAutomatic;
  fmShowData.tname:=ListBox1.Items.Strings[ListBox1.ItemIndex];
  TfmTableData(fmShowData).databasetable:=ListBox1.Items.Strings[ListBox1.ItemIndex];
  fmShowData.Show;
end;

【问题讨论】:

  • 可能是世界上最长的代码行?不可能调试这样的线路。正如 David 建议的那样,将其分解为多行并缩小搜索范围,以找出导致 A/V 的确切原因。

标签: delphi delphi-xe2 runtime-error access-violation


【解决方案1】:

让我们看看导致问题的代码行:

if ((TTableSpec(MainForm.DBSchema.Tables.FindComponent(
  ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i] is TFieldSpec) 
  and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.DBSchema.Tables.FindComponent(
  ListBox1.Items.Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec)
  .DataTypeId<>DataTypeId_Blob) and (TDomainSpec(TFieldSpec(TTableSpec(MainForm.
  DBSchema.Tables.FindComponent(ListBox1.Items.
  Strings[ListBox1.ItemIndex])).Fields.Components[i]).DomainSpec).
  DataTypeId<>DataTypeId_WideMemo)) then

很难找到关于那行代码的任何内容。纯粹而简单,这是一个彻头彻尾的讽刺。

首先要做的是使用一些局部变量。例如:

ComponentName := ListBox1.Items.Strings[ListBox1.ItemIndex];

那么我们可以这样写:

Component1 := MainForm.DBSchema.Tables.FindComponent(ComponentName);

继续我们的思路:

Component2 := TTableSpec(Component1).Fields.Components[i];

那么代码可以是这样的:

if (Component2 is TFieldSpec) 
and (TDomainSpec(TFieldSpec(Component2).DomainSpec).DataTypeId<>DataTypeId_Blob) 
and (TDomainSpec(TFieldSpec(Component2).DomainSpec).DataTypeId<>DataTypeId_WideMemo)) then

您的首要任务是按照我建议的思路以理智的方式重新编写代码。

然后是实际的错误。读取地址 $00000040 时出现访问冲突。这是尝试取消引用空指针的标志。很明显这里有nil。我说不出来。但是你的调试器会告诉你。确保将其配置为在引发异常时中断。这是 IDE 调试器选项中的设置。

很可能其中一个FindComponent 调用返回nil。或者DomainSpecnil。您可能需要将单个 if 语句拆分为多个 if 语句,以便于调试。

我的最后一点是重申我最初的建议。这段代码是行不通的。立即将其重构为合理的形式。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-12-15
  • 1970-01-01
  • 2015-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多