快速获取文件版本的便捷方法






3.80/5 (4投票s)
Microsoft FileVersionInfo 类的替代方案
引言
微软的 FileVersionInfo
类非常方便地用于获取可执行文件的 VERSION 资源。但是,该类的性能相当慢。如果您需要获取大量文件的版本信息,则需要一种更快的方法。
Using the Code
该代码使用了 Matt Pietrek 的 PEDUMP 程序中的一种技术,将可执行文件映射到内存中。但是,代码没有遍历 PE 格式的目录结构,而是使用标准的 WinAPI 资源函数来获取 VERSION 资源。一个有趣的(不规范的)技术被用来将文件起始地址增加一个字节,以获得与 LoadLibraryEx
返回的相同内存指针,而 LoadLibraryEx
是应该用于处理资源的文件。LoadLibraryEx
的问题是,与将文件映射到内存(即使使用 LOAD_LIBRARY_AS_DATAFILE
标志)相比,它仍然很慢。该代码也有助于演示各种 Interop 技术。
class FastFileVersionInfo
{
[StructLayout(LayoutKind.Sequential)]
struct IMAGE_DOS_HEADER
{
public UInt16 e_magic; // Magic number
/*
public UInt16 e_cblp; // Bytes on last page of file
public UInt16 e_cp; // Pages in file
public UInt16 e_crlc; // Relocations
public UInt16 e_cparhdr; // Size of header in paragraphs
public UInt16 e_minalloc; // Minimum extra paragraphs needed
public UInt16 e_maxalloc; // Maximum extra paragraphs needed
public UInt16 e_ss; // Initial (relative) SS value
public UInt16 e_sp; // Initial SP value
public UInt16 e_csum; // Checksum
public UInt16 e_ip; // Initial IP value
public UInt16 e_cs; // Initial (relative) CS value
public UInt16 e_lfarlc; // File address of relocation table
public UInt16 e_ovno; // Overlay number
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public UInt16[] e_res1; // Reserved words
public UInt16 e_oemid; // OEM identifier (for e_oeminfo)
public UInt16 e_oeminfo; // OEM information; e_oemid specific
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public UInt16[] e_res2; // Reserved words
public Int32 e_lfanew; // File address of new EXE header
*/
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct VS_VERSIONINFO
{
public UInt16 wLength;
public UInt16 wValueLength;
public UInt16 wType;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string szKey;
public UInt16 Padding1;
}
static Int32 nPaddingOffset =
Marshal.OffsetOf(typeof(VS_VERSIONINFO), "Padding1").ToInt32();
[StructLayout(LayoutKind.Sequential)]
struct VS_FIXEDFILEINFO
{
public UInt32 dwSignature;
public UInt32 dwStrucVersion;
public UInt32 dwFileVersionMS;
public UInt32 dwFileVersionLS;
public UInt32 dwProductVersionMS;
public UInt32 dwProductVersionLS;
public UInt32 dwFileFlagsMask;
public UInt32 dwFileFlags;
public UInt32 dwFileOS;
public UInt32 dwFileType;
public UInt32 dwFileSubtype;
public UInt32 dwFileDateMS;
public UInt32 dwFileDateLS;
};
[DllImport("Kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFile(string fileName, UInt32 fileAccess,
UInt32 fileShare, IntPtr securityAttributes, UInt32 creationDisposition,
UInt32 flags, IntPtr template);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr CreateFileMapping
(IntPtr hFile, IntPtr lpFileMappingAttributes, UInt32 flProtect,
UInt32 dwMaximumSizeHigh, UInt32 dwMaximumSizeLow, string lpName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr MapViewOfFile
(IntPtr hFileMappingObject, UInt32 dwDesiredAccess, UInt32 dwFileOffsetHigh,
UInt32 dwFileOffsetLow, IntPtr dwNumberOfBytesToMap);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr FindResource(IntPtr hModule, string lpName, Int32 lpType);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);
[DllImport("kernel32.dll")]
static extern IntPtr LockResource(IntPtr hResData);
const UInt32 GENERIC_READ = 0x80000000;
const UInt32 FILE_SHARE_READ = 0x00000001;
const UInt32 OPEN_EXISTING = 3;
const UInt32 FILE_ATTRIBUTE_NORMAL = 0x00000080;
const Int32 INVALID_HANDLE_VALUE = -1;
const UInt32 PAGE_READONLY = 0x02;
const UInt32 FILE_MAP_READ = 0x0004;
const UInt16 IMAGE_DOS_SIGNATURE = 0x5A4D;
const Int32 RT_VERSION = 16;
public static string GetVersion(string sFile)
{
String sVersion = null;
IntPtr hFile = CreateFile(sFile, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);
if (hFile.ToInt32() == INVALID_HANDLE_VALUE)
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
IntPtr hFileMapping =
CreateFileMapping(hFile, IntPtr.Zero, PAGE_READONLY, 0, 0, null);
if (hFileMapping.ToInt32() == 0)
{
int returnCode = Marshal.GetLastWin32Error();
CloseHandle(hFile);
throw new System.ComponentModel.Win32Exception(returnCode);
}
IntPtr pMappedFileBase =
MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, IntPtr.Zero);
if (pMappedFileBase.ToInt32() == 0)
{
int returnCode = Marshal.GetLastWin32Error();
CloseHandle(hFileMapping);
CloseHandle(hFile);
throw new System.ComponentModel.Win32Exception(returnCode);
}
IMAGE_DOS_HEADER DOSHeader =
(IMAGE_DOS_HEADER) Marshal.PtrToStructure
(pMappedFileBase, typeof(IMAGE_DOS_HEADER));
if (DOSHeader.e_magic == IMAGE_DOS_SIGNATURE)
{
IntPtr hModule = new IntPtr(pMappedFileBase.ToInt32() + 1);
IntPtr hRes = FindResource(hModule, "#1", RT_VERSION);
if (hRes.ToInt32() != 0)
{
IntPtr hGlobal = LoadResource(hModule, hRes);
if (hGlobal.ToInt32() != 0)
{
IntPtr lpRes = LockResource(hGlobal);
if (lpRes.ToInt32() != 0)
{
VS_VERSIONINFO VersionInfo =
(VS_VERSIONINFO) Marshal.PtrToStructure(lpRes, typeof(VS_VERSIONINFO));
if (VersionInfo.wValueLength > 0)
{
IntPtr pFI = new IntPtr(lpRes.ToInt32() + nPaddingOffset);
while ((pFI.ToInt32() & 0x04) != 0)
pFI = new IntPtr(pFI.ToInt32() + 2);
VS_FIXEDFILEINFO FileInfo = (VS_FIXEDFILEINFO) Marshal.PtrToStructure
(pFI, typeof(VS_FIXEDFILEINFO));
UInt32 v1 = (FileInfo.dwFileVersionMS & 0xffff0000) >> 16;
UInt32 v2 = FileInfo.dwFileVersionMS & 0x0000ffff;
UInt32 v3 = (FileInfo.dwFileVersionLS & 0xffff0000) >> 16;
UInt32 v4 = FileInfo.dwFileVersionLS & 0x0000ffff;
sVersion = v1.ToString() + '.' + v2.ToString() + '.' +
v3.ToString() + '.' + v4.ToString();
}
}
}
}
}
UnmapViewOfFile(pMappedFileBase);
CloseHandle(hFileMapping);
CloseHandle(hFile);
return sVersion;
}
}
历史
- 2008 年 1 月 24 日:初始发布