5

我熟悉Jeff Atwood 关于错误总是程序员的错的文章,但我相信我确实在 Delphi .pas 文件中发现了一个错误。

具体来说,我使用的是 Delphi 2007,错误位于 DBCommon.pas 文件的第 955 行,该文件在我的机器上位于此处:

C:\program files\codegear\rad studio\5.0\source\Win32\db\DBCommon.pas

代码是这样的:

...
  FieldIndex := StrToInt(Token);
  if DataSet.FieldCount >= FieldIndex then
    LastField := DataSet.Fields[FieldIndex-1].FieldName else
...

如果 "Token" 的值为零,那么我们尝试访问 DataSet.Fields 的索引 -1,导致列表索引越界错误。

这个错误不会向用户提出,因为它是在它变得那么高之前被处理的,但是每次发生这种情况时让调试器中断是非常令人恼火的。

我可以“忽略这种异常类型”,但索引越界错误很常见,我不想普遍忽略它们。

导致 FieldIndex 为零的情况是当您有一个其 ORDER BY 包含函数的 SELECT 语句时,如:

ORDER BY
  CASE WHEN FIELD1 = FIELD3 THEN 1 ELSE 2 END
 ,CASE WHEN FIELD2 = FIELD4 THEN 1 ELSE 2 END

我可以修复 DBCommon.pas 中的错误,但 Delphi 不会重新编译自己,我的更改也不会生效。如果我重命名 .DCU 文件,它只会抱怨找不到“DBCommon.dcu”。

所以(最后)我的问题是:我可以用我的修复重新编译 DBCommon.pas,如果可以,如何?

4

7 回答 7

12

您可能可以将 dbcommon.pas 放在您的项目目录中。然后它将与项目的其余部分一起编译。

于 2009-10-07T20:07:07.253 回答
4

你可以,但通常你不必这样做。有时重新编译 VCL 单元意味着重新编译所有其余的 VCL 单元,因为您更改了单元的接口,或者因为编译器感到困惑并认为您已经更改了接口。重新编译 VCL 单元也排除了使用大多数运行时包的可能性,因为您无法重新编译 Delphi 的运行时包。

您可以使用运行时修补,而不是重新编译。我在TNT Unicode 控件中使用了该方法,但Madshi还提供了一种用您自己的实现替换函数的方法。如果您将 DBCommon.GetIndexForOrderBy 的实现复制到您自己的单元中并进行修复,您可以使用此命令为您自己的 VCL 版本打补丁:

var
  Old_GetIndexForOrderBy: Pointer;

HookCode(@DBCommon.GetIndexForOrderBy,
         @Fixed_GetIndexForOrderBy,
         Old_GetIndexForOrderBy,
         0);

使用 Tnt Unicode 库,OverwriteProcedure在 TntSystem 单元中找到例程。它不是公开的,因此您需要在单元界面中声明它或将其复制到您自己的单元中。然后你可以像上面的 Madshi 代码一样调用它:

var
  Old_GetIndexForOrderBy_Data: TOverwrittenData;

OverwriteProcedure(@DBCommon.GetIndexForOrderBy,
                   @Fixed_GetIndexForOrderBy,
                   @Old_GetIndexForOrderBy_Data);
于 2009-10-08T04:38:25.540 回答
4

有关如何创建可以重新编译修改后的 VCL 源的情况,请参阅以前的答案。但是,我要补充一点,您认真考虑使用“供应商分支”SCM 模式管理变更控制系统中的变更。

简单来说(以SVN为参考):

  • 创建原始供应商提供的文件的“供应商来源”副本。这是您的“原始”参考副本。

  • 创建一个代表该原始副本的分支(例如,Delphi 2009 版本的 VCL 的“2009”)

  • 在单独的“供应商库”文件夹中创建一个进一步的分支。这是您应该在项目中引用的库的副本

  • 对供应商源的任何修改都在“供应商库”分支中进行。

  • 当供应商提供新版本的库时,您将新版本签入“供应商源”项目并为新版本创建新分支。

然后,您可以轻松区分供应商版本。但更重要的是(使用 SubVersion,可能还有其他 SCM 系统)您还应该能够简单地将新供应商源与您的“供应商库”分支合并(即自动),以便轻松地将供应商更改与您自己的修改合并。

这一切都比我刚刚在 O'Reilly 的优秀著作中描述的要好得多:“使用 SubVersion 进行版本控制”

但是请注意,由于版权问题,不再支持该书中提到的“loaddirs”实用程序,因此更新“供应商丢弃”目前是一项手动操作,但这很少发生,并不是主要负担。

我们自己也在使用这种模式,尽管在 VCL 的情况下,我们不会在“供应商源”或“供应商库”中维护整个 VCL 源树的完整副本,而是只跟踪更改的和相关的单元。对于在供应商分支下管理的其他库,我们通常会维护完整的副本,但认为这对于 VCL 不是必需的。

然而,我们只是刚刚实现了这个模式,所以我们可能还决定我们也需要对 VCL 采取更全面的方法。

ymmv

于 2009-10-08T00:46:27.560 回答
2

“我熟悉 Jeff Atwood 关于错误总是程序员的错的文章,但我相信我确实在 Delphi .pas 文件中发现了一个错误”

你在跟我开玩笑吗?使用 Delphi,你总是首先责怪 Borland :) 有什么奇怪的,去谷歌看看它是否是一个 Delphi 错误。只有在找不到任何类似的报告时,您才可以找到并逐行检查您的代码。

重新安装 Delphi 后,我必须在 6(六)个地方修补原始 PAS 文件。在全新的 Delphi 安装中会出现大量错误,并且可以轻松复制。Delphi(我们都喜欢的那个)充满了错误。围绕这一点创造了一个完整的历史。有很多人发布外部补丁(例如http://andy.jgknet.de/blog/?page_id=288)而 Borland/Imprise/GoGear/Embarcadero 一直忽略它们。他们在发行版中包含 FastMM 真是一个奇迹。

无论如何,我已经重新编译了那些 PAS 文件,现在我将原来的 DCU 替换为打补丁的 DCU。

于 2010-09-08T06:57:22.790 回答
2

我们的项目源代码树下有一个名为 VCL 的文件夹,我们将希望稍微修改的 VCL 源代码的副本放入其中。你的例子是做同样事情的好人选。您将需要修改项目的搜索路径,以便“您的”VCL 文件夹在您的路径上比您的 Delphi 安装下的“源”文件夹更早。您可能还会发现,如果您复制一个 VCL 源单元并对其进行修改,您还必须将其他 VCL 源单元复制到可能是依赖项的“您的”文件夹中。

我们这样做的原因是我们希望我们的构建具有零编译器提示和警告。VCL 源代码的某些部分不是无提示/警告的。

于 2009-10-07T20:17:53.360 回答
1

简单——是的。使用上述答案之一[汤姆或康纳]。将 DBCommon.pas 复制到您的项目文件夹,而不是编辑原始文件。这不会影响其他项目和编译,因为它不会在路径上。

于 2009-10-07T20:42:26.433 回答
1

您可以设置: DataSetProvider.Option.poRetainServerOrder = True

于 2014-01-20T01:52:45.753 回答