Win32/C++ 获取PE文件版本信息

CVersionUtils.h

#pragma once

#include <wtypesbase.h>
#include <tchar.h>
#include <string>
#include <map>

#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif

namespace CVersionUtils
{
    // https://learn.microsoft.com/zh-cn/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
    // 版本号结构
    typedef union _VERSON_NUMBER {
        DWORD   Data;
        struct {
            WORD wLow;      // 版本号低16位
            WORD wHigh;     // 版本号高16位
        }Version;
        _VERSON_NUMBER() : Data(0) {}
    }VERSON_NUMBER;

    typedef struct _VS_VER_FIXEDFILEINFO {

        DWORD dwSignature;                  // 0xFEEF04BD
        VERSON_NUMBER dwStrucVersion;       // 此结构的二进制版本号。 此成员的高序字包含主版本号,低序字包含次要版本号。
        VERSON_NUMBER dwFileVersionMS;      // 文件二进制版本号中最重要的 32 位。 此成员与 dwFileVersionLS 一起使用,形成用于数字比较的 64 位值。
        VERSON_NUMBER dwFileVersionLS;      // 文件二进制版本号中最低有效 32 位。 此成员与 dwFileVersionMS 一起使用,形成用于数字比较的 64 位值。
        VERSON_NUMBER dwProductVersionMS;   // 分发此文件的产品的二进制版本号中最重要的 32 位。 此成员与 dwProductVersionLS 一起使用,形成用于数字比较的 64 位值。
        VERSON_NUMBER dwProductVersionLS;   // 分发此文件的产品的二进制版本号中最低有效 32 位。 此成员与 dwProductVersionMS 一起使用,形成用于数字比较的 64 位值。

        DWORD dwFileFlagsMask;              // 包含指定 dwFileFlags 中的有效位的位掩码。 仅当在创建文件时定义位时,位才有效。

        struct {
            DWORD fVS_FF_DEBUG : 1;             // 该文件包含调试信息,或者在启用调试功能的情况下进行编译。
            DWORD fVS_FF_PRERELEASE : 1;        // 该文件是开发版本,而不是商业发布的产品。
            DWORD fVS_FF_PRIVATEBUILD : 1;      // 文件不是使用标准发布过程生成的。 如果设置了此标志, StringFileInfo 结构应包含 PrivateBuild 条目。
            DWORD fVS_FF_PATCHED : 1;           // 该文件已修改,与同一版本号的原始发货文件不同。
            DWORD fVS_FF_INFOINFERRED : 1;      // 文件的版本结构是动态创建的;因此,此结构中的某些成员可能为空或不正确。 切勿在文件的 VS_VERSIONINFO 数据中设置此标志。
            DWORD fVS_FF_SPECIALBUILD : 1;      // 该文件由原始公司使用标准发布过程生成,但是相同版本号的正常文件的变体。 如果设置了此标志, StringFileInfo 结构应包含 SpecialBuild 条目。
            DWORD fVS_FF_Reserved : 26;         // 保留未使用
        }dwFileFlags;   // 包含指定文件的布尔属性的位掩码。

        union _FILE_OS {
            DWORD dwData;
            struct {
                enum eFileOSLo : WORD {
                    eVOS_LO_UNKNOWN = 0x0000L,  // 系统不知道设计该文件的操作系统
                    eVOS_LO_WINDOWS16 = 0x0001L,  // 该文件是为 16 位 Windows 设计的
                    eVOS_LO_PM16 = 0x0002L,  // 该文件是为 16 位 Presentation Manager 设计的
                    eVOS_LO_PM32 = 0x0003L,  // 该文件是为 32 位 Presentation Manager 设计的
                    eVOS_LO_WINDOWS32 = 0x0004L,  // 该文件专为 32 位 Windows 设计
                }Low;

                enum eFileOSHi : WORD {
                    eVOS_HI_UNKNOWN = 0x0000L,  // 系统不知道设计该文件的操作系统
                    eVOS_HI_DOS = 0x0001L,  // 该文件是针对 MS-DOS 设计的
                    eVOS_HI_OS216 = 0x0002L,  // 该文件是为 16 位 OS/2 设计的
                    eVOS_HI_OS232 = 0x0003L,  // 该文件是为 32 位 OS/2 设计的
                    eVOS_HI_NT = 0x0004L,  // 该文件是为 Windows NT 设计的
                }High;
            };

        }dwFileOS;      // 为其设计此文件的操作系统

        enum eFileType : DWORD {
            eVFT_UNKNOWN = 0x00000000L,  // 系统不知道文件类型
            eVFT_APP = 0x00000001L,  // 该文件包含一个应用程序
            eVFT_DLL = 0x00000002L,  // 该文件包含一个 DLL
            eVFT_DRV = 0x00000003L,  // 该文件包含设备驱动程序
            eVFT_FONT = 0x00000004L,  // 该文件包含字体
            eVFT_VXD = 0x00000005L,  // 该文件包含一个虚拟设备
            eVFT_STATIC_LIB = 0x00000007L   // 该文件包含一个静态链接库
        }dwFileType;    // 文件的常规类型

        union {
            // 如果 dwFileTypeVFT_FONT, 则 dwFileSubtype 可以是以下值之一。
            enum eFileSubtypeDrv : DWORD {
                eVFT2_DRV_UNKNOWN = 0x00000000L,  //系统未知驱动程序类型。
                eVFT2_DRV_PRINTER = 0x00000001L,  //文件包含打印机驱动程序。
                eVFT2_DRV_KEYBOARD = 0x00000002L,  //文件包含键盘驱动程序。
                eVFT2_DRV_LANGUAGE = 0x00000003L,  //文件包含语言驱动程序。
                eVFT2_DRV_DISPLAY = 0x00000004L,  //文件包含显示驱动程序。
                eVFT2_DRV_MOUSE = 0x00000005L,  //文件包含鼠标驱动程序。
                eVFT2_DRV_NETWORK = 0x00000006L,  //文件包含网络驱动程序。
                eVFT2_DRV_SYSTEM = 0x00000007L,  //文件包含系统驱动程序。
                eVFT2_DRV_INSTALLABLE = 0x00000008L,  //文件包含可安装的驱动程序。
                eVFT2_DRV_SOUND = 0x00000009L,  //该文件包含声音驱动程序。
                eVFT2_DRV_COMM = 0x0000000AL,  //文件包含通信驱动程序。
                eVFT2_DRV_VERSIONED_PRINTER = 0x0000000CL,  //文件包含版本控制打印机驱动程序。
            }dwFileTypeVFT_DRV;     // 驱动文件类型

            // 如果 dwFileTypeVFT_VXD, 则 dwFileSubtype 包含虚拟设备控制块中包含的虚拟设备标识符。
            enum eFileSubtypeFont : DWORD {
                eVFT2_FONT_UNKNOWN = 0x00000000L,  //系统未知字体类型。
                eVFT2_FONT_RASTER = 0x00000001L,  //文件包含光栅字体。
                eVFT2_FONT_TRUETYPE = 0x00000003L,  //文件包含 TrueType 字体。
                eVFT2_FONT_VECTOR = 0x00000002L,  //文件包含矢量字体。
            }dwFileTypeVFT_FONT;    // 字体文件类型

        }dwFileSubtype;     // 文件的子类型

        DWORD dwFileDateMS; // 文件的 64 位二进制创建日期和时间戳中最高有效 32 位。
        DWORD dwFileDateLS; // 文件的 64 位二进制创建日期和时间戳的最低有效 32 位。
    } VS_VER_FIXEDFILEINFO, * PVS_VER_FIXEDFILEINFO;

