【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;          // 签名,固定为 0xFEEF04BD
  DWORD   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;     // 始终为 0
  WORD  wType;            // 资源类型(1 表示文本数据)
  WCHAR szKey[10];        // 关键字 "VarFileInfo"
  WORD  Padding;          // 对齐填充
  // 包含语言和代码页信息的子结构
} VarFileInfo, *LPVarFileInfo;

StringFileInfo 结构

typedef struct {
  WORD  wLength;          // 结构总长度
  WORD  wValueLength;     // 始终为 0
  WORD  wType;            // 资源类型(1 表示文本数据)
  WCHAR szKey[13];        // 关键字 "StringFileInfo"
  WORD  Padding;          // 对齐填充
  // 包含特定语言代码页的字符串信息块
} StringFileInfo, *LPStringFileInfo;

StringTable 结构

typedef struct {
  WORD  wLength;          // 结构总长度
  WORD  wValueLength;     // 始终为 0
  WORD  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 的示例代码

读取版本信息

uses
  Windows, SysUtils, Classes;

function GetFileVersionInfo(const FileName: string): string;
var
  hFile: DWORD;
  dwSize: DWORD;
  pData: Pointer;
  pFixedInfo: PVS_FIXEDFILEINFO;
  dwVerSize: DWORD;
  FileVerMS, FileVerLS: DWORD;
  ProductVerMS, ProductVerLS: DWORD;
begin
  Result := '';
  
  // 获取版本信息大小
  dwSize := GetFileVersionInfoSize(PChar(FileName), hFile);
  if dwSize = 0 then Exit;
  
  // 分配内存并读取版本信息
  GetMem(pData, dwSize);
  try
    if GetFileVersionInfo(PChar(FileName), 0, dwSize, pData) then
    begin
      // 获取固定版本信息
      if VerQueryValue(pData, '\', Pointer(pFixedInfo), dwVerSize) then
      begin
        FileVerMS := 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;
  finally
    FreeMem(pData);
  end;
end;

function GetStringFileInfo(const FileName, ValueName: string): string;
var
  hFile: DWORD;
  dwSize: DWORD;
  pData: Pointer;
  pValue: PChar;
  dwValueSize: DWORD;
  LangID: array[0..1] of WORD;
  LangCode: string;
begin
  Result := '';
  
  // 获取版本信息大小
  dwSize := GetFileVersionInfoSize(PChar(FileName), hFile);
  if dwSize = 0 then Exit;
  
  // 分配内存并读取版本信息
  GetMem(pData, dwSize);
  try
    if GetFileVersionInfo(PChar(FileName), 0, dwSize, pData) then
    begin
      // 获取语言ID
      if VerQueryValue(pData, '\VarFileInfo\Translation', Pointer(LangID), dwValueSize) then
      begin
        // 构建语言代码页路径
        LangCode := Format('\StringFileInfo\%.4x%.4x\%s', 
                          [LangID[0], LangID[1], ValueName]);
        
        // 获取字符串值
        if VerQueryValue(pData, PChar(LangCode), Pointer(pValue), dwValueSize) then
        begin
          Result := StrPas(pValue);
        end;
      end;
    end;
  finally
    FreeMem(pData);
  end;
end;

更新版本信息

procedure UpdateFileVersionInfo(const FileName, NewVersion: string);
var
  hUpdate: 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 then
      RaiseLastOSError;
      
    try
      // 更新RT_VERSION资源
      if not UpdateResource(hUpdate, RT_VERSION, MAKEINTRESOURCE(1),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), 
        @VersionData[0], VersionSize) then
        RaiseLastOSError;
      
      // 完成更新
      if not EndUpdateResource(hUpdate, False) then
        RaiseLastOSError;
    except
      EndUpdateResource(hUpdate, True);
      raise;
    end;
  finally
    VersionInfo.Free;
  end;
end;

完整的版本信息读取示例

procedure ShowFileVersionInfo(const FileName: string);
var
  VersionInfo: string;
  CompanyName: string;
  FileDescription: string;
  LegalCopyright: string;
  ProductName: string;
begin
  VersionInfo := GetFileVersionInfo(FileName);
  if VersionInfo = '' then
  begin
    Writeln('无法读取版本信息');
    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
BEGIN
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x0804, 0x04B0  // 简体中文, Unicode
    END
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "080404B0"
        BEGIN
            VALUE "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", ""
        END
    END
END

关键要点

  1. 资源 ID 要求:RT_VERSION 资源的 ID 必须为 1,否则 Windows 资源管理器无法正确显示版本信息
  2. 对齐要求:所有结构必须在 DWORD 边界上对齐
  3. 多语言支持:通过 VarFileInfo 和多个 StringTable 可以支持多种语言的版本信息
  4. 版本号格式:版本号由 4 个 16 位整数组成,格式为 Major.Minor.Build.Revision
  5. 字符串编码:所有字符串必须使用 Unicode 编码
  6. API 函数:使用 GetFileVersionInfo、VerQueryValue 等 API 函数读取版本信息

理解 RT_VERSION 格式对于版本控制、软件部署和系统集成等任务非常重要,它提供了一种标准化的方式来存储和检索应用程序的元数据。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海纳老吴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值