Как преобразовать строку с нулевым символом в конце в AnsiString? - PullRequest
3 голосов
/ 20 сентября 2009

У меня есть код, который прекрасно компилируется с 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, используйте Typecast или обозначение.

Таким образом, вопрос заключается в том, чтобы удалить все вызовы StrPas? Единственный способ сделать это для компиляции - сделать hardcast для PAnsi char вроде:

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

Ответы [ 4 ]

4 голосов
/ 21 сентября 2009

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.

2 голосов
/ 20 сентября 2009

Вы отредактировали свой вопрос с полным образцом компиляции. Хорошо!

Вероятно, проще всего было бы использовать для этого 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?

Действительно ли Data - это указатель на символы (Ansi или Unicode)? Не должно ли в конечном итоге указывать на PVSFixedFileInfo?

(Дополнительный вопрос: почему ваша бизнес-логика находится внутри пользовательского интерфейса? Если бы вы где-то разбили свою логику на отдельную функцию, у вас, вероятно, была бы подпрограмма компиляции, которая могла бы послужить основой для вашего вопроса. ответили на вопрос в первую очередь).

Для вопросов, подобных вашему, я обычно заглядываю единицы измерения в JCL или JVCL . Они приложили немало усилий, чтобы быть совместимыми с Юникодом.

Их код, который использует 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;

Надеюсь, это поможет вам найти решение.

- Йерун

2 голосов
/ 20 сентября 2009

Я думаю, что главная проблема здесь в том, что данные набираются как Pointer, а не PChar.

В любом случае приведение выполняет всю работу за вас, поэтому, если вам все равно придется изменить код, приведение ничем не уступает вызову функции.

Позвольте мне перефразировать это. Если у вас есть строка в PChar или PAnsiChar, она совместима с назначением String. Причина, по которой вам нужен актерский состав, заключается в том, что вы набрали его как Pointer.

1 голос
/ 21 сентября 2009

Преобразовать PAnsiChar в AnsiString, а затем в строку Unicode:

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