4

我有一个通过一些数据源附加到 TDBGrid 的内存数据集。问题是,每当用户在数字字段中输入减号并按 ENTER 或导航到另一条记录时,都会触发 AV(验证错误)。我无法从 TDBGrid 或 TClientDataSet(OnEditError、OnPostError)中捕获 AV。

我在这里做错什么了吗。如果没有,是否有人有解决方法?

使用 Delphi XE2 Enterprise,第 4 次更新;最新的 MIDAS.DLL;目标平台:Win64。

==================================================== ==============================

请尝试以下方法:

1- 创建一个新的 VCL 应用程序/表单,并将对象:TClientDataSet、一个 TDBGrid 和一个 TDataSource 添加到表单中。ClientDataSet 未附加到任何数据库(内存中)

2- 向 Table1(TClientDataSet)添加一个持久的数字字段。

3-链接上述三个对象。持久字段的列应该出现在 TDBGrid 上。

4- 在主窗体的 OnShow 方法中添加以下行:

Table1.Active := 假;

表1.创建数据集;

Table1.Active := 真;

5- 运行您的程序并在数字字段列中键入减号,然后按 Enter。

检查 AV,然后尝试将 OnEditError 和/或 OnPostError 事件处理程序添加到 Table1 以捕获 AV。您不能中止帖子。意思是,AV 会发生,你只能使用 Application.OnException 处理程序来捕获它。这不好。假设您的应用程序包含许多表单和例程,而不是更高级别,您应该能够在程序运行的级别上执行某些操作。

我希望我的问题更清楚。

4

1 回答 1

7

正如 Ken 所说,没有示例代码很难为您提供帮助。所以我无法直接回答你的问题,但是通过一些提示你可能能够自己弄清楚。所以我要试着教你钓鱼。;)

我确实对 Delphi 3 和 6 中的 TClientDataSet 有相当多的经验。我怀疑事情已经发生了很大的变化。另外,我将更多地关注技术,以帮助您朝着正确的方向发展。

首先,确保使用 Debug DCU 进行编译,以便在 VCL 代码中获得断点。不要害怕阅读并单步执行该代码——这是一种很好的学习方式。此外,您还可以看到 Borland/CodeGear/Embarcadero 在其丑陋的荣耀中所犯的错误。

我假设您已经启用了 Stop on Exceptions(因此您知道自己特别遇到了 AV 错误)。

做你的测试。当您获得您的 AV 时,您可能会出现在 DbClient.pas(或者可能是较低级别的单元之一)中。

通过调用堆栈寻找一个位置,您在try..except块的try部分中,该块要么吞下异常,要么短路到另一个处理程序,然后吞下它。

注意: DbClient.pas 中充斥着以下错误代码的大量实例:

except //swallowing or squashing exceptions is 
end;   //very dangerous and should be avoided.

except
  //short-circuiting to Application.HandleException (usually informs the user)
  //but is not much better. The point is the rest of the program up the 
  //call-stack remains blissfully unaware that something went wrong.
  Application.HandleException(Self);
  Action := raAbort;
end;

旁注:仅仅因为 Borland 做到了,并不意味着它是好的做法。你和其他许多人已经并将为此而焦头烂额。

一旦你发现了这一点,你就成功了一半。您现在知道为什么您没有被告知 AV。
现在,在构成该调用堆栈一部分的代码中的某处将成为您希望如何处理错误的线索。您还可以在 VCL 代码中稍早放置一个断点,以便您可以单步执行以查看导致异常的事件序列。由于您特别提到了 AV(访问冲突),因此请查找引用nil的对象。

D5 DbClient.pas 的以下代码片段中可能导致问题的一个示例

    FOnReconcileError(DataSet, E, UpdateKind, Action);
  finally
    E.Free;
  end;
except
  Application.HandleException(Self);
  Action := raAbort;
end;

从上面可以看出,某些情况需要您实现 OnReconcileError 事件处理程序。没有它,dll就不会被回调到上面的代码片段中,许多错误都被悄悄地踢到地毯下,让你摸不着头脑。

我对 TClientDataSet 的经验是,您必须做一些额外的事情才能“正确”使用它。这在文档中并不是特别明显。虽然一旦你弄清楚了你应该做的所有点点滴滴,如何做的例子就更好了。

不幸的是,我对所有这些事情的记忆有点模糊。所以这是一个非常粗略的指南,可能缺少很多。

  • 考虑使用 OnReconcileError 事件处理程序。
  • 考虑实现一个 UpdateProvider 对象。
  • 您提到将其用作内存数据集,因此您可能正确使用了 CreateDataSet。但是,如果我没记错的话,您有 2 个用于指定字段的选项,并且一些更精细的属性有点挑剔。
  • 无关提示:注意字符串字段的长度,TClientDataSet 过去(并且可能仍然)以类似于短字符串的方式占用内存。

编辑

根据问题中的其他信息并使用上述技术,您将收到从以下方法引发的 EDatabaseError。

procedure TIntegerField.SetAsString(const Value: string);
var
  E: Integer;
  L: Longint;
begin
  if Value = '' then Clear else
  begin
    Val(Value, L, E);
    if E <> 0 then DatabaseErrorFmt(SInvalidIntegerValue, [Value, DisplayName]);
    SetAsInteger(L);
  end;
end;

在调用堆栈的上层,您将获得以下方法,该方法提示如何解决问题。

procedure TField.SetEditText(const Value: string);
begin
  if Assigned(FOnSetText) then FOnSetText(Self, Value) else SetText(Value);
end;

此外,如果您一直跟踪调用堆栈直到KeyPress,您会注意到根本不涉及任何数据集或网格代码。

您尝试在 OnPost 错误中处理错误的尝试无效的原因是您没有接近尝试发布您的记录。您收到字段验证错误,处理该错误的地方是在现场。

回到提示如何解决错误 - 您必须为该字段实现 OnSetText 事件。

但是,我建议不要这样做。开箱即用的行为是完全可以接受的!用户收到一条错误消息,说明他们做错了什么,他们有机会修复它并重试。如果他们改变主意,他们可以点击 Escape 并取消编辑。

于 2012-06-06T18:31:00.237 回答