    // 语言与代码页 https://learn.microsoft.com/zh-cn/windows/win32/menurc/varfileinfo-block
    typedef struct _LANGANDCODEPAGE {

        WORD wLanguage;
        WORD wCodePage;
        _LANGANDCODEPAGE() : wLanguage(0), wCodePage(0) {}
        bool operator < (const _LANGANDCODEPAGE& r) const;
        bool operator == (const _LANGANDCODEPAGE& r) const;

    }LANGANDCODEPAGE, * PLANGANDCODEPAGE;

    // 版本号辅助类
    class CVersionNumber
    {
    public:
        CVersionNumber();
        CVersionNumber(const _tstring& strVer);
        CVersionNumber(WORD v1, WORD v2, WORD v3, WORD v4);
        CVersionNumber& operator = (const _tstring& strVer);
        _tstring GetString() const;
        bool IsEmpty() const;
        void Clear();
        bool operator == (const CVersionNumber& ref);
        bool operator != (const CVersionNumber& ref);
        bool operator < (const CVersionNumber& ref);
        bool operator <= (const CVersionNumber& ref);
        bool operator > (const CVersionNumber& ref);
        bool operator >= (const CVersionNumber& ref);

    private:
        int _Compare(const CVersionNumber& ref) const;

    private:
        WORD m_nVer[4];         //版本号
    };

