potisanのプログラミングメモ

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

C++20&Win API&WIL エクスプローラーのカラムの名前一覧を取得する

C++(Mirosoft Visual Studio 2015)でCOMを利用してエクスプローラーのカラムの名前一覧を取得するサンプルコードです。ラムダ式を適用する前のものとして残しておきます。エラー処理やメモリ管理は省略しています。

/*
 * エクスプローラーのカラムの名前一覧を取得する
 *
 * 参考
 * https://msdn.microsoft.com/ja-jp/library/windows/desktop/ff728864%28v=vs.85%29.aspx
 */

#include <ranges>
#include <iostream>
#include <string>
#include <vector>

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

#include <wil/com.h>

namespace WilUtil
{
    template <typename T, typename err_policy = wil::err_exception_policy>
    wil::com_ptr_t<T, err_policy> SHGetDesktopFolder() noexcept(false)
    {
        wil::com_ptr_t<IShellFolder, err_policy> desktop;
        THROW_IF_FAILED(::SHGetDesktopFolder(desktop.put()));
        return desktop.query<T>();
    }

    template <>
    wil::com_ptr_t<IShellFolder> SHGetDesktopFolder() noexcept(false)
    {
        wil::com_ptr<IShellFolder> desktop;
        THROW_IF_FAILED(::SHGetDesktopFolder(desktop.put()));
        return std::move(desktop);
    }
}

std::vector<std::wstring> GetColumnCanonicalNames(IShellFolder2* pfolder)
{
    if (pfolder == nullptr) return {};

    std::vector<std::wstring> columnCanonicalNames;
    for (UINT i = 0; ; i++)
    {
        SHCOLUMNID colid;
        if (FAILED(pfolder->MapColumnToSCID(i, &colid)))
            break;

        wil::com_ptr<IPropertyDescription2> pPropDesc;
        if (FAILED(::PSGetPropertyDescription(colid, IID_PPV_ARGS(pPropDesc.put()))))
            break;

        wil::unique_cotaskmem_string columnCanonicalName;
        if (FAILED(pPropDesc->GetCanonicalName(&columnCanonicalName)))
            break;

        columnCanonicalNames.push_back(columnCanonicalName.get());
    }

    return std::move(columnCanonicalNames);
}

template <std::ranges::range T>
requires std::ranges::sized_range<T> && std::same_as<std::ranges::range_value_t<T>, std::wstring>
std::wstring join(const T& range, std::wstring_view sep)
{
    size_t buffer_size{ 0 };
    size_t count{ std::ranges::size(range) };
    for (const auto& s : range)
        buffer_size += s.size();
    buffer_size += sep.size() * (count - 1);

    std::wstring buffer;
    buffer.reserve(buffer_size);
    size_t i{ 0 };
    for (const auto& s : range)
    {
        buffer.append(s);
        if (++i != count)
            buffer.append(sep);
    }
    return std::move(buffer);
}

int main()
{
    auto coinit{ wil::CoInitializeEx() };
    auto desktop{ WilUtil::SHGetDesktopFolder<IShellFolder2>() };
    auto columnCanonicalNames{ GetColumnCanonicalNames(desktop.get()) };

    std::wcout.imbue(std::locale("", std::locale::ctype));
    std::wcout << join(columnCanonicalNames, L", ") << std::endl;

    return 0;
}