レジストリに登録された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>
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);
}
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"));
}
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>
template<typename T>
concept range_value_sized = requires(T) {
std::ranges::size(std::declval<T>());
};
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;
}
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);
}
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);
}
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>
class FileVersionInfo final
{
public:
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());
}
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));
}
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;
};