我能否重新编译Delphi IDE使用的.PAS文件?

5
我熟悉Jeff Atwood 关于错误总是程序员的错的文章,但我确信在 Delphi .pas 文件中发现了一个真正的 bug。
具体来说,我正在使用 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吗?如果可以,如何操作?

你能重现这个bug吗?如果可以,请QC它,或者给我发送一个可重现的案例(几乎任何内容发送到pluimers点com都会出现在我的收件箱中)。 - Jeroen Wiert Pluimers
我熟悉Jeff Atwood关于错误总是程序员的错的文章,但我相信我真正地在Delphi .pas文件中发现了一个bug。而那个.pas文件的创建者是谁呢?没错,就是一个程序员 :) - The_Fox
1
重复:https://dev59.com/IUjSa4cB1Zd3GeqPDTI4 - Rob Kennedy
确实,这是一个重复的问题。谢谢Rob。 - JosephStyons
@Jeroen - 我可以在我的项目中重现这个错误,但那里的情况有些复杂。当我设置一个简单的测试用例时,该错误不会发生。我需要弄清楚如何以简单的方式重现它,如果我成功了,我会将其提交给QC。 - JosephStyons
7个回答

12

你可以将dbcommon.pas放在项目目录中,然后它会和项目的其余部分一起编译。


