在 Delphi 的Vcl.Buttons单元中,他们调用:
Caption := LoadResString(BitBtnCaptions[Value]);
BitnBtnCaptions
如下数组在哪里:
BitnBtnCaptions: array[TBitBtnKind] of Pointer = (
nil, @SOKButton, @SCancelButton, @SHelpButton, @SYesButton, @SNoButton,
@SCloseButton, @SAbortButton, @SRetryButton, @SIgnoreButton,
@SAllButton);
常数BitnBtnCaptions
为:
resourcestring
SOKButton = 'OK';
SCancelButton = 'Cancel';
SYesButton = '&Yes';
SNoButton = '&No';
SHelpButton = '&Help';
SCloseButton = '&Close';
SIgnoreButton = '&Ignore';
SRetryButton = '&Retry';
SAbortButton = 'Abort';
SAllButton = '&All';
因此它本质上就是在调用:
resourcestring
SOKButton = 'OK';
s := LoadResString(@SOKButton);
LoadResString的声明是:
function LoadResString(ResStringRec: PResStringRec): string;
此函数需要一个指向TResStringRec的指针:
PResStringRec = ^TResStringRec;
TResStringRec = packed record
// 32bit = 8 bytes
// 64bit = 16 bytes
Module: ^HMODULE;
Identifier: NativeUint;
end;
但是我们正在传递一个资源字符串。
这是否意味着我们正在将一个字符串传递给一个仅接受PResStringRec的函数?
你为什么问这个?
我问这个问题是因为我要打电话:
Result := LoadResString(@SMsgDlgOK);
并且对于类型指针评估(即 {$T+}
或{$TYPEDADDRESS ON}
),它会给出不兼容的类型警告:
E2010 不兼容的类型:“PResStringRec”和“Pointer”
当然,我也可以用强制转换来强制执行:
Result := LoadResString(PResStringRec(@SMsgDlgOK));
硬铸?
但对于本应是正确的做法,这似乎有点苛刻。有点奇怪。
更糟糕的是,如果我要做硬石膏,我最好知道自己在做什么。
在我看来,唯一可行的方法是:
- 魔法常数
SMsgDlgOk
- 其实是一个
TResStringRec
。
我们不想盲目地使用硬铸件将三角形件强行放入方形孔中。
这让我想到了我的问题:资源字符串
resourcestring
SMsgDlgOK = 'OK';
字符串?还是一条记录?
但我一定得这么做吗?
我是否需要实际执行该Vcl.Buttons
操作?是否Vcl.Buttons
需要执行该操作?
我们不能简单地替换:
s := LoadResString(@SOKButton);
和
s := SOKButton
这难道不是resourcestring关键字的全部意义吗?它将字符串放入字符串表(本地化人员可以在其中对它们进行本地化),并在运行时执行所有神奇的操作(即LoadResString)以将resourcestrings公开为字符串?
如果这不是真的:为什么不是?
- 什么是 resourcestring?
- 它与 LoadResString(resourceString) 有何不同?
我通过拨打以下电话可以获得什么:
LoadResString(@SOKButton)
仅仅使用 SOKButton
?
如果我必须使用 LoadResString ,那么强制进行类型转换是否真的、真的、真正、真正地 100%安全?
- 理想的:
s := SOKButton
- 当前:
s := LoadResString(@SOKButton)
//类型指针检查失败 - 正确的 (?):
s := LoadResString(PResStringRec(@SOKButton))
奖金聊天
如果资源字符串实际上是,TResStringRect
那么我应该能够看到它。所以我检查它们是什么:
╔════════════════════════════╤══════════╗
║ Watch Name │ Value ║
╠════════════════════════════╪══════════╣
║ PResStringRec(@SMsgDlgOK) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈╢
║ PResStringRec(@SMsgDlgYes) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈╢
║ PResStringRec(@SMsgDlgNo) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╚════════════════════════════╧══════════╝
每个资源字符串:
- 住在同一地址
- 有相同的模块
- 具有相同的标识符
所以有些事情不对劲;对我来说这看起来不像是一个记录。
那么为什么代码可以Vcl.Buttons
工作呢?
我很少使用 ResourceStrings。但是当我使用时,我只是在单元顶部定义它们,并将它们视为全局字符串常量,并直接引用它们。它们作为单例放入全局内存中,而不是放在特定单元中,因为
const string
声明最终会重复。我认为您所指出的部分内容与它们的设计有关,即由不同的单元“覆盖”以处理语言之间的切换。因此,如果您加载分配了不同字符串的单元,它们将被放入该全局内存区域,并且您的应用中引用它们的每个引用都会受到影响,例如菜单标题、按钮标题等。
您在 VCL 中找到的代码正在添加另一个间接级别(带有索引选择器的数组),而您的应用程序可能不需要它。
你可能会这么说:
如果在运行时加载了另一个定义的单元
OK_caption = 'GO!'
,那么从那时起对该字符串的每个引用都会发生变化。再次强调,它们被设计为全局字符串常量。它们通常用于列出菜单和按钮标题和提示、警告和错误代码说明等。如果代码中分散着数十或数百个字符串,那么您可以使用它们代替难以更改的相同字符串文字:
myButton.Caption := 'OK'
现在有人想将其更改为“Go!”。您是想在一个地方更改它,还是在数百个地方更改它?我从未考虑过创建这样的字符串常量数组。VCL 有特定的需求,可以简化其他一些事情,但这与正常使用无关。