C++でWindows開発する場合、MicrosoftがGitHubでMITライセンス公開するWIL(Windows Implementation Libraries)を使えます。WILはSTLと類似した設計でムーブ(譲渡)や範囲forに対応しており、put()
、wil::out_param
、wil::out_param_ptr
によりATLのような読みやすいCOMサポートも提供しています。
以下のコードはSTLとWILを組み合わせてデスクトップ上の項目の表示名を列挙します。
高階関数を多用したコード1
// C++20 // STL #include <vector> #include <string> #include <iostream> #include <functional> #include <algorithm> // Win32 API #pragma comment(lib, "shlwapi.lib") #define STRICT #define STRICT_TYPED_ITEMIDS #define NOMINMAX #include <Windows.h> #include <ShlObj.h> #include <shlwapi.h> // WIL #include <wil/com.h> #include <wil/resource.h> namespace wil_util { // 例外ポリシーのSHGetDesktopFolderラッパー関数 inline wil::com_ptr<IShellFolder> SHGetDesktopFolder() { wil::com_ptr<IShellFolder> p; THROW_IF_FAILED(SHGetDesktopFolder(&p)); return std::move(p); } template <typename T> concept com_raw_ptr_callable = requires(T t) { wil::com_raw_ptr(t); }; // IEnumIDListの要素を列挙しながら与えられた式を繰り返し呼び出します。 template <typename ValueType, com_raw_ptr_callable IEnumIDListPtr> std::vector<ValueType> MakeVectorFromIEnumIDListItems( IEnumIDListPtr& obj, std::function<ValueType(PITEMID_CHILD)> fn) { wil::unique_cotaskmem_ptr<ITEMID_CHILD> pidl; auto raw_obj = wil::com_raw_ptr<IEnumIDList>(obj); std::vector<ValueType> result; while (raw_obj->Next(1, wil::out_param_ptr<PITEMID_CHILD*>(pidl), nullptr) == S_OK) { result.push_back(fn(pidl.get())); pidl.reset(); } return std::move(result); } } int main() { // 日本語出力の準備 std::wcout.imbue(std::locale("Japanese", std::locale::ctype)); auto couninit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED); auto desktop = wil_util::SHGetDesktopFolder(); wil::com_ptr<IEnumIDList> enumIdList; THROW_IF_FAILED(desktop->EnumObjects( nullptr, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDESUPERHIDDEN, &enumIdList)); auto displayNames = wil_util::MakeVectorFromIEnumIDListItems<std::wstring>( enumIdList, [&desktop](PITEMID_CHILD pidl) -> std::wstring { STRRET sr; if (SUCCEEDED(desktop->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sr))) { wil::unique_hlocal_string s; if (SUCCEEDED(StrRetToStrW(&sr, pidl, &s))) { return s.get(); } } return {}; }); std::ranges::for_each(displayNames, [](const std::wstring& displayName) { std::wcout << displayName << std::endl; }); return 0; }
高階関数を多用したコード2
// C++20 // STL #include <algorithm> #include <vector> #include <string> #include <iostream> #include <functional> // Win32 API #pragma comment(lib, "shlwapi.lib") #define STRICT #define STRICT_TYPED_ITEMIDS #define NOMINMAX #include <Windows.h> #include <ShlObj.h> #include <shlwapi.h> // WIL #include <wil/com.h> #include <wil/resource.h> namespace wil_util { // 例外ポリシーのSHGetDesktopFolderラッパー関数 inline wil::com_ptr<IShellFolder> SHGetDesktopFolder() { wil::com_ptr<IShellFolder> p; THROW_IF_FAILED(SHGetDesktopFolder(&p)); return std::move(p); } template <typename T> concept com_raw_ptr_callable = requires(T t) { wil::com_raw_ptr(t); }; // IEnumIDListの要素を列挙しながら与えられた式を繰り返し呼び出します。 template <com_raw_ptr_callable IEnumIDListPtr> void ForEachIEnumIDListItems( IEnumIDListPtr& obj, std::function<void(PITEMID_CHILD)> fn) { wil::unique_cotaskmem_ptr<ITEMID_CHILD> pidl; auto raw_obj = wil::com_raw_ptr<IEnumIDList>(obj); while (raw_obj->Next(1, wil::out_param_ptr<PITEMID_CHILD*>(pidl), nullptr) == S_OK) { fn(pidl.get()); pidl.reset(); } } } int main() { // 日本語出力の準備 std::wcout.imbue(std::locale("Japanese", std::locale::ctype)); auto couninit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED); auto desktop = wil_util::SHGetDesktopFolder(); wil::com_ptr<IEnumIDList> enumIdList; THROW_IF_FAILED(desktop->EnumObjects( nullptr, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDESUPERHIDDEN, &enumIdList)); std::vector<std::wstring> displayNames; wil_util::ForEachIEnumIDListItems(enumIdList, [&](PITEMID_CHILD pidl) -> void { STRRET sr; if (SUCCEEDED(desktop->GetDisplayNameOf(pidl, SHGDN_NORMAL, &sr))) { wil::unique_hlocal_string s; if (SUCCEEDED(StrRetToStrW(&sr, pidl, &s))) { displayNames.emplace_back(s.get()); } } }); std::ranges::for_each(displayNames, [](const std::wstring& displayName) { std::wcout << displayName << std::endl; }); return 0; }
素直なコード
// C++20 // STL #include <vector> #include <string> #include <iostream> // Win32 API #pragma comment(lib, "shlwapi.lib") #define STRICT #define STRICT_TYPED_ITEMIDS #define NOMINMAX #include <Windows.h> #include <ShlObj.h> #include <shlwapi.h> // WIL #include <wil/com.h> #include <wil/resource.h> int main() { // 日本語出力の準備 std::wcout.imbue(std::locale("Japanese", std::locale::ctype)); auto couninit = wil::CoInitializeEx(COINIT_APARTMENTTHREADED); wil::com_ptr<IShellFolder> desktop; THROW_IF_FAILED(SHGetDesktopFolder(&desktop)); wil::com_ptr<IEnumIDList> enumIdList; THROW_IF_FAILED(desktop->EnumObjects( nullptr, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDESUPERHIDDEN, &enumIdList)); std::vector<wil::unique_cotaskmem_ptr<ITEMID_CHILD>> pidls; for (decltype(pidls)::value_type pidl; enumIdList->Next(1, wil::out_param(pidl), nullptr) == S_OK;) { pidls.push_back(std::move(pidl)); } std::vector<std::wstring> displayNames; for (const auto& pidl : pidls) { STRRET sr; if (SUCCEEDED(desktop->GetDisplayNameOf(pidl.get(), SHGDN_NORMAL, &sr))) { wil::unique_hlocal_string s; if (SUCCEEDED(StrRetToStrW(&sr, pidl.get(), &s))) { displayNames.emplace_back(s.get()); } } } for (const auto& displayName : displayNames) { std::wcout << displayName << std::endl; } return 0; }