You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在无预先信息时获取VersionInfo::StringFileInfo的可用块?

如何枚举StringFileInfo中的可用块

要确定VersionInfo::StringFileInfo段的所有可用块,核心是读取VarFileInfo中的"Translation"值——这里存储了所有语言ID与代码页的配对组合,而StringFileInfo的块名正是这两个值拼接的十六进制字符串(比如语言ID 0x0809 + 代码页0x04E4 → "080904E4")。

以下是具体实现方案:

步骤概述

  1. 调用GetFileVersionInfoSize检查文件是否包含版本信息,并获取所需缓冲区大小
  2. 分配缓冲区,调用GetFileVersionInfo加载版本信息到缓冲区
  3. 用VerQueryValue读取"VarFileInfo\Translation",得到所有语言-代码页配对
  4. 对每个配对生成对应的StringFileInfo块路径,即可枚举或验证该块是否存在

C/C++ 实现示例

#include <windows.h>
#include <stdio.h>
#include <vector>

void EnumStringFileInfoBlocks(const wchar_t* filePath)
{
    DWORD dummy;
    DWORD versionSize = GetFileVersionInfoSizeW(filePath, &dummy);
    if (versionSize == 0)
    {
        printf("文件无版本信息或读取失败\n");
        return;
    }

    std::vector<BYTE> buffer(versionSize);
    if (!GetFileVersionInfoW(filePath, 0, versionSize, buffer.data()))
    {
        printf("加载版本信息失败\n");
        return;
    }

    // 读取Translation数组:每个元素是DWORD,低16位=语言ID,高16位=代码页
    DWORD* translations = nullptr;
    UINT translationCount = 0;
    if (!VerQueryValueW(buffer.data(), L"\\VarFileInfo\\Translation", (LPVOID*)&translations, &translationCount))
    {
        printf("读取Translation信息失败\n");
        return;
    }
    translationCount /= sizeof(DWORD); // 转换为条目数量

    printf("找到%d个StringFileInfo块:\n", translationCount);
    for (UINT i = 0; i < translationCount; ++i)
    {
        WORD langID = LOWORD(translations[i]);
        WORD codePage = HIWORD(translations[i]);
        
        // 生成块名称:语言ID和代码页转为8位十六进制字符串
        wchar_t blockName[9];
        swprintf_s(blockName, L"%04X%04X", langID, codePage);
        
        printf("块名称: %ls\n", blockName);
        
        // 示例:读取该块下的CompanyName
        wchar_t queryPath[64];
        swprintf_s(queryPath, L"\\StringFileInfo\\%ls\\CompanyName", blockName);
        
        wchar_t* value = nullptr;
        UINT valueSize = 0;
        if (VerQueryValueW(buffer.data(), queryPath, (LPVOID*)&value, &valueSize))
        {
            printf("  CompanyName: %ls\n", value);
        }
        else
        {
            printf("  未找到CompanyName字段\n");
        }
    }
}

int main()
{
    EnumStringFileInfoBlocks(L"C:\\Windows\\notepad.exe");
    return 0;
}

Delphi 实现示例

procedure EnumStringFileInfoBlocks(const FilePath: string);
var
  VersionSize, Dummy: DWORD;
  Buffer: Pointer;
  Translations: PDWORD;
  TranslationCount: UINT;
  I: Integer;
  LangID, CodePage: Word;
  BlockName, QueryPath: string;
  Value: PChar;
  ValueSize: UINT;
begin
  VersionSize := GetFileVersionInfoSize(PChar(FilePath), Dummy);
  if VersionSize = 0 then
  begin
    Writeln('文件无版本信息或读取失败');
    Exit;
  end;

  Buffer := AllocMem(VersionSize);
  try
    if not GetFileVersionInfo(PChar(FilePath), 0, VersionSize, Buffer) then
    begin
      Writeln('加载版本信息失败');
      Exit;
    end;

    // 读取Translation数组
    if not VerQueryValue(Buffer, 'VarFileInfo\Translation', Pointer(Translations), TranslationCount) then
    begin
      Writeln('读取Translation信息失败');
      Exit;
    end;
    TranslationCount := TranslationCount div SizeOf(DWORD);

    Writeln(Format('找到%d个StringFileInfo块:', [TranslationCount]));
    for I := 0 to TranslationCount - 1 do
    begin
      LangID := LoWord(Translations^);
      CodePage := HiWord(Translations^);
      Inc(Translations);

      // 生成块名称
      BlockName := Format('%.4X%.4X', [LangID, CodePage]);
      Writeln('块名称: ' + BlockName);

      // 读取该块下的CompanyName示例
      QueryPath := 'StringFileInfo\' + BlockName + '\CompanyName';
      if VerQueryValue(Buffer, PChar(QueryPath), Pointer(Value), ValueSize) then
        Writeln('  CompanyName: ' + Trim(Value))
      else
        Writeln('  未找到CompanyName字段');
    end;
  finally
    FreeMem(Buffer);
  end;
end;

// 调用示例
procedure TForm1.Button1Click(Sender: TObject);
begin
  EnumStringFileInfoBlocks('C:\Windows\notepad.exe');
end;

关键说明

  • Translation数组中的每个DWORD,低16位是语言ID(如0x0409代表英语(美国),0x0809代表英语(英国)),高16位是代码页(如0x04E4代表1252多语言代码页)
  • 生成块名称时,需将语言ID和代码页分别格式化为4位十六进制字符串,再拼接成8位字符串
  • 若需要枚举块内的所有字段,可进一步解析StringFileInfo块的结构,或通过已知的标准字段名(如ProductName、FileVersion等)逐一查询

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 18:15:18