Na unidade Vcl.Buttons do Delphi eles chamam:
Caption := LoadResString(BitBtnCaptions[Value]);
Onde BitnBtnCaptions
está uma matriz como:
BitnBtnCaptions: array[TBitBtnKind] of Pointer = (
nil, @SOKButton, @SCancelButton, @SHelpButton, @SYesButton, @SNoButton,
@SCloseButton, @SAbortButton, @SRetryButton, @SIgnoreButton,
@SAllButton);
E as BitnBtnCaptions
constantes são:
resourcestring
SOKButton = 'OK';
SCancelButton = 'Cancel';
SYesButton = '&Yes';
SNoButton = '&No';
SHelpButton = '&Help';
SCloseButton = '&Close';
SIgnoreButton = '&Ignore';
SRetryButton = '&Retry';
SAbortButton = 'Abort';
SAllButton = '&All';
Então, basicamente, ele está chamando:
resourcestring
SOKButton = 'OK';
s := LoadResString(@SOKButton);
A declaração de LoadResString é:
function LoadResString(ResStringRec: PResStringRec): string;
Esta função requer um ponteiro para um TResStringRec :
PResStringRec = ^TResStringRec;
TResStringRec = packed record
// 32bit = 8 bytes
// 64bit = 16 bytes
Module: ^HMODULE;
Identifier: NativeUint;
end;
Mas estamos passando uma resourcestring .
Isso significa que estamos passando uma string para uma função que só aceita um PResStringRec em caso de erro?
Por que você pergunta?
Pergunto porque estou chamando:
Result := LoadResString(@SMsgDlgOK);
e com avaliação de ponteiro digitado ( ie {$T+}
ou {$TYPEDADDRESS ON}
) ele emite um aviso de tipo incompatível:
E2010 Tipos incompatíveis: 'PResStringRec' e 'Pointer'
E é claro que eu posso simplesmente forçar com um elenco forte:
Result := LoadResString(PResStringRec(@SMsgDlgOK));
Elenco difícil?
Mas isso parece um pouco duro para algo que supostamente é a maneira correta de fazer algo. Cheira um pouco estranho.
E o pior é que se estou fazendo um gesso forte , é melhor saber o que estou fazendo.
E do jeito que eu vejo, a única maneira de isso funcionar é se:
- a constante mágica
SMsgDlgOk
- na verdade é um
TResStringRec
.
Não queremos forçar uma peça triangular no furo quadrado , usando um molde rígido às cegas.
O que me leva à minha pergunta: uma resourcestring
resourcestring
SMsgDlgOK = 'OK';
uma string? Ou é um registro?
Mas eu realmente tenho que fazer isso?
Eu preciso realmente fazer o que ele Vcl.Buttons
faz? Vcl.Buttons
Precisa fazer o que ele está fazendo?
Não podemos simplesmente substituir:
s := LoadResString(@SOKButton);
com
s := SOKButton
Não era esse o ponto principal da palavra-chave resourcestring ? Ela coloca as strings na tabela strings (onde os localizadores podem localizá-las) e faz toda a mágica em tempo de execução (ou seja, LoadResString ) para expor as resourcestrings como strings ?
E se isso não for verdade: por que não?
- O que é resourcestring?
- e como ele difere de LoadResString(resourceString)?
O que ganho ligando para:
LoadResString(@SOKButton)
sobre apenas usar SOKButton
?
E se eu tiver que usar LoadResString , é realmente , realmente , verdadeiramente , juro, 100% seguro forçar o typecast?
- Ideal :
s := SOKButton
- Atual :
s := LoadResString(@SOKButton)
// falha na verificação de ponteiro digitado - Correto (?) :
s := LoadResString(PResStringRec(@SOKButton))
Bate-papo bônus
Se um resourcestring realmente for um TResStringRect
, então eu deveria ser capaz de vê-lo. Então eu inspeciono o que eles são:
╔════════════════════════════╤══════════╗
║ Watch Name │ Value ║
╠════════════════════════════╪══════════╣
║ PResStringRec(@SMsgDlgOK) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈╢
║ PResStringRec(@SMsgDlgYes) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╟┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┼┈┈┈┈┈┈┈┈┈┈╢
║ PResStringRec(@SMsgDlgNo) │ $AD3C54 ║
║ ├──Module │ $400000 ║
║ ╰──Identifier │ 0 ║
╚════════════════════════════╧══════════╝
Cada sequência de recursos:
- mora no mesmo endereço
- tem o mesmo módulo
- tem o mesmo identificador
Então algo não está certo; não parece um disco para mim.
Então por que o código funciona Vcl.Buttons
?
Eu não uso muito ResourceStrings. Mas quando uso, eu apenas os defino no topo de uma unidade e os trato como se fossem apenas constantes de string globais e que devem ser referenciadas diretamente. Eles são colocados em memória global como singletons, não dentro de unidades específicas, da mesma forma que
const string
declarações acabariam como duplicatas.Acho que parte do que você está apontando está relacionado ao design deles para serem "sobrepostos" por diferentes unidades para lidar com a troca entre idiomas. Então, se você carregar uma unidade com diferentes strings atribuídas, elas serão colocadas naquela área de memória global e cada referência no seu aplicativo que se refere a elas será afetada, como legendas de menu, legendas de botão, etc.
O código que você encontrou no VCL está adicionando outro nível de indireção (uma matriz deles com um seletor de índice) que seu aplicativo provavelmente não precisa.
Você diria algo como:
e se você carregasse outra unidade em tempo de execução que definisse
OK_caption = 'GO!'
, então cada referência a essa string seria alterada daquele ponto em diante.Novamente, elas são projetadas para serem usadas como constantes de string globais. Elas são tipicamente usadas para listar legendas e dicas de menus e botões, explicações de códigos de aviso e erro, coisas assim. Você as usaria no lugar da mesma string literal em todos os lugares, o que dificultaria a alteração se houvesse dezenas ou centenas delas espalhadas pelo seu código:
myButton.Caption := 'OK'
e agora alguém quer alterá-la para 'Go!' em todos os lugares. Você quer alterá-la em APENAS UM LUGAR, ou centenas?Nunca pensei em fazer um array dessas constantes de string. A VCL tem uma necessidade específica que simplifica algumas outras coisas, mas isso não tem nada a ver com o uso normal.