Ver系関数を使ってPEファイルのバージョン情報を取得するサンプルコードです。ひっそりとstd::format
(実質fmt
)を使用しています。
#include <format> #include <string> #include <span> #include <unordered_map> #include <vector> #define STRICT #define NOMINMAX #include <Windows.h> #pragma comment(lib, "version.lib") class file_version_info final { public: struct translation final { union { DWORD value; struct { WORD language; WORD codepage; }; }; translation(DWORD x) : value(x) {} std::wstring to_subblock_string() const { return std::format(L"{:04x}{:04x}", language, codepage); } constexpr operator DWORD() const { return value; } }; public: static UINT get_size(std::wstring_view filename, DWORD flags = 0) { return ::GetFileVersionInfoSizeExW(flags, filename.data(), nullptr); } file_version_info(std::wstring_view filename, DWORD flags = 0) { auto size = get_size(filename, flags); m_data.resize(size); if (!::GetFileVersionInfoExW(flags, filename.data(), 0, size, m_data.data())) m_data.clear(); } file_version_info(const file_version_info& source) = delete; constexpr file_version_info(file_version_info&& source) { m_data = std::move(source.m_data); } constexpr const std::vector<BYTE>& data_vector() const { return m_data; } const std::span<BYTE> query_value(std::wstring_view sub_block) const { LPVOID p; UINT size; if (!::VerQueryValueW(m_data.data(), sub_block.data(), &p, &size)) return {}; if (sub_block.compare(L"\\") != 0) size *= 2; return std::span<BYTE>(reinterpret_cast<LPBYTE>(p), size); } const VS_FIXEDFILEINFO& query_root_block() const { return *reinterpret_cast<const VS_FIXEDFILEINFO*>(query_value(L"\\").data()); } const std::span<translation> query_translations() const { auto sp = query_value(L"\\VarFileInfo\\Translation"); return std::span<translation>( reinterpret_cast<translation*>(sp.data()), sp.size() / sizeof(translation)); } std::wstring_view query_string_value(translation translation, std::wstring_view string_name) const { auto sub_block = std::format(L"\\StringFileInfo\\{}\\{}", translation.to_subblock_string(), string_name); auto sp = query_value(sub_block); return std::wstring_view((WCHAR*)sp.data(), sp.size() / sizeof(WCHAR)); } static auto get_predefined_string_names() { return std::array{ L"Comments", L"FileDescription", L"InternalName", L"ProductName", L"CompanyName", L"LegalCopyright", L"ProductVersion", L"FileDescription", L"LegalTrademarks", L"PrivateBuild", L"FileVersion", L"OriginalFilename", L"SpecialBuild" }; } std::unordered_map<std::wstring_view, std::wstring_view> query_predefined_string_values( translation translation) const { auto names = get_predefined_string_names(); std::unordered_map<std::wstring_view, std::wstring_view> map; for (const auto& name : names) { map.insert_or_assign(name, query_string_value(translation, name)); } return std::move(map); } private: std::vector<BYTE> m_data; }; class winver_util final { public: template <DWORD INIT = 100, DWORD EXPAND = 100, DWORD LIMIT = 500> static std::wstring get_language_name(DWORD lang) { std::vector<WCHAR> buffer; for (DWORD size = INIT; size < LIMIT; size += EXPAND) { buffer.resize(size); auto ret = ::VerLanguageNameW(lang, buffer.data(), buffer.size()); if (ret < size) return std::wstring(buffer.data(), buffer.size()); } return {}; } }; #include <iostream> void output_info(std::wstring_view filename, DWORD flags) { file_version_info fileVerInfo(filename, flags); const auto& root_block = fileVerInfo.query_root_block(); auto translations = fileVerInfo.query_translations(); std::wcout << filename << std::endl << std::endl; for (auto translation : translations) { std::wcout << winver_util::get_language_name(translation) << std::endl; auto string_values = fileVerInfo.query_predefined_string_values(translation); for (const auto& [key, value] : string_values) { std::wcout << key << L": " << value << std::endl; } std::wcout << std::endl; } } int wmain() { // 日本語の出力設定 std::wcout.imbue(std::locale("Japanese", std::locale::ctype)); output_info(L"explorer.exe", 0); output_info(L"explorer.exe", FILE_VER_GET_LOCALISED); return 0; }