如何在无预先信息时获取VersionInfo::StringFileInfo的可用块?
如何枚举StringFileInfo中的可用块
要确定VersionInfo::StringFileInfo段的所有可用块,核心是读取VarFileInfo中的"Translation"值——这里存储了所有语言ID与代码页的配对组合,而StringFileInfo的块名正是这两个值拼接的十六进制字符串(比如语言ID 0x0809 + 代码页0x04E4 → "080904E4")。
以下是具体实现方案:
步骤概述
- 调用
GetFileVersionInfoSize检查文件是否包含版本信息,并获取所需缓冲区大小 - 分配缓冲区,调用
GetFileVersionInfo加载版本信息到缓冲区 - 用
VerQueryValue读取"VarFileInfo\Translation",得到所有语言-代码页配对 - 对每个配对生成对应的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
相关产品推荐
相关产品推荐

