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

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

C++でごみ箱の最初の項目の名前を取得するサンプルコード

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

サンプルコード

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

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

HRESULT GetShellFolderFromCSIDL(int csidl, const IID& riid, void** ppv);
HRESULT GetShellFolderFirstObject(IShellFolder* pFolder, SHCONTF grfFlags, LPITEMIDLIST* ppidl);

struct ShellFolderDisplayName
{
    LPWSTR Normal;
    LPWSTR InFolder;
    LPWSTR ForEditing;
    LPWSTR ForAddressBar;
    LPWSTR ForParsing;

    ShellFolderDisplayName(IShellFolder* pFolder, LPITEMIDLIST pidl)
    {
        Normal = GetShellFolderDisplayNameOfW(pFolder, pidl, SHGDN_NORMAL);
        InFolder = GetShellFolderDisplayNameOfW(pFolder, pidl, SHGDN_INFOLDER);
        ForEditing = GetShellFolderDisplayNameOfW(pFolder, pidl, SHGDN_FOREDITING);
        ForAddressBar = GetShellFolderDisplayNameOfW(pFolder, pidl, SHGDN_FORADDRESSBAR);
        ForParsing = GetShellFolderDisplayNameOfW(pFolder, pidl, SHGDN_FORPARSING);
    }

    ~ShellFolderDisplayName()
    {
        CoTaskMemFree(Normal);
        CoTaskMemFree(InFolder);
        CoTaskMemFree(ForEditing);
        CoTaskMemFree(ForAddressBar);
        CoTaskMemFree(ForParsing);
    }

private:
    ShellFolderDisplayName() { }

    LPWSTR GetShellFolderDisplayNameOfW(IShellFolder* pFolder, LPITEMIDLIST pidl, SHGDNF uFlags)
    {
        LPWSTR psz = nullptr;

        if (pFolder == nullptr)
        {
            return nullptr;
        }

        STRRET sr;
        HRESULT hr = pFolder->GetDisplayNameOf(pidl, uFlags, &sr);
        if (SUCCEEDED(hr))
        {
            hr = StrRetToStrW(&sr, pidl, &psz);
            if (sr.uType == STRRET_WSTR)
            {
                CoTaskMemFree(sr.pOleStr);
            }
        }

        return psz;
    }
};

int WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
    if (SUCCEEDED(hr))
    {
        IShellFolder* pBitBucket = nullptr;
        hr = GetShellFolderFromCSIDL(CSIDL_BITBUCKET,
            IID_IShellFolder, (void**)&pBitBucket);
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST pidl = nullptr;
            hr = GetShellFolderFirstObject(pBitBucket, SHCONTF_NONFOLDERS, &pidl);
            if (SUCCEEDED(hr))
            {
                ShellFolderDisplayName names(pBitBucket, pidl);

                ILFree(pidl);
            }
            pBitBucket->Release();
        }

        CoUninitialize();
    }

    return 0;
}

// フォルダをCSIDLから取得します。
HRESULT GetShellFolderFromCSIDL(int csidl, const IID& riid, void** ppv)
{
    if (ppv == nullptr)
    {
        return E_INVALIDARG;
    }

    IShellFolder* pDesktop = nullptr;
    HRESULT hr = SHGetDesktopFolder(&pDesktop);
    if (SUCCEEDED(hr))
    {
        LPITEMIDLIST pidl = nullptr;
        hr = SHGetFolderLocation(nullptr, csidl, nullptr, 0, &pidl);
        if (SUCCEEDED(hr))
        {
            hr = pDesktop->BindToObject(pidl, nullptr, riid, ppv);
            ILFree(pidl);
        }
        pDesktop->Release();
    }
    return hr;
}

// フォルダの最初のオブジェクトのITEMIDLISTを取得します。
// 取得したITEMIDLISTはILFree関数等で開放してください。
HRESULT GetShellFolderFirstObject(IShellFolder* pFolder, SHCONTF grfFlags, LPITEMIDLIST* ppidl)
{
    if (pFolder == nullptr || ppidl == nullptr)
    {
        return E_INVALIDARG;
    }

    IEnumIDList* pEnumIDList = nullptr;
    HRESULT hr = pFolder->EnumObjects(nullptr, grfFlags, &pEnumIDList);
    if (SUCCEEDED(hr))
    {
        ULONG celtFetched = 0;
        hr = pEnumIDList->Next(1, ppidl, &celtFetched);
        pEnumIDList->Release();
    }
    return hr;
}

備考

#pragma comment(lib, "library name")

Microsoft Visual Studio独自の表現です。プロジェクトの設定に関わらず静的ライブラリlibrary nameをビルドに追加します。

STRRET型

STRRET型はstructであり、主にIShellFolder::GetDisplayNameOfメソッドで使用されます。内容はuTypeフィールドの値で変わり、STRRET_WSTRの場合はpOleStrフィールドのPWSTR文字列をCoTaskMemFree関数で解放する必要があります。shlwapi.hにいくつかのヘルパー関数が存在しており、StrRetToStrW関数はそのひとつです。

IShellFolder::GetUIObjectOfメソッド

ひとつ以上のLPITEMIDLISTで指定されたオブジェクトのUIオブジェクトのIUnknown派生インターフェイスを取得します。右クリック時のコンテクストメニューや詳細表示のカラム、縮小表示のアイコン等がこのメソッドを介して取得可能です。

IShellFolder::BindToObjectメソッド

LPITEMIDLISTで指定されるオブジェクト(ファイルシステム上のフォルダや特殊なフォルダ)のIShellFolderインターフェイスを取得します。

ごみ箱やマイドキュメントのような特殊フォルダはこのメソッドとSHGetFolderLocation関数を組み合わせることで取得することができます。