Come convertire stringa null-terminated a un AnsiString?
-
11-09-2019 - |
Domanda
Ho qualche codice che compila bene con D7, ma fallisce con D2010. Ovviamente si tratta di un problema Unicode:
L'errore di compilazione è: E2251 ambiguo chiamata overload per 'StrPas'
Qui è l'intera procedura:
procedure GetVersionInfo;
type
PLangCharSetInfo = ^TLangCharSetInfo;
TLangCharSetInfo = record
Lang: Word;
CharSet: Word;
end;
var
FileName: array [0..260] of Char;
SubBlock: array [0..255] of Char;
VerHandle: Cardinal;
Size: Word;
Buffer: Pointer;
Data: Pointer;
DataLen: LongWord;
LangCharSetInfo: PLangCharSetInfo;
LangCharSetString: string;
begin
LabelComments.Caption := 'No version information for this program is available!';
{Get size and allocate buffer for VerInfo}
if GetModuleFileName(hInstance, FileName, SizeOf(FileName)) > 0 then
begin
Size := GetFileVersionInfoSize(FileName, VerHandle);
if Size > 0 then
begin
GetMem(Buffer, Size);
try
if GetFileVersionInfo(FileName, VerHandle, Size, Buffer) then
begin
{Query first language and that language blocks version info}
if VerQueryValue(Buffer, '\VarFileInfo\Translation', Pointer(LangCharSetInfo), DataLen) then
begin
LangCharSetString := IntToHex(LangCharSetInfo^.Lang, 4) +
IntToHex(LangCharSetInfo^.CharSet, 4);
if VerQueryValue(Buffer, StrPCopy(SubBlock, '\StringFileInfo\' + LangCharSetString + '\ProductName'), Data, DataLen) then
begin
LabelProductName.Caption := StrPas(Data);
Caption := LabelProductName.Caption;
end;
if VerQueryValue(Buffer, StrPCopy(SubBlock, '\StringFileInfo\' + LangCharSetString + '\FileVersion'), Data, DataLen) then
LabelVersion.Caption := StrPas(Data);
if VerQueryValue(Buffer, StrPCopy(SubBlock, '\StringFileInfo\' + LangCharSetString + '\LegalCopyright'), Data, DataLen) then
LabelCopyright.Caption := StrPas(Data);
if VerQueryValue(Buffer, StrPCopy(SubBlock, '\StringFileInfo\' + LangCharSetString + '\Comments'), Data, DataLen) then
LabelComments.Caption := StrPas(Data);
end;
end;
finally
FreeMem(Buffer, Size);
end;
end
end;
end;
Il dottore per StrPas dice
function StrPas(const Str: PAnsiChar): AnsiString; overload;
Questa funzione viene fornita solo per compatibilità all'indietro fasor. Per convertire una stringa alla terminazione Null a un AnsiString o nativo stringa di linguaggio Delphi, utilizzare un typecast o un segnazione.
Quindi la domanda è dovrei rimuovere tutte le chiamate a StrPas? L'unico modo in cui faccio questo per compilare è quello di fare un Hardcast per Pansi char come:
LabelProductName.Caption := StrPas(PAnsiChar(Data));
Soluzione
Hai modificato la tua domanda con un campione completo compilazione. Buona!
E 'probabilmente il più facile se si desidera utilizzare il JCL per questo (link è qui sotto). Allora si potrebbe utilizzare TJclFileVersionInfo, che fa tutto quello che volete, e prende in considerazione alcune cose esoterico che il VersionInfo può contenere.
Poi la logica di business sarebbe diventato qualcosa di simile:
function GetStringFileInfo(
const Buffer: Pointer; const SubBlock: PChar;
const LangCharSetString: string; const Kind: string): string;
var
QueryString: string;
Data: Pointer;
DataCharacters: PChar absolute Data;
DataLen: LongWord;
begin
Result := '';
QueryString := Format('%s\StringFileInfo\%s\%s', [SubBlock, LangCharSetString, Kind]);
if VerQueryValue(Buffer, PChar(QueryString), Data, DataLen) then
Result := StrPas(DataCharacters);
end;
procedure GetVersionInfoStrings(var Comments: string; var ProductName: string;
var Caption: string; var Version: string; var Copyright: string);
type
PLangCharSetInfo = ^TLangCharSetInfo;
TLangCharSetInfo = record
Lang: Word;
CharSet: Word;
end;
var
FileName: array [0 .. 260] of Char;
SubBlock: array [0 .. 255] of Char;
VerHandle: Cardinal;
Size: Word;
Buffer: Pointer;
Data: Pointer;
DataCharacters: PChar absolute Data;
DataLen: LongWord;
LangCharSetInfo: PLangCharSetInfo;
LangCharSetString: string;
begin
Comments := 'No version information for this program is available!';
{ Get size and allocate buffer for VerInfo }
if GetModuleFileName(hInstance, FileName, SizeOf(FileName)) > 0 then
begin
Size := GetFileVersionInfoSize(FileName, VerHandle);
if Size > 0 then
begin
GetMem(Buffer, Size);
try
if GetFileVersionInfo(FileName, VerHandle, Size, Buffer) then
begin
{ Query first language and that language blocks version info }
if VerQueryValue(Buffer, '\VarFileInfo\Translation', Pointer
(LangCharSetInfo), DataLen) then
begin
LangCharSetString :=
IntToHex(LangCharSetInfo^.Lang, 4) +
IntToHex(LangCharSetInfo^.CharSet, 4);
ProductName := GetStringFileInfo(Buffer, SubBlock, LangCharSetString, 'ProductName');
Version := GetStringFileInfo(Buffer, SubBlock, LangCharSetString, 'FileVersion');
Copyright := GetStringFileInfo(Buffer, SubBlock, LangCharSetString, 'LegalCopyright');
Comments := GetStringFileInfo(Buffer, SubBlock, LangCharSetString, 'Comments');
Caption := ProductName;
end;
end;
finally
FreeMem(Buffer, Size);
end;
end
end;
end;
E nell'interfaccia utente, è necessario chiamare la logica di business, e mettere i risultati nei vari controlli.
- Jeroen
Risposta prima edit:
Il codice non viene compilato come è, quindi senza più contesto, è difficile rispondere alla sua missione.
Quali sono i tipi di dati di Tampone, blocco secondario, LongCharSetString e datalen? Come hai fatto a ottenere Buffer?
sono dati veramente destinata a essere un puntatore a (ANSI o Unicode) personaggi? Non dovrebbe infine essere di punta al PVSFixedFileInfo?
(Una domanda laterale: perché è la logica di business all'interno della interfaccia utente Se tu avessi dividere la logica in una funzione separata da qualche parte, si sarebbe probabilmente hanno una routine di compilazione che avrebbe potuto servire come base per la domanda che potrebbe. hanno risposto alla domanda, in primo luogo).
Per questioni come la vostra, io di solito dare uno sguardo a unità nel JCL o < a href = "http://jvcl.delphi-jedi.org/" rel = "nofollow noreferrer"> JVCL . Hanno messo un sacco di fatica in quelli per essere compatibile con Unicode.
Il loro codice che utilizzano VerQueryValue è questo in JclFileUtils unità, metodo VersionFixedFileInfo:
// Fixed Version Info routines
function VersionFixedFileInfo(const FileName: string; var FixedInfo: TVSFixedFileInfo): Boolean;
var
Size, FixInfoLen: DWORD;
Handle: THandle;
Buffer: string;
FixInfoBuf: PVSFixedFileInfo;
begin
Result := False;
Size := GetFileVersionInfoSize(PChar(FileName), Handle);
if Size > 0 then
begin
SetLength(Buffer, Size);
if GetFileVersionInfo(PChar(FileName), Handle, Size, Pointer(Buffer)) and
VerQueryValue(Pointer(Buffer), DirDelimiter, Pointer(FixInfoBuf), FixInfoLen) and
(FixInfoLen = SizeOf(TVSFixedFileInfo)) then
begin
Result := True;
FixedInfo := FixInfoBuf^;
end;
end;
end;
Spero che questo si è iniziato a trovare la soluzione.
- Jeroen
Altri suggerimenti
StrPas non sono stati richiesti dal Delphi 1, dal momento che hanno cambiato il compilatore Delphi per convertire la stringa correttamente senza una chiamata di funzione. Vedere: http: // coding.derkeiler.com/Archive/Delphi/borland.public.delphi.language.objectpascal/2004-01/1793.html
Quindi, solo assegnarlo, come segue:
LabelProductName.Caption := PAnsiChar(Data);
E sì, è necessario rimuovere tutte le chiamate a StrPas. Non può aiutare e può solo finire nei guai in D2010.
Credo che il problema principale è che i dati viene data come Pointer
e non un PChar
.
In ogni caso, il cast fa tutto il lavoro per voi, quindi se si deve modificare il codice in ogni caso, il cast è altrettanto buono come la chiamata di funzione.
Mi permetta di riformulare questo. Se avete la stringa in un PChar
o PAnsiChar
, è l'assegnazione-compatibile con String
. Il motivo è necessario un cast qui è che avete digitato come Pointer
.
Convertire PAnsiChar a AnsiString e poi a stringa Unicode:
LabelProductName.Caption := String(AnsiString(PAnsiChar(Data)));