potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。

C++20&Win API WICコンポーネントの情報を取得する

Windowsイメージングコンポーネント(WIC、Windows Imaging Component)(Microsoft Docs)のWICコンポーネントの情報を取得するサンプルコードです。基本的な流れは「IWICImagingFactoryの作成→CreateComponentEnumeratorでIEnumUnknownを作成→IEnumUnknownで取得したIUnknownをIWICComponentInfoに変換→IWICComponentInfoから情報を取得」です。

すべてのWICコンポーネントの情報を取得する

#include <format>
#include <string>
#include <iostream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
// https://docs.microsoft.com/en-us/windows/win32/api/wincodec/
#include <wincodec.h>
#pragma comment(lib, "WindowsCodecs.lib")

// 文字列から常にnullでない文字列ポインタを返します。
// 文字列がnullptrの場合は空文字列L""のポインタを返します。
constexpr const wchar_t* strptr_notnull(std::wstring_view s) noexcept
{
    return s.empty() ? L"" : s.data();
}

// GUIDからstd::wstringを返します。
// 変換に失敗した場合、空のstd::wstringを返します。
std::wstring GUIDToWString(REFGUID guid)
{
    std::wstring s(38, L'\0');
    if (StringFromGUID2(guid, s.data(), 39) == 0)
        return {};
    return std::move(s);
}

// IWICComponentInfo::GetFriendlyNameメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetFriendlyName(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetFriendlyName(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetFriendlyName(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// IWICComponentInfo::GetAuthorメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetAuthor(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetAuthor(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetAuthor(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// IWICComponentInfo::GetSpecVersionメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetSpecVersion(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetSpecVersion(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetSpecVersion(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// IWICComponentInfo::GetVersionメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetVersion(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetVersion(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetVersion(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// WICComponentType型の値をstd::wstring_viewで返します。
// 対応する値が存在しない場合はdefaultString引数の値を返します。既定値は空のstd::wstringです。
std::wstring_view ToWStringView(
    WICComponentType value,
    std::wstring_view defaultString = {})
{
    switch (value) {
    case WICDecoder: return L"WICDecoder";
    case WICEncoder: return L"WICEncoder";
    case WICPixelFormatConverter: return L"WICPixelFormatConverter";
    case WICMetadataReader: return L"WICMetadataReader";
    case WICMetadataWriter: return L"WICMetadataWriter";
    case WICPixelFormat: return L"WICPixelFormat";
    case WICAllComponents: return L"WICAllComponents";
    default: return defaultString;
    }
}

// WICComponentSigning型の値をstd::wstring_viewで返します。
// 対応する値が存在しない場合はdefaultString引数の値を返します。既定値は空のstd::wstringです。
std::wstring_view ToWStringView(
    WICComponentSigning value,
    std::wstring_view defaultString = {})
{
    switch (value) {
    case WICComponentSigned: return L"WICComponentSigned";
    case WICComponentUnsigned: return L"WICComponentUnsigned";
    case WICComponentSafe: return L"WICComponentSafe";
    case WICComponentDisabled: return L"WICComponentDisabled";
    default: return defaultString;
    }
}

int wmain()
{
    // 日本語の出力設定(内部変換の設定)
    std::wcout.imbue(std::locale("", std::locale::ctype));

    // COMの初期化
    if (FAILED(::CoInitializeEx(nullptr, COINIT::COINIT_APARTMENTTHREADED)))
        return -1;

    // IWICImagingFactoryの作成
    // https://docs.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicimagingfactory
    IWICImagingFactory* pWicImagingFactory;
    if (SUCCEEDED(::CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&pWicImagingFactory))))
    {
        // WICコンポーネント列挙インターフェイス(IEnumUnknown)の作成
        IEnumUnknown* pEnumUnknown;
        if (SUCCEEDED(pWicImagingFactory->CreateComponentEnumerator(
            WICComponentType::WICAllComponents,
            WICComponentEnumerateOptions::WICComponentEnumerateDefault,
            &pEnumUnknown)))
        {
            std::wcout << L"FriendlyName, Author, Version, SpecVersion, CLSID, VendorGUID, SigningStatus" << std::endl;

            // コンポーネント情報の取得
            // IUnknownで取得後、IWICComponentInfoに変換します。
            IUnknown* punkWicComponentInfo;
            while (pEnumUnknown->Next(1, &punkWicComponentInfo, nullptr) == S_OK)
            {
                // https://docs.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwiccomponentinfo
                IWICComponentInfo* pWicComponentInfo;
                if (SUCCEEDED(punkWicComponentInfo->QueryInterface(IID_PPV_ARGS(&pWicComponentInfo))))
                {
                    // WICコンポーネント情報(IWICComponentInfo)の出力
                    auto friendlyName{ GetFriendlyName(pWicComponentInfo) };
                    auto author{ GetAuthor(pWicComponentInfo) };
                    auto version{ GetVersion(pWicComponentInfo) };
                    auto specVersion{ GetSpecVersion(pWicComponentInfo) };
                    std::wstring clsidStr;
                    std::wstring vendorGuidStr;
                    std::wstring componentTypeStr;
                    std::wstring signingStatusStr;
                    if (CLSID clsid; SUCCEEDED(pWicComponentInfo->GetCLSID(&clsid)))
                        clsidStr = GUIDToWString(clsid);
                    if (CLSID clsid; SUCCEEDED(pWicComponentInfo->GetVendorGUID(&clsid)))
                        vendorGuidStr = GUIDToWString(clsid);
                    if (WICComponentType type; SUCCEEDED(pWicComponentInfo->GetComponentType(&type)))
                        vendorGuidStr = ToWStringView(type);
                    if (DWORD status; SUCCEEDED(pWicComponentInfo->GetSigningStatus(&status)))
                        signingStatusStr = ToWStringView(static_cast<WICComponentSigning>(status));
                    std::wcout
                        << std::format(L"{}, {}, {}, {}, {}, {}, {}",
                            strptr_notnull(friendlyName),
                            strptr_notnull(author),
                            strptr_notnull(version),
                            strptr_notnull(specVersion),
                            strptr_notnull(clsidStr),
                            strptr_notnull(vendorGuidStr),
                            strptr_notnull(signingStatusStr))
                        << std::endl;
                }
            }

            pEnumUnknown->Release();
        }

        pWicImagingFactory->Release();
    }

    // COMの解放
    ::CoUninitialize();

    return 0;
}

