You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何使用Inno Setup仅在安装包字体版本更高时覆盖已安装字体

你说的没错,Inno Setup自带的GetVersionNumbersString这类函数确实更偏向PE格式的exe/dll文件,但.ttf、.otf这类字体文件其实也藏着版本信息,只是得借助Windows API来读取。下面给你一套完整的实现思路和代码示例,帮你搞定「仅更新更高版本字体」的需求:

具体实现步骤

1. 编写自定义函数读取文件版本

我们需要调用Windows的GetFileVersionInfoVerQueryValueAPI,来提取字体文件的版本号。同时写一个版本比较函数,方便判断哪个版本更高。

在脚本的[Code]段添加以下代码:

function GetFileVersion(const Filename: string): string;
var
  Size, Handle: DWORD;
  Buffer: PChar;
  FixedInfo: PVSFixedFileInfo;
  FixedInfoSize: DWORD;
begin
  Result := '';
  Size := GetFileVersionInfoSize(PChar(Filename), Handle);
  if Size = 0 then Exit;
  GetMem(Buffer, Size);
  try
    if GetFileVersionInfo(PChar(Filename), Handle, Size, Buffer) then
    begin
      if VerQueryValue(Buffer, '\', Pointer(FixedInfo), FixedInfoSize) then
      begin
        Result := Format('%d.%d.%d.%d', [
          HiWord(FixedInfo.dwFileVersionMS),
          LoWord(FixedInfo.dwFileVersionMS),
          HiWord(FixedInfo.dwFileVersionLS),
          LoWord(FixedInfo.dwFileVersionLS)
        ]);
      end;
    end;
  finally
    FreeMem(Buffer);
  end;
end;

// 版本比较:返回1=Ver1更高,-1=Ver2更高,0=版本相同
function CompareVersions(const Ver1, Ver2: string): Integer;
var
  V1Parts, V2Parts: TArrayOfString;
  I, V1, V2: Integer;
begin
  Result := 0;
  SplitString(Ver1, '.', V1Parts);
  SplitString(Ver2, '.', V2Parts);
  
  for I := 0 to Min(Length(V1Parts)-1, Length(V2Parts)-1) do
  begin
    V1 := StrToIntDef(V1Parts[I], 0);
    V2 := StrToIntDef(V2Parts[I], 0);
    if V1 > V2 then
    begin
      Result := 1;
      Exit;
    end
    else if V1 < V2 then
    begin
      Result := -1;
      Exit;
    end;
  end;
  
  // 前缀版本相同的情况下,更长的版本号视为更高版本
  if Length(V1Parts) > Length(V2Parts) then
    Result := 1
  else if Length(V1Parts) < Length(V2Parts) then
    Result := -1;
end;

2. 编写判断是否安装的检查函数

接下来写一个ShouldInstallFont函数,用来判断当前安装包中的字体是否需要覆盖系统里的旧版本:

function ShouldInstallFont(const FontFilename: string): Boolean;
var
  SourcePath, DestPath: string;
  SourceVersion, DestVersion: string;
begin
  // 获取安装包中字体的路径
  SourcePath := ExpandConstant('{src}\' + FontFilename);
  // 系统字体目录的目标路径
  DestPath := ExpandConstant('{fonts}\' + FontFilename);
  
  // 系统中没有该字体,直接安装
  if not FileExists(DestPath) then
  begin
    Result := True;
    Exit;
  end;
  
  // 读取安装包和系统中字体的版本
  SourceVersion := GetFileVersion(SourcePath);
  DestVersion := GetFileVersion(DestPath);
  
  // 处理版本读取失败的情况:源版本读不出则不安装;目标版本读不出则安装
  if SourceVersion = '' then
  begin
    Result := False;
    Exit;
  end;
  if DestVersion = '' then
  begin
    Result := True;
    Exit;
  end;
  
  // 只有安装包字体版本更高时,才返回True执行安装
  Result := CompareVersions(SourceVersion, DestVersion) = 1;
end;

3. 在文件段配置安装规则

最后在[Files]段中,给字体文件添加Check参数,关联我们写的判断函数:

[Files]
; 替换成你的字体文件名,Flags根据需求调整
Source: "MyCustomFont.ttf"; DestDir: "{fonts}"; Flags: uninsneveruninstall; Check: ShouldInstallFont('MyCustomFont.ttf')
注意事项
  • 权限要求:安装字体需要管理员权限,所以务必在脚本开头添加PrivilegesRequired=admin
  • 文件名匹配:上面的方案基于「字体文件名固定」的前提,如果你的字体安装后系统会自动修改文件名(比如部分特殊字体),可能需要额外通过EnumFontFamiliesExAPI枚举已安装字体的名称和路径,再做匹配,这个会复杂一些,但核心逻辑还是版本比较。
  • 卸载设置:示例中用了uninsneveruninstall,因为字体可能被其他程序依赖,不建议自动卸载,你可以根据实际需求调整这个Flag。

内容的提问来源于stack exchange,提问作者Olivier

火山引擎 最新活动