【问题标题】:How convert null-terminated string to an AnsiString?如何将空终止字符串转换为 AnsiString?
【发布时间】:2009-09-20 06:54:02
【问题描述】:

我有一些代码可以用 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;

提供此功能只是为了向后兼容。 将空终止字符串转换为 AnsiString 或本机 Delphi 语言字符串,使用类型转换或符号。

所以问题是我应该删除对 StrPas 的所有调用吗? 我进行编译的唯一方法是对 PAnsi char 进行硬转换,例如:

LabelProductName.Caption := StrPas(PAnsiChar(Data));

【问题讨论】:

    标签: delphi unicode delphi-2010


    【解决方案1】:

    自 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,它可以满足您的所有需求,并考虑到 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;
      

      而在 UI 中,您将需要调用您的业务逻辑,并将结果放入各种控件中。

      --杰罗恩

      编辑前回答:

      您的代码不会按原样编译,因此如果没有更多上下文,很难回答您的问题。

      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;
      

      希望这可以帮助您开始寻找解决方案。

      --杰罗恩

      【讨论】:

      • 感谢您的回答和示例。我现在发布了整个过程。我不想进行不必要的更改以避免引入错误。
      【解决方案3】:

      我认为这里的主要问题是 Data 被键入为 Pointer 而不是 PChar

      在任何情况下,转换都会为您完成所有工作,因此如果您无论如何都必须更改代码,转换与函数调用一样好。

      让我换个说法。如果您在PCharPAnsiChar 中有字符串,则它与String 的赋值兼容。您需要在这里进行演员表的原因是您将其键入为Pointer

      【讨论】:

      • 换句话说:声明 var Data: PAnsiChar 似乎是一个不错的解决方案。
      • 如果可以,但是,第一个方法调用不会编译,因为它看起来像使用数据的“var”参数吗?
      【解决方案4】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-12-25
        • 2020-10-15
        • 1970-01-01
        • 2012-12-23
        • 1970-01-01
        • 2019-05-09
        相关资源
        最近更新 更多