【问题标题】:How to determine Delphi Application Version如何确定 Delphi 应用程序版本
【发布时间】:2009-11-11 20:27:28
【问题描述】:

想要获取 Delphi 应用程序内部版本号并发布到标题栏

【问题讨论】:

  • 我看到大多数建议的答案都使用 GetFileVersion。此选项存在问题,我已在自己的答案中发布了详细信息。

标签: delphi version


【解决方案1】:

这是我的做法。我把它放在我几乎所有的小工具中:

procedure GetBuildInfo(var V1, V2, V3, V4: word);
var
  VerInfoSize, VerValueSize, Dummy: DWORD;
  VerInfo: Pointer;
  VerValue: PVSFixedFileInfo;
begin
  VerInfoSize := GetFileVersionInfoSize(PChar(ParamStr(0)), Dummy);
  if VerInfoSize > 0 then
  begin
      GetMem(VerInfo, VerInfoSize);
      try
        if GetFileVersionInfo(PChar(ParamStr(0)), 0, VerInfoSize, VerInfo) then
        begin
          VerQueryValue(VerInfo, '\', Pointer(VerValue), VerValueSize);
          with VerValue^ do
          begin
            V1 := dwFileVersionMS shr 16;
            V2 := dwFileVersionMS and $FFFF;
            V3 := dwFileVersionLS shr 16;
            V4 := dwFileVersionLS and $FFFF;
          end;
        end;
      finally
        FreeMem(VerInfo, VerInfoSize);
      end;
  end;
end;

function GetBuildInfoAsString: string;
var
  V1, V2, V3, V4: word;
begin
  GetBuildInfo(V1, V2, V3, V4);
  Result := IntToStr(V1) + '.' + IntToStr(V2) + '.' +
    IntToStr(V3) + '.' + IntToStr(V4);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Form1.Caption := Form1.Caption + ' - v' + GetBuildInfoAsString;
end;

【讨论】:

  • Mick,你应该像Joseph一样检查返回的VerInfoSize,如果> 0才追求,因为它可能没有文件版本信息。
  • 试一试……终于对 GetMem/FreeMem 也不错
  • 我刚刚添加了尝试...终于。
  • 米克,我可以看到你的代码不会编译,因为你的 sn-p 中没有 sFileName 变量的声明。
【解决方案2】:

当您想知道当前运行的可执行文件的版本时,我强烈建议不要使用 GetFileVersion!我有两个很好的理由这样做:

  1. 可执行文件可能无法访问(驱动器/共享断开连接),或已更改(.exe 重命名为 .bak 并替换为新的 .exe,而没有停止正在运行的进程)。
  2. 您尝试读取的版本数据实际上已经加载到内存中,您可以通过加载此资源获得,这总是比执行额外(相对较慢)的磁盘操作要好。

要在 Delphi 中加载版本资源,我使用如下代码:

uses Windows,Classes,SysUtils;
var
  verblock:PVSFIXEDFILEINFO;
  versionMS,versionLS:cardinal;
  verlen:cardinal;
  rs:TResourceStream;
  m:TMemoryStream;
  p:pointer;
  s:cardinal;
begin
  m:=TMemoryStream.Create;
  try
    rs:=TResourceStream.CreateFromID(HInstance,1,RT_VERSION);
    try
      m.CopyFrom(rs,rs.Size);
    finally
      rs.Free;
    end;
    m.Position:=0;
    if VerQueryValue(m.Memory,'\',pointer(verblock),verlen) then
      begin
        VersionMS:=verblock.dwFileVersionMS;
        VersionLS:=verblock.dwFileVersionLS;
        AppVersionString:=Application.Title+' '+
          IntToStr(versionMS shr 16)+'.'+
          IntToStr(versionMS and $FFFF)+'.'+
          IntToStr(VersionLS shr 16)+'.'+
          IntToStr(VersionLS and $FFFF);
      end;
    if VerQueryValue(m.Memory,PChar('\\StringFileInfo\\'+
      IntToHex(GetThreadLocale,4)+IntToHex(GetACP,4)+'\\FileDescription'),p,s) or
        VerQueryValue(m.Memory,'\\StringFileInfo\\040904E4\\FileDescription',p,s) then //en-us
          AppVersionString:=PChar(p)+' '+AppVersionString;
  finally
    m.Free;
  end;
end;

【讨论】:

  • 如果你让代码在内存和资源分配方面异常安全,我会投赞成票。
  • 我已经添加了 try-finally 的,应该可以解决问题。如果您想知道 TMemoryStream 的用途:VerQueryValue 无法从 rs.Memory 目录读取...
  • 重命名正在运行的可执行文件并没有什么不寻常的地方,这是 Windows 无法覆盖它的公认解决方法。更新例程通常重命名正在运行的可执行文件,以便能够将新版本复制到原始位置。
  • 代码暴露了 Delphi 中的一个错误。您请求的资源的NamePChar(1)。如果找不到资源(即没有版本信息),Delphi 将尝试抛出EResNotFound找不到资源%s)。当它尝试使用PChar0x00000001 构建字符串时,将触发访问冲突,因为在地址 $00000001 处没有要读取的 ansi 字符。
  • 除非语言环境是 0409 美元(美国英语),否则也不起作用 - 对于全球 Delphi 开发人员来说,这不是一个可靠的假设 - 否则很好的答案
【解决方案3】:

感谢上面的帖子,我为此建立了自己的库。

我相信它比这里的所有其他解决方案都更正确,所以我分享它 - 随意重复使用它......

unit KkVersion;

interface

function FileDescription: String;
function LegalCopyright: String;
function DateOfRelease: String; // Proprietary
function ProductVersion: String;
function FileVersion: String;

implementation

uses
  Winapi.Windows, System.SysUtils, System.Classes, Math;

(*
  function GetHeader(out AHdr: TVSFixedFileInfo): Boolean;

  var
  BFixedFileInfo: PVSFixedFileInfo;
  RM: TMemoryStream;
  RS: TResourceStream;
  BL: Cardinal;

  begin
  Result := False;
  RM := TMemoryStream.Create;
  try
  RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
  try
  RM.CopyFrom(RS, RS.Size);
  finally
  FreeAndNil(RS);
  end;

  // Extract header
  if not VerQueryValue(RM.Memory, '\\', Pointer(BFixedFileInfo), BL) then
  Exit;

  // Prepare result
  CopyMemory(@AHdr, BFixedFileInfo, Math.Min(sizeof(AHdr), BL));
  Result := True;
  finally
  FreeAndNil(RM);
  end;
  end;
*)

function GetVersionInfo(AIdent: String): String;

type
  TLang = packed record
    Lng, Page: WORD;
  end;

  TLangs = array [0 .. 10000] of TLang;

  PLangs = ^TLangs;

var
  BLngs: PLangs;
  BLngsCnt: Cardinal;
  BLangId: String;
  RM: TMemoryStream;
  RS: TResourceStream;
  BP: PChar;
  BL: Cardinal;
  BId: String;

begin
  // Assume error
  Result := '';

  RM := TMemoryStream.Create;
  try
    // Load the version resource into memory
    RS := TResourceStream.CreateFromID(HInstance, 1, RT_VERSION);
    try
      RM.CopyFrom(RS, RS.Size);
    finally
      FreeAndNil(RS);
    end;

    // Extract the translations list
    if not VerQueryValue(RM.Memory, '\\VarFileInfo\\Translation', Pointer(BLngs), BL) then
      Exit; // Failed to parse the translations table
    BLngsCnt := BL div sizeof(TLang);
    if BLngsCnt <= 0 then
      Exit; // No translations available

    // Use the first translation from the table (in most cases will be OK)
    with BLngs[0] do
      BLangId := IntToHex(Lng, 4) + IntToHex(Page, 4);

    // Extract field by parameter
    BId := '\\StringFileInfo\\' + BLangId + '\\' + AIdent;
    if not VerQueryValue(RM.Memory, PChar(BId), Pointer(BP), BL) then
      Exit; // No such field

    // Prepare result
    Result := BP;
  finally
    FreeAndNil(RM);
  end;
end;

function FileDescription: String;
begin
  Result := GetVersionInfo('FileDescription');
end;

function LegalCopyright: String;
begin
  Result := GetVersionInfo('LegalCopyright');
end;

function DateOfRelease: String;
begin
  Result := GetVersionInfo('DateOfRelease');
end;

function ProductVersion: String;
begin
  Result := GetVersionInfo('ProductVersion');
end;

function FileVersion: String;
begin
  Result := GetVersionInfo('FileVersion');
end;

end.

【讨论】:

  • 谢谢 - 我投票支持你是因为你没有陷入假设语言环境为 0409 美元(美国英语)的陷阱。它不在我的 Delphi XE7 上,我相信对于大多数 Delphi 开发人员来说
  • @DaveBoltman 是的,我的所有 Delphi 项目都设置为南非英语。
  • @ShaunRoselt - “南非英语” - 是的,我也是!来自克鲁格斯多普的问候
【解决方案4】:

将您的 EXE 的完整文件名传递给此函数,它将返回如下字符串: 2.1.5.9,或者任何你的版本号。

function GetFileVersion(exeName : string): string;
const
  c_StringInfo = 'StringFileInfo\040904E4\FileVersion';
var
  n, Len : cardinal;
  Buf, Value : PChar;
begin
  Result := '';
  n := GetFileVersionInfoSize(PChar(exeName),n);
  if n > 0 then begin
    Buf := AllocMem(n);
    try
      GetFileVersionInfo(PChar(exeName),0,n,Buf);
      if VerQueryValue(Buf,PChar(c_StringInfo),Pointer(Value),Len) then begin
        Result := Trim(Value);
      end;
    finally
      FreeMem(Buf,n);
    end;
  end;
end;

定义后,您可以使用它来设置表单的标题,如下所示:

procedure TForm1.FormShow(Sender: TObject);
begin 
  //ParamStr(0) is the full path and file name of the current application
  Form1.Caption := Form1.Caption + ' version ' + GetFileVersion(ParamStr(0));
end;

【讨论】:

  • Joseph,你应该尝试保护你的 AllocMem...最后
  • @Wodzu:它在 D2007 中对我有用。您的项目是否在 Project->Options->Version Info 下选中了“Include version information in project”选项? Windows 资源管理器告诉您文件版本是什么?
  • 这行不通,除非您的语言环境是 $0409(美国英语)——这不是一个可靠的假设
  • 与 Delphi 10.1 (Berlin) 配合得非常好。 :)
  • @DaveBoltman,您是对的,感谢您指出这一点。用户 Jiri Krivanek 下面有另一个版本,声称可以避免语言环境问题。我没试过,但它可能更适合你。或者,您可以将我的代码中的“040904E4”更改为您需要的语言环境。
【解决方案5】:

我们对所有应用都这样做,但我们使用 Raize 组件 RzVersioninfo。 效果很好只需要使用以下代码

在表单创建时

Caption := RzVersioninfo1.filedescripion + ': ' + RzVersionInfo1.FileVersion;

显然,如果您不希望使用 raize 的任何其他组件,请使用上述选项之一,因为 raize 组件是有成本的。

【讨论】:

  • 我喜欢这种方法,因为它非常简单和简短。
【解决方案6】:

来自http://www.martinstoeckli.ch/delphi/delphi.html#AppVersion

使用此函数,您可以获得文件的版本,其中包含 版本资源。这样您就可以显示您的版本号 信息对话框中的应用程序。将版本资源包含到 您的 Delphi 应用程序,在项目选项中设置“Versioninfo”。

【讨论】:

  • 良好的链接,但糟糕的答案。请总结一下链接的内容:我们希望通过链接找到什么样的解决方案,需要注意哪些重要功能?
猜你喜欢
  • 2011-06-17
  • 2012-04-03
  • 1970-01-01
  • 2020-12-30
  • 1970-01-01
  • 1970-01-01
  • 2017-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多