アイコンは 3 種類用意しているが、
まだ 1 つしかクラスを作っていなかった。
今日はマウントポイント用のクラスを作ることにする。

クラス名は「MountPointIconID」とする。
基本的な部分は HardLinkIconID と変わらないので、
HardLinkIconID をコピーして作ると速い。
まずは、クラスの定義を作成する。

========== MountPointIconID.hpp ==========

#ifndef mountpointiconid_hpp_included
#define mountpointiconid_hpp_included

#include <shlobj.h>
#include "Object.hpp"

class MountPointIconID : public Object, public IShellIconOverlayIdentifier {

// インタフェース
public:

    // IUnknown の実装

    virtual HRESULT STDMETHODCALLTYPE QueryInterface(
            /* [in] */ REFIID riid,
            /* [out] */ void** ppvObject);

    virtual ULONG STDMETHODCALLTYPE AddRef();

    virtual ULONG STDMETHODCALLTYPE Release();

    // IShellIconOverlayIdentifier

    virtual HRESULT STDMETHODCALLTYPE IsMemberOf(
            /* [in] */ LPCWSTR pwszPath,
            /* [in] */ DWORD dwAttrib);

    virtual HRESULT STDMETHODCALLTYPE GetOverlayInfo(
            /* [out] */ LPWSTR pwszIconFile,
            /* [in] */ int cchMax,
            /* [out] */ int* pIndex,
            /* [out] */ DWORD* pdwFlags);

    virtual HRESULT STDMETHODCALLTYPE GetPriority(
            /* [out] */ int* pPriority);

};

#endif // !mountpointiconid_hpp_included

========== end of MountPointIconID.hpp ==========

多重継承のところで書いたように、
IUnknown のメソッドは全てオーバライドが必要だ。

では、実体の定義をしよう。

========== MountPointIconID.cpp ==========

// ビルド環境用のヘッダ
#include "global.hpp"

// クラスを定義したヘッダ
#include "MountPointIconID.hpp"

// リソース ID のヘッダ
#include "resource.h"

// MountPointIconID の実装

// IUnknown

HRESULT MountPointIconID::QueryInterface(
        /* [in] */ REFIID riid,
        /* [out] */ void** ppvObject) {

    // ppvObject が NULL なら例外
    if (ppvObject == NULL) return E_POINTER;

    // ppvObject は out なので、
    // ポインタが指す元の値は無視できる
    *ppvObject = NULL;

    if (IsEqualIID(riid, IID_IShellIconOverlayIdentifier)) {
        *ppvObject = static_cast<IShellIconOverlayIdentifier*>(this);
    } else if (IsEqualIID(riid, IID_IUnknown)) {
        *ppvObject = static_cast<IShellIconOverlayIdentifier*>(this);
    } else {
        return E_NOINTERFACE;
    }

    // ポインタを作成した場合カウンタを増加
    static_cast<IUnknown*>(*ppvObject)->AddRef();

    return S_OK;
}

ULONG STDMETHODCALLTYPE MountPointIconID::AddRef() {
    return Object::AddRef();
}

ULONG STDMETHODCALLTYPE MountPointIconID::Release() {
    return Object::Release();
}

========== (MountPointIconID.cpp) ==========

対応インタフェースは HardLinkIconID と変わらないので、
IUnknown の実装も全く同じとなる。

========== (MountPointIconID.cpp) ==========

// IShellIconOverlayIdentifier

HRESULT STDMETHODCALLTYPE MountPointIconID::GetOverlayInfo(
        /* [out] */ LPWSTR pwszIconFile,
        /* [in] */ int cchMax,
        /* [out] */ int* pIndex,
        /* [out] */ DWORD* pdwFlags) {

    // 引数のチェック
    if (pwszIconFile == NULL || pIndex == NULL || pdwFlags == NULL) {
        return E_POINTER;
    }
    if (IsBadWritePtr(pwszIconFile, sizeof (WCHAR) * cchMax)) {
        return E_INVALIDARG;
    }
    if (IsBadWritePtr(pIndex, sizeof (int))) return E_INVALIDARG;
    if (IsBadWritePtr(pdwFlags, sizeof (DWORD))) return E_INVALIDARG;

    // 自分自身のファイル名とアイコンインデックスを返す
    if (!GetModuleFileName(g_moduleHandle, pwszIconFile, cchMax)) {
        return HRESULT_FROM_WIN32(GetLastError());
    }
    *pIndex = -IDI_MOUNTPOINT;
    *pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE MountPointIconID::GetPriority(
        /* [out] */ int* pPriority) {

    // 引数のチェック
    if (pPriority == NULL) return E_POINTER;
    if (IsBadWritePtr(pPriority, sizeof (int))) return E_INVALIDARG;

    // 優先順位は最高(デフォルトに任せる)
    *pPriority = 0;

    return S_OK;
}

HRESULT STDMETHODCALLTYPE MountPointIconID::IsMemberOf(
        /* [in] */ LPCWSTR pwszPath,
        /* [in] */ DWORD dwAttrib) {

    // 引数のチェック
    if (pwszPath == NULL) return E_FAIL;
    if (IsBadStringPtrW(pwszPath, MAX_PATH)) return E_FAIL;

    // ファイル情報を得る
    WIN32_FIND_DATA stat;
    HANDLE handle = FindFirstFile(pwszPath, &stat);
    if (handle == INVALID_HANDLE_VALUE) return E_FAIL;
    FindClose(handle);

    // リパースポイントかどうか
    if (!(stat.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT))
            return S_FALSE;

    // dwReserved0 にはリパースタグが入っているので調べる
    if (stat.dwReserved0 != IO_REPARSE_TAG_MOUNT_POINT)
            return S_FALSE;

    return S_OK;
}

========== end of MountPointIconID.cpp ==========

GetOverlayInfo と GetPriority もほとんど変わらないが、
IsMemberOf はマウントポイント固有の実装となる。

マウントポイントはリパースポイントの一部なので、
ファイル属性に FILE_ATTRIBUTE_REPARSE_POINT が含まれ、
ファイルに関連付けられているリパースデータバッファに
IO_REPARSE_TAG_MOUNT_POINT リパースタグが含まれる。

これを調べるためには、GetFileAttribute で属性を調べ、
CreateFile でファイルを開き、DeviceIOControl で
リパースデータバッファを取得するのが手順だが、
ファイル属性とリパースタグを調べるだけなら、
FindFirstFile API を使うと手っ取り早い。

FindFirstFile が返す WIN32_FIND_DATA 構造体には、
ファイル属性とリパースタグの両方が含まれている。
属性は dwFileAttributes に、
リパースタグは dwReserved0 に格納されているため、
これを使うと IsMemberOf を簡単に実装する事ができる。