    // PE文件信息
    typedef struct _VERSION_INFO
    {
        LANGANDCODEPAGE langAndCodePage;            // 语言代码页
        _tstring strLanguageName;                   // 语言名
        _tstring strComments;                       // 文件注释
        _tstring strInternalName;                   // 内部名称
        _tstring strProductName;                    // 产品名称
        _tstring strCompanyName;                    // 公司名称
        _tstring strLegalCopyright;                 // 法律版权
        _tstring strProductVersion;                 // 产品版本
        _tstring strFileDescription;                // 文件描述
        _tstring strLegalTrademarks;                // 合法商标
        _tstring strPrivateBuild;                   // 私有构建
        _tstring strFileVersion;                    // 文件版本
        _tstring strOriginalFilename;               // 原始文件名
        _tstring strSpecialBuild;                   // 特殊构建
        _tstring strFileVersionEx;                  // 文件版本(从 VS_FIXEDFILEINFO 中获取)
        _tstring strProductVersionEx;               // 产品版本(从 VS_FIXEDFILEINFO 中获取)
        CVersionNumber FileVerNumber;               // 文件版本号
        CVersionNumber ProductVerNumber;            // 产品版本号
        VS_VER_FIXEDFILEINFO vsFixedInfo;           // 固定文件信息
    }VERSION_INFO;

    // 文件版本信息列表
    using VERSION_LIST = std::map<LANGANDCODEPAGE, VERSION_INFO>;

    //
    // @brief: 获取文件版本列表
    // @param: strFile              文件路径
    // @param: fLocalised           本地化
    // @ret: VERSION_LIST           文件版本信息列表
    VERSION_LIST GetFileVersionList(const _tstring& strFile, bool fLocalised = true);

    //
    // @brief: 获取文件版本
    // @param: strFile              文件路径
    // @param: fLocalised           本地化
    // @ret: VERSION_INFO           文件版本信息
    VERSION_INFO GetFileVersion(const _tstring& strFile, bool fLocalised = true);
}

CVersionUtils.cpp

#include "CVersionUtils.h"
#include <winver.h>
#include <strsafe.h>

#pragma comment(lib, "Version.lib")

namespace CVersionUtils
{

#pragma pack(push)
#pragma pack(1)
    // 包含文件的版本信息。 此信息与语言和代码页无关
    // https://learn.microsoft.com/zh-cn/windows/win32/menurc/vs-versioninfo
    typedef struct {
        WORD             wLength;       // VS_VERSIONINFO 结构的长度(以字节为单位),此长度不包括在 32 位边界上对齐任何后续版本资源数据的填充
        WORD             wValueLength;  // Value 成员的长度(以字节为单位)
        WORD             wType;         // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
        WCHAR            szKey[15];     // Unicode 字符串 L“VS_VERSION_INFO”
        WORD             Padding1;      // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD
        //VS_FIXEDFILEINFO Value
        //WORD             Padding2
        //WORD             Children
    } VS_VERSIONINFO, * PVS_VERSIONINFO;

#pragma pack(pop)

    CVersionNumber::CVersionNumber()
        :m_nVer{ 0 }
    {
    };

    CVersionNumber::CVersionNumber(const _tstring& strVer)
        :
        m_nVer{ 0 }
    {
        _stscanf_s(strVer.c_str(), _T("%hd.%hd.%hd.%hd"), &m_nVer[0], &m_nVer[1], &m_nVer[2], &m_nVer[3]);
    }

    CVersionNumber::CVersionNumber(WORD v1, WORD v2, WORD v3, WORD v4)
        :m_nVer{ v1, v2, v3, v4 }
    {
    }

    CVersionNumber& CVersionNumber::operator = (const _tstring& strVer)
    {
        _stscanf_s(strVer.c_str(), _T("%hd.%hd.%hd.%hd"), &m_nVer[0], &m_nVer[1], &m_nVer[2], &m_nVer[3]);
        return *this;
    }

    int CVersionNumber::_Compare(const CVersionNumber& ref) const
    {
        for (int i = 0; i < _countof(m_nVer); i++)
        {
            if (m_nVer[i] != ref.m_nVer[i])
            {
                return (m_nVer[i] > ref.m_nVer[i] ? 1 : -1);
            }
        }

        return 0;
    }

    _tstring CVersionNumber::GetString() const
    {
        TCHAR szBuf[MAX_PATH] = { 0 };
        (void)::StringCchPrintf(szBuf, _countof(szBuf), _T("%hd.%hd.%hd.%hd"),
            m_nVer[0],
            m_nVer[1],
            m_nVer[2],
            m_nVer[3]
        );

        return szBuf;
    }

    bool CVersionNumber::IsEmpty() const
    {
        for (const auto& item : m_nVer)
        {
            if (0 != item)
            {
                return false;
            }
        }

        return true;
    }

