如何使用Inno Setup仅在安装包字体版本更高时覆盖已安装字体
你说的没错,Inno Setup自带的GetVersionNumbersString这类函数确实更偏向PE格式的exe/dll文件,但.ttf、.otf这类字体文件其实也藏着版本信息,只是得借助Windows API来读取。下面给你一套完整的实现思路和代码示例,帮你搞定「仅更新更高版本字体」的需求:
具体实现步骤
1. 编写自定义函数读取文件版本
我们需要调用Windows的GetFileVersionInfo和VerQueryValueAPI,来提取字体文件的版本号。同时写一个版本比较函数,方便判断哪个版本更高。
在脚本的[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




