views:

1751

answers:

4

I have some code that compiles fine with D7 but fails with D2010. Obviously it is an Unicode issue:

The compile error is: E2251 Ambiguous overloaded call to 'StrPas'

Here is the whole procedure:

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;

The doc for StrPas says

function StrPas(const Str: PAnsiChar): AnsiString; overload;

This function is provided fasor backwards compatibility only. To convert a null-terminated string to an AnsiString or native Delphi language string, use a typecast or an signment.

So the question is should I remove all calls to StrPas ? The only way I make this to compile is to do a hardcast to PAnsi char like:

LabelProductName.Caption := StrPas(PAnsiChar(Data));
+2  A: 

I think the main problem here is that Data is typed as a Pointer and not a PChar.

In any case, the cast does all the work for you so if you have to change the code anyway, the cast is just as good as the function call.

Let me rephrase that. If you have the string in a PChar or PAnsiChar, it is assignment-compatible with String. The reason you need a cast here is that you've typed it as Pointer.

Lasse V. Karlsen
In other words: declaring var Data: PAnsiChar seems to be a decent solution.
Uwe Raabe
If you can, but then, won't that first method call not compile, since it looks like it uses a "var" parameter for Data?
Lasse V. Karlsen
+1  A: 

You edited your question with a full compiling sample. Good!

It is probably the easiest if you'd use the JCL for this (link is below). Then you could use TJclFileVersionInfo, which does all you want, and takes into account some esotheric things that the VersionInfo can hold.

Then your business logic would become something like this:

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;

And in the UI, you will need to call your business logic, and put the results into the various controls.

--jeroen

Answer before edit:

Your code does not compile as is, so without more context, it is hard to answer your quest.

What are the data types of Buffer, SubBlock, LongCharSetString and DataLen? How did you obtain Buffer?

Is Data really meant to be a Pointer to (Ansi or Unicode) characters? Shouldn't it ultimately be of pointing to PVSFixedFileInfo?

(A side question: why is your business logic inside the UI? If you had split your logic into a separate function somewhere, you'd probably have a compiling routine that could have served as the base for your question. That might have answered the question in the first place).

For questions like yours, I usually take a peek at units in the the JCL or JVCL. They have put a lot of effort in those to be Unicode compatible.

Their code that use VerQueryValue is this one in unit JclFileUtils, method 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;

Hope this gets you started in finding your solution.

--jeroen

Jeroen Pluimers
Thanks for your answer and the example. I posted the whole procedure now. I don't want to change more than necessary to avoid introduce bugs.
Roland Bengtsson
+3  A: 

StrPas has not been required since Delphi 1, since they changed the Delphi compiler to convert the string correctly without a function call. See: http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.language.objectpascal/2004-01/1793.html

So just assign it, as follows:

LabelProductName.Caption := PAnsiChar(Data);

And yes, you should remove all calls to StrPas. It can't help and can only get you in trouble in D2010.

lkessler
+1  A: 

Convert PAnsiChar to AnsiString and then to Unicode string:

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