potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

C++17&Win32 API STRRET型出力引数をstd::wstring型で受け取るクラス

Win32 APIで使われるSTRRET型の出力引数をstd::wstring型で受け取るクラスのコードです。

// out_STRRET_into_wstring.hpp
#include <string>

// 同じwstringを受け取るインスタンスを同時に使用しないでください。
// 次のようなコードは不適切です。
// 
// std::wstring s1 {...};
// f(out_STRRET_into_wstring(s1), out_STRRET_into_wstring(s1))
//
class out_STRRET_into_wstring
{
private:
    std::wstring& result;
    LPCITEMIDLIST pidl;
    STRRET sr;

    out_STRRET_into_wstring() = delete;
    out_STRRET_into_wstring(out_STRRET_into_wstring&) = delete;
    out_STRRET_into_wstring(out_STRRET_into_wstring&&) = delete;

public:
    out_STRRET_into_wstring(std::wstring& result, LPCITEMIDLIST pidl = nullptr)
        : result{ result }, pidl{ pidl }, sr{} {}
    ~out_STRRET_into_wstring() {
        LPWSTR psz;
        if (SUCCEEDED(StrRetToStrW(&sr, pidl, &psz))) {
            try {
                result.assign(psz);
            }
            catch (...) {
                result.clear();
            }
            CoTaskMemFree(psz);
        }
        else {
            result.clear();
        }
    }
    constexpr operator STRRET* () {
        return &sr;
    }
};

使用例

#include <iostream>
#include <string>

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

#include <wil/com.h>

#include "out_STRRET_into_wstring.hpp"

using namespace std;
using namespace wil;

using unique_itemidlist = unique_any<LPITEMIDLIST, decltype(&CoTaskMemFree), &CoTaskMemFree>;

int main()
{
    auto coinit{ CoInitializeEx(COINIT_APARTMENTTHREADED) };
    if (!coinit)
        return 0;

    com_ptr<IShellFolder> desktop;
    THROW_IF_FAILED(SHGetDesktopFolder(desktop.put()));

    com_ptr<IEnumIDList> enumIdList;
    THROW_IF_FAILED(desktop->EnumObjects(nullptr,
        SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &enumIdList));

    wcout.imbue(locale("japanese", locale::ctype));
    for (;;)
    {
        unique_itemidlist pidl;
        ULONG fetched;
        HRESULT hr = enumIdList->Next(1, pidl.put(), &fetched);
        if (hr != S_OK)
        {
            THROW_IF_FAILED(hr);
            break;
        }

        wstring display_name;
        THROW_IF_FAILED(desktop->GetDisplayNameOf(pidl.get(), SHGDN_NORMAL,
            out_STRRET_into_wstring(display_name, pidl.get())));

        wcout << display_name << endl;
    }
}