请注意,除非所有基于dbcommon.pas的其他单元也被复制到该目录中,否则此操作将无法正常工作,或者未修改的VCL源位于源路径上。 - mghie
3
应该可以正常运作。该单元的界面版本不应更改,因此使用它的任何单元都应接受重新编译的版本。 - Rob Kennedy
@Rob:你说得对,我试过了,确实可以。我记得在尝试更改第三方库实现部分的字符串时遇到了问题,那里有什么不同呢?是否有关于什么会更改接口版本和什么不会更改的文档? - mghie
1
如果应用程序中使用了运行时包,则此方法将无法正常工作。 - Lars Truijens
1
Mghie,我不知道可能发生了什么变化。Barry Kelly上个月总结了规则(https://dev59.com/SEnSa4cB1Zd3GeqPOoj3),但根据我的经验,这种方法一直非常脆弱,所以我尽量避免重新编译任何东西。这就是为什么我的源代码和DCU文件夹始终是分开的,并且也是我在这里回答的动机所在。我们之前讨论过修补的问题:https://dev59.com/WkbRa4cB1Zd3GeqPzFBe - Rob Kennedy

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库,在TntSystem单元中查找OverwriteProcedure例程。它不是公共的,因此您需要在单元接口中声明它或将其复制到自己的单元中。然后,您可以像上面的Madshi代码一样调用它:
var
  Old_GetIndexForOrderBy_Data: TOverwrittenData;

OverwriteProcedure(@DBCommon.GetIndexForOrderBy,
                   @Fixed_GetIndexForOrderBy,
                   @Old_GetIndexForOrderBy_Data);

4
请参阅之前的答案,了解如何创建可以重新编译修改后的VCL源代码的情况。但我想补充一点,您应该认真考虑在更改控制系统中管理自己的更改,使用“供应商分支”SCM模式。
简单地说(以SVN为例):
- 创建原始供应商提供文件的“供应商源”副本。这是您的“原始”参考副本。 - 创建代表该原始副本的分支(例如,“2009”表示Delphi 2009版本的VCL) - 进一步创建一个分支到单独的“供应商库”文件夹。这是您应该在项目中引用的库的副本。 - 任何对供应商源的修改都在“供应商库”分支中进行。 - 当供应商提供库的新版本时,您将其检入“供应商源”项目,并为新版本创建一个新分支。
然后,您可以轻松地比较供应商修订版。但更重要的是(使用SubVersion和可能的其他SCM系统),您还应该能够将新的供应商源与您自己的修改自动合并到“供应商库”分支中,以轻松地合并供应商更改和自己的修改。
这在优秀的O'Reilly图书《SubVersion版本控制》中有更好的描述。
请注意,《SubVersion版本控制》中提到的“loaddirs”实用程序由于版权问题不再受支持,因此更新“供应商drops”目前是手动操作,但这只会偶尔发生,不是重大负担。
我们自己正在使用这种模式,尽管在VCL的情况下,我们没有在“供应商源”或“供应商库”中维护整个VCL源代码树的完整副本,而是仅跟踪更改和依赖单位。对于在供应商分支下管理的其他库,我们通常会维护完整的副本,但我们认为对于VCL来说这并不必要。
然而,我们刚刚实施了这种模式,因此我们可能会决定在VCL中采取更全面的方法。

你如何处理使用不同版本的TMS的两个项目?我们还没有找到任何像“如果一个开发者更新,所有人都更新”这样的好的解决方案。请注意,我所说的不是源代码本身,而是安装在IDE中的组件。 - Lieven Keersmaekers
1
@Lieven - 这不是我们需要处理的事情,因为我们认为第三方库对所有项目都是“共同”的。在这种情况下,例如,如果/当我们更新DevExpress,那么所有项目都会被更新。(续) - Deltics
1
@Lieven,续。我们确实有不同IDE级别的项目(BDS2006和RS2010)。当这成为必需时,为了满足需求,我们将供应商分支移动到每个项目文件夹结构下,因此我们最终的2006供应商分支与我们的2010(和“当前”)供应商分支是分开的。但是,我们从标准安装程序安装IDE组件,而不是这些分支。对于我们来说,这是可能的,因为我们不使用运行时包,因此只要IDE组件的发布属性与供应商分支中的属性匹配,一切都很好。 - Deltics

2
我们的项目源代码树下有一个名为VCL的文件夹,我们会在其中放置我们希望稍作修改的VCL源代码副本。你的示例非常适合这样做。你需要修改项目的搜索路径,使得“你的”VCL文件夹位于Delphi安装下的“Source”文件夹之前。你可能还会发现,如果你将一个VCL源代码单元复制出来并进行修改,你还需要将其他VCL源代码单元复制到“你的”文件夹中,这些可能是依赖项。我们这样做的原因是希望我们的构建过程中没有编译器提示和警告。VCL源代码中有一些部分不符合此要求。

2
“我熟悉Jeff Atwood关于错误总是程序员的错的文章,但我相信我真正地在Delphi.pas文件中找到了一个bug。”
“你在开玩笑吗?用Delphi时总是首先责备Borland :) 如果出现奇怪的情况,去Google一下看它是否是Delphi的bug。只有在找不到类似报告时,才会坐下来逐行检查代码。”
“在重新安装Delphi后,我必须在6个位置上修补原始PAS文件。在新的Delphi安装中会出现大量的错误,而且很容易重现。Delphi(我们所有人都喜欢的那个)充满了漏洞。围绕这一点已经有了整个历史。有很多人发布外部补丁(例如http://andy.jgknet.de/blog/?page_id=288),但Borland/Imprise/GoGear/Embarcadero却一直忽略它们。他们能够在发布中包含FastMM是真正的奇迹。”
“无论如何,我已经重新编译了那些PAS文件,现在我用修补过的DCUs替换了原始的DCUs。”

1
你可以设置: DataSetProvider.Option.poRetainServerOrder = True
这将使数据集提供程序保留服务器排序。

1

简单来说 - 是的。使用上面Tom或Connor的答案之一。将DBCommon.pas复制到您的项目文件夹中,而不是编辑原始文件。这样可以避免影响其他项目和编译,因为它不会在路径上。


1
正如您可能已经注意到的那样,Stack Overflow 默认使用按投票数排序的方式来显示答案。当提到其他答案时,请更具体地说明。"上面的答案"并不能告诉我们您真正谈论的是哪些答案。您可以通过复制每个答案下方的“链接”地址直接链接到它们。 - Rob Kennedy
抱歉,当我回复时基本上只有两个相同的话语。为了明确起见,我会编辑回答。 - Despatcher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接