デコーダーの対応拡張子を取得する

#include <format>
#include <string>
#include <iostream>

#define STRICT
#define NOMINMAX
#include <Windows.h>
// https://docs.microsoft.com/en-us/windows/win32/api/wincodec/
#include <wincodec.h>
#pragma comment(lib, "WindowsCodecs.lib")

// 文字列から常にnullでない文字列ポインタを返します。
// 文字列がnullptrの場合は空文字列L""のポインタを返します。
constexpr const wchar_t* strptr_notnull(std::wstring_view s) noexcept
{
    return s.empty() ? L"" : s.data();
}

// IWICComponentInfo::GetFriendlyNameメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetFriendlyName(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetFriendlyName(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetFriendlyName(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// IWICBitmapDecoderInfo::GetFileExtensionsメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetFileExtensions(IWICBitmapDecoderInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetFileExtensions(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetFileExtensions(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

// IWICComponentInfo::GetAuthorメソッドの結果をstd::wstringで返します。
// 失敗時は空のstd::wstringを返します。
std::wstring GetAuthor(IWICComponentInfo* info)
{
    UINT actualLen;
    if (SUCCEEDED(info->GetAuthor(0, nullptr, &actualLen)))
    {
        if (actualLen == 0) return L"";
        std::wstring s(actualLen - 1, L'\0');
        if (SUCCEEDED(info->GetAuthor(actualLen, s.data(), &actualLen)))
        {
            return std::move(s);
        }
    }
    return {};
}

int wmain()
{
    // 日本語の出力設定(内部変換の設定)
    std::wcout.imbue(std::locale("", std::locale::ctype));

    // COMの初期化
    if (FAILED(::CoInitializeEx(nullptr, COINIT::COINIT_APARTMENTTHREADED)))
        return -1;

    // IWICImagingFactoryの作成
    // https://docs.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicimagingfactory
    IWICImagingFactory* pWicImagingFactory;
    if (SUCCEEDED(::CoCreateInstance(
        CLSID_WICImagingFactory,
        nullptr,
        CLSCTX_INPROC_SERVER,
        IID_PPV_ARGS(&pWicImagingFactory))))
    {
        // WICコンポーネント列挙インターフェイス(IEnumUnknown)の作成
        // デコーダーのみ列挙
        IEnumUnknown* pEnumUnknown;
        if (SUCCEEDED(pWicImagingFactory->CreateComponentEnumerator(
            WICComponentType::WICDecoder,
            WICComponentEnumerateOptions::WICComponentEnumerateDefault,
            &pEnumUnknown)))
        {
            std::wcout << L"FriendlyName, Author, FileExts" << std::endl;

            // デコーダー情報の取得
            // IUnknownで取得後、IWICBitmapDecoderInfoに変換します。
            IUnknown* punkWicComponentInfo;
            while (pEnumUnknown->Next(1, &punkWicComponentInfo, nullptr) == S_OK)
            {
            https://docs.microsoft.com/en-us/windows/win32/api/wincodec/nn-wincodec-iwicbitmapdecoderinfo
                IWICBitmapDecoderInfo* pWicDecoderInfo;
                if (SUCCEEDED(punkWicComponentInfo->QueryInterface(IID_PPV_ARGS(&pWicDecoderInfo))))
                {
                    // デコーダー情報の出力
                    // このコードではIWICBitmapDecoderInfoの継承するIWICBitmapCodecInfoのメソッドしか使いません。
                    auto friendlyName{ GetFriendlyName(pWicDecoderInfo) };
                    auto author{ GetAuthor(pWicDecoderInfo) };
                    auto fileExts{GetFileExtensions(pWicDecoderInfo)};
                    std::wcout
                        << std::format(L"{}, {}, \"{}\"",
                            strptr_notnull(friendlyName),
                            strptr_notnull(author),
                            strptr_notnull(fileExts))
                        << std::endl;
                }
            }

            pEnumUnknown->Release();
        }

        pWicImagingFactory->Release();
    }

    // COMの解放
    ::CoUninitialize();

    return 0;
}