potisanのプログラミングメモ

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

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;
    }
}