Как преобразовать строку с нулевым завершением в AnsiString?
-
11-09-2019 - |
Вопрос
У меня есть код, который нормально компилируется с D7, но не работает с D2010.Очевидно, это проблема Unicode:
Ошибка компиляции:E2251 Неоднозначный перегруженный вызов StrPas.
Вот вся процедура:
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;
В документе StrPas говорится
function StrPas(const Str: PAnsiChar): AnsiString; overload;
Эта функция предоставляется только для обеспечения обратной совместимости.Чтобы преобразовать строку с нулевым концевым, в якоризную строку Delphi, используйте тип или подпись.
Итак, вопрос в том, следует ли мне удалить все вызовы StrPas?Единственный способ скомпилировать это — выполнить жесткую привязку к символу PAnsi, например:
LabelProductName.Caption := StrPas(PAnsiChar(Data));
Решение
Вы отредактировали свой вопрос, используя полный образец компиляции.Хороший!
Вероятно, проще всего использовать для этого JCL (ссылка ниже).Затем вы можете использовать TJclFileVersionInfo, который делает все, что вы хотите, и учитывает некоторые эзотерические вещи, которые может содержать VersionInfo.
Тогда ваша бизнес-логика будет выглядеть примерно так:
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;
А в пользовательском интерфейсе вам нужно будет вызвать свою бизнес-логику и поместить результаты в различные элементы управления.
--джероен
Ответ перед редактированием:
Ваш код не компилируется как есть, поэтому без дополнительного контекста вам будет сложно ответить на ваш вопрос.
Каковы типы данных Buffer, SubBlock, LongCharSetString и DataLen?Как вы получили Buffer?
Действительно ли данные предназначены для использования в качестве указателя на символы (Ansi или Unicode)?Разве это не должно в конечном итоге указывать на PVSFixedFileInfo?
(Побочный вопрос:почему ваша бизнес-логика находится внутри пользовательского интерфейса?Если бы вы где-то разделили свою логику на отдельную функцию, у вас, вероятно, была бы процедура компиляции, которая могла бы послужить основой для вашего вопроса.Это, возможно, и ответило на вопрос в первую очередь).
По вопросам, подобным вашему, я обычно смотрю на единицы измерения в JCL или JVCL.Они приложили много усилий, чтобы обеспечить совместимость с Unicode.
Их код, использующий VerQueryValue, находится в модуле JclFileUtils, метод 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;
Надеюсь, это поможет вам начать поиск решения.
--джероен
Другие советы
StrPas не требуется начиная с Delphi 1, поскольку они изменили компилятор Delphi, чтобы он правильно конвертировал строку без вызова функции.Видеть: http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.language.objectpascal/2004-01/1793.html
Поэтому просто назначьте его следующим образом:
LabelProductName.Caption := PAnsiChar(Data);
И да, вам следует удалить все вызовы StrPas.Это не поможет и может только доставить вам неприятности в D2010.
Я думаю, что основная проблема здесь в том, что данные вводятся как Pointer
и не PChar
.
В любом случае, приведение сделает всю работу за вас, поэтому, если вам все равно придется изменить код, приведение будет таким же эффективным, как и вызов функции.
Позвольте мне перефразировать это.Если у вас есть строка в PChar
или PAnsiChar
, он совместим по присваиванию с String
.Причина, по которой вам здесь нужен приведение, заключается в том, что вы ввели его как Pointer
.
Преобразуйте PAnsiChar в AnsiString, а затем в строку Unicode:
LabelProductName.Caption := String(AnsiString(PAnsiChar(Data)));