potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++&Win32 API プロパティシステムに登録されたプロパティの正規名と取り得る値を列挙する

Windowsのプロパティシステムに登録されたプロパティの正規名(Canonical Name)と取り得る値(Possible Value)を列挙するサンプルです。PSGetPropertySystemIPropertySystemを取得した後、IPropertyDescriptionListIPropertyDescriptionIPropertyEnumTypeListPROPVARIANTと取得しています。

プロパティの取り得る値にはstd::wcoutが日本語と判断しないユニコード文字(enハイフン\u2013等)が含まれるため、mainの最初でユニコードモードに設定しています(関連記事)。また、RAIIリソースラッパーのためにWILを使用しています。

#include <io.h>
#include <fcntl.h>
#include <iostream>
#include <vector>

#define STRICT
#include <Windows.h>
#pragma comment(lib, "propsys.lib")
#include <PropSys.h>
#include <propvarutil.h>

// Microsoft.Windows.ImplementationLibrary 1.0.210204.1
#include "wil/com.h"
#include "wil/resource.h"

using namespace std;

struct PropDescCanonicanNameAndIPropertyEnumTypeList
{
    wil::unique_cotaskmem_string canonicalName;
    wil::com_ptr<IPropertyEnumTypeList> propEnumTypeList;

    PropDescCanonicanNameAndIPropertyEnumTypeList(
        wil::unique_cotaskmem_string&& canonicalName,
        wil::com_ptr<IPropertyEnumTypeList>&& propEnumTypeList)
        : canonicalName(std::move(canonicalName)), propEnumTypeList(move(propEnumTypeList))
    { }
};

// プロパティシステムに登録されたプロパティの正規名とIPropertyEnumTypeListのリストを取得します。
vector<PropDescCanonicanNameAndIPropertyEnumTypeList> GetPropDescCanonicanNameAndIPropertyEnumTypeLists()
{
    wil::com_ptr<IPropertySystem> propSys;
    THROW_IF_FAILED(::PSGetPropertySystem(IID_PPV_ARGS(propSys.put())));

    wil::com_ptr<IPropertyDescriptionList> propDescList;
    THROW_IF_FAILED(propSys->EnumeratePropertyDescriptions(
        PROPDESC_ENUMFILTER::PDEF_ALL, IID_PPV_ARGS(propDescList.put())));

    UINT propDescCount;
    THROW_IF_FAILED(propDescList->GetCount(&propDescCount));

    vector<PropDescCanonicanNameAndIPropertyEnumTypeList> result;
    result.reserve(propDescCount);

    for (UINT i = 0; i < propDescCount; i++)
    {
        wil::com_ptr<IPropertyDescription> propDesc;
        THROW_IF_FAILED(propDescList->GetAt(i, IID_PPV_ARGS(propDesc.put())));

        wil::unique_cotaskmem_string canonicalName;
        THROW_IF_FAILED(propDesc->GetCanonicalName(canonicalName.put()));

        wil::com_ptr<IPropertyEnumTypeList> propEnumTypeList;
        THROW_IF_FAILED(propDesc->GetEnumTypeList(IID_PPV_ARGS(propEnumTypeList.put())));

        result.emplace_back(std::move(canonicalName), move(propEnumTypeList));
    }

    return move(result);
}

// IPropertyEnumTypeListからIPropertyEnumTypeのRAIIラッパーのベクトルを取得します。
vector<wil::com_ptr<IPropertyEnumType>> GetPropertyEnumTypesFromList(IPropertyEnumTypeList* propEnumTypeList)
{
    THROW_HR_IF_NULL(E_INVALIDARG, propEnumTypeList);

    UINT propTypeCount;
    THROW_IF_FAILED(propEnumTypeList->GetCount(&propTypeCount));

    vector<wil::com_ptr<IPropertyEnumType>> propEnumTypes;
    propEnumTypes.reserve(propTypeCount);

    for (UINT i = 0; i < propTypeCount; i++)
    {
        wil::com_ptr<IPropertyEnumType> propEnumType;
        THROW_IF_FAILED(propEnumTypeList->GetAt(i, IID_PPV_ARGS(propEnumType.put())));
        propEnumTypes.emplace_back(move(propEnumType));
    }

    return move(propEnumTypes);
}

// PROPVARIANTを書式化したWILのunique_cotaskmem_stringを作成します。
wil::unique_cotaskmem_string PropVariantToWILUniqueCoTaskMemString(const PROPVARIANT& value)
{
    wil::unique_cotaskmem_string s;
    if (FAILED(::PropVariantToStringAlloc(value, s.put())))
        return {};
    return std::move(s);
}

int main()
{
    ios_base::sync_with_stdio(false);
    _setmode(_fileno(stdout), _O_U16TEXT);

    auto coinit = wil::CoInitializeEx();

    auto infos = GetPropDescCanonicanNameAndIPropertyEnumTypeLists();
    decltype(infos) infos_noempty;
    decltype(infos) infos_empty;
    for (auto i = infos.begin(), end = infos.end(); i != end; i++)
    {
        UINT propEnumTypeCount{ 0 };
        i->propEnumTypeList->GetCount(&propEnumTypeCount);
        if (propEnumTypeCount != 0)
            infos_noempty.emplace_back(move(*i));
        else
            infos_empty.emplace_back(move(*i));
    }
    infos.clear();

    wcout << L"■IPropertyEnumTypeを持つプロパティ" << endl;
    for (const auto& [canonicalName, propEnumTypeList] : infos_noempty)
    {
        // IPropertyEnumTypeオブジェクトのリストを作成
        auto propEnumTypes = GetPropertyEnumTypesFromList(propEnumTypeList.get());

        wcout << canonicalName.get() << endl;
        for (const auto& propEnumType : propEnumTypes)
        {
            // エラーを無視して取得します。
            wil::unique_cotaskmem_string displayText;
            PROPENUMTYPE enumType = PROPENUMTYPE::PET_DISCRETEVALUE;
            wil::unique_prop_variant value;
            wil::unique_prop_variant rangeMinValue;
            wil::unique_prop_variant rangeSetValue;
            propEnumType->GetDisplayText(displayText.put());
            propEnumType->GetEnumType(&enumType);
            propEnumType->GetValue(value.addressof());
            propEnumType->GetRangeMinValue(rangeMinValue.addressof());
            propEnumType->GetRangeSetValue(rangeSetValue.addressof());

            wcout
                << wil::string_get_not_null(displayText) << L", "
                << enumType << L", "
                << wil::string_get_not_null(PropVariantToWILUniqueCoTaskMemString(value)) << L", "
                << wil::string_get_not_null(PropVariantToWILUniqueCoTaskMemString(rangeMinValue)) << L", "
                << wil::string_get_not_null(PropVariantToWILUniqueCoTaskMemString(rangeSetValue)) << L", "
                << endl;
        }
        wcout << endl;
    }
    wcout << endl;
    
    wcout << L"■IPropertyEnumTypeを持たないプロパティ" << endl;
    for (const auto& [canonicalName, propEnumTypeList] : infos_empty)
    {
        wcout << canonicalName.get() << endl;
    }
}

このサンプルはCUIですが、出力量が多いのでGUIで列挙した方が実用的かもしれません。