    void CVersionNumber::Clear()
    {
        for (auto& item : m_nVer)
        {
            item = 0;
        }
    }

    bool CVersionNumber::operator == (const CVersionNumber& ref)
    {
        return _Compare(ref) == 0;
    }

    bool CVersionNumber::operator != (const CVersionNumber& ref)
    {
        return _Compare(ref) != 0;
    }

    bool CVersionNumber::operator < (const CVersionNumber& ref)
    {
        return _Compare(ref) < 0;
    }

    bool CVersionNumber::operator <= (const CVersionNumber& ref)
    {
        return _Compare(ref) <= 0;
    }

    bool CVersionNumber::operator > (const CVersionNumber& ref)
    {
        return _Compare(ref) > 0;
    }

    bool CVersionNumber::operator >= (const CVersionNumber& ref)
    {
        return _Compare(ref) >= 0;
    }

    bool _LANGANDCODEPAGE::operator < (const _LANGANDCODEPAGE& r) const
    {
        if (this->wLanguage < r.wLanguage)
        {
            return true;
        }

        if (this->wCodePage < r.wCodePage)
        {
            return true;
        }

        return false;
    }

    bool _LANGANDCODEPAGE::operator == (const _LANGANDCODEPAGE& r) const
    {
        return this->wLanguage == r.wLanguage && this->wCodePage == r.wCodePage;
    }

