【问题标题】:How to modify a constant in a Delphi library file without changing the library file?如何在不更改库文件的情况下修改 Delphi 库文件中的常量?
【发布时间】:2018-03-22 10:19:48
【问题描述】:

我使用了一个第三方库,我需要为其更改一个常量。我想自定义库而不覆盖文件。 pas 文件是库文件,不是可继承的类。

目前我可以通过编辑文件来实现我的目标

unit libraryconstants;

interface

uses
  System.Types;

const
  constant1 = 'foo';
  constant2 = 32;
  constant3: Integer = 12;
  constant4: TSize = (cx: 32; cy: 32);

不知何故,我需要像这样更改constant4

constant4: TSize = (cx: 16; cy: 8);

我可以编辑 libraryconstants.pas 并保存它,但是当我更新库时(因为发布了新版本),我将丢失此更改。当然,我可以在每次更新库时提醒我应用此更改,但如果可能的话,我想避免这种情况。

由于常量不是已发布的属性,我不知道如何达到预期的效果。我希望尽可能少地干扰库代码。

我正在寻找我不知道的 Delphi 语言“技巧”。

谢谢。

【问题讨论】:

  • 该常量存储在只读存储器中。在启动时,例如在单元初始化中,将内存保护设置为可写。修改内存,恢复内存保护。
  • @DavidHeffernan - 不错的 hack,即使它违反了所有规则......
  • @DavidHeffernan 你有样品吗?谢谢。
  • 我会继续修补源代码而不是在运行时修补。您可以通过常用代码路径中的 Assert 来提醒自己。
  • 使用带有“供应商分支”的版本控制(这正是它的用例):svnbook.red-bean.com/en/1.7/svn.advanced.vendorbr.html

标签: delphi constants delphi-10-seattle


【解决方案1】:

我错误地认为类型常量存储在只读内存中,就像在其他语言中一样。事实并非如此。所以你可以很容易地通过指针访问这个类型常量的值。

PSize(@constant4).cx := 16;
PSize(@constant4).cy := 8;

将此代码添加到您的一个单元的初始化部分。您需要确保它运行得足够早,以便在执行任何依赖于该常量的代码之前影响更改。

我认为我的误解来自于字符串文字存储在只读内存中的知识。所以我假设类型常量也是如此。我怀疑当添加可分配类型常量“功能”时,编译器开关只是让编译器拒绝写入类型常量,而不是将它们移动到只读内存。

请注意,我所说的只读内存在桌面编译器上是正确的。我不确定在移动编译器上是否属实。很可能此代码在移动编译器上因运行时内存保护错误而失败。在这种情况下,您需要在写入之前临时更改内存保护。

【讨论】:

  • 这真是个新闻。非常感谢,它工作得很好。
【解决方案2】:

制作一个新的单位:

unit libraryconstantspatch;

interface

implementation

uses System.Types, libraryconstants;

initialization

asm
        mov     eax,offset libraryconstants.constant4
        mov     [eax+offset TSize.cx],16
        mov     [eax+offset TSize.cy],8
end

finalization

end.

然后在应用程序中的 libraryconstants 单元之后的 USES 语句中列出该单元。

【讨论】:

  • 嗯,显然我是。我不敢相信恩巴弄错了。尽管如此,asm 还是个坏主意。使用TSize((@constant4)^).cx := 16; TSize((@constant4)^).cy := 8;
  • 而ASM的原因是如果常量定义在{$J-}状态,你不能直接用PASCAL改变它,但是你可以通过ASM。
  • 呃,我之前评论中的代码显示了如何做到这一点
  • 我不会称之为“直接”。 “直接”将是 constant4.cx:=16;
  • 您认为 asm 更可取?真的。跨平台呢?
【解决方案3】:

我的建议:不要改常量,改方法。

检查使用该常量的方法(过程或函数),然后使用新常量“重载”该方法。

如果它在一个类中,只需继承该类,并且只修改方法,使用新名称,如过程'aMethod_size2'。名称 size2 表明了这一点。

据我所知,这是通常的做法。

【讨论】:

    【解决方案4】:

    在项目设置中(CTRL+SHIFT+F11):

    Delphi compiler \ Compiling \ Syntax options
    

    将“可分配类型常量”的值更改为 True。 然后您可以将类型化常量修改为常规变量:

    constant4.cx := 16;
    constant4.cy := 8;
    

    【讨论】:

    • 除非原始源代码包含 {$J-} 编译器指令(或将来获得)。
    • 绝对不推荐为整个项目启用可赋值的类型常量。这是一个偶然存在并与旧代码兼容的设置。你绝对不应该使用它。
    • @David 不建议这样做,因为更改常量 - 是个坏主意。但它在问题中被问到。如果我必须在 hacking(更改内存保护状态 -> 不仅该常量将是可写的,...)和编译器设置之间进行选择,我更喜欢更改项目的编译器选项。
    • 您只需要临时更改保护。它可以恢复。碰巧我对保护的看法是错误的。类型化的常量不受保护。但无论如何,即使它们是,启用类型化常量也是一个糟糕的主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-19
    相关资源
    最近更新 更多