.NET C#中FileVersionInfo.GetVersionInfo无法读取PyInstaller生成EXE属性
问题解决:读取PyInstaller生成EXE的版本信息(替代FileVersionInfo.GetVersionInfo)
问题背景
一直使用FileVersionInfo.GetVersionInfo读取可执行文件/DLL的版本属性,但遇到PyInstaller生成的EXE时,该方法无法读取信息,而文件资源管理器能正常查看。该EXE的版本信息定义示例如下:
VSVersionInfo( ffi=FixedFileInfo( filevers=(1, 2, 3, 4), prodvers=(1, 2, 3, 4), mask=0x3f, flags=0x0, OS=0x40004, fileType=0x1, subtype=0x0, date=(0, 0) ), kids=[ StringFileInfo( [ StringTable( u'040904B0', [StringStruct(u'CompanyName', u'Your Company Name'), StringStruct(u'FileDescription', u'Description of your program'), StringStruct(u'FileVersion', u'1.2.3.4'), StringStruct(u'InternalName', u'yourprogram'), StringStruct(u'LegalCopyright', u'© Your Company Name. All rights reserved.'), StringStruct(u'OriginalFilename', u'yourprogram.exe'), StringStruct(u'ProductName', u'Your Product Name'), StringStruct(u'ProductVersion', u'1.2.3.4')]) ]), VarFileInfo([VarStruct(u'Translation', [1033, 1200])]) ] )
目前通过调用version.dll的GetFileVersionInfo和VerQueryValue可以读取,但希望用更直观的实现方式。环境信息:
- .NET Framework 4.8
- Windows 10
- 资源管理器可正常查看所有属性
原因分析
PyInstaller生成的EXE版本信息使用了UTF-16LE编码(对应Translation值1200),而FileVersionInfo.GetVersionInfo在处理这种编码的字符串表时存在兼容问题,导致无法正确解析信息。
解决方案:封装原生API为直观的.NET方法
把原生的版本信息读取API封装成静态类,提供简洁的调用接口,既避免繁琐的手动Marshal操作,又保持代码直观:
using System; using System.Runtime.InteropServices; public static class PyExeVersionReader { [DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool GetFileVersionInfo(string lptstrFilename, IntPtr hHandle, uint dwLen, byte[] lpData); [DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool VerQueryValue(byte[] pBlock, string lpSubBlock, out IntPtr lplpBuffer, out uint puLen); [DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern uint GetFileVersionInfoSize(string lptstrFilename, out IntPtr lpdwHandle); public static string GetVersionProperty(string filePath, string propertyName) { uint bufferSize = GetFileVersionInfoSize(filePath, out _); if (bufferSize == 0) return null; byte[] buffer = new byte[bufferSize]; if (!GetFileVersionInfo(filePath, IntPtr.Zero, bufferSize, buffer)) return null; // 自动解析Translation获取字符串表路径 if (VerQueryValue(buffer, @"\VarFileInfo\Translation", out IntPtr translationPtr, out uint translationLen)) { ushort[] translation = new ushort[translationLen / 2]; Marshal.Copy(translationPtr, translation, 0, translation.Length); string stringTablePath = $@"\StringFileInfo\{translation[0]:X4}{translation[1]:X4}\{propertyName}"; if (VerQueryValue(buffer, stringTablePath, out IntPtr valuePtr, out uint valueLen)) { return Marshal.PtrToStringUni(valuePtr); } } // fallback到PyInstaller常用的040904B0编码表 string fallbackPath = $@"\StringFileInfo\040904B0\{propertyName}"; if (VerQueryValue(buffer, fallbackPath, out IntPtr fallbackPtr, out uint fallbackLen)) { return Marshal.PtrToStringUni(fallbackPtr); } return null; } // 封装常用属性的快捷方法,调用方式接近FileVersionInfo public static string GetCompanyName(string filePath) => GetVersionProperty(filePath, "CompanyName"); public static string GetFileDescription(string filePath) => GetVersionProperty(filePath, "FileDescription"); public static string GetFileVersion(string filePath) => GetVersionProperty(filePath, "FileVersion"); public static string GetProductName(string filePath) => GetVersionProperty(filePath, "ProductName"); }
使用示例
string exePath = @"C:\path\to\your\pyinstaller-built.exe"; string company = PyExeVersionReader.GetCompanyName(exePath); string fileVersion = PyExeVersionReader.GetFileVersion(exePath); Console.WriteLine($"厂商名称: {company}"); Console.WriteLine($"文件版本: {fileVersion}");
说明
- 自动解析EXE的
Translation信息,适配不同语言和编码的版本字符串表,无需硬编码路径 - 提供的快捷方法调用风格和
FileVersionInfo一致,保持代码直观性 - 同时兼容PyInstaller生成的EXE和常规Windows可执行文件
内容的提问来源于stack exchange,提问作者vamshi krishna
相关产品推荐
相关产品推荐

