如何将以null结尾的字符串转换为AnsiString?

4

我有一些代码,在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;

此函数仅提供向后兼容性。要将以 null 结尾的字符串转换为 AnsiString 或本地 Delphi 语言字符串,请使用类型转换或赋值。

那么问题是,我是否应该删除对 StrPas 的所有调用? 我唯一能编译的方法是进行硬转换为 PAnsi char,如下:

LabelProductName.Caption := StrPas(PAnsiChar(Data));
4个回答

4
自从 Delphi 1 以来,就不再需要使用 StrPas 函数了,因为他们改变了 Delphi 编译器的方式,可以在不调用函数的情况下正确地转换字符串。请参见:http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.language.objectpascal/2004-01/1793.html 因此,只需按照以下方式进行赋值:
LabelProductName.Caption := PAnsiChar(Data);

是的,你应该删除所有对StrPas的调用。在D2010中,它无法帮助你,只会让你陷入麻烦。


2

你已经编辑了你的问题并提供了一个完整的编译样例,很好!

如果你使用JCL(链接在下面),这可能是最简单的方法。然后你可以使用TJclFileVersionInfo,它可以完成你想要的所有操作,并考虑到版本信息中可能包含的一些奇特事物。

那么你的业务逻辑将变成这样:

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;

在用户界面中,您需要调用业务逻辑,并将结果放入各种控件中。

--jeroen

编辑前的回答:

您的代码无法直接编译,因此如果没有更多上下文信息,很难回答您的问题。

Buffer、SubBlock、LongCharSetString和DataLen的数据类型是什么?您是如何获取Buffer的?

Data是否真的意味着指向(Ansi或Unicode)字符的指针?它最终不应该指向PVSFixedFileInfo吗?

(一个旁边的问题:为什么您的业务逻辑在UI内部?如果您将逻辑拆分到某个单独的函数中,您可能会有一个编译例程,可以作为您问题的基础。这可能首先就回答了问题)。

对于像您这样的问题,我通常会查看JCLJVCL中的单元。他们在这些方面投入了大量的精力,以确保兼容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;

希望这能帮助你开始找到解决方案。
--jeroen

谢谢你的回答和示例。我现在发布了整个过程。为了避免引入错误,我不想更改超过必要的部分。 - Roland Bengtsson

2
我认为主要问题在于Data被定义为指针(Pointer),而不是PChar。
无论如何,类型转换已为你完成所有工作,所以如果必须更改代码,那么类型转换和函数调用一样好。
让我重述一遍。如果字符串在PChar或PAnsiChar中,则可以与String相互赋值。需要转换的原因是你将其定义为指针(Pointer)。

1
换句话说:声明变量Data: PAnsiChar似乎是一个不错的解决方案。 - Uwe Raabe
1
如果你这样做,那么第一个方法调用不会编译通过,因为它看起来使用了一个“var”参数来表示Data。 - Lasse V. Karlsen

1

将 PAnsiChar 转换为 AnsiString,然后再转换为 Unicode 字符串:

LabelProductName.Caption := String(AnsiString(PAnsiChar(Data)));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接