【问题标题】:Access violation on combobox clear组合框清除访问冲突
【发布时间】:2015-11-08 00:54:36
【问题描述】:

我正在使用 Delphi XE8 编写一个可以访问多个数据库的客户端/服务器应用程序。

作为设置表单的一部分,TComboBoxEx 用于显示和选择动态记录数组,其中包含每个数据库的信息,两个按钮用于在数组中添加或删除记录。

数组保存和加载到单个格式化字符串,每条记录的名称属性用作每个组合框项的标题。

在表单显示和添加或删除数据库按钮按下更新过程时调用,该过程首先清除并重新加载数组,然后重新填充组合框。

procedure TSettingsForm.UpdateDatabaseCombo;
var
  i: integer;
begin
  LoadDatabaseList; // Clears and reloads databaselist dynamic array.

  with dbCombo.Items do
  begin
    BeginUpdate;
    if Length(DatabaseList) <> 0 then
    begin
      Clear;    // This section works fine.
      for i := 0 to High(DatabaseList) do
      begin
        Add(DatabaseList[i].dbName);
        dbCombo.ItemsEx[i].ImageIndex := 17;
      end;
    end;

    if (Length(DatabaseList) < 1) and (Count <> 0) then
    begin
      Clear;  // Access violation on this line.
    end;
    EndUpdate;
  end;
end;

该过程在启动时初始加载数组时运行良好,即使没有保存记录,并且添加了新记录。 但是,在删除记录期间会出现问题,或者我应该说在删除记录后更新组合框(从数据库列表中删除记录没有问题)。

如果数组中存储的记录不止一个,一切正常,组合框将被清除并重新填充。然而,如果要删除的记录是最后存储的,即索引 0,如前所述,记录会按预期从数组中删除,但组合框更新过程在调用 clear 方法时会抛出错误Access violation at address 0040B2D2 in module ***** Read of address FFFFFFE7。之后组合框项目列表被清除,但最后一个选定项目的文本仍然存在。

我尝试了许多不同的方法来解决这个问题,接近成功的方法是使用删除项目方法,

Delete(dbCombo.ItemIndex);

这将再次从组合框中清除最后一项,无一例外,但文本仍然保留。

请任何人帮助解决这个问题,并指出我缺少的简单的小东西。


Addition........

根据要求,这是UpdateDatabaseCombo 例程的更新代码,

procedure TSettingsForm.UpdateDatabaseCombo;
var
  i: integer;
begin
  LoadDatabaseList; // Clears and reloads databaselist dynamic array.

  if dbCombo.Items.Count <> 0 then
    dbCombo.Items.Clear;

  with dbCombo.Items do
  begin
    BeginUpdate;
    if Length(DatabaseList) <> 0 then
    begin
      for i := 0 to High(DatabaseList) do
      begin
        Add(DatabaseList[i].dbName);
        dbCombo.ItemsEx[i].ImageIndex := 17;
      end;
    end;
    EndUpdate;
  end;
end;

我注意到,如果在没有检查 items.count 值是否大于 0 的情况下调用 items.clear 并且每次调用例程时都会引发异常,并且计数检查到位,只会引发异常当组合框中的最后一项被删除时。

我还添加了从数组中删除记录的例程,

procedure TSettingsForm.DBRemoveButtonClick(Sender: TObject);
var
  ALength: Cardinal;
  i: Cardinal;
begin
  ALength := Length(DatabaseList);
  Assert(ALength > 0);
  Assert(dbCombo.ItemIndex < ALength);
  for i := dbCombo.ItemIndex + 1 to ALength - 1 do
    DatabaseList[i - 1] := DatabaseList[i];
  SetLength(DatabaseList, ALength - 1);
  SaveDatabaseList;
  UpdateDatabaseCombo; 
  if dbCombo.Items.Count <> 0 then
    dbCombo.ItemIndex := 0;
end;

如果在组合框为空时调用clear,程序中的任何地方也会发生异常。

【问题讨论】:

  • 去掉with 语句,改为对每个属性使用dbCombo.Items,然后使用调试器找出问题所在。这是在代码中使用 with 时可能发生的坏事的又一个例子。它使问题难以发现并且无法调试(至少在您做正确的事情并消除使用with 之前)。
  • 感谢@Ken 的回复,但在这种情况下你错了,因为问题不在于with 循环,为了证明这一点,我删除了第二个if 语句替换为if dbCombo.Items.Count &lt;&gt; 0 then dbCombo.Items.Clear加载数组后立即但结果仍然是相同的异常错误。 ps 对上一条评论感到抱歉,误按了回车。
  • @KenWhite 没有错:无论如何,您都应该避免使用“with”。无论如何,我怀疑你的问题在于你没有向我们展示的东西。调用 dbCombo.Items.Clear 两次不应导致 AV。请在您的 q 中添加您在 DatabaseList 中添加和删除记录的代码。
  • @Ken,请原谅我,我并不是说您对 with 的使用有误,我只是说在这种情况下这不是问题的原因,反正我现已更新问题以提供更多信息。
  • 我认为您应该尝试在您的配置中启用“使用调试 dcu”。然后运行。然后使用调用堆栈找出调用组合框清除时异常究竟发生在哪里。

标签: arrays delphi combobox delphi-xe8


【解决方案1】:

非常感谢大家的帮助。终于找到问题了,

'God I feel so silly'.

罪魁祸首是在OnChange 事件处理程序中,我在其中放置了一条指令,从数据库记录密码属性中填充编辑框,当从组合框中选择数据库时,该属性会自动更改。

问题是我没有添加条件语句来检查数据库列表中是否有要加载的项目,因此当没有引发异常时。

两天时间浪费在一个简单的if 声明上。我已经有几年没有做过任何真正的编程了,我真的很怀念那些拔头发的时刻。

再次,非常感谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 2013-02-23
    • 1970-01-01
    • 1970-01-01
    • 2018-11-16
    • 2013-06-23
    相关资源
    最近更新 更多