【Delphi】操纵EXE文件中版本信息(RT_VERSION)

RT_VERSION 是 Windows 可执行文件中用于存储版本信息的重要资源类型,它包含了应用程序的版本号、公司信息、版权声明等元数据。
基本概念
RT_VERSION 资源类型的 ID 为 16(0x10),它采用层次化的结构组织版本信息,主要包含以下几个部分:
- VS_VERSIONINFO:顶层结构,包含固定版本信息和子结构
- VS_FIXEDFILEINFO:固定格式的版本信息,与语言无关
- VarFileInfo:存储语言和代码页信息
- StringFileInfo:存储用户可读的字符串版本信息
数据结构定义
VS_VERSIONINFO 结构
typedef struct {WORD wLength; // 整个结构的长度(字节)WORD wValueLength; // VS_FIXEDFILEINFO 的长度WORD wType; // 资源类型(1 表示文本数据)WCHAR szKey[16]; // 关键字 "VS_VERSION_INFO"WORD Padding1; // 对齐填充VS_FIXEDFILEINFO Value; // 固定版本信息WORD Padding2; // 对齐填充WORD Children; // 子结构数量(通常为 2)
} VS_VERSIONINFO, *LPVS_VERSIONINFO;VS_FIXEDFILEINFO 结构
typedef struct {DWORD dwSignature; // 签名,固定为 0xFEEF04BDDWORD dwStrucVersion; // 结构版本号(高字为主版本,低字为次版本)DWORD dwFileVersionMS; // 文件版本号(高 32 位)DWORD dwFileVersionLS; // 文件版本号(低 32 位)DWORD dwProductVersionMS; // 产品版本号(高 32 位)DWORD dwProductVersionLS; // 产品版本号(低 32 位)DWORD dwFileFlagsMask; // 文件标志掩码DWORD dwFileFlags; // 文件标志DWORD dwFileOS; // 目标操作系统DWORD dwFileType; // 文件类型DWORD dwFileSubtype; // 文件子类型DWORD dwFileDateMS; // 文件创建日期(高 32 位)DWORD dwFileDateLS; // 文件创建日期(低 32 位)
} VS_FIXEDFILEINFO, *LPVS_FIXEDFILEINFO;VarFileInfo 结构
typedef struct {WORD wLength; // 结构总长度WORD wValueLength; // 始终为 0WORD wType; // 资源类型(1 表示文本数据)WCHAR szKey[10]; // 关键字 "VarFileInfo"WORD Padding; // 对齐填充// 包含语言和代码页信息的子结构
} VarFileInfo, *LPVarFileInfo;StringFileInfo 结构
typedef struct {WORD wLength; // 结构总长度WORD wValueLength; // 始终为 0WORD wType; // 资源类型(1 表示文本数据)WCHAR szKey[13]; // 关键字 "StringFileInfo"WORD Padding; // 对齐填充// 包含特定语言代码页的字符串信息块
} StringFileInfo, *LPStringFileInfo;StringTable 结构
typedef struct {WORD wLength; // 结构总长度WORD wValueLength; // 始终为 0WORD wType; // 资源类型(1 表示文本数据)WCHAR szKey[8]; // 语言代码页标识符(如 "040904B0")WORD Padding; // 对齐填充// 包含多个版本信息字符串
} StringTable, *LPStringTable;String 结构
typedef struct {WORD wLength; // 字符串结构总长度WORD wValueLength; // 字符串值的长度(字节)WORD wType; // 资源类型(1 表示文本数据)WCHAR szKey[]; // 字符串名称(如 "CompanyName")WORD Padding; // 对齐填充WCHAR Value[]; // 字符串值
} String, *LPString;重要枚举值
文件标志 (dwFileFlags)
VS_FF_DEBUG = $00000001; // 包含调试信息
VS_FF_PRERELEASE = $00000002; // 预发布版本
VS_FF_PATCHED = $00000004; // 已修补的文件
VS_FF_PRIVATEBUILD = $00000008; // 私有构建版本
VS_FF_INFOINFERRED = $00000010; // 信息推断而来
VS_FF_SPECIALBUILD = $00000020; // 特殊构建版本目标操作系统 (dwFileOS)
VOS_UNKNOWN = $00000000; // 未知操作系统
VOS_DOS = $00010000; // MS-DOS
VOS_NT = $00040000; // Windows NT
VOS__WINDOWS16 = $00000001; // 16位Windows
VOS__WINDOWS32 = $00000004; // 32位Windows
VOS_NT_WINDOWS32 = $00040004; // Windows NT 32位文件类型 (dwFileType)
VFT_UNKNOWN = $00000000; // 未知类型
VFT_APP = $00000001; // 应用程序
VFT_DLL = $00000002; // 动态链接库
VFT_DRV = $00000003; // 设备驱动程序
VFT_FONT = $00000004; // 字体文件
VFT_VXD = $00000005; // VxD驱动程序
VFT_STATIC_LIB = $00000007; // 静态链接库版本信息字符串
StringFileInfo 中可以包含以下标准字符串:
| 字符串名称 | 说明 |
|---|---|
| Comments | 备注信息 |
| CompanyName | 公司名称 |
| FileDescription | 文件描述 |
| FileVersion | 文件版本字符串 |
| InternalName | 内部名称 |
| LegalCopyright | 版权信息 |
| LegalTrademarks | 商标信息 |
| OriginalFilename | 原始文件名 |
| PrivateBuild | 私有构建信息 |
| ProductName | 产品名称 |
| ProductVersion | 产品版本字符串 |
| SpecialBuild | 特殊构建信息 |
Delphi 中处理 RT_VERSION 的示例代码
读取版本信息
usesWindows, SysUtils, Classes;function GetFileVersionInfo(const FileName: string): string;
varhFile: DWORD;dwSize: DWORD;pData: Pointer;pFixedInfo: PVS_FIXEDFILEINFO;dwVerSize: DWORD;FileVerMS, FileVerLS: DWORD;ProductVerMS, ProductVerLS: DWORD;
beginResult := '';// 获取版本信息大小dwSize := GetFileVersionInfoSize(PChar(FileName), hFile);if dwSize = 0 then Exit;// 分配内存并读取版本信息GetMem(pData, dwSize);tryif GetFileVersionInfo(PChar(FileName), 0, dwSize, pData) thenbegin// 获取固定版本信息if VerQueryValue(pData, '\', Pointer(pFixedInfo), dwVerSize) thenbeginFileVerMS := pFixedInfo.dwFileVersionMS;FileVerLS := pFixedInfo.dwFileVersionLS;ProductVerMS := pFixedInfo.dwProductVersionMS;ProductVerLS := pFixedInfo.dwProductVersionLS;Result := Format('文件版本: %d.%d.%d.%d'#13#10 +'产品版本: %d.%d.%d.%d',[HIWORD(FileVerMS), LOWORD(FileVerMS),HIWORD(FileVerLS), LOWORD(FileVerLS),HIWORD(ProductVerMS), LOWORD(ProductVerMS),HIWORD(ProductVerLS), LOWORD(ProductVerLS)]);end;end;finallyFreeMem(pData);end;
end;function GetStringFileInfo(const FileName, ValueName: string): string;
varhFile: DWORD;dwSize: DWORD;pData: Pointer;pValue: PChar;dwValueSize: DWORD;LangID: array[0..1] of WORD;LangCode: string;
beginResult := '';// 获取版本信息大小dwSize := GetFileVersionInfoSize(PChar(FileName), hFile);if dwSize = 0 then Exit;// 分配内存并读取版本信息GetMem(pData, dwSize);tryif GetFileVersionInfo(PChar(FileName), 0, dwSize, pData) thenbegin// 获取语言IDif VerQueryValue(pData, '\VarFileInfo\Translation', Pointer(LangID), dwValueSize) thenbegin// 构建语言代码页路径LangCode := Format('\StringFileInfo\%.4x%.4x\%s', [LangID[0], LangID[1], ValueName]);// 获取字符串值if VerQueryValue(pData, PChar(LangCode), Pointer(pValue), dwValueSize) thenbeginResult := StrPas(pValue);end;end;end;finallyFreeMem(pData);end;
end;更新版本信息
procedure UpdateFileVersionInfo(const FileName, NewVersion: string);
varhUpdate: THandle;VersionInfo: TMemoryStream;VersionData: array of Byte;VersionSize: DWORD;LangID: array[0..1] of WORD;LangCode: string;
begin// 创建新版本信息数据VersionInfo := TMemoryStream.Create;try// 这里需要构建完整的VS_VERSIONINFO结构// 实际应用中需要根据NewVersion参数构建正确的版本信息VersionSize := VersionInfo.Size;SetLength(VersionData, VersionSize);VersionInfo.Position := 0;VersionInfo.ReadBuffer(VersionData[0], VersionSize);// 开始更新资源hUpdate := BeginUpdateResource(PChar(FileName), False);if hUpdate = 0 thenRaiseLastOSError;try// 更新RT_VERSION资源if not UpdateResource(hUpdate, RT_VERSION, MAKEINTRESOURCE(1),MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), @VersionData[0], VersionSize) thenRaiseLastOSError;// 完成更新if not EndUpdateResource(hUpdate, False) thenRaiseLastOSError;exceptEndUpdateResource(hUpdate, True);raise;end;finallyVersionInfo.Free;end;
end;完整的版本信息读取示例
procedure ShowFileVersionInfo(const FileName: string);
varVersionInfo: string;CompanyName: string;FileDescription: string;LegalCopyright: string;ProductName: string;
beginVersionInfo := GetFileVersionInfo(FileName);if VersionInfo = '' thenbeginWriteln('无法读取版本信息');Exit;end;CompanyName := GetStringFileInfo(FileName, 'CompanyName');FileDescription := GetStringFileInfo(FileName, 'FileDescription');LegalCopyright := GetStringFileInfo(FileName, 'LegalCopyright');ProductName := GetStringFileInfo(FileName, 'ProductName');Writeln('版本信息:');Writeln(VersionInfo);Writeln('公司名称: ', CompanyName);Writeln('文件描述: ', FileDescription);Writeln('版权信息: ', LegalCopyright);Writeln('产品名称: ', ProductName);
end;RC 文件中的版本信息定义
1 VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGINBLOCK "VarFileInfo"BEGINVALUE "Translation", 0x0804, 0x04B0 // 简体中文, UnicodeENDBLOCK "StringFileInfo"BEGINBLOCK "080404B0"BEGINVALUE "Comments", "这是一个示例应用程序"VALUE "CompanyName", "我的公司"VALUE "FileDescription", "示例应用程序"VALUE "FileVersion", "1.0.0.1"VALUE "InternalName", "SampleApp"VALUE "LegalCopyright", "© 2025 我的公司. 保留所有权利."VALUE "LegalTrademarks", "SampleApp"VALUE "OriginalFilename", "SampleApp.exe"VALUE "PrivateBuild", ""VALUE "ProductName", "SampleApp"VALUE "ProductVersion", "1.0.0.1"VALUE "SpecialBuild", ""ENDEND
END关键要点
- 资源 ID 要求:RT_VERSION 资源的 ID 必须为 1,否则 Windows 资源管理器无法正确显示版本信息
- 对齐要求:所有结构必须在 DWORD 边界上对齐
- 多语言支持:通过 VarFileInfo 和多个 StringTable 可以支持多种语言的版本信息
- 版本号格式:版本号由 4 个 16 位整数组成,格式为 Major.Minor.Build.Revision
- 字符串编码:所有字符串必须使用 Unicode 编码
- API 函数:使用 GetFileVersionInfo、VerQueryValue 等 API 函数读取版本信息
理解 RT_VERSION 格式对于版本控制、软件部署和系统集成等任务非常重要,它提供了一种标准化的方式来存储和检索应用程序的元数据。
