65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (4投票s)

2008年1月24日

CPOL
viewsIcon

26313

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 日:初始发布
© . All rights reserved.