カタバミさんのプログラミングノート

日曜プログラマーがプログラミング関係のメモを記録するブログです。

C++&STLで特殊フォルダの動作を取得するサンプルコード

Windows環境、C++STLを使用して特殊フォルダの動作一覧を取得するサンプルコードです。サンプルコードではごみ箱の動作を取得します。開発環境はWindows10、Microsoft Visual Studio Community 2019 (Version 16.0.2)、MSVC v142、Windows 10 SDK (10.0.17763.0)です。Windows デスクトップ ウィザードで空のアプリケーション(デスクトップ アプリケーション)を作成して貼り付けることで実行できます。

実行するとverbsに次の文字列が設定されます。 "link"、"properties"、"PinToStartScreen"、"empty"、"open"

#pragma comment(lib, "shlwapi.lib")

#define STRICT
#include <Windows.h>
#include <ShlObj.h>
#include <Shlwapi.h>

#include <vector>
#include <set>
#include <string>

HRESULT GetShellFolderContextMenuVerbs(std::vector<std::wstring>& verbs, int csidl, UINT uQueryContextMenuFlags);
std::vector<WORD> GetMenuAllItemIDs(HMENU hmenu);

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr))
    {
        std::vector<std::wstring> verbs;
        // TODO:CSIDL_*で取得するフォルダを指定します。
        hr = GetShellFolderContextMenuVerbs(
            verbs, CSIDL_BITBUCKET, 0/*CMF_VERBSONLY*/);
        if (SUCCEEDED(hr))
        {
            // TODO:ここで特殊フォルダの動作を処理します。

        }

        CoUninitialize();
    }

    return 0;
}

HRESULT GetShellFolderContextMenuVerbs(std::vector<std::wstring>& verbs, int csidl, UINT uQueryContextMenuFlags)
{
    IShellFolder* pDesktop = nullptr;
    HRESULT hr = SHGetDesktopFolder(&pDesktop);
    if (SUCCEEDED(hr))
    {
        LPITEMIDLIST pidl = nullptr;
        hr = SHGetFolderLocation(nullptr,
            CSIDL_BITBUCKET, nullptr, 0, &pidl);
        if (SUCCEEDED(hr))
        {
            IContextMenu* pContextMenu = nullptr;
            LPCITEMIDLIST ids[] = {pidl};
            hr = pDesktop->GetUIObjectOf(nullptr, 1, ids,
                IID_IContextMenu, nullptr, (void**)&pContextMenu);
            if (SUCCEEDED(hr))
            {
                HMENU hmenu = CreateMenu();
                if (hmenu != nullptr)
                {
                    hr = pContextMenu->QueryContextMenu(hmenu,
                        0, 0, 0, uQueryContextMenuFlags);
                    if (SUCCEEDED(hr))
                    {
                        for (auto id : GetMenuAllItemIDs(hmenu))
                        {
                            WCHAR name[256]; // TODO:文字数の可変長対応
                            hr = pContextMenu->GetCommandString(id,
                                GCS_VERBW, nullptr, (char*)name, 256);
                            if (SUCCEEDED(hr))
                            {
                                verbs.push_back(name);
                            }
                        }
                    }
                    DestroyMenu(hmenu);
                }
                pContextMenu->Release();
            }
            ILFree(pidl);
        }
        pDesktop->Release();
    }
    return hr;
}

void GetMenuAllItemIDsWorker(HMENU hmenu, std::vector<WORD>& ids)
{
    auto c = GetMenuItemCount(hmenu);
    if (c == 0)
    {
        return;
    }

    MENUITEMINFO mii = {sizeof(MENUITEMINFO), MIIM_ID | MIIM_SUBMENU};
    for (auto i = 0; i < c; i++)
    {
        if (GetMenuItemInfo(hmenu, i, TRUE, &mii))
        {
            if (mii.hSubMenu == nullptr)
            {
                ids.push_back(mii.wID);
            }
            else
            {
                GetMenuAllItemIDsWorker(mii.hSubMenu, ids);
            }
        }
    }
}

// メニューに存在するすべてのIDを取得します。
std::vector<WORD> GetMenuAllItemIDs(HMENU hmenu)
{
    std::vector<WORD> ids;
    GetMenuAllItemIDsWorker(hmenu, ids);
    // 重複要素の削除
    std::set<WORD> s(ids.begin(), ids.end());
    return std::vector<WORD>(s.begin(), s.end());
}