SHGetFileInfoで手に入れたHIMAGELISTはImageList_Destroyで開放するべからず

# 注意

筆者は`SHGetFileInfo`関数を使ったことがありません。なのにそれについての記事を書きます、あれ？

# 発端

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">SHGetFileInfo で取得した HIMAGELIST って、解放せんでええのか？</p>&mdash; そらみみ (@soramimi_jp) <a href="https://twitter.com/soramimi_jp/status/981903885043351552?ref_src=twsrc%5Etfw">2018年4月5日</a></blockquote>

たまたまこのツイートが目に入った。

```c:SHGetFileInfo
DWORD_PTR SHGetFileInfo(
  _In_    LPCTSTR    pszPath,
          DWORD      dwFileAttributes,
  _Inout_ SHFILEINFO *psfi,
          UINT       cbFileInfo,
          UINT       uFlags
);
```

https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb762179.aspx

この関数を使って大きいサイズ(48 x 48)のアイコンを取得する方法がある(後述)。
`SHGFI_SYSICONINDEX`フラグを渡して戻り値を`HIMAGELIST`にキャストしてゴニョゴニョすると`HICON`が得られるのですが、この`HIMAGELIST`はどう後始末すればいいのでしょうか？

# 一般的な`HIMAGELIST`の扱い方

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ImageList_Create で確保した HIMAGELIST は ImageList_Destroy で解放するってのはわかるけど、SHGetFileInfo が返した HIMAGELIST は解放していいのかなぁ？</p>&mdash; そらみみ (@soramimi_jp) <a href="https://twitter.com/soramimi_jp/status/981912099935825921?ref_src=twsrc%5Etfw">2018年4月5日</a></blockquote>

[`ImageList_Create`](https://msdn.microsoft.com/en-us/library/windows/desktop/bb761522(v=vs.85).aspx)で確保して[`ImageList_Destroy`](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb761524(v=vs.85).aspx)で開放という流れです。


# `SHGetFileInfo`で手に入れた`HIMAGELIST`は`ImageList_Destroy`で開放するべからず

ところで`SHGetFileInfo`のMSDNをよく見ると

>**Note**
>Once you have a handle to a system image list, you can use the Image List API to manipulate it like any other image list. Because **system image lists are created on a per-process basis, you should treat them as read-only objects. Writing to a system image list may overwrite or delete one of the system images, making it unavailable or incorrect for the remainder of the process.**

とあります。つまり

- プロセスごとにimage listが生成される
- `SHGetFileInfo`はそれへのポインタ
- だからRead-Onlyで扱う
- 変更操作をしたら同じプロセスからの以降の操作はどうなっても知らんよ

ということで、結論としては`ImageList_Destroy`で開放するべからず、となります。

# サンプルコード

動作未検証。

```cpp
#include <windows.h>
#include <Shellapi.h>
#include <Commctrl.h>
enum class IconSize : unsigned int {
    Large = 0,//SHGFI_LARGEICON 
    Small = 1,//SHGFI_SMALLICON
    ExtraLarge = 2,
};
HICON GetIcon(const ITEMIDLIST* pidFIle, IconSize iconSize)
{
    SHFILEINFO sfi;
    HIMAGELIST hImgList = reinterpret_cast<HIMAGELIST>(::SHGetFileInfo(
        reinterpret_cast<LPCSTR>(pidFIle), 0, &sfi, sizeof(SHFILEINFO),
        SHGFI_PIDL | SHGFI_SYSICONINDEX | SHGFI_ICON | (static_cast<unsigned int>(iconSize)　& 1u)
    ));
    return (IconSize::ExtraLarge == iconSizeType) ? 
        ImageList_GetIcon(hImgList, sfi.iIcon, ILD_TRANSPARENT)
        : sfi.iIcon;
}
HICON GetIcon(const char* path, IconSize iconSizeType)
{
    SHFILEINFO sfi;
    HIMAGELIST hImgList = reinterpret_cast<HIMAGELIST>(::SHGetFileInfo(
        path, 0, &sfi, sizeof(SHFILEINFO),
        SHGFI_SYSICONINDEX | SHGFI_ICON | (static_cast<unsigned int>(iconSize) & 1u)
    ));
    return (IconSize::ExtraLarge == iconSizeType) ? 
        ImageList_GetIcon(hImgList, sfi.iIcon, ILD_TRANSPARENT)
        : sfi.iIcon;
}
```

## 参考サイト

- [ファイルアトリビュート|kab-studio](http://www.kab-studio.biz/Programing/Codian/ShellExtension/08.html)
- [Delphi システムイメージリストから任意サイズのアイコンを取得する](http://www.geocities.jp/asumaroyuumaro/program/tips/SHGetImageList.html)
- [プログラミングTips : ファイルの大きなアイコンを取得する](http://www.geocities.jp/iooiau/tips/bigicon.html)
- [SHGetFileInfo function (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb762179.aspx)
- [ImageList_GetIcon function (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb761548(v=vs.85).aspx)
- [ImageList_Destroy function (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb761524(v=vs.85).aspx)
- [SHFILEINFO structure (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb759792.aspx)
- [IMAGELISTDRAWFLAGS (Windows)](https://msdn.microsoft.com/ja-jp/library/windows/desktop/bb775230(v=vs.85).aspx)
- [winapi - Who is responsible for clearing up memory from image lists? - Stack Overflow](https://stackoverflow.com/questions/829075/who-is-responsible-for-clearing-up-memory-from-image-lists)

# License

[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.ja)

<a href="https://commons.wikimedia.org/wiki/File:CC-BY_icon.svg#/media/File:CC-BY_icon.svg"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/1/16/CC-BY_icon.svg/1200px-CC-BY_icon.svg.png" alt="CC-BY icon.svg" width="88px"></a>