有对应的需求,需要获取文件的详细信息内容。该页面信息大部分来源于文件的版本信息,使用FileInfo类并不能获取到。
VERSIONINFO 资源 - Win32 apps | Microsoft Learn
信息格式:
1 VERSIONINFO
FILEVERSION 1,0,0,323
PRODUCTVERSION 1,0,0,0
FILEOS 0x4
FILETYPE 0x1
{
BLOCK "StringFileInfo"
{
BLOCK "000004B0"
{
VALUE "Comments", "这是一个示例库,由张三编写。"
VALUE "CompanyName", "webre"
VALUE "FileDescription", "webre"
VALUE "FileVersion", "1.0.0.323"
VALUE "InternalName", "webre.dll"
VALUE "LegalCopyright", "版权所有 \xA9 2024 张三"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "webre.dll"
VALUE "ProductName", "webre"
VALUE "ProductVersion", "1.0.0"
VALUE "Assembly Version", "1.0.0.323"
VALUE "Test", "Test"//自定义属性
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0000 0x04B0
}
}
为了获取versioninfo信息,需要使用version.dll内的api进行指定段内的信息获取。参考如下:
GetFileVersionInfoA function (winver.h) - Win32 apps | Microsoft Learn
GetFileVersionInfoSizeA function (winver.h) - Win32 apps | Microsoft Learn
VerQueryValueW function (winver.h) - Win32 apps | Microsoft Learn
调用代码如下:
//需要引入win32api
[DllImport("version.dll", CharSet = CharSet.Unicode)]
private static extern bool GetFileVersionInfo(string fileName, int handle, int len, byte[] data);
[DllImport("version.dll", CharSet = CharSet.Unicode)]
private static extern int GetFileVersionInfoSize(string fileName, out int handle);
[DllImport("version.dll", CharSet = CharSet.Unicode)]
private static extern bool VerQueryValue(byte[] data, string subBlock, out IntPtr buffer, out int len);
var attrs = new string[] {
"Comments","InternalName","ProductName",
"CompanyName","LegalCopyright","ProductVersion",
"FileDescription","LegalTrademarks","PrivateBuild",
"FileVersion","OriginalFilename","SpecialBuild","Test"
};
// 获取文件版本信息的大小
int handle;
int infoSize = GetFileVersionInfoSize(dllPath, out handle);
if (infoSize == 0)
{
throw new Exception("Failed to get file version info size. Error: " + Marshal.GetLastWin32Error());
}
// 分配缓冲区
byte[] infoBuffer = new byte[infoSize];
// 获取文件版本信息
if (!GetFileVersionInfo(dllPath, 0, infoSize, infoBuffer))
{
throw new Exception("Failed to get file version info. Error: " + Marshal.GetLastWin32Error());
}
// 获取所有语言代码
IntPtr buffer;
int len;
if (!VerQueryValue(infoBuffer, @"\VarFileInfo\Translation", out buffer, out len))
{
throw new Exception("Failed to get language codes. Error: " + Marshal.GetLastWin32Error());
}
// 解析语言代码
List<string> languageCodes = new List<string>();
for (int i = 0; i < len / 4; i++)
{
int langCode = Marshal.ReadInt16(buffer, i * 4);
int subLangCode = Marshal.ReadInt16(buffer, i * 4 + 2);
string languageCode = $"{langCode:X4}{subLangCode:X4}";//win下小端存储
languageCodes.Add(languageCode);
}
// 输出所有语言代码
Console.WriteLine("Available Language Codes:");
foreach (string languageCode in languageCodes)
{
Console.WriteLine(languageCode);
}
// 选择第一个语言代码
string selectedLanguageCode = languageCodes.Count > 0 ? languageCodes[0] : "040904B0";//默认为英文
foreach (var attr in attrs)
{
if (!VerQueryValue(infoBuffer, $@"\StringFileInfo\{selectedLanguageCode}\{attr}", out buffer, out len))
{
Console.WriteLine($"Failed to get {attr}. Error: " + Marshal.GetLastWin32Error());
}
else
{
string value = Marshal.PtrToStringUni(buffer);
Console.WriteLine($"{attr}: " + value);
}
}
在调用VerQueryValue后,通过buffer获取到内存地址,观察一下内存数据。
version信息映射到一段连续内存中存储。
另外还有更简单的方式,通过封装好的FileVersionInfo类进行信息获取,通过各种属性即可获取版本信息。不过,自定义的属性就获取不到了。
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(dllPath);
FileVersionInfo win平台的实现,内部也是调用了version.dll内的相关api,不过调用的都是Ex方法,并对codepage做了处理。