    static VERSION_LIST _GetFileVersionList(const _tstring& strFile, bool fLocalised)
    {
        VERSION_LIST infoResult;
        PVOID pFsRedirectionOldValue = NULL;
        bool isDisableWow64Fs = false;
        UINT cbTranslate = 0;
        DWORD dwVerSize;
        LPVOID lpVerData = NULL;

        // 加载文件
        if (strFile.empty())
        {
            return infoResult;
        }

        // 禁用文件重定向
        isDisableWow64Fs = ::Wow64DisableWow64FsRedirection(&pFsRedirectionOldValue);

        do
        {
            DWORD dwFlags = fLocalised ? FILE_VER_GET_LOCALISED : FILE_VER_GET_NEUTRAL;
            dwFlags |= FILE_VER_GET_PREFETCHED;

            // 获取版本信息数据大小
            dwVerSize = ::GetFileVersionInfoSize(strFile.c_str(), NULL);
            //dwVerSize = ::GetFileVersionInfoSizeEx(dwFlags, strFile.c_str(), NULL);
            if (0 == dwVerSize)
            {
                break;
            }

            // 分配版本信息缓冲
            lpVerData = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwVerSize);
            if (!lpVerData)
            {
                break;
            }

            // 获取版本信息
            BOOL fResult = FALSE;
            fResult = ::GetFileVersionInfo(strFile.c_str(), NULL, dwVerSize, lpVerData);
            //fResult = ::GetFileVersionInfoEx(dwFlags, strFile.c_str(), NULL, dwVerSize, lpVerData);
            if (!fResult)
            {
                break;
            }

            // 获取语言代码
            LANGANDCODEPAGE* lpTranslate = NULL;
            if (!::VerQueryValue(lpVerData, _T("\\VarFileInfo\\Translation"), (LPVOID*)&lpTranslate, &cbTranslate))
            {
                break;
            }

            DWORD dwCount = cbTranslate / sizeof(LANGANDCODEPAGE);
            for (DWORD i = 0; i < dwCount; i++)
            {
                LANGANDCODEPAGE langCodePage = lpTranslate[i];
                VERSION_INFO versionInfo;

                // 获取 VS_FIXEDFILEINFO 信息
                PVS_VERSIONINFO lpVersion = (PVS_VERSIONINFO)lpVerData;
                if (0 != lpVersion->wValueLength)
                {
                    VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO));

                    DWORD dwAlign = 4;
                    DWORD_PTR dwPadding = ((DWORD_PTR)pFixedFileInfo % dwAlign);
                    if (0 != dwPadding)
                    {
                        pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)pFixedFileInfo + (dwAlign - dwPadding));
                    }

                    versionInfo.vsFixedInfo = *((PVS_VER_FIXEDFILEINFO)pFixedFileInfo);
                }

                // 查询所有信息
                    // 查询单个信息函数
                auto _QueryInfo = [](const _tstring& strName, const LPVOID lpData, const LANGANDCODEPAGE& code) {

                    TCHAR strQuery[MAX_PATH] = { 0 };
                    LPCTSTR lpQueryRes = NULL;
                    UINT uQueryCchSize;
                    (void)::StringCchPrintf(strQuery, _countof(strQuery),
                        _T("\\StringFileInfo\\%04x%04x\\%s"),
                        code.wLanguage, code.wCodePage, strName.c_str());
                    if (::VerQueryValue(lpData, strQuery, (LPVOID*)&lpQueryRes, &uQueryCchSize))
                    {
                        return lpQueryRes;
                    }

                    return _T("");

                    };

                auto _GetVersionStr = [](VERSON_NUMBER hi, VERSON_NUMBER lo) -> _tstring {
                    TCHAR szBuf[MAX_PATH] = { 0 };
                    (void)::StringCchPrintf(szBuf, _countof(szBuf), _T("%hd.%hd.%hd.%hd"),
                        hi.Version.wHigh,
                        hi.Version.wLow,
                        lo.Version.wHigh,
                        lo.Version.wLow
                    );

                    return szBuf;

                    };

                auto _GetLanguageNameStr = [](LANGANDCODEPAGE langCodePage) -> _tstring {
                    TCHAR szBuf[MAX_PATH] = { 0 };
                    ::VerLanguageName(langCodePage.wLanguage, szBuf, _countof(szBuf));
                    return szBuf;
                    };

                versionInfo.langAndCodePage = langCodePage;
                versionInfo.strComments = _QueryInfo(_T("Comments"), lpVerData, langCodePage);
                versionInfo.strInternalName = _QueryInfo(_T("InternalName"), lpVerData, langCodePage);
                versionInfo.strProductName = _QueryInfo(_T("ProductName"), lpVerData, langCodePage);
                versionInfo.strCompanyName = _QueryInfo(_T("CompanyName"), lpVerData, langCodePage);
                versionInfo.strLegalCopyright = _QueryInfo(_T("LegalCopyright"), lpVerData, langCodePage);
                versionInfo.strProductVersion = _QueryInfo(_T("ProductVersion"), lpVerData, langCodePage);
                versionInfo.strFileDescription = _QueryInfo(_T("FileDescription"), lpVerData, langCodePage);
                versionInfo.strLegalTrademarks = _QueryInfo(_T("LegalTrademarks"), lpVerData, langCodePage);
                versionInfo.strPrivateBuild = _QueryInfo(_T("PrivateBuild"), lpVerData, langCodePage);
                versionInfo.strFileVersion = _QueryInfo(_T("FileVersion"), lpVerData, langCodePage);
                versionInfo.strOriginalFilename = _QueryInfo(_T("OriginalFilename"), lpVerData, langCodePage);
                versionInfo.strSpecialBuild = _QueryInfo(_T("SpecialBuild"), lpVerData, langCodePage);

                VS_VER_FIXEDFILEINFO& vsFixedInfo = versionInfo.vsFixedInfo;
                versionInfo.strFileVersionEx = _GetVersionStr(vsFixedInfo.dwFileVersionMS, vsFixedInfo.dwFileVersionLS);
                versionInfo.strProductVersionEx = _GetVersionStr(vsFixedInfo.dwProductVersionMS, vsFixedInfo.dwProductVersionLS);
                versionInfo.strLanguageName = _GetLanguageNameStr(langCodePage);

                versionInfo.FileVerNumber = versionInfo.strFileVersionEx.c_str();
                versionInfo.ProductVerNumber = versionInfo.strProductVersionEx.c_str();

                infoResult.emplace(langCodePage, versionInfo);
            }

        } while (false);

        // 恢复文件重定向
        if (isDisableWow64Fs)
        {
            ::Wow64RevertWow64FsRedirection(pFsRedirectionOldValue);
        }

        if (lpVerData)
        {
            ::HeapFree(::GetProcessHeap(), 0, lpVerData);
            lpVerData = NULL;
        }

        return infoResult;
    }

    VERSION_LIST GetFileVersionList(const _tstring& strFile, bool fLocalised/* = true*/)
    {
        VERSION_LIST fileInfos = _GetFileVersionList(strFile, fLocalised);
        return fileInfos;
    }

    VERSION_INFO GetFileVersion(const _tstring& strFile, bool fLocalised/* = true*/)
    {
        VERSION_INFO info;
        VERSION_LIST fileInfos = _GetFileVersionList(strFile, fLocalised);
        if (!fileInfos.empty())
        {
            info = fileInfos.begin()->second;
        }

        return info;
    }
}

main.cpp

#include <locale.h>
#include <tchar.h>
#include "Win32Utils/CVersionUtils.h"

int _tmain(int argc, LPCTSTR argv[])
{
    setlocale(LC_ALL, "");

    CVersionUtils::VERSION_INFO info = CVersionUtils::GetFileVersion(_T("ntdll.dll"));

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值