2009-09-20 10 views
3

J'ai du code qui compile bien avec D7 mais échoue avec D2010. De toute évidence, il est un problème Unicode:Comment convertir une chaîne terminée par un caractère nul en un AnsiString?

L'erreur de compilation est: E2251 Ambigu appel surchargé à 'StrPas'

est toute la procédure ici:

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; 

Le doc pour StrPas dit

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

Cette fonction est fournie fasor rétrocompatibilité seulement. Pour convertir une chaîne terminée par un caractère NULL en une chaîne de caractères Delphi AnsiString ou natif , utilisez un transtypage ou une signature.

Donc la question est de savoir si je dois supprimer tous les appels à StrPas? La seule façon que je fais cela pour compiler est de faire un hardcast à Pansi ombles comme:

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

Répondre

2

Vous avez modifié votre question avec un exemple de compilation complet. Bien!

C'est probablement le plus simple si vous utilisiez le JCL pour cela (lien ci-dessous). Ensuite, vous pouvez utiliser TJclFileVersionInfo, qui fait tout ce que vous voulez, et prend en compte certaines choses ésothériques que le VersionInfo peut contenir.

Ensuite, votre logique métier deviendrait quelque chose comme ceci:

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; 

Et dans l'interface utilisateur, vous devez appeler votre logique métier, et de mettre les résultats dans les différents contrôles.

--jeroen

réponse avant edit:

Votre code ne compile pas tel qu'il est, sans plus de contexte, il est difficile de répondre à votre quête.

Quels sont les types de données de Buffer, SubBlock, LongCharSetString et DataLen? Comment avez-vous obtenu Buffer?

Est-ce que les données sont vraiment destinées à être des caractères Pointer to (Ansi ou Unicode)? Ne devrait-il finalement pas pointer vers PVSFixedFileInfo?

(Une question secondaire: pourquoi votre logique métier est-elle dans l'interface utilisateur?Si vous aviez divisé votre logique en une fonction distincte quelque part, vous auriez probablement une routine de compilation qui aurait pu servir de base à votre question. Cela aurait pu répondre à la question en premier lieu).

Pour des questions comme la vôtre, je jette un coup d'oeil aux unités dans le JCL ou JVCL. Ils ont fait beaucoup d'efforts pour être compatibles Unicode.

Leur code qui utilisent VerQueryValue est celui-ci dans l'unité JclFileUtils, méthode 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 vous permet de démarrer à trouver votre solution.

--jeroen

+0

Merci pour votre réponse et l'exemple. J'ai posté toute la procédure maintenant. Je ne veux pas changer plus que nécessaire pour éviter d'introduire des bugs. –

2

Je pense que le principal problème est que les données est entré comme Pointer et non un PChar.

Dans tous les cas, la distribution fait tout le travail pour vous, donc si vous devez quand même changer le code, la distribution est tout aussi bonne que l'appel de la fonction.

Permettez-moi de reformuler cela. Si vous avez la chaîne dans PChar ou PAnsiChar, elle est compatible avec String. La raison pour laquelle vous avez besoin d'un casting ici est que vous l'avez tapé comme Pointer.

+1

En d'autres termes: la déclaration de données var: PAnsiChar semble être une solution décente. –

+1

Si vous le pouvez, mais alors, cette première méthode appel ne compilera-t-elle pas, puisqu'elle semble utiliser un paramètre "var" pour Data? –

3

StrPas n'a pas été nécessaire depuis Delphi 1, car ils ont changé le compilateur Delphi pour convertir correctement la chaîne sans appel de fonction. Voir: http://coding.derkeiler.com/Archive/Delphi/borland.public.delphi.language.objectpascal/2004-01/1793.html

donc il suffit de lui assigner, comme suit:

LabelProductName.Caption := PAnsiChar(Data); 

Et oui, vous devez supprimer tous les appels à StrPas. Il ne peut pas aider et ne peut que vous causer des ennuis dans D2010.

1

Convertir PAnsiChar à AnsiString puis à chaîne Unicode:

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