Как преобразовать строку с нулевым завершением в AnsiString?

StackOverflow https://stackoverflow.com/questions/1450469

  •  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)));
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top