レジストリに登録されたCLSIDの情報(キー名、概要、InProcServer32のパス)をCSVへ書き出すコードです。コンセプト、std::wstring
、バージョン情報の取得などを含んでいます。
main.cpp
レジストリのHKEY_CLASSES_ROOT\CLSID
キーのサブキーの名前・既定値・InProcServer32
の既定値のベクトルを返します。
#include "stl_util.hpp" #define STRICT #define NOMINMAX #include <Windows.h> #include "FileVersionInfo.hpp" #include "win32_reg_util.hpp" #include "win32_util.hpp" #include <wil/resource.h> // レジストリのHKEY_CLASSES_ROOT\CLSIDキー以下に登録されたCLSID・概要・InProcServer32のパスをすべて取得します。 std::vector<std::tuple<std::wstring, std::wstring, std::wstring>> GetCLSIDDescriptionInProcServers32() { wil::unique_hkey hkeyClsid; THROW_IF_WIN32_ERROR(::RegOpenKeyExW( HKEY_CLASSES_ROOT, L"CLSID", 0, KEY_READ | KEY_WOW64_64KEY, hkeyClsid.put())); auto clsidKeyNames{ GetSubKeyNames(hkeyClsid.get()) }; auto maxClsidEntryNameLen{ max_size(clsidKeyNames) }; std::vector<std::tuple<std::wstring, std::wstring, std::wstring>> info; info.reserve(clsidKeyNames.size()); for (auto& clsidKeyName : clsidKeyNames) { wil::unique_hkey hkeyClsidEntry; if (::RegOpenKeyExW(hkeyClsid.get(), clsidKeyName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, hkeyClsidEntry.put()) != ERROR_SUCCESS) { continue; } wil::unique_hkey hkeyInprocServer32; if (::RegOpenKeyExW(hkeyClsidEntry.get(), L"InprocServer32", 0, KEY_READ | KEY_WOW64_64KEY, hkeyInprocServer32.put()) != ERROR_SUCCESS) { continue; } info.emplace_back(std::make_tuple( std::move(clsidKeyName), RegReadString(hkeyClsidEntry.get(), nullptr), RegReadStringExpanded(hkeyInprocServer32.get(), nullptr))); } return std::move(info); } // ファイルバージョン情報の既定の製品名がある値を一致するか比較します。 // 文字列のcompare結果。 int CompareFileVersionDefaultProductName(std::wstring_view path, std::wstring_view productName) { FileVersionInfo verInfo(path); auto Translations{ verInfo.QueryTranslations() }; if (Translations.empty()) return {}; return productName.compare(verInfo.QueryStringValue(Translations[0], L"CompanyName")); } // ファイルバージョン情報の既定の製品名がMicrosoft Corporationと一致するか判定します。 bool IsFileMicrosoftProduct(std::wstring_view path) { return CompareFileVersionDefaultProductName(path, L"Microsoft Corporation") == 0; } #include <sstream> int main() { std::wstring output; // 見出しの出力 output += L"CLSID,Description,InProcServer32 Path\r\n"; // 行の出力 auto infos{ GetCLSIDDescriptionInProcServers32() }; for (const auto& [clsid, description, inprocServer32] : infos) { auto path{ SearchPath(inprocServer32.c_str()) }; if (!IsFileMicrosoftProduct(path)) continue; output += std::format(L"\"{}\",\"{}\",\"{}\"\r\n", clsid, description, path); } write_utf16(L"test.csv", output); return 0; }
stl_util.hpp
#include <fstream> #include <filesystem> #include <ranges> // レンジ要素にstd::ranges::size関数が適用可能(コンセプト) template<typename T> concept range_value_sized = requires(T) { std::ranges::size(std::declval<T>()); }; // レンジ要素のstd::ranges::sizeの最大値を返します。 template <std::ranges::range T> requires range_value_sized<T> size_t max_size(const T& r) { size_t max{}; for (const auto& s : r) max = std::max(max, std::size(s)); return max; } // UTF-16文字列を指定されたパスへ出力します。BOMは付加しません。 void write_utf16(std::filesystem::path path, std::wstring_view s) { std::ofstream fout(path, std::ios::binary); fout.write(std::bit_cast<const char*>(s.data()), s.size() * sizeof(wchar_t)); fout.flush(); }
win32_reg_util.hpp
#include <vector> #include <string> // レジストリキーのすべてのサブキーの名前を取得します。 // 失敗時は空を返します。 std::vector<std::wstring> GetSubKeyNames(HKEY hkey) { DWORD subKeyCount; DWORD maxSubKeyLen; if (::RegQueryInfoKeyW(hkey, nullptr, nullptr, nullptr, &subKeyCount, &maxSubKeyLen, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) != ERROR_SUCCESS) { return {}; } std::vector<std::wstring> subKeyNames; subKeyNames.reserve(subKeyCount); std::wstring buffer(maxSubKeyLen++, L'\0'); for (DWORD i = 0; i < subKeyCount; i++) { auto nameLen{ maxSubKeyLen }; auto status = ::RegEnumKeyExW(hkey, i, buffer.data(), &nameLen, nullptr, nullptr, nullptr, nullptr); if (status != ERROR_SUCCESS) { return {}; } subKeyNames.emplace_back(buffer.c_str()); } return std::move(subKeyNames); } // 文字列に含まれる環境変数を展開した文字列を返します。 // 失敗時は空文字列を返します。 std::wstring ExpandEnvironmentStrings(std::wstring_view s) { auto required{ ::ExpandEnvironmentStringsW(s.data(), nullptr, 0) }; std::wstring s2(required - 1, L'\0'); if (::ExpandEnvironmentStringsW(s.data(), s2.data(), required) == 0) return {}; return std::move(s2); } // レジストリからREG_SZデータを読み込みます。 // 失敗時は空を返します。 std::wstring RegReadString(HKEY hkey, LPCWSTR valueName) { DWORD type; DWORD size; if (::RegQueryValueExW(hkey, valueName, nullptr, &type, nullptr, &size) != ERROR_SUCCESS) return {}; if (type != REG_SZ) return {}; std::wstring s(size / sizeof(wchar_t) - 1, L'\0'); if (::RegQueryValueExW(hkey, valueName, nullptr, &type, std::bit_cast<LPBYTE>(s.data()), &size) != ERROR_SUCCESS) { return {}; } return std::move(s); } // レジストリからREG_SZ/REG_EXPAND_SZデータを展開して読み込みます。 // 失敗時は空を返します。 std::wstring RegReadStringExpanded(HKEY hkey, LPCWSTR valueName) { DWORD type; DWORD size; if (DWORD err = ::RegQueryValueExW(hkey, valueName, nullptr, &type, nullptr, &size) != ERROR_SUCCESS) return {}; if (type != REG_SZ && type != REG_EXPAND_SZ) return {}; std::wstring s(size / sizeof(wchar_t) - 1, L'\0'); if (DWORD err = ::RegQueryValueExW(hkey, valueName, nullptr, &type, std::bit_cast<LPBYTE>(s.data()), &size) != ERROR_SUCCESS) { return {}; } if (type == REG_SZ) return std::move(s); return ExpandEnvironmentStrings(s); }
win32_util.hpp
#include <string> // パスを検索します。 // 失敗時は空を返します。 std::wstring SearchPath(LPCWSTR path) { auto size{ ::SearchPathW(nullptr, path, nullptr, 0, nullptr, nullptr) }; if (size == 0) return {}; std::wstring s(size - 1, L'\0'); auto copied{ ::SearchPathW(nullptr, path, nullptr, size, s.data(), nullptr) }; if (copied == 0) return {}; if (size == copied + 1) return std::move(s); return s.c_str(); }
FileVersionInfo.hpp
#pragma once #pragma comment(lib, "version.lib") #include <array> #include <bit> #include <span> #include <format> #include <string> #include <vector> #include <unordered_map> // Win32 APIでファイルのバージョン情報を取得するクラス class FileVersionInfo final { public: // 言語IDとコードページ struct Translation final { union { DWORD value; struct { WORD Language; WORD CodePage; }; }; Translation(DWORD x) : value(x) {} [[nodiscard]] inline std::wstring ToSubBlockString() const { return std::format(L"{:04x}{:04x}", Language, CodePage); } constexpr operator DWORD() const noexcept { return value; } }; public: // バージョン情報のバイト数を取得します。 static UINT GetSize(std::wstring_view filename, DWORD flags = 0) noexcept { return ::GetFileVersionInfoSizeExW(flags, filename.data(), nullptr); } // ファイルのバージョン情報を取得します。 FileVersionInfo(std::wstring_view filename, DWORD flags = 0) { auto size{ GetSize(filename, flags) }; m_data.resize(size); if (!::GetFileVersionInfoExW(flags, filename.data(), 0, size, m_data.data())) m_data.clear(); } FileVersionInfo(const FileVersionInfo& source) = delete; constexpr FileVersionInfo(FileVersionInfo&& source) noexcept : m_data(std::move(source.m_data)) { } // データをバイト形式で返します。 constexpr std::span<const BYTE> data() const noexcept { return std::span(m_data.cbegin(), m_data.cend()); } // サブブロックの値をバイトスパンとして取得します。 const std::span<BYTE> QueryValue(std::wstring_view sub_block) const noexcept { if (m_data.empty()) return{}; LPVOID p; UINT size; if (!::VerQueryValueW(m_data.data(), sub_block.data(), &p, &size)) return {}; if (sub_block.compare(L"\\") != 0) size *= sizeof(wchar_t); return std::span<BYTE>(std::bit_cast<LPBYTE>(p), size); } // ルートブロック(固定ファイルバージョン情報)を取得します。 const VS_FIXEDFILEINFO& QueryRootBlock() const { return *std::bit_cast<const VS_FIXEDFILEINFO*>(QueryValue(L"\\").data()); } // 対応する言語IDとコードページを取得します。 const std::span<Translation> QueryTranslations() const { auto sp{ QueryValue(L"\\VarFileInfo\\Translation") }; return std::span<Translation>( std::bit_cast<Translation*>(sp.data()), sp.size() / sizeof(Translation)); } // 言語IDとコードページに対応する文字列値を取得します。 std::wstring_view QueryStringValue(Translation Translation, std::wstring_view string_name) const { auto sub_block{ std::format(L"\\StringFileInfo\\{}\\{}", Translation.ToSubBlockString(), string_name) }; auto sp{ QueryValue(sub_block) }; if (sp.empty()) return {}; return std::wstring_view((WCHAR*)sp.data(), sp.size() / sizeof(WCHAR) - 1); } // 定義済みの名前を取得します。 static auto GetPredefinedStringNames() { 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> QueryPredefinedStringValues( Translation Translation) const { auto names{ GetPredefinedStringNames() }; std::unordered_map<std::wstring_view, std::wstring_view> map; for (const auto& name : names) { map.insert_or_assign(name, QueryStringValue(Translation, name)); } return std::move(map); } private: std::vector<BYTE> m_data; };